diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..1770177310d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +[*] +indent_style = tab +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf + +[*.yml] +indent_style = space +indent_size = 2 + +[*.py] +indent_style = space diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a5b1484486b..295cdfce5cb 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -479,8 +479,6 @@ in the SQL/updates folder. * You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any issues, runtimes, or bugs. -* Do not divide when you can easily convert it to multiplication. (ie `4/2` should be done as `4*0.5`) - * If you used regex to replace code during development of your code, post the regex in your PR for the benefit of future developers and downstream users. * All new var/proc names should use the American English spelling of words. This is for consistency with BYOND. diff --git a/.github/workflows/label_merge_conflicts.yml b/.github/workflows/label_merge_conflicts.yml new file mode 100644 index 00000000000..d2a56e2ef3d --- /dev/null +++ b/.github/workflows/label_merge_conflicts.yml @@ -0,0 +1,13 @@ +name: 'Merge Conflict Detection' +on: + push: + branches: + - master +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: mschilde/auto-label-merge-conflicts@master + with: + CONFLICT_LABEL_NAME: 'Merge Conflict' + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 74660294c3b..e76c9f6db1d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ stddef.dm /tools/dmitool/.project # ignore midi2piano build cache -/tools/midi2piano/obj/* - +/tools/midi2piano/midi2piano/obj/* diff --git a/.travis.yml b/.travis.yml index 5a731170a79..2c2b9dec61d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,72 +1,41 @@ -#pretending we're C because otherwise ruby will initialize, even with "language: dm". language: generic -sudo: false +dist: xenial +os: linux git: depth: 1 -cache: - directories: - - $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR} - -addons: - apt: - packages: - - libc6-i386 - - libgcc1:i386 - - libstdc++6:i386 - -env: - global: - - BYOND_MAJOR="513" - - BYOND_MINOR="1505" - - BYOND_MACRO_COUNT=4 - matrix: - - DM_MAPFILE="cyberiad" - - DM_MAPFILE="metastation" - - DM_MAPFILE="delta" - - DM_MAPFILE="test_all_maps" - -stages: - - File Checks - - test - -before_script: - - chmod +x ./install-byond.sh - - ./install-byond.sh -script: - - source $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin/byondsetup - - bash dm.sh -M${DM_MAPFILE} paradise.dme - jobs: include: - # Basic styling/functionality checks - - stage: File Checks - env: + - name: "Run Linters" addons: + apt: + packages: + - python3 + - python3-pip + - python3-setuptools install: - - pip install --user PyYaml -q - - pip install --user beautifulsoup4 -q - before_script: skip + - tools/travis/install_build_deps.sh + - tools/travis/install_dreamchecker.sh script: - shopt -s globstar - - (! grep 'step_[xy]' _maps/map_files/**/*.dmm) - - (num=$(grep -Ern '\\(red|blue|green|black|italic|bold|b|i[^mc])' code/ | wc -l); echo "$num BYOND text macros (expecting ${BYOND_MACRO_COUNT} or fewer)"; [ $num -le ${BYOND_MACRO_COUNT} ]) - - md5sum -c - <<< "6dc1b6bf583f3bd4176b6df494caa5f1 *html/changelogs/example.yml" - - python tools/ss13_genchangelog.py html/changelog.html html/changelogs + - find . -name "*.php" -print0 | xargs -0 -n1 php -l + - find . -name "*.json" -not -path "./nano/node_modules/*" -print0 | xargs -0 python3 ./tools/travis/json_verifier.py + - tools/travis/build_nanoui.sh + - tools/travis/check_grep.sh + - python3 tools/travis/check_line_endings.py + - ~/dreamchecker - # Compile NanoUI to make sure it works - - stage: NanoUI - language: node_js - node_js: - - "9" - env: + - name: "Compile All Maps" addons: - before_install: - - npm install -g gulp-cli - - cd ./nano/ + apt: + packages: + - libstdc++6:i386 + cache: + directories: + - $HOME/BYOND install: - - npm install --loglevel=error - before_script: + - tools/travis/install_byond.sh + - source $HOME/BYOND/byond/bin/byondsetup script: - - node node_modules/gulp/bin/gulp.js --require less-loader + - tools/travis/dm.sh -Mtravis_map_testing paradise.dme diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9099488143b..9ab4061ce6a 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,7 @@ { "recommendations": [ "gbasood.byond-dm-language-support", - "platymuus.dm-langclient" + "platymuus.dm-langclient", + "EditorConfig.EditorConfig" ] } diff --git a/README.md b/README.md index eafdb5ffe91..943397b0fa8 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,16 @@ Make sure to set the port to the one you specified in the config.txt, and set the Security box to 'Trusted'. Then press GO and the server should start up and be ready to join. +### Installation (Linux) + +The code is able to run on Linux server side, however the libraries for MySQL and logging do require extra packages. + +For MySQL, run the following: `apt-get install libmysqlclient-dev:i386` + +For RustG, run the following: `apt-get install libssl-dev:i386 pkg-config:i386 zlib1g-dev:i386` + +After installing these packages, these libraries should function as intended. + --- ### UPDATING diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 837f8f440d6..a447c7993ba 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -594,4 +594,18 @@ CREATE TABLE `connection_log` ( `ip` varchar(32) NOT NULL, `computerid` varchar(32) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +-- Table structure for table `changelog` +-- +DROP TABLE IF EXISTS `changelog`; +CREATE TABLE `changelog` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `pr_number` INT(11) NOT NULL, + `date_merged` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `author` VARCHAR(32) NOT NULL, + `cl_type` ENUM('FIX','WIP','TWEAK','SOUNDADD','SOUNDDEL','CODEADD','CODEDEL','IMAGEADD','IMAGEDEL','SPELLCHECK','EXPERIMENT') NOT NULL, + `cl_entry` TEXT NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql index b43141f6784..6096f0547e6 100644 --- a/SQL/paradise_schema_prefixed.sql +++ b/SQL/paradise_schema_prefixed.sql @@ -591,4 +591,18 @@ CREATE TABLE `SS13_connection_log` ( `ip` varchar(32) NOT NULL, `computerid` varchar(32) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +-- Table structure for table `SS13_changelog` +-- +DROP TABLE IF EXISTS `SS13_changelog`; +CREATE TABLE `SS13_changelog` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `pr_number` INT(11) NOT NULL, + `date_merged` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `author` VARCHAR(32) NOT NULL, + `cl_type` ENUM('FIX','WIP','TWEAK','SOUNDADD','SOUNDDEL','CODEADD','CODEDEL','IMAGEADD','IMAGEDEL','SPELLCHECK','EXPERIMENT') NOT NULL, + `cl_entry` TEXT NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/SQL/updates/10-11.sql b/SQL/updates/10-11.sql index 9564d3efaad..b2718db85e6 100644 --- a/SQL/updates/10-11.sql +++ b/SQL/updates/10-11.sql @@ -38,4 +38,4 @@ ALTER TABLE feedback.population CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4 ALTER TABLE feedback.privacy CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE feedback.vpn_whitelist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE feedback.watch CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE feedback.whitelist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; \ No newline at end of file +ALTER TABLE feedback.whitelist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/SQL/updates/11-12.sql b/SQL/updates/11-12.sql new file mode 100644 index 00000000000..f6e9ac01baa --- /dev/null +++ b/SQL/updates/11-12.sql @@ -0,0 +1,13 @@ +#Updating the SQL from version 11 to version 12. -AffectedArc07 +#Creating a table for the new changelog system + +DROP TABLE IF EXISTS `changelog`; +CREATE TABLE IF NOT EXISTS `changelog` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `pr_number` INT(11) NOT NULL, + `date_merged` TIMESTAMP NOT NULL DEFAULT current_timestamp(), + `author` VARCHAR(32) NOT NULL, + `cl_type` ENUM('FIX','WIP','TWEAK','SOUNDADD','SOUNDDEL','CODEADD','CODEDEL','IMAGEADD','IMAGEDEL','SPELLCHECK','EXPERIMENT') NOT NULL, + `cl_entry` TEXT NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/_build_dependencies.sh b/_build_dependencies.sh new file mode 100644 index 00000000000..24a33ed3644 --- /dev/null +++ b/_build_dependencies.sh @@ -0,0 +1,11 @@ +# This file has all the information on what versions of libraries are thrown into the code +# For dreamchecker +export SPACEMANDMM_TAG=suite-1.2 +# For NanoUI +export NODE_VERSION=9 +# For the scripts in tools +export PHP_VERSION=5.6 +# Byond Major +export BYOND_MAJOR=513 +# Byond Minor +export BYOND_MINOR=1505 diff --git a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm index 864981494ac..5a880549b8a 100644 --- a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm +++ b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm @@ -2017,7 +2017,7 @@ /obj/machinery/vending/sustenance{ desc = "A vending machine normally reserved for work camps."; name = "\improper sustenance vendor"; - product_slogans = "Enjoy your meal.;Enough calories to support any worker." + slogan_list = list("Enjoy your meal.","Enough calories to support any worker.") }, /turf/simulated/floor/plasteel{ icon_state = "floorgrime" diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm index 279afa9713d..07f00c599c3 100644 --- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm +++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm @@ -1241,21 +1241,6 @@ /obj/structure/sign/securearea, /turf/simulated/wall/mineral/plastitanium/explosive, /area/ruin/unpowered/syndicate_lava_base/main) -"cf" = ( -/obj/structure/table/reinforced, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/item/radio/intercom/syndicate, -/turf/simulated/floor/plasteel/dark, -/area/ruin/unpowered/syndicate_lava_base/telecomms) "cg" = ( /obj/machinery/portable_atmospherics/canister/nitrogen, /turf/simulated/floor/engine{ @@ -1789,7 +1774,6 @@ /area/ruin/unpowered/syndicate_lava_base/cargo) "ed" = ( /obj/structure/table, -/obj/item/paper_bin, /obj/item/pen, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -1803,6 +1787,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/item/paper_bin/syndicate, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/cargo) "ef" = ( @@ -1898,7 +1883,6 @@ "ep" = ( /obj/structure/table/reinforced, /obj/effect/decal/cleanable/dirt, -/obj/item/paper_bin, /obj/item/pen, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -1912,6 +1896,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/item/paper_bin/syndicate, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/testlab) "eq" = ( @@ -3529,10 +3514,6 @@ /obj/machinery/alarm/syndicate{ pixel_y = 24 }, -/obj/item/radio/headset/syndicate/alt{ - pixel_x = -3; - pixel_y = 3 - }, /turf/simulated/floor/plasteel/grimy, /area/ruin/unpowered/syndicate_lava_base/dormitories) "hN" = ( @@ -3558,10 +3539,6 @@ /obj/machinery/alarm/syndicate{ pixel_y = 24 }, -/obj/item/radio/headset/syndicate/alt{ - pixel_x = -3; - pixel_y = 3 - }, /turf/simulated/floor/plasteel/grimy, /area/ruin/unpowered/syndicate_lava_base/dormitories) "hR" = ( @@ -4479,10 +4456,6 @@ /obj/structure/table/wood, /obj/item/ammo_box/magazine/m10mm, /obj/item/ammo_box/magazine/sniper_rounds, -/obj/item/radio/headset/syndicate/alt{ - pixel_x = -3; - pixel_y = 3 - }, /turf/simulated/floor/plasteel/grimy, /area/ruin/unpowered/syndicate_lava_base/dormitories) "jB" = ( @@ -6785,13 +6758,12 @@ /obj/machinery/computer/message_monitor{ dir = 1 }, -/obj/item/paper/monitorkey, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/telecomms) "ov" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin, /obj/item/pen, +/obj/item/paper_bin/syndicate, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/telecomms) "ox" = ( @@ -7400,7 +7372,7 @@ mp mQ nl nJ -cf +nJ ov mn ab diff --git a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm index 62a22425926..43aa3cd98c7 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm @@ -832,6 +832,10 @@ /obj/item/shard, /turf/space, /area/space/nearstation) +"Hv" = ( +/obj/item/gps/ruin, +/turf/simulated/wall/r_wall, +/area/ruin/unpowered) (1,1,1) = {" aa @@ -973,7 +977,7 @@ aa at at aF -at +Hv aU ay ay diff --git a/_maps/map_files/RandomRuins/SpaceRuins/gasthelizards.dmm b/_maps/map_files/RandomRuins/SpaceRuins/gasthelizards.dmm index d869d21f539..0f7c36fe634 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/gasthelizards.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/gasthelizards.dmm @@ -99,14 +99,14 @@ "m" = ( /obj/structure/closet/crate/freezer, /obj/item/stack/sheet/animalhide/lizard, -/obj/item/reagent_containers/food/snacks/lizardmeat, +/obj/item/reagent_containers/food/snacks/monstermeat/lizardmeat, /turf/simulated/floor/plasteel{ icon_state = "dark" }, /area/ruin/unpowered) "n" = ( /obj/structure/closet/crate/freezer, -/obj/item/reagent_containers/food/snacks/lizardmeat, +/obj/item/reagent_containers/food/snacks/monstermeat/lizardmeat, /obj/machinery/light{ dir = 1 }, @@ -116,7 +116,7 @@ /area/ruin/unpowered) "o" = ( /obj/structure/closet/crate/freezer, -/obj/item/reagent_containers/food/snacks/lizardmeat, +/obj/item/reagent_containers/food/snacks/monstermeat/lizardmeat, /turf/simulated/floor/plasteel{ icon_state = "dark" }, diff --git a/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm index 22d4c0798aa..17d49d666bc 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm @@ -269,6 +269,10 @@ icon_state = "freezerfloor" }, /area/ruin/powered) +"Y" = ( +/obj/item/gps/ruin, +/turf/simulated/wall/r_wall, +/area/ruin/powered) (1,1,1) = {" a @@ -960,7 +964,7 @@ b f f p -f +Y f f j diff --git a/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm b/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm index 2a5c0eae533..b4e36942d6a 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm @@ -240,6 +240,13 @@ /obj/structure/shuttle/engine/propulsion, /turf/simulated/shuttle/plating, /area/ruin/powered) +"U" = ( +/obj/item/gps/ruin, +/turf/simulated/shuttle/wall{ + tag = "icon-swall12"; + icon_state = "swall12" + }, +/area/ruin/powered) (1,1,1) = {" a @@ -313,7 +320,7 @@ a c g l -q +U r u t diff --git a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm index fef87df8950..ebfaf3b94b4 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm @@ -209,7 +209,6 @@ "aI" = ( /obj/structure/table/wood, /obj/item/toy/character/wizard, -/obj/item/paper/spells, /turf/simulated/floor/carpet, /area/ruin/unpowered) "aJ" = ( diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index d046e4fb402..7cf27593f6f 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -1619,15 +1619,12 @@ /area/security/range) "adH" = ( /obj/structure/table/wood, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen/multi, /obj/machinery/light{ dir = 1; on = 1 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -16077,16 +16074,13 @@ /area/lawoffice) "aAI" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen, /obj/machinery/alarm{ dir = 8; pixel_x = 25; pixel_y = 0 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ tag = "icon-cult"; icon_state = "cult"; @@ -16863,11 +16857,8 @@ /area/magistrateoffice) "aCj" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/stamp/magistrate, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/carpet, /area/magistrateoffice) "aCk" = ( @@ -21875,11 +21866,8 @@ /area/maintenance/fpmaint2) "aMO" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ tag = "icon-cult"; icon_state = "cult"; @@ -27795,16 +27783,13 @@ /obj/item/folder/yellow, /obj/item/stamp/ce, /obj/item/book/manual/sop_engineering, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/storage/fancy/cigarettes, /obj/item/lighter/zippo, /obj/item/pen/multi, /obj/item/enginepicker{ layer = 3.1 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutralfull" @@ -27893,7 +27878,6 @@ /area/chapel/main) "aYA" = ( /obj/structure/table, -/obj/item/lighter/zippo/black, /turf/simulated/floor/plasteel{ icon_state = "vault"; dir = 5 @@ -29916,6 +29900,7 @@ network = list("SS13") }, /obj/structure/table/wood, +/obj/item/lighter/zippo/black, /turf/simulated/floor/carpet/black, /area/chapel/office) "bcx" = ( @@ -43355,12 +43340,9 @@ }, /area/hallway/primary/central/west) "bCG" = ( -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen, /obj/structure/table/wood, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/carpet, /area/bridge/meeting_room) "bCH" = ( @@ -47113,10 +47095,6 @@ /area/shuttle/constructionsite) "bJN" = ( /obj/structure/table/wood, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 10 - }, /obj/item/pen/multi/fountain, /obj/machinery/door_control{ id = "captainofficedoor"; @@ -47129,6 +47107,7 @@ /obj/item/radio/intercom{ pixel_x = -28 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/wood, /area/crew_quarters/captain) "bJO" = ( @@ -50576,15 +50555,12 @@ }, /area/toxins/lab) "bPE" = ( -/obj/item/paper_bin{ - pixel_x = 1; - pixel_y = 9 - }, /obj/structure/table/glass, /obj/item/pen/multi, /obj/item/book/manual/sop_science{ pixel_y = -14 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ dir = 2; icon_state = "cafeteria"; @@ -54151,10 +54127,6 @@ /area/hallway/primary/central/se) "bVy" = ( /obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen/multi, /obj/item/pen/multi, /obj/item/megaphone, @@ -55112,6 +55084,7 @@ /obj/item/book/manual/sop_service, /obj/item/book/manual/sop_supply, /obj/item/book/manual/sop_command, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel, /area/crew_quarters/heads) "bXa" = ( @@ -60880,7 +60853,6 @@ /area/medical/cmo) "cfW" = ( /obj/structure/table/glass, -/obj/item/paper_bin, /obj/item/pen/multi, /obj/item/folder/white{ pixel_y = 7 @@ -60890,6 +60862,7 @@ pixel_x = 32; pixel_y = 0 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "darkblue" @@ -67468,12 +67441,12 @@ /area/blueshield) "cqS" = ( /obj/structure/table/wood, -/obj/item/paper_bin, /obj/item/flashlight/lamp/green{ pixel_x = -5; pixel_y = 12 }, /obj/item/paper/ntrep, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/carpet, /area/ntrep) "cqT" = ( diff --git a/_maps/map_files/cyberiad/z2.dmm b/_maps/map_files/cyberiad/z2.dmm index 684365ada39..a4baee09320 100644 --- a/_maps/map_files/cyberiad/z2.dmm +++ b/_maps/map_files/cyberiad/z2.dmm @@ -2060,7 +2060,16 @@ icon_opened = "cabinet_open"; icon_state = "cabinet_closed" }, +/obj/item/clothing/under/color/lightpurple, +/obj/item/clothing/under/color/purple, /obj/item/storage/backpack/satchel, +/obj/item/storage/backpack/satchel, +/obj/item/clothing/head/wizard/red, +/obj/item/clothing/suit/wizrobe/red, +/obj/item/clothing/head/wizard, +/obj/item/clothing/suit/wizrobe, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, /turf/unsimulated/floor{ dir = 9; icon_state = "carpetside" @@ -2148,8 +2157,6 @@ /area/wizard_station) "fC" = ( /obj/structure/table/wood, -/obj/item/trash/tray, -/obj/item/paper/spells, /turf/unsimulated/floor{ dir = 10; icon_state = "carpetside" @@ -3248,10 +3255,10 @@ /area/shuttle/assault_pod) "iw" = ( /obj/structure/rack, +/obj/item/clothing/under/plasmaman/wizard, +/obj/item/clothing/head/helmet/space/plasmaman/wizard, /obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath/vox, -/obj/item/tank/plasma/plasmaman, -/obj/item/tank/emergency_oxygen/vox, +/obj/item/tank/plasma/plasmaman/belt/full, /turf/unsimulated/floor{ icon_state = "grimy" }, @@ -3895,10 +3902,8 @@ /area/centcom/gamma) "ke" = ( /obj/structure/rack, -/obj/item/clothing/suit/wizrobe/red, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/head/wizard/red, -/obj/item/twohanded/staff, +/obj/item/tank/emergency_oxygen/vox, +/obj/item/clothing/mask/breath/vox, /turf/unsimulated/floor{ icon_state = "grimy" }, @@ -5006,6 +5011,20 @@ name = "grass" }, /area/centcom/control) +"nc" = ( +/obj/machinery/door/airlock/centcom{ + name = "Gamma Armory"; + opacity = 1; + req_access_txt = "114" + }, +/obj/machinery/door/poddoor/impassable{ + id_tag = "CCGAMMA"; + name = "Gamma Security" + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/centcom/gamma) "nd" = ( /obj/structure/flora/ausbushes/lavendergrass, /turf/unsimulated/floor{ @@ -5381,7 +5400,7 @@ }, /area/syndicate_mothership) "nQ" = ( -/obj/machinery/door/poddoor{ +/obj/machinery/door/poddoor/impassable{ id_tag = "GRAVPULTS"; name = "Gravity Catapults" }, @@ -5391,7 +5410,7 @@ }, /area/centcom/specops) "nR" = ( -/obj/machinery/door/poddoor{ +/obj/machinery/door/poddoor/impassable{ id_tag = "GRAVPULTS"; name = "Gravity Catapults" }, @@ -5408,7 +5427,7 @@ }, /area/syndicate_mothership) "nT" = ( -/obj/machinery/door/poddoor{ +/obj/machinery/door/poddoor/impassable{ id_tag = "ASSAULT"; name = "Assault Armor" }, @@ -5854,7 +5873,7 @@ }, /area/centcom/gamma) "oR" = ( -/obj/machinery/door/poddoor{ +/obj/machinery/door/poddoor/impassable{ id_tag = "SPECOPS"; name = "Ready Room" }, @@ -5979,7 +5998,7 @@ opacity = 1; req_access_txt = "114" }, -/obj/machinery/door/poddoor{ +/obj/machinery/door/poddoor/impassable{ id_tag = "specopsoffice"; name = "Super Privacy Shutters" }, @@ -6118,7 +6137,7 @@ /turf/simulated/floor/mech_bay_recharge_floor, /area/shuttle/gamma/space) "pv" = ( -/obj/machinery/door/poddoor{ +/obj/machinery/door/poddoor/impassable{ id_tag = "CCTELE"; name = "Specops Teleporter" }, @@ -6365,7 +6384,7 @@ /area/centcom/specops) "pX" = ( /obj/machinery/door_control{ - desc = "A remote control switch to block view of the singularity."; + desc = "A remote control switch to connect the ready room to the rest of Centcom."; icon_state = "doorctrl0"; id = "SPECOPS"; name = "Ready Room"; @@ -29642,7 +29661,7 @@ fm fm md md -oO +nc fm pv sX diff --git a/_maps/map_files/cyberiad/z6.dmm b/_maps/map_files/cyberiad/z6.dmm index b2171008b2b..6cbb87b6f80 100644 --- a/_maps/map_files/cyberiad/z6.dmm +++ b/_maps/map_files/cyberiad/z6.dmm @@ -761,7 +761,7 @@ /area/derelict/bridge) "bK" = ( /obj/machinery/vending/cigarette/free{ - product_slogans = "Just remember! No capitalist.;Best enjoyed with Vodka!.;Smoke!;Nine out of ten USSP scientists agree, smoking reduces stress!;There's no cigarette like a Russian cigarette!;Cigarettes! Now with 100% less capitalism." + slogan_list = list("Just remember! No capitalist.","Best enjoyed with Vodka!.","Smoke!","Nine out of ten USSP scientists agree, smoking reduces stress!","There's no cigarette like a Russian cigarette!","Cigarettes! Now with 100% less capitalism.") }, /turf/simulated/floor/plasteel{ dir = 9; @@ -1441,7 +1441,7 @@ pixel_y = 32 }, /obj/machinery/vending/cigarette/free{ - product_slogans = "Just remember! No capitalist.;Best enjoyed with Vodka!.;Smoke!;Nine out of ten USSP scientists agree, smoking reduces stress!;There's no cigarette like a Russian cigarette!;Cigarettes! Now with 100% less capitalism." + slogan_list = list("Just remember! No capitalist.","Best enjoyed with Vodka!.","Smoke!","Nine out of ten USSP scientists agree, smoking reduces stress!","There's no cigarette like a Russian cigarette!","Cigarettes! Now with 100% less capitalism.") }, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ @@ -4163,7 +4163,7 @@ name = "suspicious button" }, /obj/machinery/vending/cigarette/free{ - product_slogans = "Just remember! No capitalist.;Best enjoyed with Vodka!.;Smoke!;Nine out of ten USSP scientists agree, smoking reduces stress!;There's no cigarette like a Russian cigarette!;Cigarettes! Now with 100% less capitalism." + slogan_list = list("Just remember! No capitalist.","Best enjoyed with Vodka!.","Smoke!","Nine out of ten USSP scientists agree, smoking reduces stress!","There's no cigarette like a Russian cigarette!","Cigarettes! Now with 100% less capitalism.") }, /turf/simulated/floor/wood{ broken = 1; @@ -5186,7 +5186,7 @@ "kW" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/vending/cigarette/free{ - product_slogans = "Just remember! No capitalist.;Best enjoyed with Vodka!.;Smoke!;Nine out of ten USSP scientists agree, smoking reduces stress!;There's no cigarette like a Russian cigarette!;Cigarettes! Now with 100% less capitalism." + slogan_list = list("Just remember! No capitalist.","Best enjoyed with Vodka!.","Smoke!","Nine out of ten USSP scientists agree, smoking reduces stress!","There's no cigarette like a Russian cigarette!","Cigarettes! Now with 100% less capitalism.") }, /turf/simulated/floor/plasteel{ dir = 5; @@ -5598,7 +5598,7 @@ }, /area/syndicate_depot/outer) "lZ" = ( -/obj/item/reagent_containers/food/snacks/spiderleg, +/obj/item/reagent_containers/food/snacks/monstermeat/spiderleg, /obj/structure/spider, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -6363,7 +6363,7 @@ icon_state = "sink"; pixel_x = 12 }, -/turf/simulated/floor/mineral/plasma, +/turf/simulated/floor/mineral/silver, /area/syndicate_depot/core) "of" = ( /obj/structure/closet/secure_closet/syndicate/depot, @@ -6448,26 +6448,9 @@ icon_state = "wood" }, /area/derelict/crew_quarters) -"oo" = ( -/obj/effect/spawner/random_spawners/syndicate/layout/door/secret, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/syndicate_depot/core) "op" = ( /turf/simulated/floor/mineral/silver, /area/syndicate_depot/core) -"oq" = ( -/obj/machinery/door/airlock/plasma{ - welded = 1 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/syndicate_depot/core) -"or" = ( -/turf/simulated/floor/mineral/plasma, -/area/syndicate_depot/core) "os" = ( /obj/structure/table, /obj/effect/spawner/random_spawners/syndicate/loot/stetchkin, @@ -6543,7 +6526,7 @@ /area/syndicate_depot/core) "oB" = ( /obj/effect/spawner/random_spawners/syndicate/mob, -/turf/simulated/floor/mineral/plasma, +/turf/simulated/floor/mineral/silver, /area/syndicate_depot/core) "oC" = ( /obj/effect/landmark{ @@ -6706,27 +6689,14 @@ icon_state = "dark" }, /area/syndicate_depot/core) -"oZ" = ( -/obj/structure/mirror{ - pixel_x = -32 - }, -/turf/simulated/floor/mineral/plasma, -/area/syndicate_depot/core) "pa" = ( /obj/structure/toilet{ dir = 8 }, /turf/simulated/floor/mineral/silver, /area/syndicate_depot/core) -"pb" = ( -/obj/machinery/portable_atmospherics/canister/toxins, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/syndicate_depot/core) "pc" = ( /obj/machinery/light, -/obj/effect/spawner/random_spawners/syndicate/loot/level3, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -9114,7 +9084,7 @@ }) "tY" = ( /obj/machinery/vending/cigarette/free{ - product_slogans = "Just remember! No capitalist.;Best enjoyed with Vodka!.;Smoke!;Nine out of ten USSP scientists agree, smoking reduces stress!;There's no cigarette like a Russian cigarette!;Cigarettes! Now with 100% less capitalism." + slogan_list = list("Just remember! No capitalist.","Best enjoyed with Vodka!.","Smoke!","Nine out of ten USSP scientists agree, smoking reduces stress!","There's no cigarette like a Russian cigarette!","Cigarettes! Now with 100% less capitalism.") }, /turf/simulated/floor/plasteel{ icon_state = "bar" @@ -10678,7 +10648,7 @@ /area/derelict/arrival) "xe" = ( /obj/machinery/vending/cigarette/free{ - product_slogans = "Just remember! No capitalist.;Best enjoyed with Vodka!.;Smoke!;Nine out of ten USSP scientists agree, smoking reduces stress!;There's no cigarette like a Russian cigarette!;Cigarettes! Now with 100% less capitalism." + slogan_list = list("Just remember! No capitalist.","Best enjoyed with Vodka!.","Smoke!","Nine out of ten USSP scientists agree, smoking reduces stress!","There's no cigarette like a Russian cigarette!","Cigarettes! Now with 100% less capitalism.") }, /turf/simulated/floor/plasteel{ dir = 2; @@ -11582,10 +11552,10 @@ }, /area/space/nearstation) "zR" = ( -/obj/machinery/portable_atmospherics/canister/toxins, /obj/machinery/light/small{ dir = 1 }, +/obj/effect/decal/cleanable/blood, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -57707,7 +57677,7 @@ of ot mA mA -pb +nX pi mA py @@ -59251,7 +59221,7 @@ oE oR oE oz -pb +mA mA mA mm @@ -61558,7 +61528,7 @@ mm mB mm mm -oo +mm mm lV mA @@ -61816,7 +61786,7 @@ mA zP mm zR -oo +mm oL ns mA @@ -62072,7 +62042,7 @@ mA mA mt mm -oq +mm mm mm mm @@ -62332,7 +62302,7 @@ mm op oB oM -oZ +oM ob mA pu @@ -62586,7 +62556,7 @@ zP mA mA ob -or +op nJ oe pa diff --git a/_maps/test_all_maps.dm b/_maps/test_all_maps.dm deleted file mode 100644 index 94bd9bac437..00000000000 --- a/_maps/test_all_maps.dm +++ /dev/null @@ -1,145 +0,0 @@ -// This is for Travis testing. DO NOT SET THIS AS THE GAME'S MAP NORMALLY! - -#if !defined(USING_MAP_DATUM) - // Away missions - #include "map_files\RandomZLevels\academy.dmm" - #include "map_files\RandomZLevels\beach.dmm" - #include "map_files\RandomZLevels\blackmarketpackers.dmm" - #include "map_files\RandomZLevels\centcomAway.dmm" - #include "map_files\RandomZLevels\evil_santa.dmm" - #include "map_files\RandomZLevels\example.dmm" - #include "map_files\RandomZLevels\moonoutpost19.dmm" - #include "map_files\RandomZLevels\spacebattle.dmm" - #include "map_files\RandomZLevels\spacehotel.dmm" - #include "map_files\RandomZLevels\stationCollision.dmm" - #include "map_files\RandomZLevels\terrorspiders.dmm" - #include "map_files\RandomZLevels\undergroundoutpost45.dmm" - #include "map_files\RandomZLevels\wildwest.dmm" - - - // Lavaland Ruins - #include "map_files\RandomRuins\LavaRuins\lavaland_biodome_beach.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_biodome_clown_planet.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_biodome_winter.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_animal_hospital.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_ash_walker1.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_blooddrunk1.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_blooddrunk2.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_blooddrunk3.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_cube.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_cultaltar.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_dead_ratvar.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_envy.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_fountain_hall.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_gluttony.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_greed.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_hermit.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_hierophant.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_pizzaparty.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_pride.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_puzzle.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_random_ripley.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_seed_vault.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_sloth.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_survivalpod.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_swarmer_crash.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_syndicate_base1.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_ufo_crash.dmm" - #include "map_files\RandomRuins\LavaRuins\lavaland_surface_xeno_nest.dmm" - - - // Space Ruins - #include "map_files\RandomRuins\SpaceRuins\abandonedzoo.dmm" - #include "map_files\RandomRuins\SpaceRuins\asteroid1.dmm" - #include "map_files\RandomRuins\SpaceRuins\asteroid2.dmm" - #include "map_files\RandomRuins\SpaceRuins\asteroid3.dmm" - #include "map_files\RandomRuins\SpaceRuins\asteroid4.dmm" - #include "map_files\RandomRuins\SpaceRuins\asteroid5.dmm" - #include "map_files\RandomRuins\SpaceRuins\deepstorage.dmm" - #include "map_files\RandomRuins\SpaceRuins\derelict1.dmm" - #include "map_files\RandomRuins\SpaceRuins\derelict2.dmm" - #include "map_files\RandomRuins\SpaceRuins\derelict3.dmm" - #include "map_files\RandomRuins\SpaceRuins\derelict4.dmm" - #include "map_files\RandomRuins\SpaceRuins\derelict5.dmm" - #include "map_files\RandomRuins\SpaceRuins\emptyshell.dmm" - #include "map_files\RandomRuins\SpaceRuins\gasthelizards.dmm" - #include "map_files\RandomRuins\SpaceRuins\intactemptyship.dmm" - #include "map_files\RandomRuins\SpaceRuins\listeningpost.dmm" - #include "map_files\RandomRuins\SpaceRuins\mechtransport.dmm" - #include "map_files\RandomRuins\SpaceRuins\oldstation.dmm" - #include "map_files\RandomRuins\SpaceRuins\onehalf.dmm" - #include "map_files\RandomRuins\SpaceRuins\spacebar.dmm" - #include "map_files\RandomRuins\SpaceRuins\turretedoutpost.dmm" - #include "map_files\RandomRuins\SpaceRuins\way_home.dmm" - - - // Shuttle Templates - #include "map_files\shuttles\cargo_base.dmm" - #include "map_files\shuttles\emergency_bar.dmm" - #include "map_files\shuttles\emergency_clown.dmm" - #include "map_files\shuttles\emergency_cramped.dmm" - #include "map_files\shuttles\emergency_cyb.dmm" - #include "map_files\shuttles\emergency_dept.dmm" - #include "map_files\shuttles\emergency_meta.dmm" - #include "map_files\shuttles\emergency_mil.dmm" - #include "map_files\shuttles\emergency_narnar.dmm" - #include "map_files\shuttles\emergency_old.dmm" - #include "map_files\shuttles\ferry_base.dmm" - #include "map_files\shuttles\ferry_meat.dmm" - - // Other templates - #include "map_files\templates\spacehotel\n_01.dmm" - #include "map_files\templates\spacehotel\n_02.dmm" - #include "map_files\templates\spacehotel\n_03.dmm" - #include "map_files\templates\spacehotel\n_04.dmm" - #include "map_files\templates\spacehotel\n_05.dmm" - #include "map_files\templates\spacehotel\n_06.dmm" - #include "map_files\templates\spacehotel\n_07.dmm" - #include "map_files\templates\spacehotel\n_08.dmm" - #include "map_files\templates\spacehotel\n_09.dmm" - #include "map_files\templates\spacehotel\n_10.dmm" - #include "map_files\templates\spacehotel\n_11.dmm" - #include "map_files\templates\spacehotel\n_12.dmm" - #include "map_files\templates\spacehotel\n_13.dmm" - #include "map_files\templates\spacehotel\n_14.dmm" - #include "map_files\templates\spacehotel\n_15.dmm" - #include "map_files\templates\spacehotel\n_16.dmm" - #include "map_files\templates\spacehotel\n_17.dmm" - #include "map_files\templates\spacehotel\n_18.dmm" - #include "map_files\templates\spacehotel\n_19.dmm" - - #include "map_files\templates\spacehotel\s_01.dmm" - #include "map_files\templates\spacehotel\s_02.dmm" - #include "map_files\templates\spacehotel\s_03.dmm" - #include "map_files\templates\spacehotel\s_04.dmm" - #include "map_files\templates\spacehotel\s_05.dmm" - #include "map_files\templates\spacehotel\s_06.dmm" - - #include "map_files\templates\light_floor_1.dmm" - #include "map_files\templates\light_floor_2.dmm" - #include "map_files\templates\light_floor_3.dmm" - #include "map_files\templates\medium_shuttle1.dmm" - #include "map_files\templates\medium_shuttle2.dmm" - #include "map_files\templates\medium_shuttle3.dmm" - #include "map_files\templates\shelter_1.dmm" - #include "map_files\templates\shelter_2.dmm" - #include "map_files\templates\small_asteroid_1.dmm" - #include "map_files\templates\small_shuttle_1.dmm" - - // This is pointless, we don't run the server - /*#define MAP_TRANSITION_CONFIG list(AWAY_MISSION = UNAFFECTED, - AWAY_MISSION = UNAFFECTED, - AWAY_MISSION = UNAFFECTED, - AWAY_MISSION = UNAFFECTED, - AWAY_MISSION = UNAFFECTED, - AWAY_MISSION = UNAFFECTED, - AWAY_MISSION = UNAFFECTED, - AWAY_MISSION = UNAFFECTED, - AWAY_MISSION = UNAFFECTED, - AWAY_MISSION = UNAFFECTED) - - #define USING_MAP_DATUM /datum/map*/ - -#elif !defined(MAP_OVERRIDE) - #warn a map has already been included. -#endif diff --git a/_maps/travis_map_testing.dm b/_maps/travis_map_testing.dm new file mode 100644 index 00000000000..89fc4ec49ce --- /dev/null +++ b/_maps/travis_map_testing.dm @@ -0,0 +1,157 @@ +// This is for Travis testing. DO NOT SET THIS AS THE GAME'S MAP NORMALLY! + +#if !defined(USING_MAP_DATUM) + // Cyberiad + #include "map_files/Cyberiad/cyberiad.dmm" + #include "map_files/Cyberiad/z2.dmm" + #include "map_files/Cyberiad/z3.dmm" + #include "map_files/Cyberiad/z4.dmm" + #include "map_files/Cyberiad/z6.dmm" + + // Debug Maps + #include "map_files/Debug/singletile.dmm" + #include "map_files/Debug/smoothing.dmm" + + // Delta + #include "map_files/Delta/delta.dmm" + + // Generic Z Levels + #include "map_files/Generic/Lavaland.dmm" + #include "map_files/Generic/z6.dmm" + #include "map_files/Generic/z7.dmm" + + // MetaStation + #include "map_files/MetaStation/MetaStation.v41A.II.dmm" + #include "map_files/MetaStation/z2.dmm" + #include "map_files/MetaStation/z3.dmm" + #include "map_files/MetaStation/z4.dmm" + + // Lavaland Ruins + #include "map_files/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_biodome_clown_planet.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk1.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk2.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk3.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_cube.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_dead_ratvar.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_envy.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_fountain_hall.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_gluttony.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_greed.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_hierophant.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_pizzaparty.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_pride.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_puzzle.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_random_ripley.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_swarmer_crash.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_ufo_crash.dmm" + #include "map_files/RandomRuins/LavaRuins/lavaland_surface_xeno_nest.dmm" + + // Space Ruins + #include "map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm" + #include "map_files/RandomRuins/SpaceRuins/asteroid1.dmm" + #include "map_files/RandomRuins/SpaceRuins/asteroid2.dmm" + #include "map_files/RandomRuins/SpaceRuins/asteroid3.dmm" + #include "map_files/RandomRuins/SpaceRuins/asteroid4.dmm" + #include "map_files/RandomRuins/SpaceRuins/asteroid5.dmm" + #include "map_files/RandomRuins/SpaceRuins/deepstorage.dmm" + #include "map_files/RandomRuins/SpaceRuins/derelict1.dmm" + #include "map_files/RandomRuins/SpaceRuins/derelict2.dmm" + #include "map_files/RandomRuins/SpaceRuins/derelict3.dmm" + #include "map_files/RandomRuins/SpaceRuins/derelict4.dmm" + #include "map_files/RandomRuins/SpaceRuins/derelict5.dmm" + #include "map_files/RandomRuins/SpaceRuins/emptyshell.dmm" + #include "map_files/RandomRuins/SpaceRuins/gasthelizards.dmm" + #include "map_files/RandomRuins/SpaceRuins/intactemptyship.dmm" + #include "map_files/RandomRuins/SpaceRuins/listeningpost.dmm" + #include "map_files/RandomRuins/SpaceRuins/mechtransport.dmm" + #include "map_files/RandomRuins/SpaceRuins/oldstation.dmm" + #include "map_files/RandomRuins/SpaceRuins/onehalf.dmm" + #include "map_files/RandomRuins/SpaceRuins/spacebar.dmm" + #include "map_files/RandomRuins/SpaceRuins/turretedoutpost.dmm" + #include "map_files/RandomRuins/SpaceRuins/way_home.dmm" + #include "map_files/RandomRuins/SpaceRuins/wizardcrash.dmm" + + // Gateway Missions + #include "map_files/RandomZLevels/academy.dmm" + #include "map_files/RandomZLevels/beach.dmm" + #include "map_files/RandomZLevels/blackmarketpackers.dmm" + #include "map_files/RandomZLevels/centcomAway.dmm" + #include "map_files/RandomZLevels/evil_santa.dmm" + #include "map_files/RandomZLevels/example.dmm" + #include "map_files/RandomZLevels/moonoutpost19.dmm" + #include "map_files/RandomZLevels/spacebattle.dmm" + #include "map_files/RandomZLevels/spacehotel.dmm" + #include "map_files/RandomZLevels/stationCollision.dmm" + #include "map_files/RandomZLevels/terrorspiders.dmm" + #include "map_files/RandomZLevels/undergroundoutpost45.dmm" + #include "map_files/RandomZLevels/wildwest.dmm" + + // Shuttles + #include "map_files/shuttles/admin_admin.dmm" + #include "map_files/shuttles/admin_hospital.dmm" + #include "map_files/shuttles/cargo_base.dmm" + #include "map_files/shuttles/emergency_bar.dmm" + #include "map_files/shuttles/emergency_clown.dmm" + #include "map_files/shuttles/emergency_cramped.dmm" + #include "map_files/shuttles/emergency_cyb.dmm" + #include "map_files/shuttles/emergency_dept.dmm" + #include "map_files/shuttles/emergency_meta.dmm" + #include "map_files/shuttles/emergency_mil.dmm" + #include "map_files/shuttles/emergency_narnar.dmm" + #include "map_files/shuttles/emergency_old.dmm" + #include "map_files/shuttles/ferry_base.dmm" + #include "map_files/shuttles/ferry_meat.dmm" + + // Templates + #include "map_files/templates/light_floor_1.dmm" + #include "map_files/templates/light_floor_2.dmm" + #include "map_files/templates/light_floor_3.dmm" + #include "map_files/templates/medium_shuttle1.dmm" + #include "map_files/templates/medium_shuttle2.dmm" + #include "map_files/templates/medium_shuttle3.dmm" + #include "map_files/templates/shelter_1.dmm" + #include "map_files/templates/shelter_2.dmm" + #include "map_files/templates/small_asteroid_1.dmm" + #include "map_files/templates/small_shuttle_1.dmm" + + // Spacehotel Rooms + #include "map_files/templates/spacehotel/n_01.dmm" + #include "map_files/templates/spacehotel/n_02.dmm" + #include "map_files/templates/spacehotel/n_03.dmm" + #include "map_files/templates/spacehotel/n_04.dmm" + #include "map_files/templates/spacehotel/n_05.dmm" + #include "map_files/templates/spacehotel/n_06.dmm" + #include "map_files/templates/spacehotel/n_07.dmm" + #include "map_files/templates/spacehotel/n_08.dmm" + #include "map_files/templates/spacehotel/n_09.dmm" + #include "map_files/templates/spacehotel/n_10.dmm" + #include "map_files/templates/spacehotel/n_11.dmm" + #include "map_files/templates/spacehotel/n_12.dmm" + #include "map_files/templates/spacehotel/n_13.dmm" + #include "map_files/templates/spacehotel/n_14.dmm" + #include "map_files/templates/spacehotel/n_15.dmm" + #include "map_files/templates/spacehotel/n_16.dmm" + #include "map_files/templates/spacehotel/n_17.dmm" + #include "map_files/templates/spacehotel/n_18.dmm" + #include "map_files/templates/spacehotel/n_19.dmm" + #include "map_files/templates/spacehotel/s_01.dmm" + #include "map_files/templates/spacehotel/s_02.dmm" + #include "map_files/templates/spacehotel/s_03.dmm" + #include "map_files/templates/spacehotel/s_04.dmm" + #include "map_files/templates/spacehotel/s_05.dmm" + #include "map_files/templates/spacehotel/s_06.dmm" + +#elif !defined(MAP_OVERRIDE) + #warn a map has already been included. +#endif diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index 157b0312995..507142c0892 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -1,345 +1,345 @@ -/* -Quick overview: - -Pipes combine to form pipelines -Pipelines and other atmospheric objects combine to form pipe_networks - Note: A single pipe_network represents a completely open space - -Pipes -> Pipelines -Pipelines + Other Objects -> Pipe network -*/ -GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) -/obj/machinery/atmospherics - anchored = 1 - layer = GAS_PIPE_HIDDEN_LAYER //under wires - resistance_flags = FIRE_PROOF - max_integrity = 200 - plane = FLOOR_PLANE - idle_power_usage = 0 - active_power_usage = 0 - power_channel = ENVIRON - on_blueprints = TRUE - var/nodealert = 0 - var/can_unwrench = 0 - - var/connect_types[] = list(1) //1=regular, 2=supply, 3=scrubber - var/connected_to = 1 //same as above, currently not used for anything - var/icon_connect_type = "" //"-supply" or "-scrubbers" - - var/initialize_directions = 0 - - var/pipe_color - var/obj/item/pipe/stored - var/image/pipe_image - -/obj/machinery/atmospherics/New() - if (!armor) - armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) - ..() - - if(!pipe_color) - pipe_color = color - color = null - - if(!pipe_color_check(pipe_color)) - pipe_color = null - -/obj/machinery/atmospherics/Initialize() - . = ..() - SSair.atmos_machinery += src - -/obj/machinery/atmospherics/proc/atmos_init() - if(can_unwrench) - stored = new(src, make_from = src) - -/obj/machinery/atmospherics/Destroy() - QDEL_NULL(stored) - SSair.atmos_machinery -= src - SSair.deferred_pipenet_rebuilds -= src - for(var/mob/living/L in src) //ventcrawling is serious business - L.remove_ventcrawl() - L.forceMove(get_turf(src)) - QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else - return ..() - -// Icons/overlays/underlays -/obj/machinery/atmospherics/update_icon() - var/turf/T = get_turf(loc) - if(!T || level == 2 || !T.intact) - plane = GAME_PLANE - else - plane = FLOOR_PLANE - -/obj/machinery/atmospherics/proc/update_pipe_image() - pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view) - pipe_image.plane = HUD_PLANE - -/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) - if(!istype(GLOB.pipe_icon_manager)) - if(!safety) //to prevent infinite loops - GLOB.pipe_icon_manager = new() - check_icon_cache(1) - return 0 - - return 1 - -/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) - //Don't use this for standard pipes - if(!istype(node)) - return null - - return node.pipe_color - -/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) - if(node) - if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) - else - //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) - else - //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) - -/obj/machinery/atmospherics/proc/update_underlays() - if(check_icon_cache()) - return 1 - else - return 0 - -// Connect types -/obj/machinery/atmospherics/proc/check_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2) - var/i - var/list1[] = atmos1.connect_types - var/list2[] = atmos2.connect_types - for(i=1,i<=list1.len,i++) - var/j - for(j=1,j<=list2.len,j++) - if(list1[i] == list2[j]) - var/n = list1[i] - return n - return 0 - -/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2) - var/i - var/list1[] = atmos1.connect_types - var/list2[] = pipe2.connect_types - for(i=1,i<=list1.len,i++) - var/j - for(j=1,j<=list2.len,j++) - if(list1[i] == list2[j]) - var/n = list1[i] - return n - return 0 - -// Pipenet related functions -/obj/machinery/atmospherics/proc/returnPipenet() - return - -/obj/machinery/atmospherics/proc/returnPipenetAir() - return - -/obj/machinery/atmospherics/proc/setPipenet() - return - -/obj/machinery/atmospherics/proc/replacePipenet() - return - -/obj/machinery/atmospherics/proc/build_network(remove_deferral = FALSE) - // Called to build a network from this node - if(remove_deferral) - SSair.deferred_pipenet_rebuilds -= src - -/obj/machinery/atmospherics/proc/defer_build_network() - SSair.deferred_pipenet_rebuilds += src - -/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) - return - -/obj/machinery/atmospherics/proc/nullifyPipenet(datum/pipeline/P) - if(P) - P.other_atmosmch -= src - -//(De)construction -/obj/machinery/atmospherics/attackby(obj/item/W, mob/user) - if(can_unwrench && istype(W, /obj/item/wrench)) - var/turf/T = get_turf(src) - if(level == 1 && isturf(T) && T.intact) - to_chat(user, "You must remove the plating first.") - return 1 - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - add_fingerprint(user) - - var/unsafe_wrenching = FALSE - var/I = int_air ? int_air.return_pressure() : 0 - var/E = env_air ? env_air.return_pressure() : 0 - var/internal_pressure = I - E - - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if(internal_pressure > 2*ONE_ATMOSPHERE) - to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?") - unsafe_wrenching = TRUE //Oh dear oh dear - - if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - investigate_log("was REMOVED by [key_name(usr)]", "atmos") - - //You unwrenched a pipe full of pressure? let's splat you into the wall silly. - if(unsafe_wrenching) - unsafe_pressure_release(user,internal_pressure) - deconstruct(TRUE) - else - return ..() - -//Called when an atmospherics object is unwrenched while having a large pressure difference -//with it's locs air contents. -/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures) - if(!user) - return - - if(!pressures) - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - pressures = int_air.return_pressure() - env_air.return_pressure() - - var/fuck_you_dir = get_dir(src, user) - var/turf/general_direction = get_edge_target_turf(user, fuck_you_dir) - user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!") - //Values based on 2*ONE_ATMOS (the unsafe pressure), resulting in 20 range and 4 speed - user.throw_at(general_direction, pressures/10, pressures/50) - -/obj/machinery/atmospherics/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(can_unwrench) - if(stored) - stored.forceMove(get_turf(src)) - if(!disassembled) - stored.obj_integrity = stored.max_integrity * 0.5 - transfer_fingerprints_to(stored) - stored = null - ..() - -/obj/machinery/atmospherics/on_construction(D, P, C) - if(C) - color = C - dir = D - initialize_directions = P - var/turf/T = loc - level = T.intact ? 2 : 1 - add_fingerprint(usr) - if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets - SSair.machinery_to_construct.Add(src) - return - initialize_atmos_network() - -/obj/machinery/atmospherics/proc/initialize_atmos_network() - atmos_init() - var/list/nodes = pipeline_expansion() - for(var/obj/machinery/atmospherics/A in nodes) - A.atmos_init() - A.addMember(src) - build_network() - -// Find a connecting /obj/machinery/atmospherics in specified direction. -/obj/machinery/atmospherics/proc/findConnecting(var/direction) - for(var/obj/machinery/atmospherics/target in get_step(src,direction)) - var/can_connect = check_connect_types(target, src) - if(can_connect && (target.initialize_directions & get_dir(target,src))) - return target - -// Ventcrawling -#define VENT_SOUND_DELAY 30 -/obj/machinery/atmospherics/relaymove(mob/living/user, direction) - direction &= initialize_directions - if(!direction || !(direction in cardinal)) //cant go this way. - return - - if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug - return - - var/obj/machinery/atmospherics/target_move = findConnecting(direction) - if(target_move) - if(is_type_in_list(target_move, ventcrawl_machinery) && target_move.can_crawl_through()) - user.remove_ventcrawl() - user.forceMove(target_move.loc) //handles entering and so on - user.visible_message("You hear something squeezing through the ducts.", "You climb out the ventilation system.") - else if(target_move.can_crawl_through()) - if(returnPipenet() != target_move.returnPipenet()) - user.update_pipe_vision(target_move) - user.loc = target_move - user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement - if(world.time - user.last_played_vent > VENT_SOUND_DELAY) - user.last_played_vent = world.time - playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3) - else - if((direction & initialize_directions) || is_type_in_list(src, ventcrawl_machinery)) //if we move in a way the pipe can connect, but doesn't - or we're in a vent - user.remove_ventcrawl() - user.forceMove(src.loc) - user.visible_message("You hear something squeezing through the pipes.", "You climb out the ventilation system.") - user.canmove = 0 - spawn(1) - user.canmove = 1 - -/obj/machinery/atmospherics/AltClick(var/mob/living/L) - if(is_type_in_list(src, ventcrawl_machinery)) - L.handle_ventcrawl(src) - return - ..() - -/obj/machinery/atmospherics/proc/can_crawl_through() - return 1 - -/obj/machinery/atmospherics/proc/change_color(var/new_color) - //only pass valid pipe colors please ~otherwise your pipe will turn invisible - if(!pipe_color_check(new_color)) - return - - pipe_color = new_color - update_icon() - -// Additional icon procs -/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction) - var/turf/T = get_turf(src) - if(!istype(T)) return - if(node) - var/node_dir = get_dir(src,node) - if(node.icon_connect_type == "-supply") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, node, node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") - else if(node.icon_connect_type == "-scrubbers") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, node, node_dir, "-scrubbers") - else - add_underlay_adapter(T, node, node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") - else - add_underlay_adapter(T, , direction, "-supply") - add_underlay_adapter(T, , direction, "-scrubbers") - add_underlay_adapter(T, , direction, "") - -/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays - if(node) - if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) - else - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) - else - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type) - -/obj/machinery/atmospherics/singularity_pull(S, current_size) - if(current_size >= STAGE_FIVE) - deconstruct(FALSE) - return ..() - -/obj/machinery/atmospherics/update_remote_sight(mob/user) - user.sight |= (SEE_TURFS|BLIND) - . = ..() +/* +Quick overview: + +Pipes combine to form pipelines +Pipelines and other atmospheric objects combine to form pipe_networks + Note: A single pipe_network represents a completely open space + +Pipes -> Pipelines +Pipelines + Other Objects -> Pipe network +*/ +GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) +/obj/machinery/atmospherics + anchored = 1 + layer = GAS_PIPE_HIDDEN_LAYER //under wires + resistance_flags = FIRE_PROOF + max_integrity = 200 + plane = FLOOR_PLANE + idle_power_usage = 0 + active_power_usage = 0 + power_channel = ENVIRON + on_blueprints = TRUE + var/nodealert = 0 + var/can_unwrench = 0 + + var/connect_types[] = list(1) //1=regular, 2=supply, 3=scrubber + var/connected_to = 1 //same as above, currently not used for anything + var/icon_connect_type = "" //"-supply" or "-scrubbers" + + var/initialize_directions = 0 + + var/pipe_color + var/obj/item/pipe/stored + var/image/pipe_image + +/obj/machinery/atmospherics/New() + if (!armor) + armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) + ..() + + if(!pipe_color) + pipe_color = color + color = null + + if(!pipe_color_check(pipe_color)) + pipe_color = null + +/obj/machinery/atmospherics/Initialize() + . = ..() + SSair.atmos_machinery += src + +/obj/machinery/atmospherics/proc/atmos_init() + if(can_unwrench) + stored = new(src, make_from = src) + +/obj/machinery/atmospherics/Destroy() + QDEL_NULL(stored) + SSair.atmos_machinery -= src + SSair.deferred_pipenet_rebuilds -= src + for(var/mob/living/L in src) //ventcrawling is serious business + L.remove_ventcrawl() + L.forceMove(get_turf(src)) + QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else + return ..() + +// Icons/overlays/underlays +/obj/machinery/atmospherics/update_icon() + var/turf/T = get_turf(loc) + if(!T || level == 2 || !T.intact) + plane = GAME_PLANE + else + plane = FLOOR_PLANE + +/obj/machinery/atmospherics/proc/update_pipe_image() + pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view) + pipe_image.plane = HUD_PLANE + +/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) + if(!istype(GLOB.pipe_icon_manager)) + if(!safety) //to prevent infinite loops + GLOB.pipe_icon_manager = new() + check_icon_cache(1) + return 0 + + return 1 + +/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) + //Don't use this for standard pipes + if(!istype(node)) + return null + + return node.pipe_color + +/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) + if(node) + if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) + else + //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) + else + //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) + +/obj/machinery/atmospherics/proc/update_underlays() + if(check_icon_cache()) + return 1 + else + return 0 + +// Connect types +/obj/machinery/atmospherics/proc/check_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2) + var/i + var/list1[] = atmos1.connect_types + var/list2[] = atmos2.connect_types + for(i=1,i<=list1.len,i++) + var/j + for(j=1,j<=list2.len,j++) + if(list1[i] == list2[j]) + var/n = list1[i] + return n + return 0 + +/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2) + var/i + var/list1[] = atmos1.connect_types + var/list2[] = pipe2.connect_types + for(i=1,i<=list1.len,i++) + var/j + for(j=1,j<=list2.len,j++) + if(list1[i] == list2[j]) + var/n = list1[i] + return n + return 0 + +// Pipenet related functions +/obj/machinery/atmospherics/proc/returnPipenet() + return + +/obj/machinery/atmospherics/proc/returnPipenetAir() + return + +/obj/machinery/atmospherics/proc/setPipenet() + return + +/obj/machinery/atmospherics/proc/replacePipenet() + return + +/obj/machinery/atmospherics/proc/build_network(remove_deferral = FALSE) + // Called to build a network from this node + if(remove_deferral) + SSair.deferred_pipenet_rebuilds -= src + +/obj/machinery/atmospherics/proc/defer_build_network() + SSair.deferred_pipenet_rebuilds += src + +/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) + return + +/obj/machinery/atmospherics/proc/nullifyPipenet(datum/pipeline/P) + if(P) + P.other_atmosmch -= src + +//(De)construction +/obj/machinery/atmospherics/attackby(obj/item/W, mob/user) + if(can_unwrench && istype(W, /obj/item/wrench)) + var/turf/T = get_turf(src) + if(level == 1 && isturf(T) && T.intact) + to_chat(user, "You must remove the plating first.") + return 1 + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + add_fingerprint(user) + + var/unsafe_wrenching = FALSE + var/I = int_air ? int_air.return_pressure() : 0 + var/E = env_air ? env_air.return_pressure() : 0 + var/internal_pressure = I - E + + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if(internal_pressure > 2*ONE_ATMOSPHERE) + to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?") + unsafe_wrenching = TRUE //Oh dear oh dear + + if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + investigate_log("was REMOVED by [key_name(usr)]", "atmos") + + //You unwrenched a pipe full of pressure? let's splat you into the wall silly. + if(unsafe_wrenching) + unsafe_pressure_release(user,internal_pressure) + deconstruct(TRUE) + else + return ..() + +//Called when an atmospherics object is unwrenched while having a large pressure difference +//with it's locs air contents. +/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures) + if(!user) + return + + if(!pressures) + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + pressures = int_air.return_pressure() - env_air.return_pressure() + + var/fuck_you_dir = get_dir(src, user) + var/turf/general_direction = get_edge_target_turf(user, fuck_you_dir) + user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!") + //Values based on 2*ONE_ATMOS (the unsafe pressure), resulting in 20 range and 4 speed + user.throw_at(general_direction, pressures/10, pressures/50) + +/obj/machinery/atmospherics/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(can_unwrench) + if(stored) + stored.forceMove(get_turf(src)) + if(!disassembled) + stored.obj_integrity = stored.max_integrity * 0.5 + transfer_fingerprints_to(stored) + stored = null + ..() + +/obj/machinery/atmospherics/on_construction(D, P, C) + if(C) + color = C + dir = D + initialize_directions = P + var/turf/T = loc + level = T.intact ? 2 : 1 + add_fingerprint(usr) + if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets + SSair.machinery_to_construct.Add(src) + return + initialize_atmos_network() + +/obj/machinery/atmospherics/proc/initialize_atmos_network() + atmos_init() + var/list/nodes = pipeline_expansion() + for(var/obj/machinery/atmospherics/A in nodes) + A.atmos_init() + A.addMember(src) + build_network() + +// Find a connecting /obj/machinery/atmospherics in specified direction. +/obj/machinery/atmospherics/proc/findConnecting(var/direction) + for(var/obj/machinery/atmospherics/target in get_step(src,direction)) + var/can_connect = check_connect_types(target, src) + if(can_connect && (target.initialize_directions & get_dir(target,src))) + return target + +// Ventcrawling +#define VENT_SOUND_DELAY 30 +/obj/machinery/atmospherics/relaymove(mob/living/user, direction) + direction &= initialize_directions + if(!direction || !(direction in GLOB.cardinal)) //cant go this way. + return + + if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug + return + + var/obj/machinery/atmospherics/target_move = findConnecting(direction) + if(target_move) + if(is_type_in_list(target_move, GLOB.ventcrawl_machinery) && target_move.can_crawl_through()) + user.remove_ventcrawl() + user.forceMove(target_move.loc) //handles entering and so on + user.visible_message("You hear something squeezing through the ducts.", "You climb out the ventilation system.") + else if(target_move.can_crawl_through()) + if(returnPipenet() != target_move.returnPipenet()) + user.update_pipe_vision(target_move) + user.loc = target_move + user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement + if(world.time - user.last_played_vent > VENT_SOUND_DELAY) + user.last_played_vent = world.time + playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3) + else + if((direction & initialize_directions) || is_type_in_list(src, GLOB.ventcrawl_machinery)) //if we move in a way the pipe can connect, but doesn't - or we're in a vent + user.remove_ventcrawl() + user.forceMove(src.loc) + user.visible_message("You hear something squeezing through the pipes.", "You climb out the ventilation system.") + user.canmove = 0 + spawn(1) + user.canmove = 1 + +/obj/machinery/atmospherics/AltClick(var/mob/living/L) + if(is_type_in_list(src, GLOB.ventcrawl_machinery)) + L.handle_ventcrawl(src) + return + ..() + +/obj/machinery/atmospherics/proc/can_crawl_through() + return 1 + +/obj/machinery/atmospherics/proc/change_color(var/new_color) + //only pass valid pipe colors please ~otherwise your pipe will turn invisible + if(!pipe_color_check(new_color)) + return + + pipe_color = new_color + update_icon() + +// Additional icon procs +/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction) + var/turf/T = get_turf(src) + if(!istype(T)) return + if(node) + var/node_dir = get_dir(src,node) + if(node.icon_connect_type == "-supply") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, node, node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + else if(node.icon_connect_type == "-scrubbers") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, node, node_dir, "-scrubbers") + else + add_underlay_adapter(T, node, node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + else + add_underlay_adapter(T, , direction, "-supply") + add_underlay_adapter(T, , direction, "-scrubbers") + add_underlay_adapter(T, , direction, "") + +/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays + if(node) + if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) + else + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) + else + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type) + +/obj/machinery/atmospherics/singularity_pull(S, current_size) + if(current_size >= STAGE_FIVE) + deconstruct(FALSE) + return ..() + +/obj/machinery/atmospherics/update_remote_sight(mob/user) + user.sight |= (SEE_TURFS|BLIND) + . = ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index 68a1033a266..0a8b9eb95a4 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -1,155 +1,155 @@ -/obj/machinery/atmospherics/binary - dir = SOUTH - initialize_directions = SOUTH|NORTH - use_power = IDLE_POWER_USE - - layer = GAS_PUMP_LAYER - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - - var/obj/machinery/atmospherics/node1 - var/obj/machinery/atmospherics/node2 - - var/datum/pipeline/parent1 - var/datum/pipeline/parent2 - -/obj/machinery/atmospherics/binary/New() - ..() - switch(dir) - if(NORTH) - initialize_directions = NORTH|SOUTH - if(SOUTH) - initialize_directions = NORTH|SOUTH - if(EAST) - initialize_directions = EAST|WEST - if(WEST) - initialize_directions = EAST|WEST - - air1 = new - air2 = new - - air1.volume = 200 - air2.volume = 200 - -/obj/machinery/atmospherics/binary/Destroy() - if(node1) - node1.disconnect(src) - node1 = null - nullifyPipenet(parent1) - if(node2) - node2.disconnect(src) - node2 = null - nullifyPipenet(parent2) - return ..() - -/obj/machinery/atmospherics/binary/atmos_init() - ..() - var/node2_connect = dir - var/node1_connect = turn(dir, 180) - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - var/c = check_connect_types(target,src) - if(c) - target.connected_to = c - connected_to = c - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - var/c = check_connect_types(target,src) - if(c) - target.connected_to = c - connected_to = c - node2 = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/binary/build_network(remove_deferral = FALSE) - if(!parent1) - parent1 = new /datum/pipeline() - parent1.build_pipeline(src) - - if(!parent2) - parent2 = new /datum/pipeline() - parent2.build_pipeline(src) - ..() - -/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - qdel(parent1) - node1 = null - else if(reference == node2) - if(istype(node2, /obj/machinery/atmospherics/pipe)) - qdel(parent2) - node2 = null - update_icon() - -/obj/machinery/atmospherics/binary/nullifyPipenet(datum/pipeline/P) - ..() - if(!P) - return - if(P == parent1) - parent1.other_airs -= air1 - parent1 = null - else if(P == parent2) - parent2.other_airs -= air2 - parent2 = null - -/obj/machinery/atmospherics/binary/returnPipenetAir(datum/pipeline/P) - if(P == parent1) - return air1 - else if(P == parent2) - return air2 - -/obj/machinery/atmospherics/binary/pipeline_expansion(datum/pipeline/P) - if(P) - if(parent1 == P) - return list(node1) - else if(parent2 == P) - return list(node2) - else - return list(node1, node2) - -/obj/machinery/atmospherics/binary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) - if(A == node1) - parent1 = P - else if(A == node2) - parent2 = P - -/obj/machinery/atmospherics/binary/returnPipenet(obj/machinery/atmospherics/A) - if(A == node1) - return parent1 - else if(A == node2) - return parent2 - -/obj/machinery/atmospherics/binary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) - if(Old == parent1) - parent1 = New - else if(Old == parent2) - parent2 = New - -/obj/machinery/atmospherics/binary/unsafe_pressure_release(var/mob/user,var/pressures) - ..() - - var/turf/T = get_turf(src) - if(T) - //Remove the gas from air1+air2 and assume it - var/datum/gas_mixture/environment = T.return_air() - var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) - var/shared_loss = lost/2 - - var/datum/gas_mixture/to_release = air1.remove(shared_loss) - to_release.merge(air2.remove(shared_loss)) - T.assume_air(to_release) - air_update_turf(1) - -/obj/machinery/atmospherics/binary/process_atmos() - ..() - return parent1 && parent2 +/obj/machinery/atmospherics/binary + dir = SOUTH + initialize_directions = SOUTH|NORTH + use_power = IDLE_POWER_USE + + layer = GAS_PUMP_LAYER + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + + var/obj/machinery/atmospherics/node1 + var/obj/machinery/atmospherics/node2 + + var/datum/pipeline/parent1 + var/datum/pipeline/parent2 + +/obj/machinery/atmospherics/binary/New() + ..() + switch(dir) + if(NORTH) + initialize_directions = NORTH|SOUTH + if(SOUTH) + initialize_directions = NORTH|SOUTH + if(EAST) + initialize_directions = EAST|WEST + if(WEST) + initialize_directions = EAST|WEST + + air1 = new + air2 = new + + air1.volume = 200 + air2.volume = 200 + +/obj/machinery/atmospherics/binary/Destroy() + if(node1) + node1.disconnect(src) + node1 = null + nullifyPipenet(parent1) + if(node2) + node2.disconnect(src) + node2 = null + nullifyPipenet(parent2) + return ..() + +/obj/machinery/atmospherics/binary/atmos_init() + ..() + var/node2_connect = dir + var/node1_connect = turn(dir, 180) + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + var/c = check_connect_types(target,src) + if(c) + target.connected_to = c + connected_to = c + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + var/c = check_connect_types(target,src) + if(c) + target.connected_to = c + connected_to = c + node2 = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/binary/build_network(remove_deferral = FALSE) + if(!parent1) + parent1 = new /datum/pipeline() + parent1.build_pipeline(src) + + if(!parent2) + parent2 = new /datum/pipeline() + parent2.build_pipeline(src) + ..() + +/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + qdel(parent1) + node1 = null + else if(reference == node2) + if(istype(node2, /obj/machinery/atmospherics/pipe)) + qdel(parent2) + node2 = null + update_icon() + +/obj/machinery/atmospherics/binary/nullifyPipenet(datum/pipeline/P) + ..() + if(!P) + return + if(P == parent1) + parent1.other_airs -= air1 + parent1 = null + else if(P == parent2) + parent2.other_airs -= air2 + parent2 = null + +/obj/machinery/atmospherics/binary/returnPipenetAir(datum/pipeline/P) + if(P == parent1) + return air1 + else if(P == parent2) + return air2 + +/obj/machinery/atmospherics/binary/pipeline_expansion(datum/pipeline/P) + if(P) + if(parent1 == P) + return list(node1) + else if(parent2 == P) + return list(node2) + else + return list(node1, node2) + +/obj/machinery/atmospherics/binary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) + if(A == node1) + parent1 = P + else if(A == node2) + parent2 = P + +/obj/machinery/atmospherics/binary/returnPipenet(obj/machinery/atmospherics/A) + if(A == node1) + return parent1 + else if(A == node2) + return parent2 + +/obj/machinery/atmospherics/binary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) + if(Old == parent1) + parent1 = New + else if(Old == parent2) + parent2 = New + +/obj/machinery/atmospherics/binary/unsafe_pressure_release(var/mob/user,var/pressures) + ..() + + var/turf/T = get_turf(src) + if(T) + //Remove the gas from air1+air2 and assume it + var/datum/gas_mixture/environment = T.return_air() + var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) + var/shared_loss = lost/2 + + var/datum/gas_mixture/to_release = air1.remove(shared_loss) + to_release.merge(air2.remove(shared_loss)) + T.assume_air(to_release) + air_update_turf(1) + +/obj/machinery/atmospherics/binary/process_atmos() + ..() + return parent1 && parent2 diff --git a/code/ATMOSPHERICS/components/binary_devices/circulator.dm b/code/ATMOSPHERICS/components/binary_devices/circulator.dm index 5574f97ea66..b45dd8929b4 100644 --- a/code/ATMOSPHERICS/components/binary_devices/circulator.dm +++ b/code/ATMOSPHERICS/components/binary_devices/circulator.dm @@ -1,125 +1,125 @@ -//node1, air1, network1 correspond to input -//node2, air2, network2 correspond to output -/obj/machinery/atmospherics/binary/circulator - name = "circulator/heat exchanger" - desc = "A gas circulator pump and heat exchanger. Its input port is on the south side, and its output port is on the north side." - icon = 'icons/obj/atmospherics/circulator.dmi' - icon_state = "circ1-off" - - var/side = CIRC_LEFT - - var/global/const/CIRC_LEFT = WEST - var/global/const/CIRC_RIGHT = EAST - - var/last_pressure_delta = 0 - - var/obj/machinery/power/generator/generator - - anchored = 1 - density = 1 - - can_unwrench = 1 - var/side_inverted = 0 - -// Creating a custom circulator pipe subtype to be delivered through cargo -/obj/item/pipe/circulator - name = "circulator/heat exchanger fitting" - -/obj/item/pipe/circulator/New(loc) - var/obj/machinery/atmospherics/binary/circulator/C = new /obj/machinery/atmospherics/binary/circulator(null) - ..(loc, make_from = C) - -/obj/machinery/atmospherics/binary/circulator/Destroy() - if(generator && generator.cold_circ == src) - generator.cold_circ = null - else if(generator && generator.hot_circ == src) - generator.hot_circ = null - return ..() - -/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() - var/datum/gas_mixture/inlet = get_inlet_air() - var/datum/gas_mixture/outlet = get_outlet_air() - var/output_starting_pressure = outlet.return_pressure() - var/input_starting_pressure = inlet.return_pressure() - - if(output_starting_pressure >= input_starting_pressure - 10) - //Need at least 10 KPa difference to overcome friction in the mechanism - last_pressure_delta = 0 - return null - - //Calculate necessary moles to transfer using PV = nRT - if(inlet.temperature > 0) - var/pressure_delta = (input_starting_pressure - output_starting_pressure) / 2 - - var/transfer_moles = pressure_delta * outlet.volume/(inlet.temperature * R_IDEAL_GAS_EQUATION) - - last_pressure_delta = pressure_delta - - //log_debug("pressure_delta = [pressure_delta]; transfer_moles = [transfer_moles];") - - //Actually transfer the gas - var/datum/gas_mixture/removed = inlet.remove(transfer_moles) - - parent1.update = 1 - parent2.update = 1 - - return removed - - else - last_pressure_delta = 0 - -/obj/machinery/atmospherics/binary/circulator/process_atmos() - ..() - update_icon() - -/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_air() - if(side_inverted==0) - return air2 - else - return air1 - -/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_air() - if(side_inverted==0) - return air1 - else - return air2 - -/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_side() - if(dir==SOUTH||dir==NORTH) - if(side_inverted==0) - return "South" - else - return "North" - -/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_side() - if(dir==SOUTH||dir==NORTH) - if(side_inverted==0) - return "North" - else - return "South" - -/obj/machinery/atmospherics/binary/circulator/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!side_inverted) - side_inverted = TRUE - else - side_inverted = FALSE - to_chat(user, "You reverse the circulator's valve settings. The inlet of the circulator is now on the [get_inlet_side(dir)] side.") - desc = "A gas circulator pump and heat exchanger. Its input port is on the [get_inlet_side(dir)] side, and its output port is on the [get_outlet_side(dir)] side." - -/obj/machinery/atmospherics/binary/circulator/update_icon() - ..() - - if(stat & (BROKEN|NOPOWER)) - icon_state = "circ[side]-p" - else if(last_pressure_delta > 0) - if(last_pressure_delta > ONE_ATMOSPHERE) - icon_state = "circ[side]-run" - else - icon_state = "circ[side]-slow" - else - icon_state = "circ[side]-off" - - return 1 \ No newline at end of file +//node1, air1, network1 correspond to input +//node2, air2, network2 correspond to output +#define CIRC_LEFT WEST +#define CIRC_RIGHT EAST + +/obj/machinery/atmospherics/binary/circulator + name = "circulator/heat exchanger" + desc = "A gas circulator pump and heat exchanger. Its input port is on the south side, and its output port is on the north side." + icon = 'icons/obj/atmospherics/circulator.dmi' + icon_state = "circ1-off" + + var/side = CIRC_LEFT + + var/last_pressure_delta = 0 + + var/obj/machinery/power/generator/generator + + anchored = 1 + density = 1 + + can_unwrench = 1 + var/side_inverted = 0 + +// Creating a custom circulator pipe subtype to be delivered through cargo +/obj/item/pipe/circulator + name = "circulator/heat exchanger fitting" + +/obj/item/pipe/circulator/New(loc) + var/obj/machinery/atmospherics/binary/circulator/C = new /obj/machinery/atmospherics/binary/circulator(null) + ..(loc, make_from = C) + +/obj/machinery/atmospherics/binary/circulator/Destroy() + if(generator && generator.cold_circ == src) + generator.cold_circ = null + else if(generator && generator.hot_circ == src) + generator.hot_circ = null + return ..() + +/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() + var/datum/gas_mixture/inlet = get_inlet_air() + var/datum/gas_mixture/outlet = get_outlet_air() + var/output_starting_pressure = outlet.return_pressure() + var/input_starting_pressure = inlet.return_pressure() + + if(output_starting_pressure >= input_starting_pressure - 10) + //Need at least 10 KPa difference to overcome friction in the mechanism + last_pressure_delta = 0 + return null + + //Calculate necessary moles to transfer using PV = nRT + if(inlet.temperature > 0) + var/pressure_delta = (input_starting_pressure - output_starting_pressure) / 2 + + var/transfer_moles = pressure_delta * outlet.volume/(inlet.temperature * R_IDEAL_GAS_EQUATION) + + last_pressure_delta = pressure_delta + + //log_debug("pressure_delta = [pressure_delta]; transfer_moles = [transfer_moles];") + + //Actually transfer the gas + var/datum/gas_mixture/removed = inlet.remove(transfer_moles) + + parent1.update = 1 + parent2.update = 1 + + return removed + + else + last_pressure_delta = 0 + +/obj/machinery/atmospherics/binary/circulator/process_atmos() + ..() + update_icon() + +/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_air() + if(side_inverted==0) + return air2 + else + return air1 + +/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_air() + if(side_inverted==0) + return air1 + else + return air2 + +/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_side() + if(dir==SOUTH||dir==NORTH) + if(side_inverted==0) + return "South" + else + return "North" + +/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_side() + if(dir==SOUTH||dir==NORTH) + if(side_inverted==0) + return "North" + else + return "South" + +/obj/machinery/atmospherics/binary/circulator/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!side_inverted) + side_inverted = TRUE + else + side_inverted = FALSE + to_chat(user, "You reverse the circulator's valve settings. The inlet of the circulator is now on the [get_inlet_side(dir)] side.") + desc = "A gas circulator pump and heat exchanger. Its input port is on the [get_inlet_side(dir)] side, and its output port is on the [get_outlet_side(dir)] side." + +/obj/machinery/atmospherics/binary/circulator/update_icon() + ..() + + if(stat & (BROKEN|NOPOWER)) + icon_state = "circ[side]-p" + else if(last_pressure_delta > 0) + if(last_pressure_delta > ONE_ATMOSPHERE) + icon_state = "circ[side]-run" + else + icon_state = "circ[side]-slow" + else + icon_state = "circ[side]-off" + + return 1 diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm index b9902dbb066..c0ed34ef206 100644 --- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm @@ -1,269 +1,269 @@ -/obj/machinery/atmospherics/binary/dp_vent_pump - icon = 'icons/atmos/vent_pump.dmi' - icon_state = "map_dp_vent" - - //node2 is output port - //node1 is input port - - req_one_access_txt = "24;10" - - name = "dual-port air vent" - desc = "Has a valve and pump attached to it. There are two ports." - - can_unwrench = 1 - - level = 1 - - connect_types = list(1,2,3) //connects to regular, supply and scrubbers pipes - - var/on = 0 - var/pump_direction = 1 //0 = siphoning, 1 = releasing - - var/external_pressure_bound = ONE_ATMOSPHERE - var/input_pressure_min = 0 - var/output_pressure_max = 0 - - var/frequency = ATMOS_VENTSCRUB - var/id_tag = null - var/datum/radio_frequency/radio_connection - var/advcontrol = 0//does this device listen to the AAC - - settagwhitelist = list("id_tag") - - var/pressure_checks = 1 - //1: Do not pass external_pressure_bound - //2: Do not pass input_pressure_min - //4: Do not pass output_pressure_max - -/obj/machinery/atmospherics/binary/dp_vent_pump/New() - ..() - if(!id_tag) - assign_uid() - id_tag = num2text(uid) - icon = null - -/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume - name = "large dual port air vent" - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/on - on = TRUE - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() - ..() - air1.volume = 1000 - air2.volume = 1000 - -/obj/machinery/atmospherics/binary/volume_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) - ..() - - if(!check_icon_cache()) - return - - overlays.Cut() - - var/vent_icon = "vent" - - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - vent_icon += "h" - - if(!powered()) - vent_icon += "off" - else - vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]" - - overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon) - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - return - else - if(node1) - add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) - else - add_underlay(T, node1, turn(dir, -180)) - if(node2) - add_underlay(T, node2, dir, node2.icon_connect_type) - else - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/dp_vent_pump/process_atmos() - ..() - if(!on) - return 0 - - var/datum/gas_mixture/environment = loc.return_air() - var/environment_pressure = environment.return_pressure() - - if(pump_direction) //input -> external - var/pressure_delta = 10000 - - if(pressure_checks&1) - pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure)) - if(pressure_checks&2) - pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min)) - - if(pressure_delta > 0) - if(air1.temperature > 0) - var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - - loc.assume_air(removed) - - parent1.update = 1 - air_update_turf() - else //external -> output - var/pressure_delta = 10000 - - if(pressure_checks&1) - pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound)) - if(pressure_checks&4) - pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure())) - - if(pressure_delta > 0) - if(environment.temperature > 0) - var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) - - var/datum/gas_mixture/removed = loc.remove_air(transfer_moles) - - air2.merge(removed) - - parent2.update = 1 - air_update_turf() - return 1 - -//Radio remote control -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id_tag, - "device" = "ADVP", - "power" = on, - "direction" = pump_direction?("release"):("siphon"), - "checks" = pressure_checks, - "input" = input_pressure_min, - "output" = output_pressure_max, - "external" = external_pressure_bound, - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command") || (signal.data["advcontrol"] && !advcontrol)) - return 0 - if(signal.data["power"] != null) - on = text2num(signal.data["power"]) - - if(signal.data["power_toggle"] != null) - on = !on - - if(signal.data["direction"] != null) - pump_direction = text2num(signal.data["direction"]) - - if(signal.data["checks"] != null) - pressure_checks = text2num(signal.data["checks"]) - - if(signal.data["purge"]) - pressure_checks &= ~1 - pump_direction = 0 - - if(signal.data["stabilize"])//the fact that this was "stabalize" shows how many fucks people give about these wonders, none - pressure_checks |= 1 - pump_direction = 1 - - if(signal.data["set_input_pressure"] != null) - input_pressure_min = between( - 0, - text2num(signal.data["set_input_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(signal.data["set_output_pressure"] != null) - output_pressure_max = between( - 0, - text2num(signal.data["set_output_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(signal.data["set_external_pressure"] != null) - external_pressure_bound = between( - 0, - text2num(signal.data["set_external_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -/obj/machinery/atmospherics/binary/dp_vent_pump/attackby(var/obj/item/W as obj, var/mob/user as mob) - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - return ..() - -/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_menu(var/mob/user,var/obj/item/multitool/P) - return {" - - "} - -/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_topic(var/mob/user, var/list/href_list, var/obj/O) - . = ..() - if(.) - return . - if("toggleadvcontrol" in href_list) - advcontrol = !advcontrol - return TRUE +/obj/machinery/atmospherics/binary/dp_vent_pump + icon = 'icons/atmos/vent_pump.dmi' + icon_state = "map_dp_vent" + + //node2 is output port + //node1 is input port + + req_one_access_txt = "24;10" + + name = "dual-port air vent" + desc = "Has a valve and pump attached to it. There are two ports." + + can_unwrench = 1 + + level = 1 + + connect_types = list(1,2,3) //connects to regular, supply and scrubbers pipes + + var/on = 0 + var/pump_direction = 1 //0 = siphoning, 1 = releasing + + var/external_pressure_bound = ONE_ATMOSPHERE + var/input_pressure_min = 0 + var/output_pressure_max = 0 + + var/frequency = ATMOS_VENTSCRUB + var/id_tag = null + var/datum/radio_frequency/radio_connection + var/advcontrol = 0//does this device listen to the AAC + + settagwhitelist = list("id_tag") + + var/pressure_checks = 1 + //1: Do not pass external_pressure_bound + //2: Do not pass input_pressure_min + //4: Do not pass output_pressure_max + +/obj/machinery/atmospherics/binary/dp_vent_pump/New() + ..() + if(!id_tag) + assign_uid() + id_tag = num2text(uid) + icon = null + +/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume + name = "large dual port air vent" + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/on + on = TRUE + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() + ..() + air1.volume = 1000 + air2.volume = 1000 + +/obj/machinery/atmospherics/binary/volume_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) + ..() + + if(!check_icon_cache()) + return + + overlays.Cut() + + var/vent_icon = "vent" + + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + vent_icon += "h" + + if(!powered()) + vent_icon += "off" + else + vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]" + + overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon) + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + return + else + if(node1) + add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) + else + add_underlay(T, node1, turn(dir, -180)) + if(node2) + add_underlay(T, node2, dir, node2.icon_connect_type) + else + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/dp_vent_pump/process_atmos() + ..() + if(!on) + return 0 + + var/datum/gas_mixture/environment = loc.return_air() + var/environment_pressure = environment.return_pressure() + + if(pump_direction) //input -> external + var/pressure_delta = 10000 + + if(pressure_checks&1) + pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure)) + if(pressure_checks&2) + pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min)) + + if(pressure_delta > 0) + if(air1.temperature > 0) + var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + + loc.assume_air(removed) + + parent1.update = 1 + air_update_turf() + else //external -> output + var/pressure_delta = 10000 + + if(pressure_checks&1) + pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound)) + if(pressure_checks&4) + pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure())) + + if(pressure_delta > 0) + if(environment.temperature > 0) + var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) + + var/datum/gas_mixture/removed = loc.remove_air(transfer_moles) + + air2.merge(removed) + + parent2.update = 1 + air_update_turf() + return 1 + +//Radio remote control +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id_tag, + "device" = "ADVP", + "power" = on, + "direction" = pump_direction?("release"):("siphon"), + "checks" = pressure_checks, + "input" = input_pressure_min, + "output" = output_pressure_max, + "external" = external_pressure_bound, + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command") || (signal.data["advcontrol"] && !advcontrol)) + return 0 + if(signal.data["power"] != null) + on = text2num(signal.data["power"]) + + if(signal.data["power_toggle"] != null) + on = !on + + if(signal.data["direction"] != null) + pump_direction = text2num(signal.data["direction"]) + + if(signal.data["checks"] != null) + pressure_checks = text2num(signal.data["checks"]) + + if(signal.data["purge"]) + pressure_checks &= ~1 + pump_direction = 0 + + if(signal.data["stabilize"])//the fact that this was "stabalize" shows how many fucks people give about these wonders, none + pressure_checks |= 1 + pump_direction = 1 + + if(signal.data["set_input_pressure"] != null) + input_pressure_min = between( + 0, + text2num(signal.data["set_input_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(signal.data["set_output_pressure"] != null) + output_pressure_max = between( + 0, + text2num(signal.data["set_output_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(signal.data["set_external_pressure"] != null) + external_pressure_bound = between( + 0, + text2num(signal.data["set_external_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +/obj/machinery/atmospherics/binary/dp_vent_pump/attackby(var/obj/item/W as obj, var/mob/user as mob) + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + return ..() + +/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_menu(var/mob/user,var/obj/item/multitool/P) + return {" + + "} + +/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_topic(var/mob/user, var/list/href_list, var/obj/O) + . = ..() + if(.) + return . + if("toggleadvcontrol" in href_list) + advcontrol = !advcontrol + return TRUE diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 3448655c953..87c7c6426c1 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -1,192 +1,192 @@ -/obj/machinery/atmospherics/binary/passive_gate - //Tries to achieve target pressure at output (like a normal pump) except - // Uses no power but can not transfer gases from a low pressure area to a high pressure area - icon = 'icons/atmos/passive_gate.dmi' - icon_state = "map" - - name = "passive gate" - desc = "A one-way air valve that does not require power" - - can_unwrench = 1 - - var/on = 0 - var/target_pressure = ONE_ATMOSPHERE - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/passive_gate/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/passive_gate/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/passive_gate/update_icon() - ..() - icon_state = "[on ? "on" : "off"]" - -/obj/machinery/atmospherics/binary/passive_gate/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, 180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/passive_gate/process_atmos() - ..() - if(!on) - return 0 - - var/output_starting_pressure = air2.return_pressure() - var/input_starting_pressure = air1.return_pressure() - - if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10)) - //No need to pump gas if target is already reached or input pressure is too low - //Need at least 10 KPa difference to overcome friction in the mechanism - return 1 - - //Calculate necessary moles to transfer using PV = nRT - if((air1.total_moles() > 0) && (air1.temperature>0)) - var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2) - //Can not have a pressure delta that would cause output_pressure > input_pressure - - var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - air2.merge(removed) - - parent1.update = 1 - - parent2.update = 1 - return 1 - -//Radio remote control -/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = on, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - if("power" in signal.data) - on = text2num(signal.data["power"]) - - if("power_toggle" in signal.data) - on = !on - - if("set_output_pressure" in signal.data) - target_pressure = between( - 0, - text2num(signal.data["set_output_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if("status" in signal.data) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/passive_gate/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/passive_gate/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) - ui.open() - -/obj/machinery/atmospherics/binary/passive_gate/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - return data - -/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure)) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/W, mob/user, params) - if(!istype(W, /obj/item/wrench)) - return ..() - if(on) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - return ..() +/obj/machinery/atmospherics/binary/passive_gate + //Tries to achieve target pressure at output (like a normal pump) except + // Uses no power but can not transfer gases from a low pressure area to a high pressure area + icon = 'icons/atmos/passive_gate.dmi' + icon_state = "map" + + name = "passive gate" + desc = "A one-way air valve that does not require power" + + can_unwrench = 1 + + var/on = 0 + var/target_pressure = ONE_ATMOSPHERE + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/passive_gate/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/passive_gate/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/passive_gate/update_icon() + ..() + icon_state = "[on ? "on" : "off"]" + +/obj/machinery/atmospherics/binary/passive_gate/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, 180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/passive_gate/process_atmos() + ..() + if(!on) + return 0 + + var/output_starting_pressure = air2.return_pressure() + var/input_starting_pressure = air1.return_pressure() + + if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10)) + //No need to pump gas if target is already reached or input pressure is too low + //Need at least 10 KPa difference to overcome friction in the mechanism + return 1 + + //Calculate necessary moles to transfer using PV = nRT + if((air1.total_moles() > 0) && (air1.temperature>0)) + var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2) + //Can not have a pressure delta that would cause output_pressure > input_pressure + + var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + air2.merge(removed) + + parent1.update = 1 + + parent2.update = 1 + return 1 + +//Radio remote control +/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = on, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + if("power" in signal.data) + on = text2num(signal.data["power"]) + + if("power_toggle" in signal.data) + on = !on + + if("set_output_pressure" in signal.data) + target_pressure = between( + 0, + text2num(signal.data["set_output_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if("status" in signal.data) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/passive_gate/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/passive_gate/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) + ui.open() + +/obj/machinery/atmospherics/binary/passive_gate/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + return data + +/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure)) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/W, mob/user, params) + if(!istype(W, /obj/item/wrench)) + return ..() + if(on) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 1998e67e17d..7e02f89c5e2 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -1,261 +1,261 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -Thus, the two variables affect pump operation are set in New(): - air1.volume - This is the volume of gas available to the pump that may be transfered to the output - air2.volume - Higher quantities of this cause more air to be perfected later - but overall network volume is also increased as this increases... -*/ - -/obj/machinery/atmospherics/binary/pump - icon = 'icons/atmos/pump.dmi' - icon_state = "map_off" - - name = "gas pump" - desc = "A pump" - - can_unwrench = 1 - - var/on = 0 - var/target_pressure = ONE_ATMOSPHERE - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/pump/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/binary/pump/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/binary/pump/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/binary/pump/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/binary/pump/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/binary/pump/proc/set_max() - if(powered()) - target_pressure = MAX_OUTPUT_PRESSURE - update_icon() - -/obj/machinery/atmospherics/binary/pump/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/pump/on - icon_state = "map_on" - on = 1 - -/obj/machinery/atmospherics/binary/pump/update_icon() - ..() - - if(!powered()) - icon_state = "off" - else - icon_state = "[on ? "on" : "off"]" - -/obj/machinery/atmospherics/binary/pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/pump/process_atmos() - ..() - if((stat & (NOPOWER|BROKEN)) || !on) - return 0 - - var/output_starting_pressure = air2.return_pressure() - - if( (target_pressure - output_starting_pressure) < 0.01) - //No need to pump gas if target is already reached! - return 1 - - //Calculate necessary moles to transfer using PV=nRT - if((air1.total_moles() > 0) && (air1.temperature>0)) - var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - air2.merge(removed) - - parent1.update = 1 - - parent2.update = 1 - return 1 - -//Radio remote control -/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = on, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - return 1 - -/obj/machinery/atmospherics/binary/pump/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - if(signal.data["power"]) - on = text2num(signal.data["power"]) - - if(signal.data["power_toggle"]) - on = !on - - if(signal.data["set_output_pressure"]) - target_pressure = between( - 0, - text2num(signal.data["set_output_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) - ui.open() - -/obj/machinery/atmospherics/binary/pump/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - return data - -/obj/machinery/atmospherics/binary/pump/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure)) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/binary/pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/pump/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the pump.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else if(!istype(W, /obj/item/wrench)) - return ..() - if(!(stat & NOPOWER) && on) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - return ..() \ No newline at end of file +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +Thus, the two variables affect pump operation are set in New(): + air1.volume + This is the volume of gas available to the pump that may be transfered to the output + air2.volume + Higher quantities of this cause more air to be perfected later + but overall network volume is also increased as this increases... +*/ + +/obj/machinery/atmospherics/binary/pump + icon = 'icons/atmos/pump.dmi' + icon_state = "map_off" + + name = "gas pump" + desc = "A pump" + + can_unwrench = 1 + + var/on = 0 + var/target_pressure = ONE_ATMOSPHERE + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/pump/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/binary/pump/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/binary/pump/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/binary/pump/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/binary/pump/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/binary/pump/proc/set_max() + if(powered()) + target_pressure = MAX_OUTPUT_PRESSURE + update_icon() + +/obj/machinery/atmospherics/binary/pump/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/pump/on + icon_state = "map_on" + on = 1 + +/obj/machinery/atmospherics/binary/pump/update_icon() + ..() + + if(!powered()) + icon_state = "off" + else + icon_state = "[on ? "on" : "off"]" + +/obj/machinery/atmospherics/binary/pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/pump/process_atmos() + ..() + if((stat & (NOPOWER|BROKEN)) || !on) + return 0 + + var/output_starting_pressure = air2.return_pressure() + + if( (target_pressure - output_starting_pressure) < 0.01) + //No need to pump gas if target is already reached! + return 1 + + //Calculate necessary moles to transfer using PV=nRT + if((air1.total_moles() > 0) && (air1.temperature>0)) + var/pressure_delta = target_pressure - output_starting_pressure + var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + air2.merge(removed) + + parent1.update = 1 + + parent2.update = 1 + return 1 + +//Radio remote control +/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = on, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + return 1 + +/obj/machinery/atmospherics/binary/pump/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + if(signal.data["power"]) + on = text2num(signal.data["power"]) + + if(signal.data["power_toggle"]) + on = !on + + if(signal.data["set_output_pressure"]) + target_pressure = between( + 0, + text2num(signal.data["set_output_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) + ui.open() + +/obj/machinery/atmospherics/binary/pump/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + return data + +/obj/machinery/atmospherics/binary/pump/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure)) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/binary/pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/pump/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the pump.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else if(!istype(W, /obj/item/wrench)) + return ..() + if(!(stat & NOPOWER) && on) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index 0947b2ee860..8741acb47bf 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -1,257 +1,257 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -Thus, the two variables affect pump operation are set in New(): - air1.volume - This is the volume of gas available to the pump that may be transfered to the output - air2.volume - Higher quantities of this cause more air to be perfected later - but overall network volume is also increased as this increases... -*/ - -/obj/machinery/atmospherics/binary/volume_pump - icon = 'icons/atmos/volume_pump.dmi' - icon_state = "map_off" - - name = "volumetric gas pump" - desc = "A volumetric pump" - - can_unwrench = 1 - - var/on = 0 - var/transfer_rate = 200 - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/volume_pump/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/binary/volume_pump/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/proc/set_max() - if(powered()) - transfer_rate = MAX_TRANSFER_RATE - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/on - on = 1 - icon_state = "map_on" - -/obj/machinery/atmospherics/binary/volume_pump/atmos_init() - ..() - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/volume_pump/update_icon() - ..() - - if(!powered()) - icon_state = "off" - else - icon_state = "[on ? "on" : "off"]" - -/obj/machinery/atmospherics/binary/volume_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/volume_pump/process_atmos() - ..() - if((stat & (NOPOWER|BROKEN)) || !on) - return 0 - - // Pump mechanism just won't do anything if the pressure is too high/too low - var/input_starting_pressure = air1.return_pressure() - var/output_starting_pressure = air2.return_pressure() - - if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000)) - return 1 - - var/transfer_ratio = max(1, transfer_rate/air1.volume) - - var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio) - - air2.merge(removed) - - - parent1.update = 1 - parent2.update = 1 - - return 1 - -/obj/machinery/atmospherics/binary/volume_pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency) - -/obj/machinery/atmospherics/binary/volume_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "APV", - "power" = on, - "transfer_rate" = transfer_rate, - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal) - - return 1 - -/obj/machinery/atmospherics/binary/volume_pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - if(signal.data["power"]) - on = text2num(signal.data["power"]) - - if(signal.data["power_toggle"]) - on = !on - - if(signal.data["set_transfer_rate"]) - transfer_rate = between( - 0, - text2num(signal.data["set_transfer_rate"]), - air1.volume - ) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 310, 115, state = state) - ui.open() - -/obj/machinery/atmospherics/binary/volume_pump/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["rate"] = round(transfer_rate) - data["max_rate"] = round(MAX_TRANSFER_RATE) - return data - -/obj/machinery/atmospherics/binary/volume_pump/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["rate"]) - var/rate = href_list["rate"] - if(rate == "max") - rate = MAX_TRANSFER_RATE - . = TRUE - else if(rate == "input") - rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null - if(!isnull(rate)) - . = TRUE - else if(text2num(rate) != null) - rate = text2num(rate) - . = TRUE - if(.) - transfer_rate = Clamp(rate, 0, MAX_TRANSFER_RATE) - investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/binary/volume_pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the volume pump.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else if(!istype(W, /obj/item/wrench)) - return ..() - if(!(stat & NOPOWER) && on) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - return ..() \ No newline at end of file +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +Thus, the two variables affect pump operation are set in New(): + air1.volume + This is the volume of gas available to the pump that may be transfered to the output + air2.volume + Higher quantities of this cause more air to be perfected later + but overall network volume is also increased as this increases... +*/ + +/obj/machinery/atmospherics/binary/volume_pump + icon = 'icons/atmos/volume_pump.dmi' + icon_state = "map_off" + + name = "volumetric gas pump" + desc = "A volumetric pump" + + can_unwrench = 1 + + var/on = 0 + var/transfer_rate = 200 + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/volume_pump/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/binary/volume_pump/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/proc/set_max() + if(powered()) + transfer_rate = MAX_TRANSFER_RATE + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/on + on = 1 + icon_state = "map_on" + +/obj/machinery/atmospherics/binary/volume_pump/atmos_init() + ..() + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/volume_pump/update_icon() + ..() + + if(!powered()) + icon_state = "off" + else + icon_state = "[on ? "on" : "off"]" + +/obj/machinery/atmospherics/binary/volume_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/volume_pump/process_atmos() + ..() + if((stat & (NOPOWER|BROKEN)) || !on) + return 0 + + // Pump mechanism just won't do anything if the pressure is too high/too low + var/input_starting_pressure = air1.return_pressure() + var/output_starting_pressure = air2.return_pressure() + + if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000)) + return 1 + + var/transfer_ratio = max(1, transfer_rate/air1.volume) + + var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio) + + air2.merge(removed) + + + parent1.update = 1 + parent2.update = 1 + + return 1 + +/obj/machinery/atmospherics/binary/volume_pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency) + +/obj/machinery/atmospherics/binary/volume_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "APV", + "power" = on, + "transfer_rate" = transfer_rate, + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal) + + return 1 + +/obj/machinery/atmospherics/binary/volume_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + if(signal.data["power"]) + on = text2num(signal.data["power"]) + + if(signal.data["power_toggle"]) + on = !on + + if(signal.data["set_transfer_rate"]) + transfer_rate = between( + 0, + text2num(signal.data["set_transfer_rate"]), + air1.volume + ) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 310, 115, state = state) + ui.open() + +/obj/machinery/atmospherics/binary/volume_pump/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["rate"] = round(transfer_rate) + data["max_rate"] = round(MAX_TRANSFER_RATE) + return data + +/obj/machinery/atmospherics/binary/volume_pump/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["rate"]) + var/rate = href_list["rate"] + if(rate == "max") + rate = MAX_TRANSFER_RATE + . = TRUE + else if(rate == "input") + rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null + if(!isnull(rate)) + . = TRUE + else if(text2num(rate) != null) + rate = text2num(rate) + . = TRUE + if(.) + transfer_rate = Clamp(rate, 0, MAX_TRANSFER_RATE) + investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/binary/volume_pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the volume pump.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else if(!istype(W, /obj/item/wrench)) + return ..() + if(!(stat & NOPOWER) && on) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm index a1f80916aee..d03329e24fc 100644 --- a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm +++ b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm @@ -91,4 +91,4 @@ return WEST else return 0 - \ No newline at end of file + diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm index cde1c9e80b0..d75e2e683c1 100644 --- a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm +++ b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm @@ -31,7 +31,7 @@ icon_state = "base" ports = new() - for(var/d in cardinal) + for(var/d in GLOB.cardinal) var/datum/omni_port/new_port = new(src, d) switch(d) if(NORTH) diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index e45a8c3956f..84e212598c8 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -1,267 +1,267 @@ -/obj/machinery/atmospherics/trinary/filter - icon = 'icons/atmos/filter.dmi' - icon_state = "map" - - can_unwrench = 1 - - name = "gas filter" - - var/target_pressure = ONE_ATMOSPHERE - - var/filter_type = 0 -/* -Filter types: --1: Nothing - 0: Toxins: Toxins, Oxygen Agent B - 1: Oxygen: Oxygen ONLY - 2: Nitrogen: Nitrogen ONLY - 3: Carbon Dioxide: Carbon Dioxide ONLY - 4: Sleeping Agent (N2O) -*/ - - var/frequency = 0 - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/filter/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/filter/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/trinary/filter/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/trinary/filter/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/trinary/filter/proc/set_max() - if(powered()) - target_pressure = MAX_OUTPUT_PRESSURE - update_icon() - -/obj/machinery/atmospherics/trinary/filter/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/trinary/filter/flipped - icon_state = "mmap" - flipped = 1 - -/obj/machinery/atmospherics/trinary/filter/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/trinary/filter/update_icon() - ..() - - if(flipped) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += on ? "on" : "off" - else - icon_state += "off" - on = 0 - -/obj/machinery/atmospherics/trinary/filter/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - - add_underlay(T, node1, turn(dir, -180)) - - if(flipped) - add_underlay(T, node2, turn(dir, 90)) - else - add_underlay(T, node2, turn(dir, -90)) - - add_underlay(T, node3, dir) - -/obj/machinery/atmospherics/trinary/filter/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/filter/process_atmos() - ..() - if(!on) - return 0 - - var/output_starting_pressure = air3.return_pressure() - - if(output_starting_pressure >= target_pressure || air2.return_pressure() >= target_pressure ) - //No need to mix if target is already full! - return 1 - - //Calculate necessary moles to transfer using PV=nRT - - var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles - - if(air1.temperature > 0) - transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - - if(transfer_moles > 0) - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - - if(!removed) - return - var/datum/gas_mixture/filtered_out = new - filtered_out.temperature = removed.temperature - - switch(filter_type) - if(0) //removing hydrocarbons - filtered_out.toxins = removed.toxins - removed.toxins = 0 - - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/oxygen_agent_b)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - if(1) //removing O2 - filtered_out.oxygen = removed.oxygen - removed.oxygen = 0 - - if(2) //removing N2 - filtered_out.nitrogen = removed.nitrogen - removed.nitrogen = 0 - - if(3) //removing CO2 - filtered_out.carbon_dioxide = removed.carbon_dioxide - removed.carbon_dioxide = 0 - - if(4)//removing N2O - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/sleeping_agent)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - else - filtered_out = null - - - air2.merge(filtered_out) - air3.merge(removed) - - parent2.update = 1 - - parent3.update = 1 - - parent1.update = 1 - - return 1 - -/obj/machinery/atmospherics/trinary/filter/atmos_init() - set_frequency(frequency) - ..() - -/obj/machinery/atmospherics/trinary/filter/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/filter/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_filter.tmpl", name, 475, 155, state = state) - ui.open() - -/obj/machinery/atmospherics/trinary/filter/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["filter_type"] = filter_type - return data - -/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if(href_list["filter"]) - filter_type = text2num(href_list["filter"]) - investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos") - . = TRUE - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the filter.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else - return ..() \ No newline at end of file +/obj/machinery/atmospherics/trinary/filter + icon = 'icons/atmos/filter.dmi' + icon_state = "map" + + can_unwrench = 1 + + name = "gas filter" + + var/target_pressure = ONE_ATMOSPHERE + + var/filter_type = 0 +/* +Filter types: +-1: Nothing + 0: Toxins: Toxins, Oxygen Agent B + 1: Oxygen: Oxygen ONLY + 2: Nitrogen: Nitrogen ONLY + 3: Carbon Dioxide: Carbon Dioxide ONLY + 4: Sleeping Agent (N2O) +*/ + + var/frequency = 0 + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/filter/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/filter/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/trinary/filter/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/trinary/filter/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/trinary/filter/proc/set_max() + if(powered()) + target_pressure = MAX_OUTPUT_PRESSURE + update_icon() + +/obj/machinery/atmospherics/trinary/filter/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/trinary/filter/flipped + icon_state = "mmap" + flipped = 1 + +/obj/machinery/atmospherics/trinary/filter/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/trinary/filter/update_icon() + ..() + + if(flipped) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += on ? "on" : "off" + else + icon_state += "off" + on = 0 + +/obj/machinery/atmospherics/trinary/filter/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + + add_underlay(T, node1, turn(dir, -180)) + + if(flipped) + add_underlay(T, node2, turn(dir, 90)) + else + add_underlay(T, node2, turn(dir, -90)) + + add_underlay(T, node3, dir) + +/obj/machinery/atmospherics/trinary/filter/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/filter/process_atmos() + ..() + if(!on) + return 0 + + var/output_starting_pressure = air3.return_pressure() + + if(output_starting_pressure >= target_pressure || air2.return_pressure() >= target_pressure ) + //No need to mix if target is already full! + return 1 + + //Calculate necessary moles to transfer using PV=nRT + + var/pressure_delta = target_pressure - output_starting_pressure + var/transfer_moles + + if(air1.temperature > 0) + transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + + if(transfer_moles > 0) + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + + if(!removed) + return + var/datum/gas_mixture/filtered_out = new + filtered_out.temperature = removed.temperature + + switch(filter_type) + if(0) //removing hydrocarbons + filtered_out.toxins = removed.toxins + removed.toxins = 0 + + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/oxygen_agent_b)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + if(1) //removing O2 + filtered_out.oxygen = removed.oxygen + removed.oxygen = 0 + + if(2) //removing N2 + filtered_out.nitrogen = removed.nitrogen + removed.nitrogen = 0 + + if(3) //removing CO2 + filtered_out.carbon_dioxide = removed.carbon_dioxide + removed.carbon_dioxide = 0 + + if(4)//removing N2O + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/sleeping_agent)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + else + filtered_out = null + + + air2.merge(filtered_out) + air3.merge(removed) + + parent2.update = 1 + + parent3.update = 1 + + parent1.update = 1 + + return 1 + +/obj/machinery/atmospherics/trinary/filter/atmos_init() + set_frequency(frequency) + ..() + +/obj/machinery/atmospherics/trinary/filter/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/filter/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_filter.tmpl", name, 475, 155, state = state) + ui.open() + +/obj/machinery/atmospherics/trinary/filter/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + data["filter_type"] = filter_type + return data + +/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure) && !..()) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + if(href_list["filter"]) + filter_type = text2num(href_list["filter"]) + investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos") + . = TRUE + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the filter.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else + return ..() diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index 19849582a0f..6128c1afa33 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -1,233 +1,233 @@ -/obj/machinery/atmospherics/trinary/mixer - icon = 'icons/atmos/mixer.dmi' - icon_state = "map" - - can_unwrench = 1 - - name = "gas mixer" - - var/target_pressure = ONE_ATMOSPHERE - var/node1_concentration = 0.5 - var/node2_concentration = 0.5 - - //node 3 is the outlet, nodes 1 & 2 are intakes - -/obj/machinery/atmospherics/trinary/mixer/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/mixer/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/mixer/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/trinary/mixer/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/trinary/mixer/flipped - icon_state = "mmap" - flipped = 1 - -/obj/machinery/atmospherics/trinary/mixer/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/proc/set_max() - if(powered()) - target_pressure = MAX_OUTPUT_PRESSURE - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/update_icon(safety = 0) - ..() - - if(flipped) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += on ? "on" : "off" - else - icon_state += "off" - on = 0 - -/obj/machinery/atmospherics/trinary/mixer/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - - add_underlay(T, node1, turn(dir, -180)) - - if(flipped) - add_underlay(T, node2, turn(dir, 90)) - else - add_underlay(T, node2, turn(dir, -90)) - - add_underlay(T, node3, dir) - -/obj/machinery/atmospherics/trinary/mixer/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/New() - ..() - air3.volume = 300 - -/obj/machinery/atmospherics/trinary/mixer/process_atmos() - ..() - if(!on) - return 0 - - var/output_starting_pressure = air3.return_pressure() - - if(output_starting_pressure >= target_pressure) - //No need to mix if target is already full! - return 1 - - //Calculate necessary moles to transfer using PV=nRT - - var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles1 = 0 - var/transfer_moles2 = 0 - - if(air1.temperature > 0) - transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - if(air2.temperature > 0) - transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) - - var/air1_moles = air1.total_moles() - var/air2_moles = air2.total_moles() - - if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2)) - if(!transfer_moles1 || !transfer_moles2) return - var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2) - - transfer_moles1 *= ratio - transfer_moles2 *= ratio - - //Actually transfer the gas - - if(transfer_moles1 > 0) - var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1) - air3.merge(removed1) - - if(transfer_moles2 > 0) - var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2) - air3.merge(removed2) - - if(transfer_moles1) - parent1.update = 1 - - if(transfer_moles2) - parent2.update = 1 - - parent3.update = 1 - - return 1 - -/obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state) - ui.open() - -/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["node1_concentration"] = round(node1_concentration*100) - data["node2_concentration"] = round(node2_concentration*100) - return data - -/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if(href_list["node1"]) - var/value = text2num(href_list["node1"]) - node1_concentration = max(0, min(1, node1_concentration + value)) - node2_concentration = max(0, min(1, node2_concentration - value)) - investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["node2"]) - var/value = text2num(href_list["node2"]) - node2_concentration = max(0, min(1, node2_concentration + value)) - node1_concentration = max(0, min(1, node1_concentration - value)) - investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") - . = TRUE - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the mixer.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else - return ..() \ No newline at end of file +/obj/machinery/atmospherics/trinary/mixer + icon = 'icons/atmos/mixer.dmi' + icon_state = "map" + + can_unwrench = 1 + + name = "gas mixer" + + var/target_pressure = ONE_ATMOSPHERE + var/node1_concentration = 0.5 + var/node2_concentration = 0.5 + + //node 3 is the outlet, nodes 1 & 2 are intakes + +/obj/machinery/atmospherics/trinary/mixer/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/mixer/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/mixer/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/trinary/mixer/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/trinary/mixer/flipped + icon_state = "mmap" + flipped = 1 + +/obj/machinery/atmospherics/trinary/mixer/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/proc/set_max() + if(powered()) + target_pressure = MAX_OUTPUT_PRESSURE + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/update_icon(safety = 0) + ..() + + if(flipped) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += on ? "on" : "off" + else + icon_state += "off" + on = 0 + +/obj/machinery/atmospherics/trinary/mixer/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + + add_underlay(T, node1, turn(dir, -180)) + + if(flipped) + add_underlay(T, node2, turn(dir, 90)) + else + add_underlay(T, node2, turn(dir, -90)) + + add_underlay(T, node3, dir) + +/obj/machinery/atmospherics/trinary/mixer/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/New() + ..() + air3.volume = 300 + +/obj/machinery/atmospherics/trinary/mixer/process_atmos() + ..() + if(!on) + return 0 + + var/output_starting_pressure = air3.return_pressure() + + if(output_starting_pressure >= target_pressure) + //No need to mix if target is already full! + return 1 + + //Calculate necessary moles to transfer using PV=nRT + + var/pressure_delta = target_pressure - output_starting_pressure + var/transfer_moles1 = 0 + var/transfer_moles2 = 0 + + if(air1.temperature > 0) + transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + if(air2.temperature > 0) + transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) + + var/air1_moles = air1.total_moles() + var/air2_moles = air2.total_moles() + + if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2)) + if(!transfer_moles1 || !transfer_moles2) return + var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2) + + transfer_moles1 *= ratio + transfer_moles2 *= ratio + + //Actually transfer the gas + + if(transfer_moles1 > 0) + var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1) + air3.merge(removed1) + + if(transfer_moles2 > 0) + var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2) + air3.merge(removed2) + + if(transfer_moles1) + parent1.update = 1 + + if(transfer_moles2) + parent2.update = 1 + + parent3.update = 1 + + return 1 + +/obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state) + ui.open() + +/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + data["node1_concentration"] = round(node1_concentration*100) + data["node2_concentration"] = round(node2_concentration*100) + return data + +/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure) && !..()) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + if(href_list["node1"]) + var/value = text2num(href_list["node1"]) + node1_concentration = max(0, min(1, node1_concentration + value)) + node2_concentration = max(0, min(1, node2_concentration - value)) + investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["node2"]) + var/value = text2num(href_list["node2"]) + node2_concentration = max(0, min(1, node2_concentration + value)) + node1_concentration = max(0, min(1, node1_concentration - value)) + investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") + . = TRUE + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the mixer.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else + return ..() diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm index 8087db9966e..79fae0f7d88 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm @@ -1,213 +1,213 @@ -/obj/machinery/atmospherics/trinary - dir = SOUTH - initialize_directions = SOUTH|NORTH|WEST - use_power = IDLE_POWER_USE - - var/on = 0 - layer = GAS_FILTER_LAYER - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - var/datum/gas_mixture/air3 - - var/obj/machinery/atmospherics/node1 - var/obj/machinery/atmospherics/node2 - var/obj/machinery/atmospherics/node3 - - var/datum/pipeline/parent1 - var/datum/pipeline/parent2 - var/datum/pipeline/parent3 - - var/flipped = 0 - -/obj/machinery/atmospherics/trinary/New() - ..() - - if(!flipped) - switch(dir) - if(NORTH) - initialize_directions = EAST|NORTH|SOUTH - if(SOUTH) - initialize_directions = SOUTH|WEST|NORTH - if(EAST) - initialize_directions = EAST|WEST|SOUTH - if(WEST) - initialize_directions = WEST|NORTH|EAST - else - switch(dir) - if(NORTH) - initialize_directions = SOUTH|NORTH|WEST - if(SOUTH) - initialize_directions = NORTH|SOUTH|EAST - if(EAST) - initialize_directions = WEST|EAST|NORTH - if(WEST) - initialize_directions = EAST|WEST|SOUTH - - air1 = new - air2 = new - air3 = new - - air1.volume = 200 - air2.volume = 200 - air3.volume = 200 - -/obj/machinery/atmospherics/trinary/Destroy() - if(node1) - node1.disconnect(src) - node1 = null - nullifyPipenet(parent1) - if(node2) - node2.disconnect(src) - node2 = null - nullifyPipenet(parent2) - if(node3) - node3.disconnect(src) - node3 = null - nullifyPipenet(parent3) - return ..() - -/obj/machinery/atmospherics/trinary/atmos_init() - ..() - //Mixer: - //1 and 2 is input - //Node 3 is output - //If we flip the mixer, 1 and 3 shall exchange positions - - //Filter: - //Node 1 is input - //Node 2 is filtered output - //Node 3 is rest output - //If we flip the filter, 1 and 3 shall exchange positions - - var/node1_connect = turn(dir, -180) - var/node2_connect = turn(dir, -90) - var/node3_connect = dir - - if(flipped) - node2_connect = turn(node2_connect, -180) - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - node2 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) - if(target.initialize_directions & get_dir(target,src)) - node3 = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/trinary/build_network(remove_deferral = FALSE) - if(!parent1) - parent1 = new /datum/pipeline() - parent1.build_pipeline(src) - - if(!parent2) - parent2 = new /datum/pipeline() - parent2.build_pipeline(src) - - if(!parent3) - parent3 = new /datum/pipeline() - parent3.build_pipeline(src) - ..() - -/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - qdel(parent1) - node1 = null - else if(reference == node2) - if(istype(node2, /obj/machinery/atmospherics/pipe)) - qdel(parent2) - node2 = null - else if(reference == node3) - if(istype(node3, /obj/machinery/atmospherics/pipe)) - qdel(parent3) - node3 = null - update_icon() - -/obj/machinery/atmospherics/trinary/nullifyPipenet(datum/pipeline/P) - ..() - if(!P) - return - if(P == parent1) - parent1.other_airs -= air1 - parent1 = null - else if(P == parent2) - parent2.other_airs -= air2 - parent2 = null - else if(P == parent3) - parent3.other_airs -= air3 - parent3 = null - -/obj/machinery/atmospherics/trinary/returnPipenetAir(datum/pipeline/P) - if(P == parent1) - return air1 - else if(P == parent2) - return air2 - else if(P == parent3) - return air3 - -/obj/machinery/atmospherics/trinary/pipeline_expansion(datum/pipeline/P) - if(P) - if(parent1 == P) - return list(node1) - else if(parent2 == P) - return list(node2) - else if(parent3 == P) - return list(node3) - return list(node1, node2, node3) - -/obj/machinery/atmospherics/trinary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) - if(A == node1) - parent1 = P - else if(A == node2) - parent2 = P - else if(A == node3) - parent3 = P - -/obj/machinery/atmospherics/trinary/returnPipenet(obj/machinery/atmospherics/A) - if(A == node1) - return parent1 - else if(A == node2) - return parent2 - else if(A == node3) - return parent3 - -/obj/machinery/atmospherics/trinary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) - if(Old == parent1) - parent1 = New - else if(Old == parent2) - parent2 = New - else if(Old == parent3) - parent3 = New - -/obj/machinery/atmospherics/trinary/unsafe_pressure_release(var/mob/user,var/pressures) - ..() - - var/turf/T = get_turf(src) - if(T) - //Remove the gas from air1+air2+air3 and assume it - var/datum/gas_mixture/environment = T.return_air() - var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) - lost += pressures*environment.volume/(air3.temperature * R_IDEAL_GAS_EQUATION) - var/shared_loss = lost/3 - - var/datum/gas_mixture/to_release = air1.remove(shared_loss) - to_release.merge(air2.remove(shared_loss)) - to_release.merge(air3.remove(shared_loss)) - T.assume_air(to_release) - air_update_turf(1) - -/obj/machinery/atmospherics/trinary/process_atmos() - ..() - return parent1 && parent2 && parent3 +/obj/machinery/atmospherics/trinary + dir = SOUTH + initialize_directions = SOUTH|NORTH|WEST + use_power = IDLE_POWER_USE + + var/on = 0 + layer = GAS_FILTER_LAYER + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + var/datum/gas_mixture/air3 + + var/obj/machinery/atmospherics/node1 + var/obj/machinery/atmospherics/node2 + var/obj/machinery/atmospherics/node3 + + var/datum/pipeline/parent1 + var/datum/pipeline/parent2 + var/datum/pipeline/parent3 + + var/flipped = 0 + +/obj/machinery/atmospherics/trinary/New() + ..() + + if(!flipped) + switch(dir) + if(NORTH) + initialize_directions = EAST|NORTH|SOUTH + if(SOUTH) + initialize_directions = SOUTH|WEST|NORTH + if(EAST) + initialize_directions = EAST|WEST|SOUTH + if(WEST) + initialize_directions = WEST|NORTH|EAST + else + switch(dir) + if(NORTH) + initialize_directions = SOUTH|NORTH|WEST + if(SOUTH) + initialize_directions = NORTH|SOUTH|EAST + if(EAST) + initialize_directions = WEST|EAST|NORTH + if(WEST) + initialize_directions = EAST|WEST|SOUTH + + air1 = new + air2 = new + air3 = new + + air1.volume = 200 + air2.volume = 200 + air3.volume = 200 + +/obj/machinery/atmospherics/trinary/Destroy() + if(node1) + node1.disconnect(src) + node1 = null + nullifyPipenet(parent1) + if(node2) + node2.disconnect(src) + node2 = null + nullifyPipenet(parent2) + if(node3) + node3.disconnect(src) + node3 = null + nullifyPipenet(parent3) + return ..() + +/obj/machinery/atmospherics/trinary/atmos_init() + ..() + //Mixer: + //1 and 2 is input + //Node 3 is output + //If we flip the mixer, 1 and 3 shall exchange positions + + //Filter: + //Node 1 is input + //Node 2 is filtered output + //Node 3 is rest output + //If we flip the filter, 1 and 3 shall exchange positions + + var/node1_connect = turn(dir, -180) + var/node2_connect = turn(dir, -90) + var/node3_connect = dir + + if(flipped) + node2_connect = turn(node2_connect, -180) + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + node2 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) + if(target.initialize_directions & get_dir(target,src)) + node3 = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/trinary/build_network(remove_deferral = FALSE) + if(!parent1) + parent1 = new /datum/pipeline() + parent1.build_pipeline(src) + + if(!parent2) + parent2 = new /datum/pipeline() + parent2.build_pipeline(src) + + if(!parent3) + parent3 = new /datum/pipeline() + parent3.build_pipeline(src) + ..() + +/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + qdel(parent1) + node1 = null + else if(reference == node2) + if(istype(node2, /obj/machinery/atmospherics/pipe)) + qdel(parent2) + node2 = null + else if(reference == node3) + if(istype(node3, /obj/machinery/atmospherics/pipe)) + qdel(parent3) + node3 = null + update_icon() + +/obj/machinery/atmospherics/trinary/nullifyPipenet(datum/pipeline/P) + ..() + if(!P) + return + if(P == parent1) + parent1.other_airs -= air1 + parent1 = null + else if(P == parent2) + parent2.other_airs -= air2 + parent2 = null + else if(P == parent3) + parent3.other_airs -= air3 + parent3 = null + +/obj/machinery/atmospherics/trinary/returnPipenetAir(datum/pipeline/P) + if(P == parent1) + return air1 + else if(P == parent2) + return air2 + else if(P == parent3) + return air3 + +/obj/machinery/atmospherics/trinary/pipeline_expansion(datum/pipeline/P) + if(P) + if(parent1 == P) + return list(node1) + else if(parent2 == P) + return list(node2) + else if(parent3 == P) + return list(node3) + return list(node1, node2, node3) + +/obj/machinery/atmospherics/trinary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) + if(A == node1) + parent1 = P + else if(A == node2) + parent2 = P + else if(A == node3) + parent3 = P + +/obj/machinery/atmospherics/trinary/returnPipenet(obj/machinery/atmospherics/A) + if(A == node1) + return parent1 + else if(A == node2) + return parent2 + else if(A == node3) + return parent3 + +/obj/machinery/atmospherics/trinary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) + if(Old == parent1) + parent1 = New + else if(Old == parent2) + parent2 = New + else if(Old == parent3) + parent3 = New + +/obj/machinery/atmospherics/trinary/unsafe_pressure_release(var/mob/user,var/pressures) + ..() + + var/turf/T = get_turf(src) + if(T) + //Remove the gas from air1+air2+air3 and assume it + var/datum/gas_mixture/environment = T.return_air() + var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) + lost += pressures*environment.volume/(air3.temperature * R_IDEAL_GAS_EQUATION) + var/shared_loss = lost/3 + + var/datum/gas_mixture/to_release = air1.remove(shared_loss) + to_release.merge(air2.remove(shared_loss)) + to_release.merge(air3.remove(shared_loss)) + T.assume_air(to_release) + air_update_turf(1) + +/obj/machinery/atmospherics/trinary/process_atmos() + ..() + return parent1 && parent2 && parent3 diff --git a/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm b/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm index 7b3f8299339..31039a412d5 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm @@ -177,4 +177,4 @@ switch_side() #undef TVALVE_STATE_STRAIGHT -#undef TVALVE_STATE_SIDE \ No newline at end of file +#undef TVALVE_STATE_SIDE diff --git a/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm b/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm index a9c3b98ad74..9b06019e49e 100644 --- a/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm +++ b/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm @@ -45,4 +45,4 @@ parent.update = 1 - return 1 \ No newline at end of file + return 1 diff --git a/code/ATMOSPHERICS/components/unary_devices/tank.dm b/code/ATMOSPHERICS/components/unary_devices/tank.dm index cb82417033b..11a752b9030 100644 --- a/code/ATMOSPHERICS/components/unary_devices/tank.dm +++ b/code/ATMOSPHERICS/components/unary_devices/tank.dm @@ -110,4 +110,4 @@ var/datum/gas/oxygen_agent_b/trace_gas = new trace_gas.moles = (50 * ONE_ATMOSPHERE) * (air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - air_contents.trace_gases += trace_gas \ No newline at end of file + air_contents.trace_gases += trace_gas diff --git a/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm b/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm index 5a6e76a182b..6cd050de022 100644 --- a/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm +++ b/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm @@ -77,4 +77,4 @@ parent.update = 1 - return 1 \ No newline at end of file + return 1 diff --git a/code/ATMOSPHERICS/components/unary_devices/unary_base.dm b/code/ATMOSPHERICS/components/unary_devices/unary_base.dm index 4ef7743fc8c..14c93dd0490 100644 --- a/code/ATMOSPHERICS/components/unary_devices/unary_base.dm +++ b/code/ATMOSPHERICS/components/unary_devices/unary_base.dm @@ -102,4 +102,4 @@ /obj/machinery/atmospherics/unary/process_atmos() ..() - return parent \ No newline at end of file + return parent diff --git a/code/ATMOSPHERICS/datum_icon_manager.dm b/code/ATMOSPHERICS/datum_icon_manager.dm index 75cb0fce8ce..2286b3523a0 100644 --- a/code/ATMOSPHERICS/datum_icon_manager.dm +++ b/code/ATMOSPHERICS/datum_icon_manager.dm @@ -6,26 +6,16 @@ // atmospherics devices. //-------------------------------------------- -#define PIPE_COLOR_GREY "#ffffff" //yes white is grey -#define PIPE_COLOR_RED "#ff0000" -#define PIPE_COLOR_BLUE "#0000ff" -#define PIPE_COLOR_CYAN "#00ffff" -#define PIPE_COLOR_GREEN "#00ff00" -#define PIPE_COLOR_YELLOW "#ffcc00" -#define PIPE_COLOR_PURPLE "#5c1ec0" - -var/global/list/pipe_colors = list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE) - /proc/pipe_color_lookup(var/color) - for(var/C in pipe_colors) - if(color == pipe_colors[C]) + for(var/C in GLOB.pipe_colors) + if(color == GLOB.pipe_colors[C]) return "[C]" /proc/pipe_color_check(var/color) if(!color) return 1 - for(var/C in pipe_colors) - if(color == pipe_colors[C]) + for(var/C in GLOB.pipe_colors) + if(color == GLOB.pipe_colors[C]) return 1 return 0 @@ -105,10 +95,10 @@ var/global/list/pipe_colors = list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_ var/image/I = image('icons/atmos/pipes.dmi', icon_state = state) pipe_icons[cache_name] = I - for(var/pipe_color in pipe_colors) + for(var/pipe_color in GLOB.pipe_colors) I = image('icons/atmos/pipes.dmi', icon_state = state) - I.color = pipe_colors[pipe_color] - pipe_icons[state + "[pipe_colors[pipe_color]]"] = I + I.color = GLOB.pipe_colors[pipe_color] + pipe_icons[state + "[GLOB.pipe_colors[pipe_color]]"] = I pipe = new ('icons/atmos/heat.dmi') for(var/state in pipe.IconStates()) @@ -137,10 +127,10 @@ var/global/list/pipe_colors = list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_ if(findtext(state, "core") || findtext(state, "4way")) var/image/I = image('icons/atmos/manifold.dmi', icon_state = state) manifold_icons[state] = I - for(var/pipe_color in pipe_colors) + for(var/pipe_color in GLOB.pipe_colors) I = image('icons/atmos/manifold.dmi', icon_state = state) - I.color = pipe_colors[pipe_color] - manifold_icons[state + pipe_colors[pipe_color]] = I + I.color = GLOB.pipe_colors[pipe_color] + manifold_icons[state + GLOB.pipe_colors[pipe_color]] = I /datum/pipe_icon_manager/proc/gen_device_icons() if(!device_icons) @@ -185,10 +175,10 @@ var/global/list/pipe_colors = list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_ var/cache_name = state - for(var/D in cardinal) + for(var/D in GLOB.cardinal) var/image/I = image(icon('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D)) underlays[cache_name + "[D]"] = I - for(var/pipe_color in pipe_colors) + for(var/pipe_color in GLOB.pipe_colors) I = image(icon('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D)) - I.color = pipe_colors[pipe_color] - underlays[state + "[D]" + "[pipe_colors[pipe_color]]"] = I + I.color = GLOB.pipe_colors[pipe_color] + underlays[state + "[D]" + "[GLOB.pipe_colors[pipe_color]]"] = I diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index 43082c38ded..2cf3edfb275 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -1,276 +1,276 @@ -/datum/pipeline - var/datum/gas_mixture/air - var/list/datum/gas_mixture/other_airs = list() - - var/list/obj/machinery/atmospherics/pipe/members = list() - var/list/obj/machinery/atmospherics/other_atmosmch = list() - - var/update = 1 - - var/alert_pressure = 0 - -/datum/pipeline/New() - SSair.networks += src - -/datum/pipeline/Destroy() - SSair.networks -= src - if(air && air.volume) - temporarily_store_air() - for(var/obj/machinery/atmospherics/pipe/P in members) - P.parent = null - for(var/obj/machinery/atmospherics/A in other_atmosmch) - A.nullifyPipenet(src) - return ..() - -/datum/pipeline/process()//This use to be called called from the pipe networks - if(update) - update = 0 - reconcile_air() - return - -var/pipenetwarnings = 10 - -/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/base) - var/volume = 0 - if(istype(base, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/E = base - volume = E.volume - alert_pressure = E.alert_pressure - members += E - if(E.air_temporary) - air = E.air_temporary - E.air_temporary = null - else - addMachineryMember(base) - if(!air) - air = new - var/list/possible_expansions = list(base) - while(possible_expansions.len>0) - for(var/obj/machinery/atmospherics/borderline in possible_expansions) - - var/list/result = borderline.pipeline_expansion(src) - - if(result.len>0) - for(var/obj/machinery/atmospherics/P in result) - if(istype(P, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/item = P - if(!members.Find(item)) - - if(item.parent) - log_runtime(EXCEPTION("[item.type] \[\ref[item]] added to a pipenet while still having one ([item.parent]) (pipes leading to the same spot stacking in one turf). Nearby: [item.x], [item.y], [item.z].")) - members += item - possible_expansions += item - - volume += item.volume - item.parent = src - - alert_pressure = min(alert_pressure, item.alert_pressure) - - if(item.air_temporary) - air.merge(item.air_temporary) - item.air_temporary = null - else - P.setPipenet(src, borderline) - addMachineryMember(P) - - possible_expansions -= borderline - - air.volume = volume - -/datum/pipeline/proc/addMachineryMember(obj/machinery/atmospherics/A) - other_atmosmch |= A - var/datum/gas_mixture/G = A.returnPipenetAir(src) - other_airs |= G - -/datum/pipeline/proc/addMember(obj/machinery/atmospherics/A, obj/machinery/atmospherics/N) - if(istype(A, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/P = A - P.parent = src - var/list/adjacent = P.pipeline_expansion() - for(var/obj/machinery/atmospherics/pipe/I in adjacent) - if(I.parent == src) - continue - var/datum/pipeline/E = I.parent - merge(E) - if(!members.Find(P)) - members += P - air.volume += P.volume - else - A.setPipenet(src, N) - addMachineryMember(A) - -/datum/pipeline/proc/merge(datum/pipeline/E) - air.volume += E.air.volume - members.Add(E.members) - for(var/obj/machinery/atmospherics/pipe/S in E.members) - S.parent = src - air.merge(E.air) - for(var/obj/machinery/atmospherics/A in E.other_atmosmch) - A.replacePipenet(E, src) - other_atmosmch.Add(E.other_atmosmch) - other_airs.Add(E.other_airs) - E.members.Cut() - E.other_atmosmch.Cut() - qdel(E) - -/obj/machinery/atmospherics/proc/addMember(obj/machinery/atmospherics/A) - var/datum/pipeline/P = returnPipenet(A) - P.addMember(A, src) - -/obj/machinery/atmospherics/pipe/addMember(obj/machinery/atmospherics/A) - parent.addMember(A, src) - -/datum/pipeline/proc/temporarily_store_air() - //Update individual gas_mixtures by volume ratio - - for(var/obj/machinery/atmospherics/pipe/member in members) - member.air_temporary = new - member.air_temporary.volume = member.volume - - member.air_temporary.oxygen = air.oxygen*member.volume/air.volume - member.air_temporary.nitrogen = air.nitrogen*member.volume/air.volume - member.air_temporary.toxins = air.toxins*member.volume/air.volume - member.air_temporary.carbon_dioxide = air.carbon_dioxide*member.volume/air.volume - - member.air_temporary.temperature = air.temperature - - if(air.trace_gases.len) - for(var/datum/gas/trace_gas in air.trace_gases) - var/datum/gas/corresponding = new trace_gas.type() - member.air_temporary.trace_gases += corresponding - - corresponding.moles = trace_gas.moles*member.volume/air.volume - -/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) - var/total_heat_capacity = air.heat_capacity() - var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) - - if(istype(target, /turf/simulated)) - var/turf/simulated/modeled_location = target - - if(modeled_location.blocks_air) - - if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - modeled_location.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - modeled_location.temperature += heat/modeled_location.heat_capacity - - else - var/delta_temperature = 0 - var/sharer_heat_capacity = 0 - - delta_temperature = (air.temperature - modeled_location.air.temperature) - sharer_heat_capacity = modeled_location.air.heat_capacity() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/total_heat_capacity - sharer_temperature_delta = heat/sharer_heat_capacity - else - return 1 - - air.temperature += self_temperature_delta - - modeled_location.air.temperature += sharer_temperature_delta - - - else - if((target.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - target.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - update = 1 - -/datum/pipeline/proc/reconcile_air() - var/list/datum/gas_mixture/GL = list() - var/list/datum/pipeline/PL = list() - PL += src - - for(var/i=1;i<=PL.len;i++) - var/datum/pipeline/P = PL[i] - if(!P) - return - GL += P.air - GL += P.other_airs - for(var/obj/machinery/atmospherics/binary/valve/V in P.other_atmosmch) - if(V.open) - PL |= V.parent1 - PL |= V.parent2 - for(var/obj/machinery/atmospherics/trinary/tvalve/T in P.other_atmosmch) - if(!T.state) - if(src != T.parent2) // otherwise dc'd side connects to both other sides! - PL |= T.parent1 - PL |= T.parent3 - else - if(src != T.parent3) - PL |= T.parent1 - PL |= T.parent2 - for(var/obj/machinery/atmospherics/unary/portables_connector/C in P.other_atmosmch) - if(C.connected_device) - GL += C.portableConnectorReturnAir() - - var/total_volume = 0 - var/total_thermal_energy = 0 - var/total_heat_capacity = 0 - var/total_oxygen = 0 - var/total_nitrogen = 0 - var/total_toxins = 0 - var/total_carbon_dioxide = 0 - var/list/total_trace_gases = list() - - for(var/datum/gas_mixture/G in GL) - total_volume += G.volume - total_thermal_energy += G.thermal_energy() - total_heat_capacity += G.heat_capacity() - - total_oxygen += G.oxygen - total_nitrogen += G.nitrogen - total_toxins += G.toxins - total_carbon_dioxide += G.carbon_dioxide - - if(G.trace_gases.len) - for(var/datum/gas/trace_gas in G.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in total_trace_gases - if(!corresponding) - corresponding = new trace_gas.type() - total_trace_gases += corresponding - - corresponding.moles += trace_gas.moles - - if(total_volume > 0) - - //Calculate temperature - var/temperature = 0 - - if(total_heat_capacity > 0) - temperature = total_thermal_energy/total_heat_capacity - - //Update individual gas_mixtures by volume ratio - for(var/datum/gas_mixture/G in GL) - G.oxygen = total_oxygen*G.volume/total_volume - G.nitrogen = total_nitrogen*G.volume/total_volume - G.toxins = total_toxins*G.volume/total_volume - G.carbon_dioxide = total_carbon_dioxide*G.volume/total_volume - - G.temperature = temperature - - if(total_trace_gases.len) - for(var/datum/gas/trace_gas in total_trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in G.trace_gases - if(!corresponding) - corresponding = new trace_gas.type() - G.trace_gases += corresponding - - corresponding.moles = trace_gas.moles*G.volume/total_volume +/datum/pipeline + var/datum/gas_mixture/air + var/list/datum/gas_mixture/other_airs = list() + + var/list/obj/machinery/atmospherics/pipe/members = list() + var/list/obj/machinery/atmospherics/other_atmosmch = list() + + var/update = 1 + + var/alert_pressure = 0 + +/datum/pipeline/New() + SSair.networks += src + +/datum/pipeline/Destroy() + SSair.networks -= src + if(air && air.volume) + temporarily_store_air() + for(var/obj/machinery/atmospherics/pipe/P in members) + P.parent = null + for(var/obj/machinery/atmospherics/A in other_atmosmch) + A.nullifyPipenet(src) + return ..() + +/datum/pipeline/process()//This use to be called called from the pipe networks + if(update) + update = 0 + reconcile_air() + return + +GLOBAL_VAR_INIT(pipenetwarnings, 10) + +/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/base) + var/volume = 0 + if(istype(base, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/E = base + volume = E.volume + alert_pressure = E.alert_pressure + members += E + if(E.air_temporary) + air = E.air_temporary + E.air_temporary = null + else + addMachineryMember(base) + if(!air) + air = new + var/list/possible_expansions = list(base) + while(possible_expansions.len>0) + for(var/obj/machinery/atmospherics/borderline in possible_expansions) + + var/list/result = borderline.pipeline_expansion(src) + + if(result.len>0) + for(var/obj/machinery/atmospherics/P in result) + if(istype(P, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/item = P + if(!members.Find(item)) + + if(item.parent) + log_runtime(EXCEPTION("[item.type] \[\ref[item]] added to a pipenet while still having one ([item.parent]) (pipes leading to the same spot stacking in one turf). Nearby: [item.x], [item.y], [item.z].")) + members += item + possible_expansions += item + + volume += item.volume + item.parent = src + + alert_pressure = min(alert_pressure, item.alert_pressure) + + if(item.air_temporary) + air.merge(item.air_temporary) + item.air_temporary = null + else + P.setPipenet(src, borderline) + addMachineryMember(P) + + possible_expansions -= borderline + + air.volume = volume + +/datum/pipeline/proc/addMachineryMember(obj/machinery/atmospherics/A) + other_atmosmch |= A + var/datum/gas_mixture/G = A.returnPipenetAir(src) + other_airs |= G + +/datum/pipeline/proc/addMember(obj/machinery/atmospherics/A, obj/machinery/atmospherics/N) + if(istype(A, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/P = A + P.parent = src + var/list/adjacent = P.pipeline_expansion() + for(var/obj/machinery/atmospherics/pipe/I in adjacent) + if(I.parent == src) + continue + var/datum/pipeline/E = I.parent + merge(E) + if(!members.Find(P)) + members += P + air.volume += P.volume + else + A.setPipenet(src, N) + addMachineryMember(A) + +/datum/pipeline/proc/merge(datum/pipeline/E) + air.volume += E.air.volume + members.Add(E.members) + for(var/obj/machinery/atmospherics/pipe/S in E.members) + S.parent = src + air.merge(E.air) + for(var/obj/machinery/atmospherics/A in E.other_atmosmch) + A.replacePipenet(E, src) + other_atmosmch.Add(E.other_atmosmch) + other_airs.Add(E.other_airs) + E.members.Cut() + E.other_atmosmch.Cut() + qdel(E) + +/obj/machinery/atmospherics/proc/addMember(obj/machinery/atmospherics/A) + var/datum/pipeline/P = returnPipenet(A) + P.addMember(A, src) + +/obj/machinery/atmospherics/pipe/addMember(obj/machinery/atmospherics/A) + parent.addMember(A, src) + +/datum/pipeline/proc/temporarily_store_air() + //Update individual gas_mixtures by volume ratio + + for(var/obj/machinery/atmospherics/pipe/member in members) + member.air_temporary = new + member.air_temporary.volume = member.volume + + member.air_temporary.oxygen = air.oxygen*member.volume/air.volume + member.air_temporary.nitrogen = air.nitrogen*member.volume/air.volume + member.air_temporary.toxins = air.toxins*member.volume/air.volume + member.air_temporary.carbon_dioxide = air.carbon_dioxide*member.volume/air.volume + + member.air_temporary.temperature = air.temperature + + if(air.trace_gases.len) + for(var/datum/gas/trace_gas in air.trace_gases) + var/datum/gas/corresponding = new trace_gas.type() + member.air_temporary.trace_gases += corresponding + + corresponding.moles = trace_gas.moles*member.volume/air.volume + +/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) + var/total_heat_capacity = air.heat_capacity() + var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) + + if(istype(target, /turf/simulated)) + var/turf/simulated/modeled_location = target + + if(modeled_location.blocks_air) + + if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - modeled_location.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + modeled_location.temperature += heat/modeled_location.heat_capacity + + else + var/delta_temperature = 0 + var/sharer_heat_capacity = 0 + + delta_temperature = (air.temperature - modeled_location.air.temperature) + sharer_heat_capacity = modeled_location.air.heat_capacity() + + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 + + if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) + + self_temperature_delta = -heat/total_heat_capacity + sharer_temperature_delta = heat/sharer_heat_capacity + else + return 1 + + air.temperature += self_temperature_delta + + modeled_location.air.temperature += sharer_temperature_delta + + + else + if((target.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - target.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + update = 1 + +/datum/pipeline/proc/reconcile_air() + var/list/datum/gas_mixture/GL = list() + var/list/datum/pipeline/PL = list() + PL += src + + for(var/i=1;i<=PL.len;i++) + var/datum/pipeline/P = PL[i] + if(!P) + return + GL += P.air + GL += P.other_airs + for(var/obj/machinery/atmospherics/binary/valve/V in P.other_atmosmch) + if(V.open) + PL |= V.parent1 + PL |= V.parent2 + for(var/obj/machinery/atmospherics/trinary/tvalve/T in P.other_atmosmch) + if(!T.state) + if(src != T.parent2) // otherwise dc'd side connects to both other sides! + PL |= T.parent1 + PL |= T.parent3 + else + if(src != T.parent3) + PL |= T.parent1 + PL |= T.parent2 + for(var/obj/machinery/atmospherics/unary/portables_connector/C in P.other_atmosmch) + if(C.connected_device) + GL += C.portableConnectorReturnAir() + + var/total_volume = 0 + var/total_thermal_energy = 0 + var/total_heat_capacity = 0 + var/total_oxygen = 0 + var/total_nitrogen = 0 + var/total_toxins = 0 + var/total_carbon_dioxide = 0 + var/list/total_trace_gases = list() + + for(var/datum/gas_mixture/G in GL) + total_volume += G.volume + total_thermal_energy += G.thermal_energy() + total_heat_capacity += G.heat_capacity() + + total_oxygen += G.oxygen + total_nitrogen += G.nitrogen + total_toxins += G.toxins + total_carbon_dioxide += G.carbon_dioxide + + if(G.trace_gases.len) + for(var/datum/gas/trace_gas in G.trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in total_trace_gases + if(!corresponding) + corresponding = new trace_gas.type() + total_trace_gases += corresponding + + corresponding.moles += trace_gas.moles + + if(total_volume > 0) + + //Calculate temperature + var/temperature = 0 + + if(total_heat_capacity > 0) + temperature = total_thermal_energy/total_heat_capacity + + //Update individual gas_mixtures by volume ratio + for(var/datum/gas_mixture/G in GL) + G.oxygen = total_oxygen*G.volume/total_volume + G.nitrogen = total_nitrogen*G.volume/total_volume + G.toxins = total_toxins*G.volume/total_volume + G.carbon_dioxide = total_carbon_dioxide*G.volume/total_volume + + G.temperature = temperature + + if(total_trace_gases.len) + for(var/datum/gas/trace_gas in total_trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in G.trace_gases + if(!corresponding) + corresponding = new trace_gas.type() + G.trace_gases += corresponding + + corresponding.moles = trace_gas.moles*G.volume/total_volume diff --git a/code/ATMOSPHERICS/pipes/cap.dm b/code/ATMOSPHERICS/pipes/cap.dm index 8421685d44c..15b53d83e9c 100644 --- a/code/ATMOSPHERICS/pipes/cap.dm +++ b/code/ATMOSPHERICS/pipes/cap.dm @@ -123,4 +123,4 @@ connect_types = list(2) layer = 2.39 icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE \ No newline at end of file + color = PIPE_COLOR_BLUE diff --git a/code/ATMOSPHERICS/pipes/manifold.dm b/code/ATMOSPHERICS/pipes/manifold.dm index 13e35d10c7b..2eef5ad74c4 100644 --- a/code/ATMOSPHERICS/pipes/manifold.dm +++ b/code/ATMOSPHERICS/pipes/manifold.dm @@ -33,7 +33,7 @@ /obj/machinery/atmospherics/pipe/manifold/atmos_init() ..() - for(var/D in cardinal) + for(var/D in GLOB.cardinal) if(D == dir) continue for(var/obj/machinery/atmospherics/target in get_step(src, D)) @@ -116,7 +116,7 @@ /obj/machinery/atmospherics/pipe/manifold/update_icon(var/safety = 0) ..() - + if(!check_icon_cache()) return diff --git a/code/ATMOSPHERICS/pipes/manifold4w.dm b/code/ATMOSPHERICS/pipes/manifold4w.dm index d2cee29c0ba..2b384b3684b 100644 --- a/code/ATMOSPHERICS/pipes/manifold4w.dm +++ b/code/ATMOSPHERICS/pipes/manifold4w.dm @@ -90,7 +90,7 @@ /obj/machinery/atmospherics/pipe/manifold4w/update_icon(var/safety = 0) ..() - + if(!check_icon_cache()) return @@ -137,7 +137,7 @@ /obj/machinery/atmospherics/pipe/manifold4w/atmos_init() ..() - for(var/D in cardinal) + for(var/D in GLOB.cardinal) for(var/obj/machinery/atmospherics/target in get_step(src, D)) if(target.initialize_directions & get_dir(target,src)) var/c = check_connect_types(target,src) diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple.dm index b966690733b..94983f62acd 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple.dm @@ -48,7 +48,7 @@ if(initPipe) normalize_dir() var/N = 2 - for(var/D in cardinal) + for(var/D in GLOB.cardinal) if(D & initialize_directions) N-- for(var/obj/machinery/atmospherics/target in get_step(src, D)) @@ -138,7 +138,7 @@ /obj/machinery/atmospherics/pipe/simple/update_icon(var/safety = 0) ..() - + if(!check_icon_cache()) return diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_he.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_he.dm index 35df3c60d23..9c6b0da2121 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_he.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_he.dm @@ -69,7 +69,7 @@ if(initPipe) normalize_dir() var/N = 2 - for(var/D in cardinal) + for(var/D in GLOB.cardinal) if(D & initialize_directions_he) N-- for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src, D)) diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm index db982639c2f..e0bd78bc2de 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm @@ -68,4 +68,4 @@ color = PIPE_COLOR_GREEN /obj/machinery/atmospherics/pipe/simple/hidden/purple - color = PIPE_COLOR_PURPLE \ No newline at end of file + color = PIPE_COLOR_PURPLE diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm index ce6ff78b2d0..e7e5ec55e90 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm @@ -8,4 +8,4 @@ fatigue_pressure = 900*ONE_ATMOSPHERE alert_pressure = 900*ONE_ATMOSPHERE - level = 2 \ No newline at end of file + level = 2 diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm index 1ca4da646ac..410c992c74c 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm @@ -65,4 +65,4 @@ /obj/machinery/atmospherics/pipe/simple/visible/universal/update_underlays() ..() - update_icon() \ No newline at end of file + update_icon() diff --git a/code/LINDA/LINDA_fire.dm b/code/LINDA/LINDA_fire.dm index 9467f7a3742..6ec505d9f9a 100644 --- a/code/LINDA/LINDA_fire.dm +++ b/code/LINDA/LINDA_fire.dm @@ -66,7 +66,7 @@ if(!fake) SSair.hotspots += src perform_exposure() - dir = pick(cardinal) + dir = pick(GLOB.cardinal) air_update_turf() /obj/effect/hotspot/proc/perform_exposure() @@ -133,7 +133,7 @@ //Possible spread due to radiated heat if(location.air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_SPREAD) var/radiated_temperature = location.air.temperature*FIRE_SPREAD_RADIOSITY_SCALE - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(location.atmos_adjacent_turfs & direction)) var/turf/simulated/wall/W = get_step(src, direction) if(istype(W)) @@ -309,7 +309,7 @@ if(dist == max_dist) continue - for(var/dir in cardinal) + for(var/dir in GLOB.cardinal) var/turf/link = get_step(T, dir) if (!link) continue @@ -338,4 +338,4 @@ if(prob(chance) || bypass_rng) T.visible_message("[T] melts!") T.burn_down() - return affected \ No newline at end of file + return affected diff --git a/code/LINDA/LINDA_system.dm b/code/LINDA/LINDA_system.dm index c9d168a0159..c5bc65d3bf7 100644 --- a/code/LINDA/LINDA_system.dm +++ b/code/LINDA/LINDA_system.dm @@ -62,7 +62,7 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5) /turf/proc/CalculateAdjacentTurfs() atmos_adjacent_turfs_amount = 0 - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) var/turf/T = get_step(src, direction) if(!istype(T)) continue @@ -89,7 +89,7 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5) var/adjacent_turfs = list() var/turf/simulated/curloc = src - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(curloc.atmos_adjacent_turfs & direction)) continue @@ -99,11 +99,11 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5) if(!alldir) return adjacent_turfs - for(var/direction in diagonals) + for(var/direction in GLOB.diagonals) var/matchingDirections = 0 var/turf/simulated/S = get_step(curloc, direction) - for(var/checkDirection in cardinal) + for(var/checkDirection in GLOB.cardinal) if(!(S.atmos_adjacent_turfs & checkDirection)) continue var/turf/simulated/checkTurf = get_step(S, checkDirection) @@ -142,44 +142,33 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5) return T.atmos_spawn_air(text, amount) -var/const/SPAWN_HEAT = 1 -var/const/SPAWN_20C = 2 -var/const/SPAWN_TOXINS = 4 -var/const/SPAWN_OXYGEN = 8 -var/const/SPAWN_CO2 = 16 -var/const/SPAWN_NITROGEN = 32 - -var/const/SPAWN_N2O = 64 - -var/const/SPAWN_AIR = 256 - /turf/simulated/proc/atmos_spawn_air(var/flag, var/amount) if(!text || !amount || !air) return var/datum/gas_mixture/G = new - if(flag & SPAWN_20C) + if(flag & LINDA_SPAWN_20C) G.temperature = T20C - if(flag & SPAWN_HEAT) + if(flag & LINDA_SPAWN_HEAT) G.temperature += 1000 - if(flag & SPAWN_TOXINS) + if(flag & LINDA_SPAWN_TOXINS) G.toxins += amount - if(flag & SPAWN_OXYGEN) + if(flag & LINDA_SPAWN_OXYGEN) G.oxygen += amount - if(flag & SPAWN_CO2) + if(flag & LINDA_SPAWN_CO2) G.carbon_dioxide += amount - if(flag & SPAWN_NITROGEN) + if(flag & LINDA_SPAWN_NITROGEN) G.nitrogen += amount - if(flag & SPAWN_N2O) + if(flag & LINDA_SPAWN_N2O) var/datum/gas/sleeping_agent/T = new T.moles += amount G.trace_gases += T - if(flag & SPAWN_AIR) + if(flag & LINDA_SPAWN_AIR) G.oxygen += MOLES_O2STANDARD * amount G.nitrogen += MOLES_N2STANDARD * amount diff --git a/code/LINDA/LINDA_turf_tile.dm b/code/LINDA/LINDA_turf_tile.dm index b86ad2299ff..72f77d2440f 100644 --- a/code/LINDA/LINDA_turf_tile.dm +++ b/code/LINDA/LINDA_turf_tile.dm @@ -149,7 +149,7 @@ if (planet_atmos) atmos_adjacent_turfs_amount++ - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(atmos_adjacent_turfs & direction)) continue @@ -260,9 +260,9 @@ /turf/simulated/proc/get_atmos_overlay_by_name(name) switch(name) if("plasma") - return plmaster + return GLOB.plmaster if("sleeping_agent") - return slmaster + return GLOB.slmaster return null /turf/simulated/proc/tile_graphic() @@ -412,13 +412,13 @@ archive() else //Does particate in air exchange so only consider directions not considered during process_cell() - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(atmos_adjacent_turfs & direction) && !(atmos_supeconductivity & direction)) conductivity_directions += direction if(conductivity_directions>0) //Conduct with tiles around me - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(conductivity_directions&direction) var/turf/neighbor = get_step(src,direction) @@ -499,7 +499,7 @@ turf/simulated/proc/radiate_to_spess() //Radiate excess tile heat to space /turf/simulated/Initialize_Atmos(times_fired) ..() update_visuals() - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(atmos_adjacent_turfs & direction)) continue var/turf/enemy_tile = get_step(src, direction) diff --git a/code/__DEFINES/_readme.dm b/code/__DEFINES/_readme.dm index 42ad52286fe..8bf6ada6477 100644 --- a/code/__DEFINES/_readme.dm +++ b/code/__DEFINES/_readme.dm @@ -11,4 +11,4 @@ and most importantly, how to undo your changes if you screw it up. - Sayu -*/ \ No newline at end of file +*/ diff --git a/code/__DEFINES/_spacemandmm.dm b/code/__DEFINES/_spacemandmm.dm new file mode 100644 index 00000000000..ca54ca4b932 --- /dev/null +++ b/code/__DEFINES/_spacemandmm.dm @@ -0,0 +1,26 @@ +// This file contains all the defines related to the spacemanDMM linter (dreamchecker). When running under normal conditions, BYOND will ignore all these +#ifdef SPACEMAN_DMM + #define RETURN_TYPE(X) set SpacemanDMM_return_type = X + #define SHOULD_CALL_PARENT(X) set SpacemanDMM_should_call_parent = X + #define UNLINT(X) SpacemanDMM_unlint(X) + #define SHOULD_NOT_OVERRIDE(X) set SpacemanDMM_should_not_override = X + #define SHOULD_NOT_SLEEP(X) + #define SHOULD_BE_PURE(X) + #define PRIVATE_PROC(X) + #define PROTECTED_PROC(X) + #define VAR_FINAL var/SpacemanDMM_final + #define VAR_PRIVATE var + #define VAR_PROTECTED var +#else + #define RETURN_TYPE(X) + #define SHOULD_CALL_PARENT(X) + #define UNLINT(X) X + #define SHOULD_NOT_OVERRIDE(X) + #define SHOULD_NOT_SLEEP(X) + #define SHOULD_BE_PURE(X) + #define PRIVATE_PROC(X) + #define PROTECTED_PROC(X) + #define VAR_FINAL var + #define VAR_PRIVATE var + #define VAR_PROTECTED var +#endif diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm index 666971e7209..10b815e547c 100644 --- a/code/__DEFINES/_tick.dm +++ b/code/__DEFINES/_tick.dm @@ -10,4 +10,4 @@ #define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 ) #define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 ) -#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 ) \ No newline at end of file +#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 ) diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm new file mode 100644 index 00000000000..ea319971a1b --- /dev/null +++ b/code/__DEFINES/access.dm @@ -0,0 +1,111 @@ +#define ACCESS_SECURITY 1 // Security equipment +#define ACCESS_BRIG 2 // Brig timers and permabrig +#define ACCESS_ARMORY 3 +#define ACCESS_FORENSICS_LOCKERS 4 +#define ACCESS_MEDICAL 5 +#define ACCESS_MORGUE 6 +#define ACCESS_TOX 7 +#define ACCESS_TOX_STORAGE 8 +#define ACCESS_GENETICS 9 +#define ACCESS_ENGINE 10 +#define ACCESS_ENGINE_EQUIP 11 +#define ACCESS_MAINT_TUNNELS 12 +#define ACCESS_EXTERNAL_AIRLOCKS 13 +#define ACCESS_EMERGENCY_STORAGE 14 +#define ACCESS_CHANGE_IDS 15 +#define ACCESS_AI_UPLOAD 16 +#define ACCESS_TELEPORTER 17 +#define ACCESS_EVA 18 +#define ACCESS_HEADS 19 +#define ACCESS_CAPTAIN 20 +#define ACCESS_ALL_PERSONAL_LOCKERS 21 +#define ACCESS_CHAPEL_OFFICE 22 +#define ACCESS_TECH_STORAGE 23 +#define ACCESS_ATMOSPHERICS 24 +#define ACCESS_BAR 25 +#define ACCESS_JANITOR 26 +#define ACCESS_CREMATORIUM 27 +#define ACCESS_KITCHEN 28 +#define ACCESS_ROBOTICS 29 +#define ACCESS_RD 30 +#define ACCESS_CARGO 31 +#define ACCESS_CONSTRUCTION 32 +#define ACCESS_CHEMISTRY 33 +#define ACCESS_CARGO_BOT 34 +#define ACCESS_HYDROPONICS 35 +#define ACCESS_MANUFACTURING 36 +#define ACCESS_LIBRARY 37 +#define ACCESS_LAWYER 38 +#define ACCESS_VIROLOGY 39 +#define ACCESS_CMO 40 +#define ACCESS_QM 41 +#define ACCESS_COURT 42 +#define ACCESS_CLOWN 43 +#define ACCESS_MIME 44 +#define ACCESS_SURGERY 45 +#define ACCESS_THEATRE 46 +#define ACCESS_RESEARCH 47 +#define ACCESS_MINING 48 +#define ACCESS_MINING_OFFICE 49 //not in use +#define ACCESS_MAILSORTING 50 +#define ACCESS_MINT 51 +#define ACCESS_MINT_VAULT 52 +#define ACCESS_HEADS_VAULT 53 +#define ACCESS_MINING_STATION 54 +#define ACCESS_XENOBIOLOGY 55 +#define ACCESS_CE 56 +#define ACCESS_HOP 57 +#define ACCESS_HOS 58 +#define ACCESS_RC_ANNOUNCE 59 //Request console announcements +#define ACCESS_KEYCARD_AUTH 60 //Used for events which require at least two people to confirm them +#define ACCESS_TCOMSAT 61 // has access to the entire telecomms satellite / machinery +#define ACCESS_GATEWAY 62 +#define ACCESS_SEC_DOORS 63 // Security front doors +#define ACCESS_PSYCHIATRIST 64 // Psychiatrist's office +#define ACCESS_XENOARCH 65 +#define ACCESS_PARAMEDIC 66 +#define ACCESS_BLUESHIELD 67 +#define ACCESS_SALVAGE_CAPTAIN 69 // Salvage ship captain's quarters +#define ACCESS_MECHANIC 70 +#define ACCESS_PILOT 71 +#define ACCESS_NTREP 73 +#define ACCESS_MAGISTRATE 74 +#define ACCESS_MINISAT 75 +#define ACCESS_MINERAL_STOREROOM 76 +#define ACCESS_NETWORK 77 + +#define ACCESS_WEAPONS 99 //Weapon authorization for secbots + + //BEGIN CENTCOM ACCESS +#define ACCESS_CENT_GENERAL 101//General facilities. +#define ACCESS_CENT_LIVING 102//Living quarters. +#define ACCESS_CENT_MEDICAL 103//Medical. +#define ACCESS_CENT_SECURITY 104//Security. +#define ACCESS_CENT_STORAGE 105//Storage areas. +#define ACCESS_CENT_SHUTTLES 106//Shuttle docks. +#define ACCESS_CENT_TELECOMMS 107//Telecomms. +#define ACCESS_CENT_TELEPORTER 108//Teleporter +#define ACCESS_CENT_SPECOPS 109//Special Ops. +#define ACCESS_CENT_SPECOPS_COMMANDER 110//Special Ops Commander. +#define ACCESS_CENT_BLACKOPS 111//Black Ops. +#define ACCESS_CENT_THUNDER 112//Thunderdome. +#define ACCESS_CENT_BRIDGE 113//Bridge. +#define ACCESS_CENT_COMMANDER 114//Commander's Office/ID computer. + +//The Syndicate +#define ACCESS_SYNDICATE 150//General Syndicate Access +#define ACCESS_SYNDICATE_LEADER 151//Nuke Op Leader Access +#define ACCESS_VOX 152//Vox Access +#define ACCESS_SYNDICATE_COMMAND 153//Admin syndi officer + +//Trade Stations +#define ACCESS_TRADE_SOL 160 + +//MONEY +#define ACCESS_CRATE_CASH 200 + +//Awaymissions +#define ACCESS_AWAY01 271 + +//Ghost roles +#define ACCESS_FREE_GOLEMS 300 diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 65f4da2b50a..95494fb88cf 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -40,10 +40,11 @@ #define R_MOD 8192 #define R_MENTOR 16384 #define R_PROCCALL 32768 +#define R_VIEWRUNTIMES 65536 -#define R_MAXPERMISSION 32768 //This holds the maximum value for a permission. It is used in iteration, so keep it updated. +#define R_MAXPERMISSION 65536 //This holds the maximum value for a permission. It is used in iteration, so keep it updated. -#define R_HOST 65535 +#define R_HOST 131071 // Sum of all permissions to allow easy setting #define ADMIN_QUE(user,display) "[display]" #define ADMIN_FLW(user,display) "[display]" @@ -70,4 +71,4 @@ ///Max amount of keypress messages per second over two seconds before client is autokicked #define MAX_KEYPRESS_AUTOKICK 50 ///Length of held key rolling buffer -#define HELD_KEY_BUFFER_LENGTH 15 \ No newline at end of file +#define HELD_KEY_BUFFER_LENGTH 15 diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index e69de29bb2d..8b137891791 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -0,0 +1 @@ + diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index 2f296960622..de20ccd1a4c 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -131,4 +131,4 @@ #define ATMOS_ALARM_DANGER 2 //LAVALAND -#define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland \ No newline at end of file +#define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland diff --git a/code/__DEFINES/bots.dm b/code/__DEFINES/bots.dm index 75bf8a0a553..c283445d0ad 100644 --- a/code/__DEFINES/bots.dm +++ b/code/__DEFINES/bots.dm @@ -38,4 +38,4 @@ #define SENTIENCE_ARTIFICIAL 2 #define SENTIENCE_OTHER 3 #define SENTIENCE_MINEBOT 4 -#define SENTIENCE_BOSS 5 \ No newline at end of file +#define SENTIENCE_BOSS 5 diff --git a/code/__DEFINES/callbacks.dm b/code/__DEFINES/callbacks.dm index 26c81f8b143..18059a60839 100644 --- a/code/__DEFINES/callbacks.dm +++ b/code/__DEFINES/callbacks.dm @@ -1,4 +1,4 @@ #define GLOBAL_PROC "some_magic_bullshit" #define CALLBACK new /datum/callback -#define INVOKE_ASYNC ImmediateInvokeAsync \ No newline at end of file +#define INVOKE_ASYNC ImmediateInvokeAsync diff --git a/code/__DEFINES/clothing.dm b/code/__DEFINES/clothing.dm index c44c65397dd..1678ea69c8e 100644 --- a/code/__DEFINES/clothing.dm +++ b/code/__DEFINES/clothing.dm @@ -78,7 +78,7 @@ //flags for covering body parts #define GLASSESCOVERSEYES 1 -#define MASKCOVERSEYES 2 // get rid of some of the other retardation in these flags +#define MASKCOVERSEYES 2 // get rid of some of the other mess in these flags #define HEADCOVERSEYES 4 // feel free to realloc these numbers for other purposes #define MASKCOVERSMOUTH 8 // on other items, these are just for mask/head #define HEADCOVERSMOUTH 16 @@ -92,9 +92,7 @@ #define BLOCKHEADHAIR 4 // temporarily removes the user's hair overlay. Leaves facial hair. #define BLOCKHAIR 32768 // temporarily removes the user's hair, facial and otherwise. -#define ONESIZEFITSALL 1 // determines if something can be worn by a fatty or not. - //flags for muzzle speech blocking #define MUZZLE_MUTE_NONE 0 // Does not mute you. #define MUZZLE_MUTE_MUFFLE 1 // Muffles everything you say "MHHPHHMMM!!! -#define MUZZLE_MUTE_ALL 2 // Completely mutes you. \ No newline at end of file +#define MUZZLE_MUTE_ALL 2 // Completely mutes you. diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 13f7777d386..2bb53c91ce4 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -99,3 +99,12 @@ #define COLOR_ASSEMBLY_LBLUE "#5D99BE" #define COLOR_ASSEMBLY_BLUE "#38559E" #define COLOR_ASSEMBLY_PURPLE "#6F6192" + +// Pipe colours +#define PIPE_COLOR_GREY "#ffffff" //yes white is grey +#define PIPE_COLOR_RED "#ff0000" +#define PIPE_COLOR_BLUE "#0000ff" +#define PIPE_COLOR_CYAN "#00ffff" +#define PIPE_COLOR_GREEN "#00ff00" +#define PIPE_COLOR_YELLOW "#ffcc00" +#define PIPE_COLOR_PURPLE "#5c1ec0" diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 956930aae70..2f98b6ee7b5 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -35,7 +35,6 @@ #define CANPUSH 8 #define PASSEMOTES 16 //Mob has a cortical borer or holders inside of it that need to see emotes. #define GOTTAGOFAST 32 -#define GOTTAGOFAST_METH 64 #define IGNORESLOWDOWN 128 #define GODMODE 4096 #define FAKEDEATH 8192 //Replaces stuff like changeling.changeling_fakedeath diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index c94dff5ca6c..c9691c195b8 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -288,4 +288,4 @@ #define COMSIG_XENO_SLIME_CLICK_SHIFT "xeno_slime_click_shift" //from slime ShiftClickOn(): (/mob) #define COMSIG_XENO_TURF_CLICK_SHIFT "xeno_turf_click_shift" //from turf ShiftClickOn(): (/mob) #define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt" //from turf AltClickOn(): (/mob) -#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob) \ No newline at end of file +#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob) diff --git a/code/__DEFINES/crafting.dm b/code/__DEFINES/crafting.dm index 0fbd76b9e21..33c80e970cf 100644 --- a/code/__DEFINES/crafting.dm +++ b/code/__DEFINES/crafting.dm @@ -33,4 +33,4 @@ #define RECIPE_GRILL "Grill" #define RECIPE_CANDY "Candy" -#define RECIPE_FAIL null \ No newline at end of file +#define RECIPE_FAIL null diff --git a/code/__DEFINES/criminal_status.dm b/code/__DEFINES/criminal_status.dm new file mode 100644 index 00000000000..75c33909fc2 --- /dev/null +++ b/code/__DEFINES/criminal_status.dm @@ -0,0 +1,10 @@ +//criminal status defines +#define SEC_RECORD_STATUS_NONE "None" +#define SEC_RECORD_STATUS_ARREST "*Arrest*" +#define SEC_RECORD_STATUS_EXECUTE "*Execute*" +#define SEC_RECORD_STATUS_INCARCERATED "Incarcerated" +#define SEC_RECORD_STATUS_RELEASED "Released" +#define SEC_RECORD_STATUS_PAROLLED "Parolled" +#define SEC_RECORD_STATUS_DEMOTE "Demote" +#define SEC_RECORD_STATUS_SEARCH "Search" +#define SEC_RECORD_STATUS_MONITOR "Monitor" diff --git a/code/__DEFINES/dna.dm b/code/__DEFINES/dna.dm new file mode 100644 index 00000000000..7984c1a6dde --- /dev/null +++ b/code/__DEFINES/dna.dm @@ -0,0 +1,57 @@ +// Defines for all genetics/DNA related stuff + +// What each index means: +#define DNA_OFF_LOWERBOUND 1 // changed as lists start at 1 not 0 +#define DNA_OFF_UPPERBOUND 2 +#define DNA_ON_LOWERBOUND 3 +#define DNA_ON_UPPERBOUND 4 + +// Define block bounds (off-low,off-high,on-low,on-high) +// Used in setupgame.dm +#define DNA_DEFAULT_BOUNDS list(1,2049,2050,4095) +#define DNA_HARDER_BOUNDS list(1,3049,3050,4095) +#define DNA_HARD_BOUNDS list(1,3490,3500,4095) + +// UI Indices (can change to mutblock style, if desired) +#define DNA_UI_HAIR_R 1 +#define DNA_UI_HAIR_G 2 +#define DNA_UI_HAIR_B 3 +#define DNA_UI_HAIR2_R 4 +#define DNA_UI_HAIR2_G 5 +#define DNA_UI_HAIR2_B 6 +#define DNA_UI_BEARD_R 7 +#define DNA_UI_BEARD_G 8 +#define DNA_UI_BEARD_B 9 +#define DNA_UI_BEARD2_R 10 +#define DNA_UI_BEARD2_G 11 +#define DNA_UI_BEARD2_B 12 +#define DNA_UI_SKIN_TONE 13 +#define DNA_UI_SKIN_R 14 +#define DNA_UI_SKIN_G 15 +#define DNA_UI_SKIN_B 16 +#define DNA_UI_HACC_R 17 +#define DNA_UI_HACC_G 18 +#define DNA_UI_HACC_B 19 +#define DNA_UI_HEAD_MARK_R 20 +#define DNA_UI_HEAD_MARK_G 21 +#define DNA_UI_HEAD_MARK_B 22 +#define DNA_UI_BODY_MARK_R 23 +#define DNA_UI_BODY_MARK_G 24 +#define DNA_UI_BODY_MARK_B 25 +#define DNA_UI_TAIL_MARK_R 26 +#define DNA_UI_TAIL_MARK_G 27 +#define DNA_UI_TAIL_MARK_B 28 +#define DNA_UI_EYES_R 29 +#define DNA_UI_EYES_G 30 +#define DNA_UI_EYES_B 31 +#define DNA_UI_GENDER 32 +#define DNA_UI_BEARD_STYLE 33 +#define DNA_UI_HAIR_STYLE 34 +/*#define DNA_UI_BACC_STYLE 23*/ +#define DNA_UI_HACC_STYLE 35 +#define DNA_UI_HEAD_MARK_STYLE 36 +#define DNA_UI_BODY_MARK_STYLE 37 +#define DNA_UI_TAIL_MARK_STYLE 38 +#define DNA_UI_LENGTH 38 // Update this when you add something, or you WILL break shit. + +#define DNA_SE_LENGTH 55 // Was STRUCDNASIZE, size 27. 15 new blocks added = 42, plus room to grow. diff --git a/code/__DEFINES/game.dm b/code/__DEFINES/game.dm index 5c262724a14..4ad2ca587d9 100644 --- a/code/__DEFINES/game.dm +++ b/code/__DEFINES/game.dm @@ -2,4 +2,4 @@ #define GAME_STATE_PREGAME 1 #define GAME_STATE_SETTING_UP 2 #define GAME_STATE_PLAYING 3 -#define GAME_STATE_FINISHED 4 \ No newline at end of file +#define GAME_STATE_FINISHED 4 diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm index 308e7a5f6d9..73a812e8f5b 100644 --- a/code/__DEFINES/gamemode.dm +++ b/code/__DEFINES/gamemode.dm @@ -53,4 +53,4 @@ #define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone" #define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel" #define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva" -#define SPECIAL_ROLE_EVENTMISC "Event Role" \ No newline at end of file +#define SPECIAL_ROLE_EVENTMISC "Event Role" diff --git a/code/__DEFINES/genetics.dm b/code/__DEFINES/genetics.dm index c8f98c1fbeb..4ac05376e9d 100644 --- a/code/__DEFINES/genetics.dm +++ b/code/__DEFINES/genetics.dm @@ -104,6 +104,7 @@ #define NUTRITION_LEVEL_FED 350 #define NUTRITION_LEVEL_HUNGRY 250 #define NUTRITION_LEVEL_STARVING 150 +#define NUTRITION_LEVEL_HYPOGLYCEMIA 100 #define NUTRITION_LEVEL_CURSED 0 //Used as an upper limit for species that continuously gain nutriment @@ -151,7 +152,6 @@ #define NO_SCAN "no_scan" #define NO_PAIN "no_pain" #define IS_PLANT "is_plant" -#define CAN_BE_FAT "can_be_fat" #define NO_INTORGANS "no_internal_organs" #define RADIMMUNE "rad_immunity" #define NOGUNS "no_guns" @@ -166,4 +166,4 @@ #define NO_GERMS "no_germs" #define NO_DECAY "no_decay" #define PIERCEIMMUNE "pierce_immunity" -#define NO_HUNGER "no_hunger" \ No newline at end of file +#define NO_HUNGER "no_hunger" diff --git a/code/__DEFINES/hydroponics.dm b/code/__DEFINES/hydroponics.dm index f6693b0bcfc..8a3890d45f9 100644 --- a/code/__DEFINES/hydroponics.dm +++ b/code/__DEFINES/hydroponics.dm @@ -57,4 +57,4 @@ #define TRAIT_BIOLUM_COLOUR 37 #define TRAIT_IMMUTABLE 38 #define TRAIT_RARITY 39 -#define TRAIT_BATTERY_RECHARGE 40 \ No newline at end of file +#define TRAIT_BATTERY_RECHARGE 40 diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 33ce299c540..671897b612d 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -28,13 +28,13 @@ #define is_pen(W) (istype(W, /obj/item/pen)) -var/list/static/global/pointed_types = typecacheof(list( +GLOBAL_LIST_INIT(pointed_types, typecacheof(list( /obj/item/pen, /obj/item/screwdriver, /obj/item/reagent_containers/syringe, - /obj/item/kitchen/utensil/fork)) + /obj/item/kitchen/utensil/fork))) -#define is_pointed(W) (is_type_in_typecache(W, pointed_types)) +#define is_pointed(W) (is_type_in_typecache(W, GLOB.pointed_types)) GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list( /obj/item/stack/sheet/glass, diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm new file mode 100644 index 00000000000..d72628e2cb7 --- /dev/null +++ b/code/__DEFINES/job.dm @@ -0,0 +1,64 @@ +/////////////////////////////// +// WARNING // +//////////////////////////////////////////////////////////////////////// +// Do NOT touch the values associated with these defines, as they are // +// used by the game database to keep track of job flags. Do NOT touch // +//////////////////////////////////////////////////////////////////////// + +#define JOBCAT_ENGSEC (1<<0) + +#define JOB_CAPTAIN (1<<0) +#define JOB_HOS (1<<1) +#define JOB_WARDEN (1<<2) +#define JOB_DETECTIVE (1<<3) +#define JOB_OFFICER (1<<4) +#define JOB_CHIEF (1<<5) +#define JOB_ENGINEER (1<<6) +#define JOB_ATMOSTECH (1<<7) +#define JOB_AI (1<<8) +#define JOB_CYBORG (1<<9) +#define JOB_CENTCOM (1<<10) +#define JOB_SYNDICATE (1<<11) + +#define JOBCAT_MEDSCI (1<<1) + +#define JOB_RD (1<<0) +#define JOB_SCIENTIST (1<<1) +#define JOB_CHEMIST (1<<2) +#define JOB_CMO (1<<3) +#define JOB_DOCTOR (1<<4) +#define JOB_GENETICIST (1<<5) +#define JOB_VIROLOGIST (1<<6) +#define JOB_PSYCHIATRIST (1<<7) +#define JOB_ROBOTICIST (1<<8) +#define JOB_PARAMEDIC (1<<9) +#define JOB_CORONER (1<<10) + + +#define JOBCAT_SUPPORT (1<<2) + +#define JOB_HOP (1<<0) +#define JOB_BARTENDER (1<<1) +#define JOB_BOTANIST (1<<2) +#define JOB_CHEF (1<<3) +#define JOB_JANITOR (1<<4) +#define JOB_LIBRARIAN (1<<5) +#define JOB_QUARTERMASTER (1<<6) +#define JOB_CARGOTECH (1<<7) +#define JOB_MINER (1<<8) +#define JOB_LAWYER (1<<9) +#define JOB_CHAPLAIN (1<<10) +#define JOB_CLOWN (1<<11) +#define JOB_MIME (1<<12) +#define JOB_CIVILIAN (1<<13) + + +#define JOBCAT_KARMA (1<<3) + +#define JOB_NANO (1<<0) +#define JOB_BLUESHIELD (1<<1) +#define JOB_BARBER (1<<3) +#define JOB_MECHANIC (1<<4) +#define JOB_BRIGDOC (1<<5) +#define JOB_JUDGE (1<<6) +#define JOB_PILOT (1<<7) diff --git a/code/__DEFINES/js.dm b/code/__DEFINES/js.dm new file mode 100644 index 00000000000..e1a86e664d4 --- /dev/null +++ b/code/__DEFINES/js.dm @@ -0,0 +1,91 @@ +//this function places received data into element with specified id. +#define JS_BYJAX {" + +function replaceContent() { + var args = Array.prototype.slice.call(arguments); + var id = args\[0\]; + var content = args\[1\]; + var callback = null; + if(args\[2\]){ + callback = args\[2\]; + if(args\[3\]){ + args = args.slice(3); + } + } + var parent = document.getElementById(id); + if(typeof(parent)!=='undefined' && parent!=null){ + parent.innerHTML = content?content:''; + } + if(callback && window\[callback\]){ + window\[callback\].apply(null,args); + } +} +"} + +/* +sends data to control_id:replaceContent + +receiver - mob +control_id - window id (for windows opened with browse(), it'll be "windowname.browser") +target_element - HTML element id +new_content - HTML content +callback - js function that will be called after the data is sent +callback_args - arguments for callback function + +Be sure to include required js functions in your page, or it'll raise an exception. + +And yes I know this is a proc in a defines file, but its highly relevant so it can be here +*/ +proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) + if(receiver && target_element && control_id) // && winexists(receiver, control_id)) + var/list/argums = list(target_element, new_content) + if(callback) + argums += callback + if(callback_args) + argums += callback_args + argums = list2params(argums) +/* if(callback_args) + argums += "&[list2params(callback_args)]" +*/ + receiver << output(argums,"[control_id]:replaceContent") + return + + +// Misc JS for some dropdowns +#define JS_DROPDOWNS {" +function dropdowns() { + var divs = document.getElementsByTagName('div'); + var headers = new Array(); + var links = new Array(); + for(var i=0;i=0) { + elem.className = elem.className.replace('visible','hidden'); + this.className = this.className.replace('open','closed'); + this.innerHTML = this.innerHTML.replace('-','+'); + } + else { + elem.className = elem.className.replace('hidden','visible'); + this.className = this.className.replace('closed','open'); + this.innerHTML = this.innerHTML.replace('+','-'); + } + return false; + } + })(links\[i\]); + } + } +} +"} diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index db88f46a676..ed4cb0d5940 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -110,4 +110,4 @@ #define ABOVE_HUD_LAYER 22 #define SPLASHSCREEN_LAYER 23 -#define SPLASHSCREEN_PLANE 23 \ No newline at end of file +#define SPLASHSCREEN_PLANE 23 diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index 7357c5f441e..34528090e86 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -89,4 +89,28 @@ #define FLASH_LIGHT_DURATION 2 #define FLASH_LIGHT_POWER 3 -#define FLASH_LIGHT_RANGE 3.8 \ No newline at end of file +#define FLASH_LIGHT_RANGE 3.8 + +/// Returns the red part of a #RRGGBB hex sequence as number +#define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4)) + +/// Returns the green part of a #RRGGBB hex sequence as number +#define GETGREENPART(hexa) hex2num(copytext(hexa, 4, 6)) + +/// Returns the blue part of a #RRGGBB hex sequence as number +#define GETBLUEPART(hexa) hex2num(copytext(hexa, 6, 8)) + +/// Parse the hexadecimal color into lumcounts of each perspective. +#define PARSE_LIGHT_COLOR(source) \ +do { \ + if (source.light_color) { \ + var/__light_color = source.light_color; \ + source.lum_r = GETREDPART(__light_color) / 255; \ + source.lum_g = GETGREENPART(__light_color) / 255; \ + source.lum_b = GETBLUEPART(__light_color) / 255; \ + } else { \ + source.lum_r = 1; \ + source.lum_g = 1; \ + source.lum_b = 1; \ + }; \ +} while (FALSE) diff --git a/code/__DEFINES/logs.dm b/code/__DEFINES/logs.dm new file mode 100644 index 00000000000..25d1fac7323 --- /dev/null +++ b/code/__DEFINES/logs.dm @@ -0,0 +1,6 @@ +#define ATTACK_LOG "Attack" +#define DEFENSE_LOG "Defense" +#define CONVERSION_LOG "Conversion" +#define SAY_LOG "Say" +#define EMOTE_LOG "Emote" +#define MISC_LOG "Misc" diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 7a691c44df2..b56024bfab8 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -88,3 +88,7 @@ #define SUPERMATTER_DANGER 4 // Integrity < 75% #define SUPERMATTER_EMERGENCY 5 // Integrity < 50% #define SUPERMATTER_DELAMINATING 6 // Pretty obvious, Integrity < 25% + +// Firelock states +#define FD_OPEN 1 +#define FD_CLOSED 2 diff --git a/code/__DEFINES/mecha.dm b/code/__DEFINES/mecha.dm index e8955e64b50..b414f146705 100644 --- a/code/__DEFINES/mecha.dm +++ b/code/__DEFINES/mecha.dm @@ -19,4 +19,4 @@ #define MECHA_SECURE_BOLTS 1 #define MECHA_LOOSE_BOLTS 2 #define MECHA_OPEN_HATCH 3 -#define MECHA_UNSECURE_CELL 4 \ No newline at end of file +#define MECHA_UNSECURE_CELL 4 diff --git a/code/__DEFINES/medal.dm b/code/__DEFINES/medal.dm index 81dea5f373b..b47d562efee 100644 --- a/code/__DEFINES/medal.dm +++ b/code/__DEFINES/medal.dm @@ -21,4 +21,4 @@ #define DRAKE_SCORE "Drakes Killed" #define LEGION_SCORE "Legion Killed" #define SWARMER_BEACON_SCORE "Swarmer Beacons Killed" -#define TENDRIL_CLEAR_SCORE "Tendrils Killed" \ No newline at end of file +#define TENDRIL_CLEAR_SCORE "Tendrils Killed" diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 51b895ff12d..cf27ec09143 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -122,10 +122,10 @@ ) #define FOR_DVIEW(type, range, center, invis_flags) \ - dview_mob.loc = center; \ - dview_mob.see_invisible = invis_flags; \ - for(type in view(range, dview_mob)) -#define END_FOR_DVIEW dview_mob.loc = null + GLOB.dview_mob.loc = center; \ + GLOB.dview_mob.see_invisible = invis_flags; \ + for(type in view(range, GLOB.dview_mob)) +#define END_FOR_DVIEW GLOB.dview_mob.loc = null //Turf locational stuff #define get_turf(A) (get_step(A, 0)) @@ -319,7 +319,7 @@ #define INVESTIGATE_BOMB "bombs" // The SQL version required by this version of the code -#define SQL_VERSION 11 +#define SQL_VERSION 12 // Vending machine stuff #define CAT_NORMAL 1 @@ -345,7 +345,7 @@ #define MOUSE_OPACITY_OPAQUE 2 // Defib stats -#define DEFIB_TIME_LIMIT 120 +#define DEFIB_TIME_LIMIT 300 #define DEFIB_TIME_LOSS 60 //different types of atom colorations @@ -419,4 +419,19 @@ #define ENGTYPE_TESLA "Tesla" #define SUMMON_GUNS "guns" -#define SUMMON_MAGIC "magic" \ No newline at end of file +#define SUMMON_MAGIC "magic" + +// Medical stuff +#define SYMPTOM_ACTIVATION_PROB 3 + +// Atmos stuff that fucking terrifies me +#define LINDA_SPAWN_HEAT 1 +#define LINDA_SPAWN_20C 2 +#define LINDA_SPAWN_TOXINS 4 +#define LINDA_SPAWN_OXYGEN 8 +#define LINDA_SPAWN_CO2 16 +#define LINDA_SPAWN_NITROGEN 32 + +#define LINDA_SPAWN_N2O 64 + +#define LINDA_SPAWN_AIR 256 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 293d97e4fa3..e1e15a926d6 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -239,4 +239,4 @@ #define is_admin(user) (check_rights(R_ADMIN, 0, (user)) != 0) -#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; \ No newline at end of file +#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; diff --git a/code/__DEFINES/move_force.dm b/code/__DEFINES/move_force.dm index 750821bd898..8051a516298 100644 --- a/code/__DEFINES/move_force.dm +++ b/code/__DEFINES/move_force.dm @@ -16,4 +16,4 @@ #define MOVE_FORCE_NORMAL MOVE_FORCE_DEFAULT #define MOVE_FORCE_WEAK MOVE_FORCE_DEFAULT / 2 #define MOVE_FORCE_VERY_WEAK (MOVE_FORCE_DEFAULT / MOVE_FORCE_CRUSH_RATIO) + 1 -#define MOVE_FORCE_EXTREMELY_WEAK MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3) \ No newline at end of file +#define MOVE_FORCE_EXTREMELY_WEAK MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3) diff --git a/code/__DEFINES/pda.dm b/code/__DEFINES/pda.dm index d22b90870b0..c5d32f03ba8 100644 --- a/code/__DEFINES/pda.dm +++ b/code/__DEFINES/pda.dm @@ -1,3 +1,3 @@ #define PDA_APP_UPDATE 0 #define PDA_APP_NOUPDATE 1 -#define PDA_APP_UPDATE_SLOW 2 \ No newline at end of file +#define PDA_APP_UPDATE_SLOW 2 diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 54fd59307f4..eb5fad0ff27 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -7,9 +7,10 @@ #define SOUND_BUZZ 64 #define SOUND_INSTRUMENTS 128 #define SOUND_MENTORHELP 256 -#define SOUND_DISCO 512 +#define SOUND_DISCO 512 +#define SOUND_AI_VOICE 1024 -#define SOUND_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|SOUND_HEARTBEAT|SOUND_BUZZ|SOUND_INSTRUMENTS|SOUND_MENTORHELP|SOUND_DISCO) +#define SOUND_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|SOUND_HEARTBEAT|SOUND_BUZZ|SOUND_INSTRUMENTS|SOUND_MENTORHELP|SOUND_DISCO|SOUND_AI_VOICE) #define CHAT_OOC 1 #define CHAT_DEAD 2 diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm index 6adb876da8d..0aaee0533d4 100644 --- a/code/__DEFINES/qdel.dm +++ b/code/__DEFINES/qdel.dm @@ -20,4 +20,4 @@ #define QDELING(X) (X.gc_destroyed) #define QDELETED(X) (!X || QDELING(X)) -#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) \ No newline at end of file +#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) diff --git a/code/__DEFINES/radio.dm b/code/__DEFINES/radio.dm index e49a9ea9b67..e0c71836c72 100644 --- a/code/__DEFINES/radio.dm +++ b/code/__DEFINES/radio.dm @@ -1,60 +1,60 @@ - -#define DISPLAY_FREQ 1435 //status displays -#define ATMOS_FIRE_FREQ 1437 //air alarms -#define ENGINE_FREQ 1438 //engine components -#define ATMOS_VENTSCRUB 1439 //vents, scrubbers, atmos control -#define ATMOS_DISTRO_FREQ 1443 //distro loop -#define ATMOS_TANKS_FREQ 1441 //atmos supply tanks -#define BOT_BEACON_FREQ 1445 //bot navigation beacons -#define AIRLOCK_FREQ 1449 //airlock controls, electropack, magnets - -#define RSD_FREQ 1457 //radio signal device -#define IMPL_FREQ 1451 //tracking implant - -#define RADIO_LOW_FREQ 1200 //minimum radio freq -#define PUBLIC_LOW_FREQ 1441 //minimum radio chat freq -#define PUBLIC_HIGH_FREQ 1489 //maximum radio chat freq -#define RADIO_HIGH_FREQ 1600 //maximum radio freq - -#define SYND_FREQ 1213 -#define SYNDTEAM_FREQ 1244 -#define DTH_FREQ 1341 //Special Operations -#define AI_FREQ 1343 -#define ERT_FREQ 1345 -#define COMM_FREQ 1353 //Command -#define BOT_FREQ 1447 //mulebot, secbot, ed209 - - -// Department channels -#define PUB_FREQ 1459 //standard radio chat -#define SEC_FREQ 1359 //security -#define ENG_FREQ 1357 //engineering -#define SCI_FREQ 1351 //science -#define MED_FREQ 1355 //medical -#define SUP_FREQ 1347 //cargo -#define SRV_FREQ 1349 //service - -// Internal department channels -#define MED_I_FREQ 1485 -#define SEC_I_FREQ 1475 - -// Transmission methods -#define TRANSMISSION_WIRE 0 -#define TRANSMISSION_RADIO 1 - -//This filter is special because devices belonging to default also recieve signals sent to any other filter. -#define RADIO_DEFAULT "radio_default" -#define RADIO_TO_AIRALARM "radio_airalarm" //air alarms -#define RADIO_FROM_AIRALARM "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms -#define RADIO_CHAT "radio_telecoms" -#define RADIO_ATMOSIA "radio_atmos" -#define RADIO_NAVBEACONS "radio_navbeacon" -#define RADIO_AIRLOCK "radio_airlock" -#define RADIO_SECBOT "radio_secbot" -#define RADIO_HONKBOT "radio_honkbot" -#define RADIO_MULEBOT "radio_mulebot" -#define RADIO_CLEANBOT "10" -#define RADIO_FLOORBOT "11" -#define RADIO_MEDBOT "12" -#define RADIO_MAGNETS "radio_magnet" -#define RADIO_LOGIC "radio_logic" + +#define DISPLAY_FREQ 1435 //status displays +#define ATMOS_FIRE_FREQ 1437 //air alarms +#define ENGINE_FREQ 1438 //engine components +#define ATMOS_VENTSCRUB 1439 //vents, scrubbers, atmos control +#define ATMOS_DISTRO_FREQ 1443 //distro loop +#define ATMOS_TANKS_FREQ 1441 //atmos supply tanks +#define BOT_BEACON_FREQ 1445 //bot navigation beacons +#define AIRLOCK_FREQ 1449 //airlock controls, electropack, magnets + +#define RSD_FREQ 1457 //radio signal device +#define IMPL_FREQ 1451 //tracking implant + +#define RADIO_LOW_FREQ 1200 //minimum radio freq +#define PUBLIC_LOW_FREQ 1441 //minimum radio chat freq +#define PUBLIC_HIGH_FREQ 1489 //maximum radio chat freq +#define RADIO_HIGH_FREQ 1600 //maximum radio freq + +#define SYND_FREQ 1213 +#define SYNDTEAM_FREQ 1244 +#define DTH_FREQ 1341 //Special Operations +#define AI_FREQ 1343 +#define ERT_FREQ 1345 +#define COMM_FREQ 1353 //Command +#define BOT_FREQ 1447 //mulebot, secbot, ed209 + + +// Department channels +#define PUB_FREQ 1459 //standard radio chat +#define SEC_FREQ 1359 //security +#define ENG_FREQ 1357 //engineering +#define SCI_FREQ 1351 //science +#define MED_FREQ 1355 //medical +#define SUP_FREQ 1347 //cargo +#define SRV_FREQ 1349 //service + +// Internal department channels +#define MED_I_FREQ 1485 +#define SEC_I_FREQ 1475 + +// Transmission methods +#define TRANSMISSION_WIRE 0 +#define TRANSMISSION_RADIO 1 + +//This filter is special because devices belonging to default also recieve signals sent to any other filter. +#define RADIO_DEFAULT "radio_default" +#define RADIO_TO_AIRALARM "radio_airalarm" //air alarms +#define RADIO_FROM_AIRALARM "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms +#define RADIO_CHAT "radio_telecoms" +#define RADIO_ATMOSIA "radio_atmos" +#define RADIO_NAVBEACONS "radio_navbeacon" +#define RADIO_AIRLOCK "radio_airlock" +#define RADIO_SECBOT "radio_secbot" +#define RADIO_HONKBOT "radio_honkbot" +#define RADIO_MULEBOT "radio_mulebot" +#define RADIO_CLEANBOT "10" +#define RADIO_FLOORBOT "11" +#define RADIO_MEDBOT "12" +#define RADIO_MAGNETS "radio_magnet" +#define RADIO_LOGIC "radio_logic" diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index e2dd3ee3ac7..564247455e3 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -16,3 +16,6 @@ // Is an open container for all intents and purposes. #define OPENCONTAINER (REFILLABLE | DRAINABLE | TRANSPARENT) + +#define REAGENT_TOUCH 1 +#define REAGENT_INGEST 2 diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 34ad4d69e54..8021fa4452d 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -1,81 +1,81 @@ - - -//Values for antag preferences, event roles, etc. unified here - - - -//These are synced with the Database, if you change the values of the defines -//then you MUST update the database! -// If you're adding a new role, remember to update modules/admin/topic.dm, so admins can dish out -// justice if someone's abusing your role -#define ROLE_SYNDICATE "Syndicate" -#define ROLE_TRAITOR "traitor" -#define ROLE_OPERATIVE "operative" -#define ROLE_CHANGELING "changeling" -#define ROLE_WIZARD "wizard" -#define ROLE_REV "revolutionary" -#define ROLE_ALIEN "xenomorph" -#define ROLE_PAI "pAI" -#define ROLE_CULTIST "cultist" -#define ROLE_BLOB "blob" -#define ROLE_NINJA "space ninja" -#define ROLE_MONKEY "monkey" -#define ROLE_GANG "gangster" -#define ROLE_SHADOWLING "shadowling" -#define ROLE_ABDUCTOR "abductor" -#define ROLE_REVENANT "revenant" -#define ROLE_HOG_GOD "hand of god: god" // We're prolly gonna port this one day or another -#define ROLE_HOG_CULTIST "hand of god: cultist" -#define ROLE_DEVIL "devil" -#define ROLE_RAIDER "vox raider" -#define ROLE_TRADER "trader" -#define ROLE_VAMPIRE "vampire" -// Role tags for EVERYONE! -#define ROLE_BORER "cortical borer" -#define ROLE_DEMON "slaughter demon" -#define ROLE_SENTIENT "sentient animal" -#define ROLE_POSIBRAIN "positronic brain" -#define ROLE_GUARDIAN "guardian" -#define ROLE_MORPH "morph" -#define ROLE_ERT "emergency response team" -#define ROLE_NYMPH "Dionaea" -#define ROLE_GSPIDER "giant spider" -#define ROLE_DRONE "drone" -#define ROLE_DEATHSQUAD "deathsquad" -#define ROLE_EVENTMISC "eventmisc" -#define ROLE_GHOST "ghost role" - -//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. -//The gamemode specific ones are just so the gamemodes can query whether a player is old enough -//(in game days played) to play that role -var/global/list/special_roles = list( - ROLE_ABDUCTOR = /datum/game_mode/abduction, // Abductor - ROLE_BLOB = /datum/game_mode/blob, // Blob - ROLE_CHANGELING = /datum/game_mode/changeling, // Changeling - ROLE_BORER, // Cortical borer - ROLE_CULTIST = /datum/game_mode/cult, // Cultist - ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil - ROLE_GSPIDER, // Giant spider - ROLE_GUARDIAN, // Guardian - ROLE_MORPH, // Morph - ROLE_OPERATIVE = /datum/game_mode/nuclear, // Operative - ROLE_PAI, // PAI - ROLE_POSIBRAIN, // Positronic brain - ROLE_REVENANT, // Revenant - ROLE_REV = /datum/game_mode/revolution, // Revolutionary - ROLE_SENTIENT, // Sentient animal - ROLE_SHADOWLING = /datum/game_mode/shadowling, // Shadowling - ROLE_DEMON, // Slaguther demon - ROLE_NINJA, // Space ninja - ROLE_TRADER, // Trader - ROLE_TRAITOR = /datum/game_mode/traitor, // Traitor - ROLE_VAMPIRE = /datum/game_mode/vampire, // Vampire - ROLE_RAIDER = /datum/game_mode/heist, // Vox raider - ROLE_ALIEN, // Xenomorph - ROLE_WIZARD = /datum/game_mode/wizard, // Wizard - // UNUSED/BROKEN ANTAGS -// ROLE_HOG_GOD = /datum/game_mode/hand_of_god, -// ROLE_HOG_CULTIST = /datum/game_mode/hand_of_god, -// ROLE_MONKEY = /datum/game_mode/monkey, Sooner or later these are going to get ported -// ROLE_GANG = /datum/game_mode/gang, -) + + +//Values for antag preferences, event roles, etc. unified here + + + +//These are synced with the Database, if you change the values of the defines +//then you MUST update the database! +// If you're adding a new role, remember to update modules/admin/topic.dm, so admins can dish out +// justice if someone's abusing your role +#define ROLE_SYNDICATE "Syndicate" +#define ROLE_TRAITOR "traitor" +#define ROLE_OPERATIVE "operative" +#define ROLE_CHANGELING "changeling" +#define ROLE_WIZARD "wizard" +#define ROLE_REV "revolutionary" +#define ROLE_ALIEN "xenomorph" +#define ROLE_PAI "pAI" +#define ROLE_CULTIST "cultist" +#define ROLE_BLOB "blob" +#define ROLE_NINJA "space ninja" +#define ROLE_MONKEY "monkey" +#define ROLE_GANG "gangster" +#define ROLE_SHADOWLING "shadowling" +#define ROLE_ABDUCTOR "abductor" +#define ROLE_REVENANT "revenant" +#define ROLE_HOG_GOD "hand of god: god" // We're prolly gonna port this one day or another +#define ROLE_HOG_CULTIST "hand of god: cultist" +#define ROLE_DEVIL "devil" +#define ROLE_RAIDER "vox raider" +#define ROLE_TRADER "trader" +#define ROLE_VAMPIRE "vampire" +// Role tags for EVERYONE! +#define ROLE_BORER "cortical borer" +#define ROLE_DEMON "slaughter demon" +#define ROLE_SENTIENT "sentient animal" +#define ROLE_POSIBRAIN "positronic brain" +#define ROLE_GUARDIAN "guardian" +#define ROLE_MORPH "morph" +#define ROLE_ERT "emergency response team" +#define ROLE_NYMPH "Dionaea" +#define ROLE_GSPIDER "giant spider" +#define ROLE_DRONE "drone" +#define ROLE_DEATHSQUAD "deathsquad" +#define ROLE_EVENTMISC "eventmisc" +#define ROLE_GHOST "ghost role" + +//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. +//The gamemode specific ones are just so the gamemodes can query whether a player is old enough +//(in game days played) to play that role +GLOBAL_LIST_INIT(special_roles, list( + ROLE_ABDUCTOR = /datum/game_mode/abduction, // Abductor + ROLE_BLOB = /datum/game_mode/blob, // Blob + ROLE_CHANGELING = /datum/game_mode/changeling, // Changeling + ROLE_BORER, // Cortical borer + ROLE_CULTIST = /datum/game_mode/cult, // Cultist + ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil + ROLE_GSPIDER, // Giant spider + ROLE_GUARDIAN, // Guardian + ROLE_MORPH, // Morph + ROLE_OPERATIVE = /datum/game_mode/nuclear, // Operative + ROLE_PAI, // PAI + ROLE_POSIBRAIN, // Positronic brain + ROLE_REVENANT, // Revenant + ROLE_REV = /datum/game_mode/revolution, // Revolutionary + ROLE_SENTIENT, // Sentient animal + ROLE_SHADOWLING = /datum/game_mode/shadowling, // Shadowling + ROLE_DEMON, // Slaguther demon + ROLE_NINJA, // Space ninja + ROLE_TRADER, // Trader + ROLE_TRAITOR = /datum/game_mode/traitor, // Traitor + ROLE_VAMPIRE = /datum/game_mode/vampire, // Vampire + ROLE_RAIDER = /datum/game_mode/heist, // Vox raider + ROLE_ALIEN, // Xenomorph + ROLE_WIZARD = /datum/game_mode/wizard // Wizard + // UNUSED/BROKEN ANTAGS +// ROLE_HOG_GOD = /datum/game_mode/hand_of_god, +// ROLE_HOG_CULTIST = /datum/game_mode/hand_of_god, +// ROLE_MONKEY = /datum/game_mode/monkey, Sooner or later these are going to get ported +// ROLE_GANG = /datum/game_mode/gang +)) diff --git a/code/__DEFINES/rolebans.dm b/code/__DEFINES/rolebans.dm index d6e54abf14a..ad6f1324afa 100644 --- a/code/__DEFINES/rolebans.dm +++ b/code/__DEFINES/rolebans.dm @@ -1,5 +1,5 @@ // Bannable antag roles -var/global/list/antag_roles = list( +GLOBAL_LIST_INIT(antag_roles, list( ROLE_TRAITOR, ROLE_OPERATIVE, ROLE_CHANGELING, @@ -18,14 +18,14 @@ var/global/list/antag_roles = list( ROLE_GUARDIAN, ROLE_MORPH, ROLE_GSPIDER, -) +)) // Bannable other roles -var/global/list/other_roles = list( +GLOBAL_LIST_INIT(other_roles, list( ROLE_SENTIENT, ROLE_NYMPH, ROLE_ERT, ROLE_GHOST, "AntagHUD", "Records" -) +)) diff --git a/code/__DEFINES/shuttle.dm b/code/__DEFINES/shuttle.dm index 22af9a38670..72ed25d1518 100644 --- a/code/__DEFINES/shuttle.dm +++ b/code/__DEFINES/shuttle.dm @@ -13,4 +13,4 @@ #define SHUTTLE_DOCKER_LANDING_CLEAR 1 #define SHUTTLE_DOCKER_BLOCKED_BY_HIDDEN_PORT 2 -#define SHUTTLE_DOCKER_BLOCKED 3 \ No newline at end of file +#define SHUTTLE_DOCKER_BLOCKED 3 diff --git a/code/__DEFINES/sight.dm b/code/__DEFINES/sight.dm index 58f557c4b75..52fc66aa814 100644 --- a/code/__DEFINES/sight.dm +++ b/code/__DEFINES/sight.dm @@ -38,4 +38,4 @@ #define VISOR_TINT (1<<1) #define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses #define VISOR_DARKNESSVIEW (1<<3) -#define VISOR_INVISVIEW (1<<4) \ No newline at end of file +#define VISOR_INVISVIEW (1<<4) diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 7e131dcaa21..b0274e9d802 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -14,4 +14,4 @@ #define SOUND_MINIMUM_PRESSURE 10 -#define FALLOFF_SOUNDS 0.5 \ No newline at end of file +#define FALLOFF_SOUNDS 0.5 diff --git a/code/__DEFINES/station_goals.dm b/code/__DEFINES/station_goals.dm index 7c13f4f9e67..0c19da7b5f1 100644 --- a/code/__DEFINES/station_goals.dm +++ b/code/__DEFINES/station_goals.dm @@ -1,2 +1,2 @@ #define BSA_SIZE_FRONT 4 -#define BSA_SIZE_BACK 6 \ No newline at end of file +#define BSA_SIZE_BACK 6 diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 6a14e6fae66..54b85d80e76 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -81,4 +81,4 @@ #define STATUS_EFFECT_CRUSHERDAMAGETRACKING /datum/status_effect/crusher_damage //tracks total kinetic crusher damage on a target -#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module \ No newline at end of file +#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index c55fec2cd87..b1773ba10d5 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -66,6 +66,7 @@ #define INIT_ORDER_TIMER 1 #define INIT_ORDER_DEFAULT 0 #define INIT_ORDER_AIR -1 +#define INIT_ORDER_SUN -2 #define INIT_ORDER_MINIMAP -3 #define INIT_ORDER_ASSETS -4 #define INIT_ORDER_ICON_SMOOTHING -5 @@ -144,4 +145,4 @@ A.overlays |= po;\ }\ A.flags_2 &= ~OVERLAY_QUEUED_2;\ -} \ No newline at end of file +} diff --git a/code/__DEFINES/typeids.dm b/code/__DEFINES/typeids.dm index 16b6e475b8e..56e0958d1a4 100644 --- a/code/__DEFINES/typeids.dm +++ b/code/__DEFINES/typeids.dm @@ -3,4 +3,4 @@ #define TYPEID_NORMAL_LIST "f" //helper macros #define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref) - 6) ) ) -#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) \ No newline at end of file +#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 22e13f87f8a..0ac352f9ae9 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -19,4 +19,4 @@ #define VV_NULL "NULL" #define VV_RESTORE_DEFAULT "Restore to Default" #define VV_MARKED_DATUM "Marked Datum" -#define VV_REGEX "Regex" \ No newline at end of file +#define VV_REGEX "Regex" diff --git a/code/__HELPERS/AnimationLibrary.dm b/code/__HELPERS/AnimationLibrary.dm index fd79fe197f2..cc496a85b63 100644 --- a/code/__HELPERS/AnimationLibrary.dm +++ b/code/__HELPERS/AnimationLibrary.dm @@ -187,4 +187,4 @@ var/original_y = A.pixel_y animate(A, transform = matrix(punchstr, MATRIX_ROTATE), pixel_y = 16, time = 2, color = "#eeeeee", easing = BOUNCE_EASING) animate(transform = matrix(-punchstr, MATRIX_ROTATE), pixel_y = original_y, time = 2, color = "#ffffff", easing = BOUNCE_EASING) - animate(transform = null, time = 3, easing = BOUNCE_EASING) \ No newline at end of file + animate(transform = null, time = 3, easing = BOUNCE_EASING) diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 7a91c3bf9bc..f5d582f5e42 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -5,7 +5,7 @@ // will get logs that are one big line if the system is Linux and they are using notepad. This solves it by adding CR to every line ending // in the logs. ascii character 13 = CR -/var/global/log_end = world.system_type == UNIX ? ascii2text(13) : "" +GLOBAL_VAR_INIT(log_end, (world.system_type == UNIX ? ascii2text(13) : "")) #define DIRECT_OUTPUT(A, B) A << B #define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) @@ -30,13 +30,13 @@ #endif /proc/log_admin(text) - admin_log.Add(text) + GLOB.admin_log.Add(text) if(config.log_admin) - WRITE_LOG(GLOB.world_game_log, "ADMIN: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "ADMIN: [text][GLOB.log_end]") /proc/log_debug(text) if(config.log_debug) - WRITE_LOG(GLOB.world_game_log, "DEBUG: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "DEBUG: [text][GLOB.log_end]") for(var/client/C in GLOB.admins) if(check_rights(R_DEBUG, 0, C.mob) && (C.prefs.toggles & CHAT_DEBUGLOGS)) @@ -44,88 +44,88 @@ /proc/log_game(text) if(config.log_game) - WRITE_LOG(GLOB.world_game_log, "GAME: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "GAME: [text][GLOB.log_end]") /proc/log_vote(text) if(config.log_vote) - WRITE_LOG(GLOB.world_game_log, "VOTE: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "VOTE: [text][GLOB.log_end]") /proc/log_access_in(client/new_client) if(config.log_access) var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version]" - WRITE_LOG(GLOB.world_game_log, "ACCESS IN: [message][log_end]") + WRITE_LOG(GLOB.world_game_log, "ACCESS IN: [message][GLOB.log_end]") /proc/log_access_out(mob/last_mob) if(config.log_access) var/message = "[key_name(last_mob)] - IP:[last_mob.lastKnownIP] - CID:[last_mob.computer_id] - BYOND Logged Out" - WRITE_LOG(GLOB.world_game_log, "ACCESS OUT: [message][log_end]") + WRITE_LOG(GLOB.world_game_log, "ACCESS OUT: [message][GLOB.log_end]") /proc/log_say(text, mob/speaker) if(config.log_say) - WRITE_LOG(GLOB.world_game_log, "SAY: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "SAY: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_whisper(text, mob/speaker) if(config.log_whisper) - WRITE_LOG(GLOB.world_game_log, "WHISPER: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "WHISPER: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_ooc(text, client/user) if(config.log_ooc) - WRITE_LOG(GLOB.world_game_log, "OOC: [user.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "OOC: [user.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_aooc(text, client/user) if(config.log_ooc) - WRITE_LOG(GLOB.world_game_log, "AOOC: [user.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "AOOC: [user.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_looc(text, client/user) if(config.log_ooc) - WRITE_LOG(GLOB.world_game_log, "LOOC: [user.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "LOOC: [user.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_emote(text, mob/speaker) if(config.log_emote) - WRITE_LOG(GLOB.world_game_log, "EMOTE: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "EMOTE: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_attack(attacker, defender, message) if(config.log_attack) - WRITE_LOG(GLOB.world_game_log, "ATTACK: [attacker] against [defender]: [message][log_end]") //Seperate attack logs? Why? + WRITE_LOG(GLOB.world_game_log, "ATTACK: [attacker] against [defender]: [message][GLOB.log_end]") //Seperate attack logs? Why? /proc/log_adminsay(text, mob/speaker) if(config.log_adminchat) - WRITE_LOG(GLOB.world_game_log, "ADMINSAY: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "ADMINSAY: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_qdel(text) WRITE_LOG(GLOB.world_qdel_log, "QDEL: [text]") /proc/log_mentorsay(text, mob/speaker) if(config.log_adminchat) - WRITE_LOG(GLOB.world_game_log, "MENTORSAY: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "MENTORSAY: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_ghostsay(text, mob/speaker) if(config.log_say) - WRITE_LOG(GLOB.world_game_log, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_ghostemote(text, mob/speaker) if(config.log_emote) - WRITE_LOG(GLOB.world_game_log, "DEADEMOTE: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "DEADEMOTE: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_adminwarn(text) if(config.log_adminwarn) - WRITE_LOG(GLOB.world_game_log, "ADMINWARN: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "ADMINWARN: [html_decode(text)][GLOB.log_end]") /proc/log_pda(text, mob/speaker) if(config.log_pda) - WRITE_LOG(GLOB.world_game_log, "PDA: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "PDA: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_chat(text, mob/speaker) if(config.log_pda) - WRITE_LOG(GLOB.world_game_log, "CHAT: [speaker.simple_info_line()] [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "CHAT: [speaker.simple_info_line()] [html_decode(text)][GLOB.log_end]") /proc/log_misc(text) - WRITE_LOG(GLOB.world_game_log, "MISC: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "MISC: [text][GLOB.log_end]") /proc/log_world(text) SEND_TEXT(world.log, text) if(config && config.log_world_output) - WRITE_LOG(GLOB.world_game_log, "WORLD: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "WORLD: [html_decode(text)][GLOB.log_end]") /proc/log_runtime_txt(text) // different from /tg/'s log_runtime because our error handler has a log_runtime proc already that does other stuff WRITE_LOG(GLOB.world_runtime_log, text) diff --git a/code/__HELPERS/_string_lists.dm b/code/__HELPERS/_string_lists.dm index 36aa2f9b6fa..e2f0c6081e7 100644 --- a/code/__HELPERS/_string_lists.dm +++ b/code/__HELPERS/_string_lists.dm @@ -2,15 +2,15 @@ #define pick_list_replacements(FILE, KEY) (strings_replacement(FILE, KEY)) #define json_load(FILE) (json_decode(file2text(FILE))) -var/global/list/string_cache -var/global/list/string_filename_current_key +GLOBAL_LIST_EMPTY(string_cache) +GLOBAL_LIST_EMPTY(string_filename_current_key) /proc/strings_replacement(filename, key) load_strings_file(filename) - if((filename in string_cache) && (key in string_cache[filename])) - var/response = pick(string_cache[filename][key]) + if((filename in GLOB.string_cache) && (key in GLOB.string_cache[filename])) + var/response = pick(GLOB.string_cache[filename][key]) var/regex/r = regex("@pick\\((\\D+?)\\)", "g") response = r.Replace(response, /proc/strings_subkey_lookup) return response @@ -19,23 +19,23 @@ var/global/list/string_filename_current_key /proc/strings(filename as text, key as text) load_strings_file(filename) - if((filename in string_cache) && (key in string_cache[filename])) - return string_cache[filename][key] + if((filename in GLOB.string_cache) && (key in GLOB.string_cache[filename])) + return GLOB.string_cache[filename][key] else CRASH("strings list not found: strings/[filename], index=[key]") /proc/strings_subkey_lookup(match, group1) - return pick_list(string_filename_current_key, group1) + return pick_list(GLOB.string_filename_current_key, group1) /proc/load_strings_file(filename) - string_filename_current_key = filename - if(filename in string_cache) + GLOB.string_filename_current_key = filename + if(filename in GLOB.string_cache) return //no work to do - if(!string_cache) - string_cache = new + if(!GLOB.string_cache) + GLOB.string_cache = new if(fexists("strings/[filename]")) - string_cache[filename] = json_load("strings/[filename]") + GLOB.string_cache[filename] = json_load("strings/[filename]") else CRASH("file not found: strings/[filename]") diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 0db57b27eae..afb70cb81e6 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -47,4 +47,4 @@ if(A.plane != B.plane) return A.plane - B.plane else - return A.layer - B.layer \ No newline at end of file + return A.layer - B.layer diff --git a/code/__HELPERS/constants.dm b/code/__HELPERS/constants.dm index f7db3166605..b976a28e195 100644 --- a/code/__HELPERS/constants.dm +++ b/code/__HELPERS/constants.dm @@ -1,2 +1,2 @@ -var/global/const/TICKS_IN_DAY = 864000 -var/global/const/TICKS_IN_SECOND = 10 \ No newline at end of file +#define TICKS_IN_DAY 864000 +#define TICKS_IN_SECOND 10 diff --git a/code/__HELPERS/experimental.dm b/code/__HELPERS/experimental.dm index 533f67b68d9..4f13c07cbd4 100644 --- a/code/__HELPERS/experimental.dm +++ b/code/__HELPERS/experimental.dm @@ -48,4 +48,4 @@ if(A != myArea) myArea = A - . = myArea \ No newline at end of file + . = myArea diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm index 27f3537d521..0a58ed035f7 100644 --- a/code/__HELPERS/files.dm +++ b/code/__HELPERS/files.dm @@ -1,60 +1,62 @@ -//checks if a file exists and contains text -//returns text as a string if these conditions are met -/proc/return_file_text(filename) - if(fexists(filename) == 0) - error("File not found ([filename])") - return - - var/text = file2text(filename) - if(!text) - error("File empty ([filename])") - return - - return text - -//Sends resource files to client cache -/client/proc/getFiles() - for(var/file in args) - src << browse_rsc(file) - -/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) - var/path = root - - for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).") - return - - return path - -#define FTPDELAY 200 //200 tick delay to discourage spam -/* This proc is a failsafe to prevent spamming of file requests. - It is just a timer that only permits a download every [FTPDELAY] ticks. - This can be changed by modifying FTPDELAY's value above. - - PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */ -/client/proc/file_spam_check() - var/time_to_wait = fileaccess_timer - world.time - if(time_to_wait > 0) - to_chat(src, "Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.") - return 1 - fileaccess_timer = world.time + FTPDELAY - return 0 -#undef FTPDELAY \ No newline at end of file +//checks if a file exists and contains text +//returns text as a string if these conditions are met +/proc/return_file_text(filename) + if(fexists(filename) == 0) + error("File not found ([filename])") + return + + var/text = file2text(filename) + if(!text) + error("File empty ([filename])") + return + + return text + +//Sends resource files to client cache +/client/proc/getFiles() + for(var/file in args) + src << browse_rsc(file) + +/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) + // wow why was this ever a parameter + root = "data/logs/" + var/path = root + + for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).") + return + + return path + +#define FTPDELAY 200 //200 tick delay to discourage spam +/* This proc is a failsafe to prevent spamming of file requests. + It is just a timer that only permits a download every [FTPDELAY] ticks. + This can be changed by modifying FTPDELAY's value above. + + PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */ +/client/proc/file_spam_check() + var/time_to_wait = GLOB.fileaccess_timer - world.time + if(time_to_wait > 0) + to_chat(src, "Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.") + return 1 + GLOB.fileaccess_timer = world.time + FTPDELAY + return 0 +#undef FTPDELAY diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 0ab10447f8c..2217271a2e7 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -1,539 +1,523 @@ -/proc/get_area(atom/A) - if(isarea(A)) - return A - var/turf/T = get_turf(A) - return T ? T.loc : null - -/proc/get_area_name(N) //get area by its name - for(var/area/A in world) - if(A.name == N) - return A - return 0 - -/proc/get_location_name(atom/X, format_text = FALSE) - var/area/A = isarea(X) ? X : get_area(X) - if(!A) - return null - return format_text ? format_text(A.name) : A.name - -/proc/get_areas_in_range(dist=0, atom/center=usr) - if(!dist) - var/turf/T = get_turf(center) - return T ? list(T.loc) : list() - if(!center) - return list() - - var/list/turfs = RANGE_TURFS(dist, center) - var/list/areas = list() - for(var/V in turfs) - var/turf/T = V - areas |= T.loc - return areas - -// Like view but bypasses luminosity check - -/proc/hear(var/range, var/atom/source) - var/lum = source.luminosity - source.luminosity = 6 - - var/list/heard = view(range, source) - source.luminosity = lum - - return heard - -/proc/circlerange(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - - //turfs += centerturf - return turfs - -/proc/circleview(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/atoms = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/A in view(radius, centerturf)) - var/dx = A.x - centerturf.x - var/dy = A.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - atoms += A - - //turfs += centerturf - return atoms - -/proc/ff_cansee(atom/A, atom/B) - var/AT = get_turf(A) - var/BT = get_turf(B) - if(AT == BT) - return 1 - var/list/line = getline(A, B) - for(var/turf/T in line) - if(T == AT || T == BT) - break - if(T.density) - return FALSE - return TRUE - -/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) - var/dx = Loc1.x - Loc2.x - var/dy = Loc1.y - Loc2.y - - var/dist = sqrt(dx**2 + dy**2) - - return dist - -/proc/circlerangeturfs(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - -/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in view(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - - - -//var/debug_mob = 0 - -// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. -// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, -// being unable to hear people due to being in a box within a bag. - -/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) - - //debug_mob += O.contents.len - if(!recursion_limit) - return L - for(var/atom/A in O.contents) - - if(ismob(A)) - var/mob/M = A - if(client_check && !M.client) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - continue - if(sight_check && !isInSight(A, O)) - continue - L |= M - //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - - else if(include_radio && istype(A, /obj/item/radio)) - if(sight_check && !isInSight(A, O)) - continue - L |= A - - if(isobj(A) || ismob(A)) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - return L - -// The old system would loop through lists for a total of 5000 per function call, in an empty server. -// This new system will loop at around 1000 in an empty server. - -/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) - // Returns a list of mobs in range of R from source. Used in radio and say code. - - var/turf/T = get_turf(source) - var/list/hear = list() - - if(!T) - return hear - - var/list/range = hear(R, T) - - for(var/atom/A in range) - if(ismob(A)) - var/mob/M = A - if(M.client || include_clientless) - hear += M - //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - else if(istype(A, /obj/item/radio)) - hear += A - - if(isobj(A) || ismob(A)) - hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) - - return hear - - -/proc/get_mobs_in_radio_ranges(var/list/obj/item/radio/radios) - - set background = 1 - - . = list() - // Returns a list of mobs who can hear any of the radios given in @radios - var/list/speaker_coverage = list() - for(var/obj/item/radio/R in radios) - if(R) - //Cyborg checks. Receiving message uses a bit of cyborg's charge. - var/obj/item/radio/borg/BR = R - if(istype(BR) && BR.myborg) - var/mob/living/silicon/robot/borg = BR.myborg - var/datum/robot_component/CO = borg.get_component("radio") - if(!CO) - continue //No radio component (Shouldn't happen) - if(!borg.is_component_functioning("radio")) - continue //No power. - - var/turf/speaker = get_turf(R) - if(speaker) - for(var/turf/T in hear(R.canhear_range,speaker)) - speaker_coverage[T] = T - - - // Try to find all the players who can hear the message - for(var/A in GLOB.player_list + GLOB.hear_radio_list) - var/mob/M = A - if(M) - var/turf/ear = get_turf(M) - if(ear) - // Ghostship is magic: Ghosts can hear radio chatter from anywhere - if(speaker_coverage[ear] || (istype(M, /mob/dead/observer) && M.get_preference(CHAT_GHOSTRADIO))) - . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down. - return . - -/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) - var/turf/T - if(X1==X2) - if(Y1==Y2) - return 1 //Light cannot be blocked on same tile - else - var/s = SIMPLE_SIGN(Y2-Y1) - Y1+=s - while(Y1!=Y2) - T=locate(X1,Y1,Z) - if(T.opacity) - return 0 - Y1+=s - else - var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) - var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles - var/signX = SIMPLE_SIGN(X2-X1) - var/signY = SIMPLE_SIGN(Y2-Y1) - if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) - if(dy > 0) - return get_step(start, SOUTH) - else - return get_step(start, NORTH) - else - if(dx > 0) - return get_step(start, WEST) - else - return get_step(start, EAST) - -/proc/try_move_adjacent(atom/movable/AM) - var/turf/T = get_turf(AM) - for(var/direction in cardinal) - if(AM.Move(get_step(T, direction))) - break - -/proc/get_mob_by_key(var/key) - for(var/mob/M in GLOB.mob_list) - if(M.ckey == lowertext(key)) - return M - return null - -/proc/get_candidates(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) - var/roletext = get_roletext(be_special_type) - var/list/candidates = list() - // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) - while(!candidates.len && afk_bracket < 6000) - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.client != null) - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) - if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) - if(override_age || player_old_enough_antag(G.client,be_special_type)) - candidates += G.client - afk_bracket += 600 // Add a minute to the bracket, for every attempt - - return candidates - -/proc/get_candidate_ghosts(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) - var/roletext = get_roletext(be_special_type) - var/list/candidates = list() - // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) - while(!candidates.len && afk_bracket < 6000) - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.client != null) - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) - if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) - if(override_age || player_old_enough_antag(G.client,be_special_type)) - candidates += G - afk_bracket += 600 // Add a minute to the bracket, for every attempt - - return candidates - -/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) - if(!isobj(O)) O = new /obj/screen/text() - O.maptext = maptext - O.maptext_height = maptext_height - O.maptext_width = maptext_width - O.screen_loc = screen_loc - return O - -/proc/Show2Group4Delay(obj/O, list/group, delay=0) - if(!isobj(O)) return - if(!group) group = GLOB.clients - for(var/client/C in group) - C.screen += O - if(delay) - spawn(delay) - for(var/client/C in group) - C.screen -= O - -/proc/flick_overlay(image/I, list/show_to, duration) - for(var/client/C in show_to) - C.images += I - spawn(duration) - for(var/client/C in show_to) - C.images -= I - -/proc/get_active_player_count() - // Get active players who are playing in the round - var/active_players = 0 - for(var/i = 1; i <= GLOB.player_list.len; i++) - var/mob/M = GLOB.player_list[i] - if(M && M.client) - if(istype(M, /mob/new_player)) // exclude people in the lobby - continue - else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) - var/mob/dead/observer/O = M - if(O.started_as_observer) // Exclude people who started as observers - continue - active_players++ - return active_players - -/datum/projectile_data - var/src_x - var/src_y - var/time - var/distance - var/power_x - var/power_y - var/dest_x - var/dest_y - -/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ - var/power_x, var/power_y, var/dest_x, var/dest_y) - src.src_x = src_x - src.src_y = src_y - src.time = time - src.distance = distance - src.power_x = power_x - src.power_y = power_y - src.dest_x = dest_x - src.dest_y = dest_y - -/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) - - // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], - // rotated at [rotation] and with the power of [power] - // Thanks to VistaPOWA for this function - - var/power_x = power * cos(angle) - var/power_y = power * sin(angle) - var/time = 2* power_y / 10 //10 = g - - var/distance = time * power_x - - var/dest_x = src_x + distance*sin(rotation); - var/dest_y = src_y + distance*cos(rotation); - - return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) - - -/proc/mobs_in_area(var/area/the_area, var/client_needed=0, var/moblist=GLOB.mob_list) - var/list/mobs_found[0] - var/area/our_area = get_area(the_area) - for(var/mob/M in moblist) - if(client_needed && !M.client) - continue - if(our_area != get_area(M)) - continue - mobs_found += M - return mobs_found - -/proc/alone_in_area(var/area/the_area, var/mob/must_be_alone, var/check_type = /mob/living/carbon) - var/area/our_area = get_area(the_area) - for(var/C in GLOB.living_mob_list) - if(!istype(C, check_type)) - continue - if(C == must_be_alone) - continue - if(our_area == get_area(C)) - return 0 - return 1 - - -/proc/GetRedPart(const/hexa) - return hex2num(copytext(hexa, 2, 4)) - -/proc/GetGreenPart(const/hexa) - return hex2num(copytext(hexa, 4, 6)) - -/proc/GetBluePart(const/hexa) - return hex2num(copytext(hexa, 6, 8)) - -/proc/lavaland_equipment_pressure_check(turf/T) - . = FALSE - if(!istype(T)) - return - var/datum/gas_mixture/environment = T.return_air() - if(!istype(environment)) - return - var/pressure = environment.return_pressure() - if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE) - . = TRUE - -/proc/GetHexColors(const/hexa) - return list( - GetRedPart(hexa), - GetGreenPart(hexa), - GetBluePart(hexa), - ) - -/proc/MinutesToTicks(var/minutes as num) - return minutes * 60 * 10 - -/proc/SecondsToTicks(var/seconds) - return seconds * 10 - -proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/roletext = be_special_type ? get_roletext(be_special_type) : null - var/list/mob/dead/observer/candidates = list() - var/time_passed = world.time - if(!Question) - Question = "Would you like to be a special role?" - - for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) - if(!G.key || !G.client) - continue - if(be_special_type) - if(!(be_special_type in G.client.prefs.be_special)) - continue - if(antag_age_check) - if(!player_old_enough_antag(G.client, be_special_type)) - continue - if(roletext) - if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate")) - continue - if(config.use_exp_restrictions && min_hours) - if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) - continue - if(check_antaghud && cannotPossess(G)) - continue - spawn(0) - G << 'sound/misc/notice2.ogg'//Alerting them to their consideration - if(flashwindow) - window_flash(G.client) - var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round") - if(!G?.client) - return - switch(ans) - if("Yes") - to_chat(G, "Choice registered: Yes.") - if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed. - to_chat(G, "Sorry, you were too late for the consideration!") - G << 'sound/machines/buzz-sigh.ogg' - return - candidates += G - if("No") - to_chat(G, "Choice registered: No.") - return - if("Not This Round") - to_chat(G, "Choice registered: No.") - to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.") - G.client.prefs.be_special -= be_special_type - return - else - return - sleep(poll_time) - - //Check all our candidates, to make sure they didn't log off during the 30 second wait period. - for(var/mob/dead/observer/G in candidates) - if(!G.key || !G.client) - candidates.Remove(G) - - return candidates - -/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud) - var/list/selected_ghosts = list() - if(!willing_ghosts.len) - return selected_ghosts - - var/list/candidate_ghosts = willing_ghosts.Copy() - - to_chat(adminusr, "Candidate Ghosts:"); - for(var/mob/dead/observer/G in candidate_ghosts) - if(G.key && G.client) - to_chat(adminusr, "- [G] ([G.key])"); - else - candidate_ghosts -= G - - for(var/i = max_slots, (i > 0 && candidate_ghosts.len), i--) - var/this_ghost = input("Pick players. This will go on until there either no more ghosts to pick from or the [i] remaining slot(s) are full.", "Candidates") as null|anything in candidate_ghosts - candidate_ghosts -= this_ghost - selected_ghosts += this_ghost - return selected_ghosts - -/proc/window_flash(client/C) - if(ismob(C)) - var/mob/M = C - if(M.client) - C = M.client - if(!C || !C.prefs.windowflashing) - return - winset(C, "mainwindow", "flash=5") +/proc/get_area(atom/A) + RETURN_TYPE(/area) + if(isarea(A)) + return A + var/turf/T = get_turf(A) + return T ? T.loc : null + +/proc/get_area_name(N) //get area by its name + for(var/area/A in world) + if(A.name == N) + return A + return 0 + +/proc/get_location_name(atom/X, format_text = FALSE) + var/area/A = isarea(X) ? X : get_area(X) + if(!A) + return null + return format_text ? format_text(A.name) : A.name + +/proc/get_areas_in_range(dist=0, atom/center=usr) + if(!dist) + var/turf/T = get_turf(center) + return T ? list(T.loc) : list() + if(!center) + return list() + + var/list/turfs = RANGE_TURFS(dist, center) + var/list/areas = list() + for(var/V in turfs) + var/turf/T = V + areas |= T.loc + return areas + +// Like view but bypasses luminosity check + +/proc/hear(var/range, var/atom/source) + var/lum = source.luminosity + source.luminosity = 6 + + var/list/heard = view(range, source) + source.luminosity = lum + + return heard + +/proc/circlerange(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + + //turfs += centerturf + return turfs + +/proc/circleview(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/atoms = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/A in view(radius, centerturf)) + var/dx = A.x - centerturf.x + var/dy = A.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + atoms += A + + //turfs += centerturf + return atoms + +/proc/ff_cansee(atom/A, atom/B) + var/AT = get_turf(A) + var/BT = get_turf(B) + if(AT == BT) + return 1 + var/list/line = getline(A, B) + for(var/turf/T in line) + if(T == AT || T == BT) + break + if(T.density) + return FALSE + return TRUE + +/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) + var/dx = Loc1.x - Loc2.x + var/dy = Loc1.y - Loc2.y + + var/dist = sqrt(dx**2 + dy**2) + + return dist + +/proc/circlerangeturfs(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + +/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in view(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + + + +//GLOBAL_VAR_INIT(debug_mob, 0) + +// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. +// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, +// being unable to hear people due to being in a box within a bag. + +/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) + + //GLOB.debug_mob += O.contents.len + if(!recursion_limit) + return L + for(var/atom/A in O.contents) + + if(ismob(A)) + var/mob/M = A + if(client_check && !M.client) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + continue + if(sight_check && !isInSight(A, O)) + continue + L |= M + //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + + else if(include_radio && istype(A, /obj/item/radio)) + if(sight_check && !isInSight(A, O)) + continue + L |= A + + if(isobj(A) || ismob(A)) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + return L + +// The old system would loop through lists for a total of 5000 per function call, in an empty server. +// This new system will loop at around 1000 in an empty server. + +/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) + // Returns a list of mobs in range of R from source. Used in radio and say code. + + var/turf/T = get_turf(source) + var/list/hear = list() + + if(!T) + return hear + + var/list/range = hear(R, T) + + for(var/atom/A in range) + if(ismob(A)) + var/mob/M = A + if(M.client || include_clientless) + hear += M + //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + else if(istype(A, /obj/item/radio)) + hear += A + + if(isobj(A) || ismob(A)) + hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) + + return hear + + +/proc/get_mobs_in_radio_ranges(var/list/obj/item/radio/radios) + + set background = 1 + + . = list() + // Returns a list of mobs who can hear any of the radios given in @radios + var/list/speaker_coverage = list() + for(var/obj/item/radio/R in radios) + if(R) + //Cyborg checks. Receiving message uses a bit of cyborg's charge. + var/obj/item/radio/borg/BR = R + if(istype(BR) && BR.myborg) + var/mob/living/silicon/robot/borg = BR.myborg + var/datum/robot_component/CO = borg.get_component("radio") + if(!CO) + continue //No radio component (Shouldn't happen) + if(!borg.is_component_functioning("radio")) + continue //No power. + + var/turf/speaker = get_turf(R) + if(speaker) + for(var/turf/T in hear(R.canhear_range,speaker)) + speaker_coverage[T] = T + + + // Try to find all the players who can hear the message + for(var/A in GLOB.player_list + GLOB.hear_radio_list) + var/mob/M = A + if(M) + var/turf/ear = get_turf(M) + if(ear) + // Ghostship is magic: Ghosts can hear radio chatter from anywhere + if(speaker_coverage[ear] || (istype(M, /mob/dead/observer) && M.get_preference(CHAT_GHOSTRADIO))) + . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down. + return . + +/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) + var/turf/T + if(X1==X2) + if(Y1==Y2) + return 1 //Light cannot be blocked on same tile + else + var/s = SIMPLE_SIGN(Y2-Y1) + Y1+=s + while(Y1!=Y2) + T=locate(X1,Y1,Z) + if(T.opacity) + return 0 + Y1+=s + else + var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) + var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles + var/signX = SIMPLE_SIGN(X2-X1) + var/signY = SIMPLE_SIGN(Y2-Y1) + if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) + if(dy > 0) + return get_step(start, SOUTH) + else + return get_step(start, NORTH) + else + if(dx > 0) + return get_step(start, WEST) + else + return get_step(start, EAST) + +/proc/try_move_adjacent(atom/movable/AM) + var/turf/T = get_turf(AM) + for(var/direction in GLOB.cardinal) + if(AM.Move(get_step(T, direction))) + break + +/proc/get_mob_by_key(var/key) + for(var/mob/M in GLOB.mob_list) + if(M.ckey == lowertext(key)) + return M + return null + +/proc/get_candidates(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) + var/roletext = get_roletext(be_special_type) + var/list/candidates = list() + // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) + while(!candidates.len && afk_bracket < 6000) + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.client != null) + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) + if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) + if(override_age || player_old_enough_antag(G.client,be_special_type)) + candidates += G.client + afk_bracket += 600 // Add a minute to the bracket, for every attempt + + return candidates + +/proc/get_candidate_ghosts(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) + var/roletext = get_roletext(be_special_type) + var/list/candidates = list() + // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) + while(!candidates.len && afk_bracket < 6000) + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.client != null) + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) + if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) + if(override_age || player_old_enough_antag(G.client,be_special_type)) + candidates += G + afk_bracket += 600 // Add a minute to the bracket, for every attempt + + return candidates + +/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) + if(!isobj(O)) O = new /obj/screen/text() + O.maptext = maptext + O.maptext_height = maptext_height + O.maptext_width = maptext_width + O.screen_loc = screen_loc + return O + +/proc/Show2Group4Delay(obj/O, list/group, delay=0) + if(!isobj(O)) return + if(!group) group = GLOB.clients + for(var/client/C in group) + C.screen += O + if(delay) + spawn(delay) + for(var/client/C in group) + C.screen -= O + +/proc/flick_overlay(image/I, list/show_to, duration) + for(var/client/C in show_to) + C.images += I + spawn(duration) + for(var/client/C in show_to) + C.images -= I + +/proc/get_active_player_count() + // Get active players who are playing in the round + var/active_players = 0 + for(var/i = 1; i <= GLOB.player_list.len; i++) + var/mob/M = GLOB.player_list[i] + if(M && M.client) + if(istype(M, /mob/new_player)) // exclude people in the lobby + continue + else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) + var/mob/dead/observer/O = M + if(O.started_as_observer) // Exclude people who started as observers + continue + active_players++ + return active_players + +/datum/projectile_data + var/src_x + var/src_y + var/time + var/distance + var/power_x + var/power_y + var/dest_x + var/dest_y + +/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ + var/power_x, var/power_y, var/dest_x, var/dest_y) + src.src_x = src_x + src.src_y = src_y + src.time = time + src.distance = distance + src.power_x = power_x + src.power_y = power_y + src.dest_x = dest_x + src.dest_y = dest_y + +/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) + + // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], + // rotated at [rotation] and with the power of [power] + // Thanks to VistaPOWA for this function + + var/power_x = power * cos(angle) + var/power_y = power * sin(angle) + var/time = 2* power_y / 10 //10 = g + + var/distance = time * power_x + + var/dest_x = src_x + distance*sin(rotation); + var/dest_y = src_y + distance*cos(rotation); + + return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) + + +/proc/mobs_in_area(var/area/the_area, var/client_needed=0, var/moblist=GLOB.mob_list) + var/list/mobs_found[0] + var/area/our_area = get_area(the_area) + for(var/mob/M in moblist) + if(client_needed && !M.client) + continue + if(our_area != get_area(M)) + continue + mobs_found += M + return mobs_found + +/proc/alone_in_area(var/area/the_area, var/mob/must_be_alone, var/check_type = /mob/living/carbon) + var/area/our_area = get_area(the_area) + for(var/C in GLOB.living_mob_list) + if(!istype(C, check_type)) + continue + if(C == must_be_alone) + continue + if(our_area == get_area(C)) + return 0 + return 1 + +/proc/lavaland_equipment_pressure_check(turf/T) + . = FALSE + if(!istype(T)) + return + var/datum/gas_mixture/environment = T.return_air() + if(!istype(environment)) + return + var/pressure = environment.return_pressure() + if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE) + . = TRUE + +/proc/MinutesToTicks(var/minutes as num) + return minutes * 60 * 10 + +/proc/SecondsToTicks(var/seconds) + return seconds * 10 + +proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) + var/roletext = be_special_type ? get_roletext(be_special_type) : null + var/list/mob/dead/observer/candidates = list() + var/time_passed = world.time + if(!Question) + Question = "Would you like to be a special role?" + + for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) + if(!G.key || !G.client) + continue + if(be_special_type) + if(!(be_special_type in G.client.prefs.be_special)) + continue + if(antag_age_check) + if(!player_old_enough_antag(G.client, be_special_type)) + continue + if(roletext) + if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate")) + continue + if(config.use_exp_restrictions && min_hours) + if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) + continue + if(check_antaghud && cannotPossess(G)) + continue + spawn(0) + G << 'sound/misc/notice2.ogg'//Alerting them to their consideration + if(flashwindow) + window_flash(G.client) + var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round") + if(!G?.client) + return + switch(ans) + if("Yes") + to_chat(G, "Choice registered: Yes.") + if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed. + to_chat(G, "Sorry, you were too late for the consideration!") + G << 'sound/machines/buzz-sigh.ogg' + return + candidates += G + if("No") + to_chat(G, "Choice registered: No.") + return + if("Not This Round") + to_chat(G, "Choice registered: No.") + to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.") + G.client.prefs.be_special -= be_special_type + return + else + return + sleep(poll_time) + + //Check all our candidates, to make sure they didn't log off during the 30 second wait period. + for(var/mob/dead/observer/G in candidates) + if(!G.key || !G.client) + candidates.Remove(G) + + return candidates + +/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) + var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud) + var/list/selected_ghosts = list() + if(!willing_ghosts.len) + return selected_ghosts + + var/list/candidate_ghosts = willing_ghosts.Copy() + + to_chat(adminusr, "Candidate Ghosts:"); + for(var/mob/dead/observer/G in candidate_ghosts) + if(G.key && G.client) + to_chat(adminusr, "- [G] ([G.key])"); + else + candidate_ghosts -= G + + for(var/i = max_slots, (i > 0 && candidate_ghosts.len), i--) + var/this_ghost = input("Pick players. This will go on until there either no more ghosts to pick from or the [i] remaining slot(s) are full.", "Candidates") as null|anything in candidate_ghosts + candidate_ghosts -= this_ghost + selected_ghosts += this_ghost + return selected_ghosts + +/proc/window_flash(client/C) + if(ismob(C)) + var/mob/M = C + if(M.client) + C = M.client + if(!C || !C.prefs.windowflashing) + return + winset(C, "mainwindow", "flash=5") diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 2a23cc8baa0..0002bc0d488 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -1,90 +1,90 @@ - -////////////////////////// -/////Initial Building///// -////////////////////////// - -/proc/makeDatumRefLists() - //markings - init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.marking_styles_list) - //head accessory - init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list) - //hair - init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list) - //facial hair - init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list) - //underwear - init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f) - //undershirt - init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f) - //socks - init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list, GLOB.socks_m, GLOB.socks_f) - //alt heads - init_sprite_accessory_subtypes(/datum/sprite_accessory/alt_heads, GLOB.alt_heads_list) - - init_subtypes(/datum/surgery_step, GLOB.surgery_steps) - - for(var/path in (subtypesof(/datum/surgery))) - GLOB.surgeries_list += new path() - - init_datum_subtypes(/datum/job, GLOB.joblist, list(/datum/job/ai, /datum/job/cyborg), "title") - init_datum_subtypes(/datum/superheroes, GLOB.all_superheroes, null, "name") - init_datum_subtypes(/datum/language, GLOB.all_languages, null, "name") - - for(var/language_name in GLOB.all_languages) - var/datum/language/L = GLOB.all_languages[language_name] - if(!(L.flags & NONGLOBAL)) - GLOB.language_keys[":[lowertext(L.key)]"] = L - GLOB.language_keys[".[lowertext(L.key)]"] = L - GLOB.language_keys["#[lowertext(L.key)]"] = L - - var/rkey = 0 - for(var/spath in subtypesof(/datum/species)) - var/datum/species/S = new spath() - S.race_key = ++rkey //Used in mob icon caching. - GLOB.all_species[S.name] = S - - if(IS_WHITELISTED in S.species_traits) - GLOB.whitelisted_species += S.name - - init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes) - - //Pipe list building - init_subtypes(/datum/pipes, GLOB.construction_pipe_list) - for(var/D in GLOB.construction_pipe_list) - var/datum/pipes/P = D - if(P.rpd_dispensable) - GLOB.rpd_pipe_list += list(list("pipe_name" = P.pipe_name, "pipe_id" = P.pipe_id, "pipe_type" = P.pipe_type, "pipe_category" = P.pipe_category, "orientations" = P.orientations, "pipe_icon" = P.pipe_icon, "bendy" = P.bendy)) - return 1 - -/* // Uncomment to debug chemical reaction list. -/client/verb/debug_chemical_list() - - for(var/reaction in GLOB.chemical_reactions_list) - . += "GLOB.chemical_reactions_list\[\"[reaction]\"\] = \"[GLOB.chemical_reactions_list[reaction]]\"\n" - if(islist(GLOB.chemical_reactions_list[reaction])) - var/list/L = GLOB.chemical_reactions_list[reaction] - for(var/t in L) - . += " has: [t]\n" - to_chat(world, .) -*/ - - -//creates every subtype of prototype (excluding prototype) and adds it to list L. -//if no list/L is provided, one is created. -/proc/init_subtypes(prototype, list/L) - if(!istype(L)) L = list() - for(var/path in subtypesof(prototype)) - L += new path() - return L - -/proc/init_datum_subtypes(prototype, list/L, list/pexempt, assocvar) - if(!istype(L)) L = list() - for(var/path in subtypesof(prototype) - pexempt) - var/datum/D = new path() - if(istype(D)) - var/assoc - if(D.vars["[assocvar]"]) //has the var - assoc = D.vars["[assocvar]"] //access value of var - if(assoc) //value gotten - L["[assoc]"] = D //put in association - return L + +////////////////////////// +/////Initial Building///// +////////////////////////// + +/proc/makeDatumRefLists() + //markings + init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.marking_styles_list) + //head accessory + init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list) + //hair + init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list) + //facial hair + init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list) + //underwear + init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f) + //undershirt + init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f) + //socks + init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list, GLOB.socks_m, GLOB.socks_f) + //alt heads + init_sprite_accessory_subtypes(/datum/sprite_accessory/alt_heads, GLOB.alt_heads_list) + + init_subtypes(/datum/surgery_step, GLOB.surgery_steps) + + for(var/path in (subtypesof(/datum/surgery))) + GLOB.surgeries_list += new path() + + init_datum_subtypes(/datum/job, GLOB.joblist, list(/datum/job/ai, /datum/job/cyborg), "title") + init_datum_subtypes(/datum/superheroes, GLOB.all_superheroes, null, "name") + init_datum_subtypes(/datum/language, GLOB.all_languages, null, "name") + + for(var/language_name in GLOB.all_languages) + var/datum/language/L = GLOB.all_languages[language_name] + if(!(L.flags & NONGLOBAL)) + GLOB.language_keys[":[lowertext(L.key)]"] = L + GLOB.language_keys[".[lowertext(L.key)]"] = L + GLOB.language_keys["#[lowertext(L.key)]"] = L + + var/rkey = 0 + for(var/spath in subtypesof(/datum/species)) + var/datum/species/S = new spath() + S.race_key = ++rkey //Used in mob icon caching. + GLOB.all_species[S.name] = S + + if(IS_WHITELISTED in S.species_traits) + GLOB.whitelisted_species += S.name + + init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes) + + //Pipe list building + init_subtypes(/datum/pipes, GLOB.construction_pipe_list) + for(var/D in GLOB.construction_pipe_list) + var/datum/pipes/P = D + if(P.rpd_dispensable) + GLOB.rpd_pipe_list += list(list("pipe_name" = P.pipe_name, "pipe_id" = P.pipe_id, "pipe_type" = P.pipe_type, "pipe_category" = P.pipe_category, "orientations" = P.orientations, "pipe_icon" = P.pipe_icon, "bendy" = P.bendy)) + return 1 + +/* // Uncomment to debug chemical reaction list. +/client/verb/debug_chemical_list() + + for(var/reaction in GLOB.chemical_reactions_list) + . += "GLOB.chemical_reactions_list\[\"[reaction]\"\] = \"[GLOB.chemical_reactions_list[reaction]]\"\n" + if(islist(GLOB.chemical_reactions_list[reaction])) + var/list/L = GLOB.chemical_reactions_list[reaction] + for(var/t in L) + . += " has: [t]\n" + to_chat(world, .) +*/ + + +//creates every subtype of prototype (excluding prototype) and adds it to list L. +//if no list/L is provided, one is created. +/proc/init_subtypes(prototype, list/L) + if(!istype(L)) L = list() + for(var/path in subtypesof(prototype)) + L += new path() + return L + +/proc/init_datum_subtypes(prototype, list/L, list/pexempt, assocvar) + if(!istype(L)) L = list() + for(var/path in subtypesof(prototype) - pexempt) + var/datum/D = new path() + if(istype(D)) + var/assoc + if(D.vars["[assocvar]"]) //has the var + assoc = D.vars["[assocvar]"] //access value of var + if(assoc) //value gotten + L["[assoc]"] = D //put in association + return L diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index acf202f0eee..dc256a93478 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -66,7 +66,7 @@ if(AM.can_be_unanchored && !AM.anchored) return 0 - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) AM = find_type_in_direction(A, direction) if(AM == NULLTURF_BORDER) if((A.smooth & SMOOTH_BORDER)) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 0b37872decd..4e11dbf4140 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1,963 +1,975 @@ -/* -IconProcs README - -A BYOND library for manipulating icons and colors - -by Lummox JR - -version 1.0 - -The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation -routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners. - -CHANGING ICONS - -Several new procs have been added to the /icon datum to simplify working with icons. To use them, -remember you first need to setup an /icon var like so: - -var/icon/my_icon = new('iconfile.dmi') - -icon/ChangeOpacity(amount = 1) - A very common operation in DM is to try to make an icon more or less transparent. Making an icon more - transparent is usually much easier than making it less so, however. This proc basically is a frontend - for MapColors() which can change opacity any way you like, in much the same way that SetIntensity() - can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half. - If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque. -icon/GrayScale() - Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact. -icon/ColorTone(tone) - Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an - RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect. - See also the global ColorTone() proc. -icon/MinColors(icon) - The icon is blended with a second icon where the minimum of each RGB pixel is the result. - Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon. -icon/MaxColors(icon) - The icon is blended with a second icon where the maximum of each RGB pixel is the result. - Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon. -icon/Opaque(background = "#000000") - All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify. -icon/BecomeAlphaMask() - You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc. - The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white. -icon/AddAlphaMask(mask) - The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque, - the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent. - Where the mask is translucent, the current icon becomes more transparent. -icon/UseAlphaMask(mask, mode) - Sometimes you may want to take the alpha values from one icon and use them on a different icon. - This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change - so it has the same colors as before but uses the mask for opacity. - -COLOR MANAGEMENT AND HSV - -RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value. - - * The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to - cyan to blue to magenta and back to red. - * The saturation of a color is how much color is in it. A color with low saturation will be more gray, - and with no saturation at all it is a shade of gray. - * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark, - and no value at all is black. - -Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three -hex digits because it ranges from 0 to 0x5FF. - - * 0 to 0xFF - red to yellow - * 0x100 to 0x1FF - yellow to green - * 0x200 to 0x2FF - green to cyan - * 0x300 to 0x3FF - cyan to blue - * 0x400 to 0x4FF - blue to magenta - * 0x500 to 0x5FF - magenta to red - -Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible), -value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff". - -More than one HSV color can match the same RGB color. - -Here are some procs you can use for color management: - -ReadRGB(rgb) - Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used - that includes alpha, the list will have a fourth item for the alpha value. -hsv(hue, sat, val, apha) - Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa" - format. Alpha is not included in the result if null. -ReadHSV(rgb) - Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that - includes alpha, the list will have a fourth item for the alpha value. -RGBtoHSV(rgb) - Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff". -HSVtoRGB(hsv) - Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa". -BlendRGB(rgb1, rgb2, amount) - Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result; - if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an RGB or RGBA color. -BlendHSV(hsv1, hsv2, amount) - Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB - blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1, - the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an HSV or HSVA color. -BlendRGBasHSV(rgb1, rgb2, amount) - Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form. -HueToAngle(hue) - Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue. -AngleToHue(hue) - Converts an angle to a hue in the valid range. -RotateHue(hsv, angle) - Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360. - (Rotating red by 60° produces yellow.) The result is another HSV or HSVA color with the same saturation and value - as the original, but a different hue. -GrayScale(rgb) - Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string. -ColorTone(rgb, tone) - Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of - using strict shades of gray. The tone value is an RGB color; any alpha value is ignored. -*/ - -/* -Get Flat Icon DEMO by DarkCampainger - -This is a test for the get flat icon proc, modified approprietly for icons and their states. -Probably not a good idea to run this unless you want to see how the proc works in detail. -mob - icon = 'old_or_unused.dmi' - icon_state = "green" - - Login() - // Testing image underlays - underlays += image(icon='old_or_unused.dmi',icon_state="red") - underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32) - underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32) - - // Testing image overlays - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32) - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32) - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32) - - // Testing icon file overlays (defaults to mob's state) - overlays += '_flat_demoIcons2.dmi' - - // Testing icon_state overlays (defaults to mob's icon) - overlays += "white" - - // Testing dynamic icon overlays - var/icon/I = icon('old_or_unused.dmi', icon_state="aqua") - I.Shift(NORTH,16,1) - overlays+=I - - // Testing dynamic image overlays - I=image(icon=I,pixel_x = -32, pixel_y = 32) - overlays+=I - - // Testing object types (and layers) - overlays+=/obj/effect/overlayTest - - loc = locate (10,10,1) - verb - Browse_Icon() - set name = "1. Browse Icon" - // Give it a name for the cache - var/iconName = "[ckey(src.name)]_flattened.dmi" - // Send the icon to src's local cache - src<

") - - Output_Icon() - set name = "2. Output Icon" - to_chat(src, "Icon is: [bicon(getFlatIcon(src))]") - - Label_Icon() - set name = "3. Label Icon" - // Give it a name for the cache - var/iconName = "[ckey(src.name)]_flattened.dmi" - // Copy the file to the rsc manually - var/icon/I = fcopy_rsc(getFlatIcon(src)) - // Send the icon to src's local cache - src< transparent, gray -> translucent white, white -> solid white -/icon/proc/BecomeAlphaMask() - SwapColor(null, "#000000ff") // don't let transparent become gray - MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0) - -/icon/proc/UseAlphaMask(mask) - Opaque() - AddAlphaMask(mask) - -/icon/proc/AddAlphaMask(mask) - var/icon/M = new(mask) - M.Blend("#ffffff", ICON_SUBTRACT) - // apply mask - Blend(M, ICON_ADD) - -/* - HSV format is represented as "#hhhssvv" or "#hhhssvvaa" - - Hue ranges from 0 to 0x5ff (1535) - - 0x000 = red - 0x100 = yellow - 0x200 = green - 0x300 = cyan - 0x400 = blue - 0x500 = magenta - - Saturation is from 0 to 0xff (255) - - More saturation = more color - Less saturation = more gray - - Value ranges from 0 to 0xff (255) - - Higher value means brighter color - */ - -/proc/ReadRGB(rgb) - if(!rgb) return - - // interpret the HSV or HSVA value - var/i=1,start=1 - if(text2ascii(rgb) == 35) ++start // skip opening # - var/ch,which=0,r=0,g=0,b=0,alpha=0,usealpha - var/digits=0 - for(i=start, i<=length(rgb), ++i) - ch = text2ascii(rgb, i) - if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break - ++digits - if(digits == 8) break - - var/single = digits < 6 - if(digits != 3 && digits != 4 && digits != 6 && digits != 8) return - if(digits == 4 || digits == 8) usealpha = 1 - for(i=start, digits>0, ++i) - ch = text2ascii(rgb, i) - if(ch >= 48 && ch <= 57) ch -= 48 - else if(ch >= 65 && ch <= 70) ch -= 55 - else if(ch >= 97 && ch <= 102) ch -= 87 - else break - --digits - switch(which) - if(0) - r = (r << 4) | ch - if(single) - r |= r << 4 - ++which - else if(!(digits & 1)) ++which - if(1) - g = (g << 4) | ch - if(single) - g |= g << 4 - ++which - else if(!(digits & 1)) ++which - if(2) - b = (b << 4) | ch - if(single) - b |= b << 4 - ++which - else if(!(digits & 1)) ++which - if(3) - alpha = (alpha << 4) | ch - if(single) alpha |= alpha << 4 - - . = list(r, g, b) - if(usealpha) . += alpha - -/proc/ReadHSV(hsv) - if(!hsv) return - - // interpret the HSV or HSVA value - var/i=1,start=1 - if(text2ascii(hsv) == 35) ++start // skip opening # - var/ch,which=0,hue=0,sat=0,val=0,alpha=0,usealpha - var/digits=0 - for(i=start, i<=length(hsv), ++i) - ch = text2ascii(hsv, i) - if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break - ++digits - if(digits == 9) break - if(digits > 7) usealpha = 1 - if(digits <= 4) ++which - if(digits <= 2) ++which - for(i=start, digits>0, ++i) - ch = text2ascii(hsv, i) - if(ch >= 48 && ch <= 57) ch -= 48 - else if(ch >= 65 && ch <= 70) ch -= 55 - else if(ch >= 97 && ch <= 102) ch -= 87 - else break - --digits - switch(which) - if(0) - hue = (hue << 4) | ch - if(digits == (usealpha ? 6 : 4)) ++which - if(1) - sat = (sat << 4) | ch - if(digits == (usealpha ? 4 : 2)) ++which - if(2) - val = (val << 4) | ch - if(digits == (usealpha ? 2 : 0)) ++which - if(3) - alpha = (alpha << 4) | ch - - . = list(hue, sat, val) - if(usealpha) . += alpha - -/proc/HSVtoRGB(hsv) - if(!hsv) return "#000000" - var/list/HSV = ReadHSV(hsv) - if(!HSV) return "#000000" - - var/hue = HSV[1] - var/sat = HSV[2] - var/val = HSV[3] - - // Compress hue into easier-to-manage range - hue -= hue >> 8 - if(hue >= 0x5fa) hue -= 0x5fa - - var/hi,mid,lo,r,g,b - hi = val - lo = round((255 - sat) * val / 255, 1) - mid = lo + round(abs(round(hue, 510) - hue) * (hi - lo) / 255, 1) - if(hue >= 765) - if(hue >= 1275) {r=hi; g=lo; b=mid} - else if(hue >= 1020) {r=mid; g=lo; b=hi } - else {r=lo; g=mid; b=hi } - else - if(hue >= 510) {r=lo; g=hi; b=mid} - else if(hue >= 255) {r=mid; g=hi; b=lo } - else {r=hi; g=mid; b=lo } - - return (HSV.len > 3) ? rgb(r,g,b,HSV[4]) : rgb(r,g,b) - -/proc/RGBtoHSV(rgb) - if(!rgb) return "#0000000" - var/list/RGB = ReadRGB(rgb) - if(!RGB) return "#0000000" - - var/r = RGB[1] - var/g = RGB[2] - var/b = RGB[3] - var/hi = max(r,g,b) - var/lo = min(r,g,b) - - var/val = hi - var/sat = hi ? round((hi-lo) * 255 / hi, 1) : 0 - var/hue = 0 - - if(sat) - var/dir - var/mid - if(hi == r) - if(lo == b) {hue=0; dir=1; mid=g} - else {hue=1535; dir=-1; mid=b} - else if(hi == g) - if(lo == r) {hue=512; dir=1; mid=b} - else {hue=511; dir=-1; mid=r} - else if(hi == b) - if(lo == g) {hue=1024; dir=1; mid=r} - else {hue=1023; dir=-1; mid=g} - hue += dir * round((mid-lo) * 255 / (hi-lo), 1) - - return hsv(hue, sat, val, (RGB.len>3 ? RGB[4] : null)) - -/proc/hsv(hue, sat, val, alpha) - if(hue < 0 || hue >= 1536) hue %= 1536 - if(hue < 0) hue += 1536 - if((hue & 0xFF) == 0xFF) - ++hue - if(hue >= 1536) hue = 0 - if(sat < 0) sat = 0 - if(sat > 255) sat = 255 - if(val < 0) val = 0 - if(val > 255) val = 255 - . = "#" - . += TO_HEX_DIGIT(hue >> 8) - . += TO_HEX_DIGIT(hue >> 4) - . += TO_HEX_DIGIT(hue) - . += TO_HEX_DIGIT(sat >> 4) - . += TO_HEX_DIGIT(sat) - . += TO_HEX_DIGIT(val >> 4) - . += TO_HEX_DIGIT(val) - if(!isnull(alpha)) - if(alpha < 0) alpha = 0 - if(alpha > 255) alpha = 255 - . += TO_HEX_DIGIT(alpha >> 4) - . += TO_HEX_DIGIT(alpha) - -/* - Smooth blend between HSV colors - - amount=0 is the first color - amount=1 is the second color - amount=0.5 is directly between the two colors - - amount<0 or amount>1 are allowed - */ -/proc/BlendHSV(hsv1, hsv2, amount) - var/list/HSV1 = ReadHSV(hsv1) - var/list/HSV2 = ReadHSV(hsv2) - - // add missing alpha if needed - if(HSV1.len < HSV2.len) HSV1 += 255 - else if(HSV2.len < HSV1.len) HSV2 += 255 - var/usealpha = HSV1.len > 3 - - // normalize hsv values in case anything is screwy - if(HSV1[1] > 1536) HSV1[1] %= 1536 - if(HSV2[1] > 1536) HSV2[1] %= 1536 - if(HSV1[1] < 0) HSV1[1] += 1536 - if(HSV2[1] < 0) HSV2[1] += 1536 - if(!HSV1[3]) {HSV1[1] = 0; HSV1[2] = 0} - if(!HSV2[3]) {HSV2[1] = 0; HSV2[2] = 0} - - // no value for one color means don't change saturation - if(!HSV1[3]) HSV1[2] = HSV2[2] - if(!HSV2[3]) HSV2[2] = HSV1[2] - // no saturation for one color means don't change hues - if(!HSV1[2]) HSV1[1] = HSV2[1] - if(!HSV2[2]) HSV2[1] = HSV1[1] - - // Compress hues into easier-to-manage range - HSV1[1] -= HSV1[1] >> 8 - HSV2[1] -= HSV2[1] >> 8 - - var/hue_diff = HSV2[1] - HSV1[1] - if(hue_diff > 765) hue_diff -= 1530 - else if(hue_diff <= -765) hue_diff += 1530 - - var/hue = round(HSV1[1] + hue_diff * amount, 1) - var/sat = round(HSV1[2] + (HSV2[2] - HSV1[2]) * amount, 1) - var/val = round(HSV1[3] + (HSV2[3] - HSV1[3]) * amount, 1) - var/alpha = usealpha ? round(HSV1[4] + (HSV2[4] - HSV1[4]) * amount, 1) : null - - // normalize hue - if(hue < 0 || hue >= 1530) hue %= 1530 - if(hue < 0) hue += 1530 - // decompress hue - hue += round(hue / 255) - - return hsv(hue, sat, val, alpha) - -/* - Smooth blend between RGB colors - - amount=0 is the first color - amount=1 is the second color - amount=0.5 is directly between the two colors - - amount<0 or amount>1 are allowed - */ -/proc/BlendRGB(rgb1, rgb2, amount) - var/list/RGB1 = ReadRGB(rgb1) - var/list/RGB2 = ReadRGB(rgb2) - - // add missing alpha if needed - if(RGB1.len < RGB2.len) RGB1 += 255 - else if(RGB2.len < RGB1.len) RGB2 += 255 - var/usealpha = RGB1.len > 3 - - var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) - var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) - var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) - var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null - - return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) - -/proc/BlendRGBasHSV(rgb1, rgb2, amount) - return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount) - -/proc/HueToAngle(hue) - // normalize hsv in case anything is screwy - if(hue < 0 || hue >= 1536) hue %= 1536 - if(hue < 0) hue += 1536 - // Compress hue into easier-to-manage range - hue -= hue >> 8 - return hue / (1530/360) - -/proc/AngleToHue(angle) - // normalize hsv in case anything is screwy - if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) - var/hue = angle * (1530/360) - // Decompress hue - hue += round(hue / 255) - return hue - - -// positive angle rotates forward through red->green->blue -/proc/RotateHue(hsv, angle) - var/list/HSV = ReadHSV(hsv) - - // normalize hsv in case anything is screwy - if(HSV[1] >= 1536) HSV[1] %= 1536 - if(HSV[1] < 0) HSV[1] += 1536 - - // Compress hue into easier-to-manage range - HSV[1] -= HSV[1] >> 8 - - if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) - HSV[1] = round(HSV[1] + angle * (1530/360), 1) - - // normalize hue - if(HSV[1] < 0 || HSV[1] >= 1530) HSV[1] %= 1530 - if(HSV[1] < 0) HSV[1] += 1530 - // decompress hue - HSV[1] += round(HSV[1] / 255) - - return hsv(HSV[1], HSV[2], HSV[3], (HSV.len > 3 ? HSV[4] : null)) - -// Convert an rgb color to grayscale -/proc/GrayScale(rgb) - var/list/RGB = ReadRGB(rgb) - var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 - return (RGB.len > 3) ? rgb(gray, gray, gray, RGB[4]) : rgb(gray, gray, gray) - -// Change grayscale color to black->tone->white range -/proc/ColorTone(rgb, tone) - var/list/RGB = ReadRGB(rgb) - var/list/TONE = ReadRGB(tone) - - var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 - var/tone_gray = TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11 - - if(gray <= tone_gray) return BlendRGB("#000000", tone, gray/(tone_gray || 1)) - else return BlendRGB(tone, "#ffffff", (gray-tone_gray)/((255-tone_gray) || 1)) - - -/* -Get flat icon by DarkCampainger. As it says on the tin, will return an icon with all the overlays -as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included. -The _flatIcons list is a cache for generated icon files. -*/ - -// Creates a single icon from a given /atom or /image. Only the first argument is required. -/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) - //Define... defines. - var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") - - #define BLANK icon(flat_template) - #define SET_SELF(SETVAR) do { \ - var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \ - if(A.alpha<255) { \ - SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\ - } \ - if(A.color) { \ - if(islist(A.color)){ \ - SELF_ICON.MapColors(arglist(A.color))} \ - else{ \ - SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \ - } \ - ##SETVAR=SELF_ICON;\ - } while (0) - #define INDEX_X_LOW 1 - #define INDEX_X_HIGH 2 - #define INDEX_Y_LOW 3 - #define INDEX_Y_HIGH 4 - - #define flatX1 flat_size[INDEX_X_LOW] - #define flatX2 flat_size[INDEX_X_HIGH] - #define flatY1 flat_size[INDEX_Y_LOW] - #define flatY2 flat_size[INDEX_Y_HIGH] - #define addX1 add_size[INDEX_X_LOW] - #define addX2 add_size[INDEX_X_HIGH] - #define addY1 add_size[INDEX_Y_LOW] - #define addY2 add_size[INDEX_Y_HIGH] - - if(!A || A.alpha <= 0) - return BLANK - - var/noIcon = FALSE - if(start) - if(!defdir) - defdir = A.dir - if(!deficon) - deficon = A.icon - if(!defstate) - defstate = A.icon_state - if(!defblend) - defblend = A.blend_mode - - var/curicon = A.icon || deficon - var/curstate = A.icon_state || defstate - - if(!((noIcon = (!curicon)))) - var/curstates = icon_states(curicon) - if(!(curstate in curstates)) - if("" in curstates) - curstate = "" - else - noIcon = TRUE // Do not render this object. - - var/curdir - var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have - - //These should use the parent's direction (most likely) - if(!A.dir || A.dir == SOUTH) - curdir = defdir - else - curdir = A.dir - - //Try to remove/optimize this section ASAP, CPU hog. - //Determines if there's directionals. - if(!noIcon && curdir != SOUTH) - var/exist = FALSE - var/static/list/checkdirs = list(NORTH, EAST, WEST) - for(var/i in checkdirs) //Not using GLOB for a reason. - if(length(icon_states(icon(curicon, curstate, i)))) - exist = TRUE - break - if(!exist) - base_icon_dir = SOUTH - // - - if(!base_icon_dir) - base_icon_dir = curdir - - ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. - - var/curblend = A.blend_mode || defblend - - if(A.overlays.len || A.underlays.len) - var/icon/flat = BLANK - // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed - var/list/layers = list() - var/image/copy - // Add the atom's icon itself, without pixel_x/y offsets. - if(!noIcon) - copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) - copy.color = A.color - copy.alpha = A.alpha - copy.blend_mode = curblend - layers[copy] = A.layer - - // Loop through the underlays, then overlays, sorting them into the layers list - for(var/process_set in 0 to 1) - var/list/process = process_set? A.overlays : A.underlays - for(var/i in 1 to process.len) - var/image/current = process[i] - if(!current) - continue - if(current.plane != FLOAT_PLANE && current.plane != A.plane) - continue - var/current_layer = current.layer - if(current_layer < 0) - if(current_layer <= -1000) - return flat - current_layer = process_set + A.layer + current_layer / 1000 - - for(var/p in 1 to layers.len) - var/image/cmp = layers[p] - if(current_layer < layers[cmp]) - layers.Insert(p, current) - break - layers[current] = current_layer - - //sortTim(layers, /proc/cmp_image_layer_asc) - - var/icon/add // Icon of overlay being added - - // Current dimensions of flattened icon - var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) - // Dimensions of overlay being added - var/list/add_size[4] - - for(var/V in layers) - var/image/I = V - if(I.alpha == 0) - continue - - if(I == copy) // 'I' is an /image based on the object being flattened. - curblend = BLEND_OVERLAY - add = icon(I.icon, I.icon_state, base_icon_dir) - else // 'I' is an appearance object. - add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) - if(!add) - continue - // Find the new dimensions of the flat icon to fit the added overlay - add_size = list( - min(flatX1, I.pixel_x+1), - max(flatX2, I.pixel_x+add.Width()), - min(flatY1, I.pixel_y+1), - max(flatY2, I.pixel_y+add.Height()) - ) - - if(flat_size ~! add_size) - // Resize the flattened icon so the new icon fits - flat.Crop( - addX1 - flatX1 + 1, - addY1 - flatY1 + 1, - addX2 - flatX1 + 1, - addY2 - flatY1 + 1 - ) - flat_size = add_size.Copy() - - // Blend the overlay into the flattened icon - flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) - - if(A.color) - if(islist(A.color)) - flat.MapColors(arglist(A.color)) - else - flat.Blend(A.color, ICON_MULTIPLY) - - if(A.alpha < 255) - flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) - - if(no_anim) - //Clean up repeated frames - var/icon/cleaned = new /icon() - cleaned.Insert(flat, "", SOUTH, 1, 0) - . = cleaned - else - . = icon(flat, "", SOUTH) - else //There's no overlays. - if(!noIcon) - SET_SELF(.) - - //Clear defines - #undef flatX1 - #undef flatX2 - #undef flatY1 - #undef flatY2 - #undef addX1 - #undef addX2 - #undef addY1 - #undef addY2 - - #undef INDEX_X_LOW - #undef INDEX_X_HIGH - #undef INDEX_Y_LOW - #undef INDEX_Y_HIGH - - #undef BLANK - #undef SET_SELF - -/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N - var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. - for(var/V in A.overlays)//For every image in overlays. var/image/I will not work, don't try it. - var/image/I = V - if(I.layer>A.layer) - continue//If layer is greater than what we need, skip it. - var/icon/image_overlay = new(I.icon,I.icon_state)//Blend only works with icon objects. - //Also, icons cannot directly set icon_state. Slower than changing variables but whatever. - alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay. - return alpha_mask//And now return the mask. - -/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay. - var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays. - //Now we need to culculate overlays+underlays and add them together to form an image for a mask. - //var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess. - var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough. - opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick. - opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion. - for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it. - var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like. - switch(i)//Now to determine offset so the result is somewhat blurred. - if(1) I.pixel_x-- - if(2) I.pixel_x++ - if(3) I.pixel_y-- - if(4) I.pixel_y++ - overlays += I//And finally add the overlay. - -/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created. - var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon. - var/icon/alpha_mask - flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. - flat_icon.ChangeOpacity(0.5)//Make it half transparent. - if(A.Height() == 64) - alpha_mask = new('icons/mob/ancient_machine.dmi', "scanline2")//Scaline for tall icons. - else - alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect. - flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. - return flat_icon - -//For photo camera. -/proc/build_composite_icon(atom/A) - var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1) - for(var/O in A.overlays) - var/image/I = O - composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY) - return composite - -/proc/adjust_brightness(var/color, var/value) - if(!color) return "#FFFFFF" - if(!value) return color - - var/list/RGB = ReadRGB(color) - RGB[1] = Clamp(RGB[1]+value,0,255) - RGB[2] = Clamp(RGB[2]+value,0,255) - RGB[3] = Clamp(RGB[3]+value,0,255) - return rgb(RGB[1],RGB[2],RGB[3]) - -/proc/sort_atoms_by_layer(var/list/atoms) - // Comb sort icons based on levels - var/list/result = atoms.Copy() - var/gap = result.len - var/swapped = 1 - while(gap > 1 || swapped) - swapped = 0 - if(gap > 1) - gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient - if(gap < 1) - gap = 1 - for(var/i = 1; gap + i <= result.len; i++) - var/atom/l = result[i] //Fucking hate - var/atom/r = result[gap+i] //how lists work here - if(l.layer > r.layer) //no "result[i].layer" for me - result.Swap(i, gap + i) - swapped = 1 - return result - -//Interface for using DrawBox() to draw 1 pixel on a coordinate. -//Returns the same icon specifed in the argument, but with the pixel drawn -/proc/DrawPixel(var/icon/I,var/colour,var/drawX,var/drawY) - if(!I) - return 0 - var/Iwidth = I.Width() - var/Iheight = I.Height() - if(drawX > Iwidth || drawX <= 0) - return 0 - if(drawY > Iheight || drawY <= 0) - return 0 - I.DrawBox(colour,drawX, drawY) - return I - -//Interface for easy drawing of one pixel on an atom. -/atom/proc/DrawPixelOn(var/colour, var/drawX, var/drawY) - var/icon/I = new(icon) - var/icon/J = DrawPixel(I, colour, drawX, drawY) - if(J) //Only set the icon if it succeeded, the icon without the pixel is 1000x better than a black square. - icon = J - return J - return 0 - -//Hook, override to run code on- wait this is images -//Images have dir without being an atom, so they get their own definition. -//Lame. -/image/proc/setDir(newdir) - dir = newdir - -/proc/rand_hex_color() - var/list/colors = list("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f") - var/color="" - for(var/i=0;i<6;i++) - color = color+pick(colors) - return "#[color]" - -//Imagine removing pixels from the main icon that are covered by pixels from the mask icon. -//Standard behaviour is to cut pixels from the main icon that are covered by pixels from the mask icon unless passed mask_ready, see below. -/proc/get_icon_difference(var/icon/main, var/icon/mask, var/mask_ready) - /*You should skip prep if the mask is already sprited properly. This significantly improves performance by eliminating most of the realtime icon work. - e.g. A 'ready' mask is a mask where the part you want cut out is missing (no pixels, 0 alpha) from the sprite, and everything else is solid white.*/ - - if(istype(main) && istype(mask)) - if(!mask_ready) //Prep the mask if we're using a regular old sprite and not a special-made mask. - mask.Blend(rgb(255,255,255), ICON_SUBTRACT) //Make all pixels on the mask as black as possible. - mask.Opaque(rgb(255,255,255)) //Make the transparent pixels (background) white. - mask.BecomeAlphaMask() //Make all the black pixels vanish (fully transparent), leaving only the white background pixels. - - main.AddAlphaMask(mask) //Make the pixels in the main icon that are in the transparent zone of the mask icon also vanish (fully transparent). - return main \ No newline at end of file +/* +IconProcs README + +A BYOND library for manipulating icons and colors + +by Lummox JR + +version 1.0 + +The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation +routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners. + +CHANGING ICONS + +Several new procs have been added to the /icon datum to simplify working with icons. To use them, +remember you first need to setup an /icon var like so: + + var/icon/my_icon = new('iconfile.dmi') + +icon/ChangeOpacity(amount = 1) + A very common operation in DM is to try to make an icon more or less transparent. Making an icon more + transparent is usually much easier than making it less so, however. This proc basically is a frontend + for MapColors() which can change opacity any way you like, in much the same way that SetIntensity() + can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half. + If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque. +icon/GrayScale() + Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact. +icon/ColorTone(tone) + Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an + RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect. + See also the global ColorTone() proc. +icon/MinColors(icon) + The icon is blended with a second icon where the minimum of each RGB pixel is the result. + Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon. +icon/MaxColors(icon) + The icon is blended with a second icon where the maximum of each RGB pixel is the result. + Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon. +icon/Opaque(background = "#000000") + All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify. +icon/BecomeAlphaMask() + You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc. + The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white. +icon/AddAlphaMask(mask) + The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque, + the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent. + Where the mask is translucent, the current icon becomes more transparent. +icon/UseAlphaMask(mask, mode) + Sometimes you may want to take the alpha values from one icon and use them on a different icon. + This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change + so it has the same colors as before but uses the mask for opacity. + +COLOR MANAGEMENT AND HSV + +RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value. + + * The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to + cyan to blue to magenta and back to red. + * The saturation of a color is how much color is in it. A color with low saturation will be more gray, + and with no saturation at all it is a shade of gray. + * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark, + and no value at all is black. + +Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three +hex digits because it ranges from 0 to 0x5FF. + + * 0 to 0xFF - red to yellow + * 0x100 to 0x1FF - yellow to green + * 0x200 to 0x2FF - green to cyan + * 0x300 to 0x3FF - cyan to blue + * 0x400 to 0x4FF - blue to magenta + * 0x500 to 0x5FF - magenta to red + +Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible), +value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff". + +More than one HSV color can match the same RGB color. + +Here are some procs you can use for color management: + +ReadRGB(rgb) + Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used + that includes alpha, the list will have a fourth item for the alpha value. +hsv(hue, sat, val, apha) + Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa" + format. Alpha is not included in the result if null. +ReadHSV(rgb) + Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that + includes alpha, the list will have a fourth item for the alpha value. +RGBtoHSV(rgb) + Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff". +HSVtoRGB(hsv) + Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa". +BlendRGB(rgb1, rgb2, amount) + Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result; + if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. + The returned value is an RGB or RGBA color. +BlendHSV(hsv1, hsv2, amount) + Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB + blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1, + the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. + The returned value is an HSV or HSVA color. +BlendRGBasHSV(rgb1, rgb2, amount) + Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form. +HueToAngle(hue) + Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue. +AngleToHue(hue) + Converts an angle to a hue in the valid range. +RotateHue(hsv, angle) + Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360. + (Rotating red by 60° produces yellow.) The result is another HSV or HSVA color with the same saturation and value + as the original, but a different hue. +GrayScale(rgb) + Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string. +ColorTone(rgb, tone) + Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of + using strict shades of gray. The tone value is an RGB color; any alpha value is ignored. +*/ + +/* +Get Flat Icon DEMO by DarkCampainger + +This is a test for the get flat icon proc, modified approprietly for icons and their states. +Probably not a good idea to run this unless you want to see how the proc works in detail. +mob + icon = 'old_or_unused.dmi' + icon_state = "green" + + Login() + // Testing image underlays + underlays += image(icon='old_or_unused.dmi',icon_state="red") + underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32) + underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32) + + // Testing image overlays + overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32) + overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32) + overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32) + + // Testing icon file overlays (defaults to mob's state) + overlays += '_flat_demoIcons2.dmi' + + // Testing icon_state overlays (defaults to mob's icon) + overlays += "white" + + // Testing dynamic icon overlays + var/icon/I = icon('old_or_unused.dmi', icon_state="aqua") + I.Shift(NORTH,16,1) + overlays+=I + + // Testing dynamic image overlays + I=image(icon=I,pixel_x = -32, pixel_y = 32) + overlays+=I + + // Testing object types (and layers) + overlays+=/obj/effect/overlayTest + + loc = locate (10,10,1) + verb + Browse_Icon() + set name = "1. Browse Icon" + // Give it a name for the cache + var/iconName = "[ckey(src.name)]_flattened.dmi" + // Send the icon to src's local cache + src<

") + + Output_Icon() + set name = "2. Output Icon" + to_chat(src, "Icon is: [bicon(getFlatIcon(src))]") + + Label_Icon() + set name = "3. Label Icon" + // Give it a name for the cache + var/iconName = "[ckey(src.name)]_flattened.dmi" + // Copy the file to the rsc manually + var/icon/I = fcopy_rsc(getFlatIcon(src)) + // Send the icon to src's local cache + src< transparent, gray -> translucent white, white -> solid white +/icon/proc/BecomeAlphaMask() + SwapColor(null, "#000000ff") // don't let transparent become gray + MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0) + +/icon/proc/UseAlphaMask(mask) + Opaque() + AddAlphaMask(mask) + +/icon/proc/AddAlphaMask(mask) + var/icon/M = new(mask) + M.Blend("#ffffff", ICON_SUBTRACT) + // apply mask + Blend(M, ICON_ADD) + +/* + HSV format is represented as "#hhhssvv" or "#hhhssvvaa" + + Hue ranges from 0 to 0x5ff (1535) + + 0x000 = red + 0x100 = yellow + 0x200 = green + 0x300 = cyan + 0x400 = blue + 0x500 = magenta + + Saturation is from 0 to 0xff (255) + + More saturation = more color + Less saturation = more gray + + Value ranges from 0 to 0xff (255) + + Higher value means brighter color + */ + +/proc/ReadRGB(rgb) + if(!rgb) return + + // interpret the HSV or HSVA value + var/i=1,start=1 + if(text2ascii(rgb) == 35) ++start // skip opening # + var/ch,which=0,r=0,g=0,b=0,alpha=0,usealpha + var/digits=0 + for(i=start, i<=length(rgb), ++i) + ch = text2ascii(rgb, i) + if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break + ++digits + if(digits == 8) break + + var/single = digits < 6 + if(digits != 3 && digits != 4 && digits != 6 && digits != 8) return + if(digits == 4 || digits == 8) usealpha = 1 + for(i=start, digits>0, ++i) + ch = text2ascii(rgb, i) + if(ch >= 48 && ch <= 57) ch -= 48 + else if(ch >= 65 && ch <= 70) ch -= 55 + else if(ch >= 97 && ch <= 102) ch -= 87 + else break + --digits + switch(which) + if(0) + r = (r << 4) | ch + if(single) + r |= r << 4 + ++which + else if(!(digits & 1)) ++which + if(1) + g = (g << 4) | ch + if(single) + g |= g << 4 + ++which + else if(!(digits & 1)) ++which + if(2) + b = (b << 4) | ch + if(single) + b |= b << 4 + ++which + else if(!(digits & 1)) ++which + if(3) + alpha = (alpha << 4) | ch + if(single) alpha |= alpha << 4 + + . = list(r, g, b) + if(usealpha) . += alpha + +/proc/ReadHSV(hsv) + if(!hsv) return + + // interpret the HSV or HSVA value + var/i=1,start=1 + if(text2ascii(hsv) == 35) ++start // skip opening # + var/ch,which=0,hue=0,sat=0,val=0,alpha=0,usealpha + var/digits=0 + for(i=start, i<=length(hsv), ++i) + ch = text2ascii(hsv, i) + if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break + ++digits + if(digits == 9) break + if(digits > 7) usealpha = 1 + if(digits <= 4) ++which + if(digits <= 2) ++which + for(i=start, digits>0, ++i) + ch = text2ascii(hsv, i) + if(ch >= 48 && ch <= 57) ch -= 48 + else if(ch >= 65 && ch <= 70) ch -= 55 + else if(ch >= 97 && ch <= 102) ch -= 87 + else break + --digits + switch(which) + if(0) + hue = (hue << 4) | ch + if(digits == (usealpha ? 6 : 4)) ++which + if(1) + sat = (sat << 4) | ch + if(digits == (usealpha ? 4 : 2)) ++which + if(2) + val = (val << 4) | ch + if(digits == (usealpha ? 2 : 0)) ++which + if(3) + alpha = (alpha << 4) | ch + + . = list(hue, sat, val) + if(usealpha) . += alpha + +/proc/HSVtoRGB(hsv) + if(!hsv) return "#000000" + var/list/HSV = ReadHSV(hsv) + if(!HSV) return "#000000" + + var/hue = HSV[1] + var/sat = HSV[2] + var/val = HSV[3] + + // Compress hue into easier-to-manage range + hue -= hue >> 8 + if(hue >= 0x5fa) hue -= 0x5fa + + var/hi,mid,lo,r,g,b + hi = val + lo = round((255 - sat) * val / 255, 1) + mid = lo + round(abs(round(hue, 510) - hue) * (hi - lo) / 255, 1) + if(hue >= 765) + if(hue >= 1275) {r=hi; g=lo; b=mid} + else if(hue >= 1020) {r=mid; g=lo; b=hi } + else {r=lo; g=mid; b=hi } + else + if(hue >= 510) {r=lo; g=hi; b=mid} + else if(hue >= 255) {r=mid; g=hi; b=lo } + else {r=hi; g=mid; b=lo } + + return (HSV.len > 3) ? rgb(r,g,b,HSV[4]) : rgb(r,g,b) + +/proc/RGBtoHSV(rgb) + if(!rgb) return "#0000000" + var/list/RGB = ReadRGB(rgb) + if(!RGB) return "#0000000" + + var/r = RGB[1] + var/g = RGB[2] + var/b = RGB[3] + var/hi = max(r,g,b) + var/lo = min(r,g,b) + + var/val = hi + var/sat = hi ? round((hi-lo) * 255 / hi, 1) : 0 + var/hue = 0 + + if(sat) + var/dir + var/mid + if(hi == r) + if(lo == b) {hue=0; dir=1; mid=g} + else {hue=1535; dir=-1; mid=b} + else if(hi == g) + if(lo == r) {hue=512; dir=1; mid=b} + else {hue=511; dir=-1; mid=r} + else if(hi == b) + if(lo == g) {hue=1024; dir=1; mid=r} + else {hue=1023; dir=-1; mid=g} + hue += dir * round((mid-lo) * 255 / (hi-lo), 1) + + return hsv(hue, sat, val, (RGB.len>3 ? RGB[4] : null)) + +/proc/hsv(hue, sat, val, alpha) + if(hue < 0 || hue >= 1536) hue %= 1536 + if(hue < 0) hue += 1536 + if((hue & 0xFF) == 0xFF) + ++hue + if(hue >= 1536) hue = 0 + if(sat < 0) sat = 0 + if(sat > 255) sat = 255 + if(val < 0) val = 0 + if(val > 255) val = 255 + . = "#" + . += TO_HEX_DIGIT(hue >> 8) + . += TO_HEX_DIGIT(hue >> 4) + . += TO_HEX_DIGIT(hue) + . += TO_HEX_DIGIT(sat >> 4) + . += TO_HEX_DIGIT(sat) + . += TO_HEX_DIGIT(val >> 4) + . += TO_HEX_DIGIT(val) + if(!isnull(alpha)) + if(alpha < 0) alpha = 0 + if(alpha > 255) alpha = 255 + . += TO_HEX_DIGIT(alpha >> 4) + . += TO_HEX_DIGIT(alpha) + +/* + Smooth blend between HSV colors + + amount=0 is the first color + amount=1 is the second color + amount=0.5 is directly between the two colors + + amount<0 or amount>1 are allowed + */ +/proc/BlendHSV(hsv1, hsv2, amount) + var/list/HSV1 = ReadHSV(hsv1) + var/list/HSV2 = ReadHSV(hsv2) + + // add missing alpha if needed + if(HSV1.len < HSV2.len) HSV1 += 255 + else if(HSV2.len < HSV1.len) HSV2 += 255 + var/usealpha = HSV1.len > 3 + + // normalize hsv values in case anything is screwy + if(HSV1[1] > 1536) HSV1[1] %= 1536 + if(HSV2[1] > 1536) HSV2[1] %= 1536 + if(HSV1[1] < 0) HSV1[1] += 1536 + if(HSV2[1] < 0) HSV2[1] += 1536 + if(!HSV1[3]) {HSV1[1] = 0; HSV1[2] = 0} + if(!HSV2[3]) {HSV2[1] = 0; HSV2[2] = 0} + + // no value for one color means don't change saturation + if(!HSV1[3]) HSV1[2] = HSV2[2] + if(!HSV2[3]) HSV2[2] = HSV1[2] + // no saturation for one color means don't change hues + if(!HSV1[2]) HSV1[1] = HSV2[1] + if(!HSV2[2]) HSV2[1] = HSV1[1] + + // Compress hues into easier-to-manage range + HSV1[1] -= HSV1[1] >> 8 + HSV2[1] -= HSV2[1] >> 8 + + var/hue_diff = HSV2[1] - HSV1[1] + if(hue_diff > 765) hue_diff -= 1530 + else if(hue_diff <= -765) hue_diff += 1530 + + var/hue = round(HSV1[1] + hue_diff * amount, 1) + var/sat = round(HSV1[2] + (HSV2[2] - HSV1[2]) * amount, 1) + var/val = round(HSV1[3] + (HSV2[3] - HSV1[3]) * amount, 1) + var/alpha = usealpha ? round(HSV1[4] + (HSV2[4] - HSV1[4]) * amount, 1) : null + + // normalize hue + if(hue < 0 || hue >= 1530) hue %= 1530 + if(hue < 0) hue += 1530 + // decompress hue + hue += round(hue / 255) + + return hsv(hue, sat, val, alpha) + +/* + Smooth blend between RGB colors + + amount=0 is the first color + amount=1 is the second color + amount=0.5 is directly between the two colors + + amount<0 or amount>1 are allowed + */ +/proc/BlendRGB(rgb1, rgb2, amount) + var/list/RGB1 = ReadRGB(rgb1) + var/list/RGB2 = ReadRGB(rgb2) + + // add missing alpha if needed + if(RGB1.len < RGB2.len) RGB1 += 255 + else if(RGB2.len < RGB1.len) RGB2 += 255 + var/usealpha = RGB1.len > 3 + + var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) + var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) + var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) + var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null + + return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) + +/proc/BlendRGBasHSV(rgb1, rgb2, amount) + return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount) + +//Returns the perceived brightness of a color. +//https://en.wikipedia.org/wiki/Relative_luminance +//https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color +/proc/getLuminance(color) + var/list/RGB = ReadRGB(color) + var/R = RGB[1] + var/G = RGB[2] + var/B = RGB[2] + + var/Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) + return Clamp((Y * 0.01), 0, 1) //Returns the brightness of a color in decimal percentage format. Can multiply light_power by this to receive 100% brightness or a lower brightness. Not a higher brightness. + +/proc/HueToAngle(hue) + // normalize hsv in case anything is screwy + if(hue < 0 || hue >= 1536) hue %= 1536 + if(hue < 0) hue += 1536 + // Compress hue into easier-to-manage range + hue -= hue >> 8 + return hue / (1530/360) + +/proc/AngleToHue(angle) + // normalize hsv in case anything is screwy + if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) + var/hue = angle * (1530/360) + // Decompress hue + hue += round(hue / 255) + return hue + + +// positive angle rotates forward through red->green->blue +/proc/RotateHue(hsv, angle) + var/list/HSV = ReadHSV(hsv) + + // normalize hsv in case anything is screwy + if(HSV[1] >= 1536) HSV[1] %= 1536 + if(HSV[1] < 0) HSV[1] += 1536 + + // Compress hue into easier-to-manage range + HSV[1] -= HSV[1] >> 8 + + if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) + HSV[1] = round(HSV[1] + angle * (1530/360), 1) + + // normalize hue + if(HSV[1] < 0 || HSV[1] >= 1530) HSV[1] %= 1530 + if(HSV[1] < 0) HSV[1] += 1530 + // decompress hue + HSV[1] += round(HSV[1] / 255) + + return hsv(HSV[1], HSV[2], HSV[3], (HSV.len > 3 ? HSV[4] : null)) + +// Convert an rgb color to grayscale +/proc/GrayScale(rgb) + var/list/RGB = ReadRGB(rgb) + var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 + return (RGB.len > 3) ? rgb(gray, gray, gray, RGB[4]) : rgb(gray, gray, gray) + +// Change grayscale color to black->tone->white range +/proc/ColorTone(rgb, tone) + var/list/RGB = ReadRGB(rgb) + var/list/TONE = ReadRGB(tone) + + var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 + var/tone_gray = TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11 + + if(gray <= tone_gray) return BlendRGB("#000000", tone, gray/(tone_gray || 1)) + else return BlendRGB(tone, "#ffffff", (gray-tone_gray)/((255-tone_gray) || 1)) + + +/* +Get flat icon by DarkCampainger. As it says on the tin, will return an icon with all the overlays +as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included. +The _flatIcons list is a cache for generated icon files. +*/ + +// Creates a single icon from a given /atom or /image. Only the first argument is required. +/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) + //Define... defines. + var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") + + #define BLANK icon(flat_template) + #define SET_SELF(SETVAR) do { \ + var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \ + if(A.alpha<255) { \ + SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\ + } \ + if(A.color) { \ + if(islist(A.color)){ \ + SELF_ICON.MapColors(arglist(A.color))} \ + else{ \ + SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \ + } \ + ##SETVAR=SELF_ICON;\ + } while (0) + #define INDEX_X_LOW 1 + #define INDEX_X_HIGH 2 + #define INDEX_Y_LOW 3 + #define INDEX_Y_HIGH 4 + + #define flatX1 flat_size[INDEX_X_LOW] + #define flatX2 flat_size[INDEX_X_HIGH] + #define flatY1 flat_size[INDEX_Y_LOW] + #define flatY2 flat_size[INDEX_Y_HIGH] + #define addX1 add_size[INDEX_X_LOW] + #define addX2 add_size[INDEX_X_HIGH] + #define addY1 add_size[INDEX_Y_LOW] + #define addY2 add_size[INDEX_Y_HIGH] + + if(!A || A.alpha <= 0) + return BLANK + + var/noIcon = FALSE + if(start) + if(!defdir) + defdir = A.dir + if(!deficon) + deficon = A.icon + if(!defstate) + defstate = A.icon_state + if(!defblend) + defblend = A.blend_mode + + var/curicon = A.icon || deficon + var/curstate = A.icon_state || defstate + + if(!((noIcon = (!curicon)))) + var/curstates = icon_states(curicon) + if(!(curstate in curstates)) + if("" in curstates) + curstate = "" + else + noIcon = TRUE // Do not render this object. + + var/curdir + var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have + + //These should use the parent's direction (most likely) + if(!A.dir || A.dir == SOUTH) + curdir = defdir + else + curdir = A.dir + + //Try to remove/optimize this section ASAP, CPU hog. + //Determines if there's directionals. + if(!noIcon && curdir != SOUTH) + var/exist = FALSE + var/static/list/checkdirs = list(NORTH, EAST, WEST) + for(var/i in checkdirs) //Not using GLOB for a reason. + if(length(icon_states(icon(curicon, curstate, i)))) + exist = TRUE + break + if(!exist) + base_icon_dir = SOUTH + // + + if(!base_icon_dir) + base_icon_dir = curdir + + ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. + + var/curblend = A.blend_mode || defblend + + if(A.overlays.len || A.underlays.len) + var/icon/flat = BLANK + // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed + var/list/layers = list() + var/image/copy + // Add the atom's icon itself, without pixel_x/y offsets. + if(!noIcon) + copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) + copy.color = A.color + copy.alpha = A.alpha + copy.blend_mode = curblend + layers[copy] = A.layer + + // Loop through the underlays, then overlays, sorting them into the layers list + for(var/process_set in 0 to 1) + var/list/process = process_set? A.overlays : A.underlays + for(var/i in 1 to process.len) + var/image/current = process[i] + if(!current) + continue + if(current.plane != FLOAT_PLANE && current.plane != A.plane) + continue + var/current_layer = current.layer + if(current_layer < 0) + if(current_layer <= -1000) + return flat + current_layer = process_set + A.layer + current_layer / 1000 + + for(var/p in 1 to layers.len) + var/image/cmp = layers[p] + if(current_layer < layers[cmp]) + layers.Insert(p, current) + break + layers[current] = current_layer + + //sortTim(layers, /proc/cmp_image_layer_asc) + + var/icon/add // Icon of overlay being added + + // Current dimensions of flattened icon + var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) + // Dimensions of overlay being added + var/list/add_size[4] + + for(var/V in layers) + var/image/I = V + if(I.alpha == 0) + continue + + if(I == copy) // 'I' is an /image based on the object being flattened. + curblend = BLEND_OVERLAY + add = icon(I.icon, I.icon_state, base_icon_dir) + else // 'I' is an appearance object. + add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) + if(!add) + continue + // Find the new dimensions of the flat icon to fit the added overlay + add_size = list( + min(flatX1, I.pixel_x+1), + max(flatX2, I.pixel_x+add.Width()), + min(flatY1, I.pixel_y+1), + max(flatY2, I.pixel_y+add.Height()) + ) + + if(flat_size ~! add_size) + // Resize the flattened icon so the new icon fits + flat.Crop( + addX1 - flatX1 + 1, + addY1 - flatY1 + 1, + addX2 - flatX1 + 1, + addY2 - flatY1 + 1 + ) + flat_size = add_size.Copy() + + // Blend the overlay into the flattened icon + flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) + + if(A.color) + if(islist(A.color)) + flat.MapColors(arglist(A.color)) + else + flat.Blend(A.color, ICON_MULTIPLY) + + if(A.alpha < 255) + flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + + if(no_anim) + //Clean up repeated frames + var/icon/cleaned = new /icon() + cleaned.Insert(flat, "", SOUTH, 1, 0) + . = cleaned + else + . = icon(flat, "", SOUTH) + else //There's no overlays. + if(!noIcon) + SET_SELF(.) + + //Clear defines + #undef flatX1 + #undef flatX2 + #undef flatY1 + #undef flatY2 + #undef addX1 + #undef addX2 + #undef addY1 + #undef addY2 + + #undef INDEX_X_LOW + #undef INDEX_X_HIGH + #undef INDEX_Y_LOW + #undef INDEX_Y_HIGH + + #undef BLANK + #undef SET_SELF + +/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N + var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. + for(var/V in A.overlays)//For every image in overlays. var/image/I will not work, don't try it. + var/image/I = V + if(I.layer>A.layer) + continue//If layer is greater than what we need, skip it. + var/icon/image_overlay = new(I.icon,I.icon_state)//Blend only works with icon objects. + //Also, icons cannot directly set icon_state. Slower than changing variables but whatever. + alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay. + return alpha_mask//And now return the mask. + +/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay. + var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays. + //Now we need to culculate overlays+underlays and add them together to form an image for a mask. + //var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess. + var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough. + opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick. + opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion. + for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it. + var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like. + switch(i)//Now to determine offset so the result is somewhat blurred. + if(1) I.pixel_x-- + if(2) I.pixel_x++ + if(3) I.pixel_y-- + if(4) I.pixel_y++ + overlays += I//And finally add the overlay. + +/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created. + var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon. + var/icon/alpha_mask + flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. + flat_icon.ChangeOpacity(0.5)//Make it half transparent. + if(A.Height() == 64) + alpha_mask = new('icons/mob/ancient_machine.dmi', "scanline2")//Scaline for tall icons. + else + alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect. + flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. + return flat_icon + +//For photo camera. +/proc/build_composite_icon(atom/A) + var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1) + for(var/O in A.overlays) + var/image/I = O + composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY) + return composite + +/proc/adjust_brightness(var/color, var/value) + if(!color) return "#FFFFFF" + if(!value) return color + + var/list/RGB = ReadRGB(color) + RGB[1] = Clamp(RGB[1]+value,0,255) + RGB[2] = Clamp(RGB[2]+value,0,255) + RGB[3] = Clamp(RGB[3]+value,0,255) + return rgb(RGB[1],RGB[2],RGB[3]) + +/proc/sort_atoms_by_layer(var/list/atoms) + // Comb sort icons based on levels + var/list/result = atoms.Copy() + var/gap = result.len + var/swapped = 1 + while(gap > 1 || swapped) + swapped = 0 + if(gap > 1) + gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient + if(gap < 1) + gap = 1 + for(var/i = 1; gap + i <= result.len; i++) + var/atom/l = result[i] //Fucking hate + var/atom/r = result[gap+i] //how lists work here + if(l.layer > r.layer) //no "result[i].layer" for me + result.Swap(i, gap + i) + swapped = 1 + return result + +//Interface for using DrawBox() to draw 1 pixel on a coordinate. +//Returns the same icon specifed in the argument, but with the pixel drawn +/proc/DrawPixel(var/icon/I,var/colour,var/drawX,var/drawY) + if(!I) + return 0 + var/Iwidth = I.Width() + var/Iheight = I.Height() + if(drawX > Iwidth || drawX <= 0) + return 0 + if(drawY > Iheight || drawY <= 0) + return 0 + I.DrawBox(colour,drawX, drawY) + return I + +//Interface for easy drawing of one pixel on an atom. +/atom/proc/DrawPixelOn(var/colour, var/drawX, var/drawY) + var/icon/I = new(icon) + var/icon/J = DrawPixel(I, colour, drawX, drawY) + if(J) //Only set the icon if it succeeded, the icon without the pixel is 1000x better than a black square. + icon = J + return J + return 0 + +//Hook, override to run code on- wait this is images +//Images have dir without being an atom, so they get their own definition. +//Lame. +/image/proc/setDir(newdir) + dir = newdir + +/proc/rand_hex_color() + var/list/colors = list("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f") + var/color="" + for(var/i=0;i<6;i++) + color = color+pick(colors) + return "#[color]" + +//Imagine removing pixels from the main icon that are covered by pixels from the mask icon. +//Standard behaviour is to cut pixels from the main icon that are covered by pixels from the mask icon unless passed mask_ready, see below. +/proc/get_icon_difference(var/icon/main, var/icon/mask, var/mask_ready) + /*You should skip prep if the mask is already sprited properly. This significantly improves performance by eliminating most of the realtime icon work. + e.g. A 'ready' mask is a mask where the part you want cut out is missing (no pixels, 0 alpha) from the sprite, and everything else is solid white.*/ + + if(istype(main) && istype(mask)) + if(!mask_ready) //Prep the mask if we're using a regular old sprite and not a special-made mask. + mask.Blend(rgb(255,255,255), ICON_SUBTRACT) //Make all pixels on the mask as black as possible. + mask.Opaque(rgb(255,255,255)) //Make the transparent pixels (background) white. + mask.BecomeAlphaMask() //Make all the black pixels vanish (fully transparent), leaving only the white background pixels. + + main.AddAlphaMask(mask) //Make the pixels in the main icon that are in the transparent zone of the mask icon also vanish (fully transparent). + return main diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 5ffe891d95b..7d1b8ee6ebc 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -1,822 +1,822 @@ -/* - * Holds procs to help with list operations - * Contains groups: - * Misc - * Sorting - */ - -/* - * Misc - */ - - // binary search sorted insert -// IN: Object to be inserted -// LIST: List to insert object into -// TYPECONT: The typepath of the contents of the list -// COMPARE: The variable on the objects to compare -#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \ - var/__BIN_CTTL = length(LIST);\ - if(!__BIN_CTTL) {\ - LIST += IN;\ - } else {\ - var/__BIN_LEFT = 1;\ - var/__BIN_RIGHT = __BIN_CTTL;\ - var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ - var/##TYPECONT/__BIN_ITEM;\ - while(__BIN_LEFT < __BIN_RIGHT) {\ - __BIN_ITEM = LIST[__BIN_MID];\ - if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\ - __BIN_LEFT = __BIN_MID + 1;\ - } else {\ - __BIN_RIGHT = __BIN_MID;\ - };\ - __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ - };\ - __BIN_ITEM = LIST[__BIN_MID];\ - __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\ - LIST.Insert(__BIN_MID, IN);\ - } - - -//Returns a list in plain english as a string -/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) - var/total = input.len - if(!total) - return "[nothing_text]" - else if(total == 1) - return "[input[1]]" - else if(total == 2) - return "[input[1]][and_text][input[2]]" - else - var/output = "" - var/index = 1 - while(index < total) - if(index == total - 1) - comma_text = final_comma_text - - output += "[input[index]][comma_text]" - index++ - - return "[output][and_text][input[index]]" - -//Returns list element or null. Should prevent "index out of bounds" error. -/proc/listgetindex(var/list/list,index) - if(istype(list) && list.len) - if(isnum(index)) - if(InRange(index,1,list.len)) - return list[index] - else if(index in list) - return list[index] - return - -//Return either pick(list) or null if list is not of type /list or is empty -/proc/safepick(list/list) - if(!islist(list) || !list.len) - return - return pick(list) - -//Checks if the list is empty -/proc/isemptylist(list/list) - if(!list.len) - return 1 - return 0 - -//Checks for specific types in a list -/proc/is_type_in_list(atom/A, list/L) - if(!L || !L.len || !A) - return 0 - for(var/type in L) - if(istype(A, type)) - return 1 - return 0 - -//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches') -/proc/is_type_in_typecache(atom/A, list/L) - if(!L || !L.len || !A) - return 0 - return L[A.type] - -//returns a new list with only atoms that are in typecache L -/proc/typecache_filter_list(list/atoms, list/typecache) - . = list() - for(var/thing in atoms) - var/atom/A = thing - if(typecache[A.type]) - . += A - -/proc/typecache_filter_list_reverse(list/atoms, list/typecache) - . = list() - for(var/thing in atoms) - var/atom/A = thing - if(!typecache[A.type]) - . += A - -/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude) - . = list() - for(var/thing in atoms) - var/atom/A = thing - if(typecache_include[A.type] && !typecache_exclude[A.type]) - . += A - -//Like typesof() or subtypesof(), but returns a typecache instead of a list -/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE) - if(ispath(path)) - var/list/types = list() - if(only_root_path) - types = list(path) - else - types = ignore_root_path ? subtypesof(path) : typesof(path) - var/list/L = list() - for(var/T in types) - L[T] = TRUE - return L - else if(islist(path)) - var/list/pathlist = path - var/list/L = list() - if(ignore_root_path) - for(var/P in pathlist) - for(var/T in subtypesof(P)) - L[T] = TRUE - else - for(var/P in pathlist) - if(only_root_path) - L[P] = TRUE - else - for(var/T in typesof(P)) - L[T] = TRUE - return L - -//Removes any null entries from the list -/proc/listclearnulls(list/list) - if(istype(list)) - while(null in list) - list -= null - return - -/* - * Returns list containing all the entries from first list that are not present in second. - * If skiprep = 1, repeated elements are treated as one. - * If either of arguments is not a list, returns null - */ -/proc/difflist(var/list/first, var/list/second, var/skiprep=0) - if(!islist(first) || !islist(second)) - return - var/list/result = new - if(skiprep) - for(var/e in first) - if(!(e in result) && !(e in second)) - result += e - else - result = first - second - - return result - -/* - * Returns list containing entries that are in either list but not both. - * If skipref = 1, repeated elements are treated as one. - * If either of arguments is not a list, returns null - */ -/proc/uniquemergelist(var/list/first, var/list/second, var/skiprep=0) - if(!islist(first) || !islist(second)) - return - var/list/result = new - if(skiprep) - result = difflist(first, second, skiprep)+difflist(second, first, skiprep) - else - result = first ^ second - return result - -//Pretends to pick an element based on its weight but really just seems to pick a random element. -/proc/pickweight(list/L) - var/total = 0 - var/item - for(item in L) - if(!L[item]) - L[item] = 1 - total += L[item] - - total = rand(1, total) - for(item in L) - total -=L [item] - if(total <= 0) - return item - - return null - -//Pick a random element from the list and remove it from the list. -/proc/pick_n_take(list/listfrom) - if(listfrom.len > 0) - var/picked = pick(listfrom) - listfrom -= picked - return picked - return null - -//Returns the top(last) element from the list and removes it from the list (typical stack function) -/proc/pop(list/L) - if(L.len) - . = L[L.len] - L.len-- - -/proc/popleft(list/L) - if(L.len) - . = L[1] - L.Cut(1,2) - - -/* - * Sorting - */ - -//Reverses the order of items in the list -/proc/reverselist(list/L) - var/list/output = list() - if(L) - for(var/i = L.len; i >= 1; i--) - output += L[i] - return output - -//Randomize: Return the list in a random order -/proc/shuffle(var/list/L) - if(!L) - return - L = L.Copy() - - for(var/i=1, i current_index) - current_index++ - current_item = sorted_list[current_index] - - current_item_value = current_item:dd_SortValue() - current_sort_object_value = current_sort_object:dd_SortValue() - if(current_sort_object_value < current_item_value) - high_index = current_index - 1 - else if(current_sort_object_value > current_item_value) - low_index = current_index + 1 - else - // current_sort_object == current_item - low_index = current_index - break - - // Insert before low_index. - insert_index = low_index - - // Special case adding to end of list. - if(insert_index > sorted_list.len) - sorted_list += current_sort_object - continue - - // Because BYOND lists don't support insert, have to do it by: - // 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list. - list_bottom = sorted_list.Copy(insert_index) - sorted_list.Cut(insert_index) - sorted_list += current_sort_object - sorted_list += list_bottom - return sorted_list -*/ - -/proc/dd_sortedtextlist(list/incoming, case_sensitive = 0) - // Returns a new list with the text values sorted. - // Use binary search to order by sortValue. - // This works by going to the half-point of the list, seeing if the node in question is higher or lower cost, - // then going halfway up or down the list and checking again. - // This is a very fast way to sort an item into a list. - var/list/sorted_text = new() - var/low_index - var/high_index - var/insert_index - var/midway_calc - var/current_index - var/current_item - var/list/list_bottom - var/sort_result - - var/current_sort_text - for(current_sort_text in incoming) - low_index = 1 - high_index = sorted_text.len - while(low_index <= high_index) - // Figure out the midpoint, rounding up for fractions. (BYOND rounds down, so add 1 if necessary.) - midway_calc = (low_index + high_index) / 2 - current_index = round(midway_calc) - if(midway_calc > current_index) - current_index++ - current_item = sorted_text[current_index] - - if(case_sensitive) - sort_result = sorttextEx(current_sort_text, current_item) - else - sort_result = sorttext(current_sort_text, current_item) - - switch(sort_result) - if(1) - high_index = current_index - 1 // current_sort_text < current_item - if(-1) - low_index = current_index + 1 // current_sort_text > current_item - if(0) - low_index = current_index // current_sort_text == current_item - break - - // Insert before low_index. - insert_index = low_index - - // Special case adding to end of list. - if(insert_index > sorted_text.len) - sorted_text += current_sort_text - continue - - // Because BYOND lists don't support insert, have to do it by: - // 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list. - list_bottom = sorted_text.Copy(insert_index) - sorted_text.Cut(insert_index) - sorted_text += current_sort_text - sorted_text += list_bottom - return sorted_text - - -/proc/dd_sortedTextList(list/incoming) - var/case_sensitive = 1 - return dd_sortedtextlist(incoming, case_sensitive) - -/proc/subtypesof(var/path) //Returns a list containing all subtypes of the given path, but not the given path itself. - if(!path || !ispath(path)) - CRASH("Invalid path, failed to fetch subtypes of \"[path]\".") - return (typesof(path) - path) - -/datum/proc/dd_SortValue() - return "[src]" - -/obj/machinery/dd_SortValue() - return "[sanitize(name)]" - -/obj/machinery/camera/dd_SortValue() - return "[c_tag]" - -/datum/alarm/dd_SortValue() - return "[sanitize(last_name)]" - -//Picks from the list, with some safeties, and returns the "default" arg if it fails -#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default) - -#define LAZYINITLIST(L) if (!L) L = list() - -#define UNSETEMPTY(L) if (L && !L.len) L = null -#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } } -#define LAZYADD(L, I) if(!L) { L = list(); } L += I; -#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null) -#define LAZYLEN(L) length(L) -#define LAZYCLEARLIST(L) if(L) L.Cut() - -// LAZYING PT 2: THE LAZENING -#define LAZYREINITLIST(L) LAZYCLEARLIST(L); LAZYINITLIST(L); - - -//same, but returns nothing and acts on list in place -/proc/shuffle_inplace(list/L) - if(!L) - return - - for(var/i=1, i toIndex) - ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one - - L.Insert(toIndex, null) - L.Swap(fromIndex, toIndex) - L.Cut(fromIndex, fromIndex + 1) - - -//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex) -//Same as moveElement but for ranges of elements -//This will preserve associations ~Carnie -/proc/moveRange(list/L, fromIndex, toIndex, len = 1) - var/distance = abs(toIndex - fromIndex) - if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements - if(fromIndex <= toIndex) - return //no need to move - fromIndex += len //we want to shift left instead of right - - for(var/i = 0, i < distance, ++i) - L.Insert(fromIndex, null) - L.Swap(fromIndex, toIndex) - L.Cut(toIndex, toIndex + 1) - else - if(fromIndex > toIndex) - fromIndex += len - - for(var/i = 0, i < len, ++i) - L.Insert(toIndex, null) - L.Swap(fromIndex, toIndex) - L.Cut(fromIndex, fromIndex + 1) - -//Move elements from [fromIndex, fromIndex+len) to [toIndex, toIndex+len) -//Move any elements being overwritten by the move to the now-empty elements, preserving order -//Note: if the two ranges overlap, only the destination order will be preserved fully, since some elements will be within both ranges ~Carnie -/proc/swapRange(list/L, fromIndex, toIndex, len = 1) - var/distance = abs(toIndex - fromIndex) - if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements - if(fromIndex < toIndex) - toIndex += len - else - fromIndex += len - - for(var/i = 0, i < distance, ++i) - L.Insert(fromIndex, null) - L.Swap(fromIndex, toIndex) - L.Cut(toIndex, toIndex + 1) - else - if(toIndex > fromIndex) - var/a = toIndex - toIndex = fromIndex - fromIndex = a - - for(var/i = 0, i < len, ++i) - L.Swap(fromIndex++, toIndex++) - -//replaces reverseList ~Carnie -/proc/reverseRange(list/L, start = 1, end = 0) - if(L.len) - start = start % L.len - end = end % (L.len + 1) - if(start <= 0) - start += L.len - if(end <= 0) - end += L.len + 1 - - --end - while(start < end) - L.Swap(start++, end--) - - return L - -/proc/counterlist_scale(list/L, scalar) - var/list/out = list() - for(var/key in L) - out[key] = L[key] * scalar - . = out - -/proc/counterlist_sum(list/L) - . = 0 - for(var/key in L) - . += L[key] - -/proc/counterlist_normalise(list/L) - var/avg = counterlist_sum(L) - if(avg != 0) - . = counterlist_scale(L, 1 / avg) - else - . = L - -/proc/counterlist_combine(list/L1, list/L2) - for(var/key in L2) - var/other_value = L2[key] - if(key in L1) - L1[key] += other_value - else - L1[key] = other_value \ No newline at end of file +/* + * Holds procs to help with list operations + * Contains groups: + * Misc + * Sorting + */ + +/* + * Misc + */ + + // binary search sorted insert +// IN: Object to be inserted +// LIST: List to insert object into +// TYPECONT: The typepath of the contents of the list +// COMPARE: The variable on the objects to compare +#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \ + var/__BIN_CTTL = length(LIST);\ + if(!__BIN_CTTL) {\ + LIST += IN;\ + } else {\ + var/__BIN_LEFT = 1;\ + var/__BIN_RIGHT = __BIN_CTTL;\ + var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + var/##TYPECONT/__BIN_ITEM;\ + while(__BIN_LEFT < __BIN_RIGHT) {\ + __BIN_ITEM = LIST[__BIN_MID];\ + if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\ + __BIN_LEFT = __BIN_MID + 1;\ + } else {\ + __BIN_RIGHT = __BIN_MID;\ + };\ + __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + };\ + __BIN_ITEM = LIST[__BIN_MID];\ + __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\ + LIST.Insert(__BIN_MID, IN);\ + } + + +//Returns a list in plain english as a string +/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) + var/total = input.len + if(!total) + return "[nothing_text]" + else if(total == 1) + return "[input[1]]" + else if(total == 2) + return "[input[1]][and_text][input[2]]" + else + var/output = "" + var/index = 1 + while(index < total) + if(index == total - 1) + comma_text = final_comma_text + + output += "[input[index]][comma_text]" + index++ + + return "[output][and_text][input[index]]" + +//Returns list element or null. Should prevent "index out of bounds" error. +/proc/listgetindex(var/list/list,index) + if(istype(list) && list.len) + if(isnum(index)) + if(InRange(index,1,list.len)) + return list[index] + else if(index in list) + return list[index] + return + +//Return either pick(list) or null if list is not of type /list or is empty +/proc/safepick(list/list) + if(!islist(list) || !list.len) + return + return pick(list) + +//Checks if the list is empty +/proc/isemptylist(list/list) + if(!list.len) + return 1 + return 0 + +//Checks for specific types in a list +/proc/is_type_in_list(atom/A, list/L) + if(!L || !L.len || !A) + return 0 + for(var/type in L) + if(istype(A, type)) + return 1 + return 0 + +//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches') +/proc/is_type_in_typecache(atom/A, list/L) + if(!L || !L.len || !A) + return 0 + return L[A.type] + +//returns a new list with only atoms that are in typecache L +/proc/typecache_filter_list(list/atoms, list/typecache) + . = list() + for(var/thing in atoms) + var/atom/A = thing + if(typecache[A.type]) + . += A + +/proc/typecache_filter_list_reverse(list/atoms, list/typecache) + . = list() + for(var/thing in atoms) + var/atom/A = thing + if(!typecache[A.type]) + . += A + +/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude) + . = list() + for(var/thing in atoms) + var/atom/A = thing + if(typecache_include[A.type] && !typecache_exclude[A.type]) + . += A + +//Like typesof() or subtypesof(), but returns a typecache instead of a list +/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE) + if(ispath(path)) + var/list/types = list() + if(only_root_path) + types = list(path) + else + types = ignore_root_path ? subtypesof(path) : typesof(path) + var/list/L = list() + for(var/T in types) + L[T] = TRUE + return L + else if(islist(path)) + var/list/pathlist = path + var/list/L = list() + if(ignore_root_path) + for(var/P in pathlist) + for(var/T in subtypesof(P)) + L[T] = TRUE + else + for(var/P in pathlist) + if(only_root_path) + L[P] = TRUE + else + for(var/T in typesof(P)) + L[T] = TRUE + return L + +//Removes any null entries from the list +/proc/listclearnulls(list/list) + if(istype(list)) + while(null in list) + list -= null + return + +/* + * Returns list containing all the entries from first list that are not present in second. + * If skiprep = 1, repeated elements are treated as one. + * If either of arguments is not a list, returns null + */ +/proc/difflist(var/list/first, var/list/second, var/skiprep=0) + if(!islist(first) || !islist(second)) + return + var/list/result = new + if(skiprep) + for(var/e in first) + if(!(e in result) && !(e in second)) + result += e + else + result = first - second + + return result + +/* + * Returns list containing entries that are in either list but not both. + * If skipref = 1, repeated elements are treated as one. + * If either of arguments is not a list, returns null + */ +/proc/uniquemergelist(var/list/first, var/list/second, var/skiprep=0) + if(!islist(first) || !islist(second)) + return + var/list/result = new + if(skiprep) + result = difflist(first, second, skiprep)+difflist(second, first, skiprep) + else + result = first ^ second + return result + +//Pretends to pick an element based on its weight but really just seems to pick a random element. +/proc/pickweight(list/L) + var/total = 0 + var/item + for(item in L) + if(!L[item]) + L[item] = 1 + total += L[item] + + total = rand(1, total) + for(item in L) + total -=L [item] + if(total <= 0) + return item + + return null + +//Pick a random element from the list and remove it from the list. +/proc/pick_n_take(list/listfrom) + if(listfrom.len > 0) + var/picked = pick(listfrom) + listfrom -= picked + return picked + return null + +//Returns the top(last) element from the list and removes it from the list (typical stack function) +/proc/pop(list/L) + if(L.len) + . = L[L.len] + L.len-- + +/proc/popleft(list/L) + if(L.len) + . = L[1] + L.Cut(1,2) + + +/* + * Sorting + */ + +//Reverses the order of items in the list +/proc/reverselist(list/L) + var/list/output = list() + if(L) + for(var/i = L.len; i >= 1; i--) + output += L[i] + return output + +//Randomize: Return the list in a random order +/proc/shuffle(var/list/L) + if(!L) + return + L = L.Copy() + + for(var/i=1, i current_index) + current_index++ + current_item = sorted_list[current_index] + + current_item_value = current_item:dd_SortValue() + current_sort_object_value = current_sort_object:dd_SortValue() + if(current_sort_object_value < current_item_value) + high_index = current_index - 1 + else if(current_sort_object_value > current_item_value) + low_index = current_index + 1 + else + // current_sort_object == current_item + low_index = current_index + break + + // Insert before low_index. + insert_index = low_index + + // Special case adding to end of list. + if(insert_index > sorted_list.len) + sorted_list += current_sort_object + continue + + // Because BYOND lists don't support insert, have to do it by: + // 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list. + list_bottom = sorted_list.Copy(insert_index) + sorted_list.Cut(insert_index) + sorted_list += current_sort_object + sorted_list += list_bottom + return sorted_list +*/ + +/proc/dd_sortedtextlist(list/incoming, case_sensitive = 0) + // Returns a new list with the text values sorted. + // Use binary search to order by sortValue. + // This works by going to the half-point of the list, seeing if the node in question is higher or lower cost, + // then going halfway up or down the list and checking again. + // This is a very fast way to sort an item into a list. + var/list/sorted_text = new() + var/low_index + var/high_index + var/insert_index + var/midway_calc + var/current_index + var/current_item + var/list/list_bottom + var/sort_result + + var/current_sort_text + for(current_sort_text in incoming) + low_index = 1 + high_index = sorted_text.len + while(low_index <= high_index) + // Figure out the midpoint, rounding up for fractions. (BYOND rounds down, so add 1 if necessary.) + midway_calc = (low_index + high_index) / 2 + current_index = round(midway_calc) + if(midway_calc > current_index) + current_index++ + current_item = sorted_text[current_index] + + if(case_sensitive) + sort_result = sorttextEx(current_sort_text, current_item) + else + sort_result = sorttext(current_sort_text, current_item) + + switch(sort_result) + if(1) + high_index = current_index - 1 // current_sort_text < current_item + if(-1) + low_index = current_index + 1 // current_sort_text > current_item + if(0) + low_index = current_index // current_sort_text == current_item + break + + // Insert before low_index. + insert_index = low_index + + // Special case adding to end of list. + if(insert_index > sorted_text.len) + sorted_text += current_sort_text + continue + + // Because BYOND lists don't support insert, have to do it by: + // 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list. + list_bottom = sorted_text.Copy(insert_index) + sorted_text.Cut(insert_index) + sorted_text += current_sort_text + sorted_text += list_bottom + return sorted_text + + +/proc/dd_sortedTextList(list/incoming) + var/case_sensitive = 1 + return dd_sortedtextlist(incoming, case_sensitive) + +/proc/subtypesof(var/path) //Returns a list containing all subtypes of the given path, but not the given path itself. + if(!path || !ispath(path)) + CRASH("Invalid path, failed to fetch subtypes of \"[path]\".") + return (typesof(path) - path) + +/datum/proc/dd_SortValue() + return "[src]" + +/obj/machinery/dd_SortValue() + return "[sanitize(name)]" + +/obj/machinery/camera/dd_SortValue() + return "[c_tag]" + +/datum/alarm/dd_SortValue() + return "[sanitize(last_name)]" + +//Picks from the list, with some safeties, and returns the "default" arg if it fails +#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default) + +#define LAZYINITLIST(L) if (!L) L = list() + +#define UNSETEMPTY(L) if (L && !L.len) L = null +#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } } +#define LAZYADD(L, I) if(!L) { L = list(); } L += I; +#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null) +#define LAZYLEN(L) length(L) +#define LAZYCLEARLIST(L) if(L) L.Cut() + +// LAZYING PT 2: THE LAZENING +#define LAZYREINITLIST(L) LAZYCLEARLIST(L); LAZYINITLIST(L); + + +//same, but returns nothing and acts on list in place +/proc/shuffle_inplace(list/L) + if(!L) + return + + for(var/i=1, i toIndex) + ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one + + L.Insert(toIndex, null) + L.Swap(fromIndex, toIndex) + L.Cut(fromIndex, fromIndex + 1) + + +//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex) +//Same as moveElement but for ranges of elements +//This will preserve associations ~Carnie +/proc/moveRange(list/L, fromIndex, toIndex, len = 1) + var/distance = abs(toIndex - fromIndex) + if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements + if(fromIndex <= toIndex) + return //no need to move + fromIndex += len //we want to shift left instead of right + + for(var/i = 0, i < distance, ++i) + L.Insert(fromIndex, null) + L.Swap(fromIndex, toIndex) + L.Cut(toIndex, toIndex + 1) + else + if(fromIndex > toIndex) + fromIndex += len + + for(var/i = 0, i < len, ++i) + L.Insert(toIndex, null) + L.Swap(fromIndex, toIndex) + L.Cut(fromIndex, fromIndex + 1) + +//Move elements from [fromIndex, fromIndex+len) to [toIndex, toIndex+len) +//Move any elements being overwritten by the move to the now-empty elements, preserving order +//Note: if the two ranges overlap, only the destination order will be preserved fully, since some elements will be within both ranges ~Carnie +/proc/swapRange(list/L, fromIndex, toIndex, len = 1) + var/distance = abs(toIndex - fromIndex) + if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements + if(fromIndex < toIndex) + toIndex += len + else + fromIndex += len + + for(var/i = 0, i < distance, ++i) + L.Insert(fromIndex, null) + L.Swap(fromIndex, toIndex) + L.Cut(toIndex, toIndex + 1) + else + if(toIndex > fromIndex) + var/a = toIndex + toIndex = fromIndex + fromIndex = a + + for(var/i = 0, i < len, ++i) + L.Swap(fromIndex++, toIndex++) + +//replaces reverseList ~Carnie +/proc/reverseRange(list/L, start = 1, end = 0) + if(L.len) + start = start % L.len + end = end % (L.len + 1) + if(start <= 0) + start += L.len + if(end <= 0) + end += L.len + 1 + + --end + while(start < end) + L.Swap(start++, end--) + + return L + +/proc/counterlist_scale(list/L, scalar) + var/list/out = list() + for(var/key in L) + out[key] = L[key] * scalar + . = out + +/proc/counterlist_sum(list/L) + . = 0 + for(var/key in L) + . += L[key] + +/proc/counterlist_normalise(list/L) + var/avg = counterlist_sum(L) + if(avg != 0) + . = counterlist_scale(L, 1 / avg) + else + . = L + +/proc/counterlist_combine(list/L1, list/L2) + for(var/key in L2) + var/other_value = L2[key] + if(key in L1) + L1[key] += other_value + else + L1[key] = other_value diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index ec9fcb5fb31..76d422e3d82 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -1,143 +1,143 @@ -// Credits to Nickr5 for the useful procs I've taken from his library resource. - -var/const/E = 2.71828183 -var/const/Sqrt2 = 1.41421356 - -/proc/Atan2(x, y) - if(!x && !y) return 0 - var/a = arccos(x / sqrt(x*x + y*y)) - return y >= 0 ? a : -a - -// Greatest Common Divisor - Euclid's algorithm -/proc/Gcd(a, b) - return b ? Gcd(b, a % b) : a - -/proc/IsAboutEqual(a, b, deviation = 0.1) - return abs(a - b) <= deviation - -// Performs a linear interpolation between a and b. -// Note that amount=0 returns a, amount=1 returns b, and -// amount=0.5 returns the mean of a and b. -/proc/Lerp(a, b, amount = 0.5) - return a + (b - a) * amount - -/proc/Mean(...) - var/values = 0 - var/sum = 0 - for(var/val in args) - values++ - sum += val - return sum / values - -// The quadratic formula. Returns a list with the solutions, or an empty list -// if they are imaginary. -/proc/SolveQuadratic(a, b, c) - ASSERT(a) - . = list() - var/d = b*b - 4 * a * c - var/bottom = 2 * a - if(d < 0) return - var/root = sqrt(d) - . += (-b + root) / bottom - if(!d) return - . += (-b - root) / bottom - -// Will filter out extra rotations and negative rotations -// E.g: 540 becomes 180. -180 becomes 180. -/proc/SimplifyDegrees(degrees) - degrees = degrees % 360 - if(degrees < 0) - degrees += 360 - return degrees - -// min is inclusive, max is exclusive -/proc/Wrap(val, min, max) - var/d = max - min - var/t = Floor((val - min) / d) - return val - (t * d) - -//A logarithm that converts an integer to a number scaled between 0 and 1 (can be tweaked to be higher). -//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions. -/proc/TransformUsingVariable(input, inputmaximum, scaling_modifier = 0) - - var/inputToDegrees = (input/inputmaximum)*180 //Converting from a 0 -> 100 scale to a 0 -> 180 scale. The 0 -> 180 scale corresponds to degrees - var/size_factor = ((-cos(inputToDegrees) +1) /2) //returns a value from 0 to 1 - - return size_factor + scaling_modifier //scale mod of 0 results in a number from 0 to 1. A scale modifier of +0.5 returns 0.5 to 1.5 - //world<< "Transform multiplier of [src] is [size_factor + scaling_modifer]" - -/proc/RaiseToPower(num, power) - if(!power) return 1 - return (power-- > 1 ? num * RaiseToPower(num, power) : num) - -//converts a uniform distributed random number into a normal distributed one -//since this method produces two random numbers, one is saved for subsequent calls -//(making the cost negligble for every second call) -//This will return +/- decimals, situated about mean with standard deviation stddev -//68% chance that the number is within 1stddev -//95% chance that the number is within 2stddev -//98% chance that the number is within 3stddev...etc -var/gaussian_next -#define ACCURACY 10000 -/proc/gaussian(mean, stddev) - var/R1;var/R2;var/working - if(gaussian_next != null) - R1 = gaussian_next - gaussian_next = null - else - do - R1 = rand(-ACCURACY,ACCURACY)/ACCURACY - R2 = rand(-ACCURACY,ACCURACY)/ACCURACY - working = R1*R1 + R2*R2 - while(working >= 1 || working==0) - working = sqrt(-2 * log(working) / working) - R1 *= working - gaussian_next = R2 * working - return (mean + stddev * R1) -#undef ACCURACY - - - -// oof, what a mouthful -// Used in status_procs' "adjust" to let them modify a status effect by a given -// amount, without inadverdently increasing it in the wrong direction -/proc/directional_bounded_sum(orig_val, modifier, bound_lower, bound_upper) - var/new_val = orig_val + modifier - if(modifier > 0) - if(new_val > bound_upper) - new_val = max(orig_val, bound_upper) - else if(modifier < 0) - if(new_val < bound_lower) - new_val = min(orig_val, bound_lower) - return new_val - -// sqrt, but if you give it a negative number, you get 0 instead of a runtime -/proc/sqrtor0(num) - if(num < 0) - return 0 - return sqrt(num) - -/proc/round_down(num) - if(round(num) != num) - return round(num--) - else return num - -// Returns a list where [1] is all x values and [2] is all y values that overlap between the given pair of rectangles -/proc/get_overlap(x1, y1, x2, y2, x3, y3, x4, y4) - var/list/region_x1 = list() - var/list/region_y1 = list() - var/list/region_x2 = list() - var/list/region_y2 = list() - - // These loops create loops filled with x/y values that the boundaries inhabit - // ex: list(5, 6, 7, 8, 9) - for(var/i in min(x1, x2) to max(x1, x2)) - region_x1["[i]"] = TRUE - for(var/i in min(y1, y2) to max(y1, y2)) - region_y1["[i]"] = TRUE - for(var/i in min(x3, x4) to max(x3, x4)) - region_x2["[i]"] = TRUE - for(var/i in min(y3, y4) to max(y3, y4)) - region_y2["[i]"] = TRUE - - return list(region_x1 & region_x2, region_y1 & region_y2) \ No newline at end of file +// Credits to Nickr5 for the useful procs I've taken from his library resource. + +#define MATH_E 2.71828183 +#define SQRT2 1.41421356 + +/proc/Atan2(x, y) + if(!x && !y) return 0 + var/a = arccos(x / sqrt(x*x + y*y)) + return y >= 0 ? a : -a + +// Greatest Common Divisor - Euclid's algorithm +/proc/Gcd(a, b) + return b ? Gcd(b, a % b) : a + +/proc/IsAboutEqual(a, b, deviation = 0.1) + return abs(a - b) <= deviation + +// Performs a linear interpolation between a and b. +// Note that amount=0 returns a, amount=1 returns b, and +// amount=0.5 returns the mean of a and b. +/proc/Lerp(a, b, amount = 0.5) + return a + (b - a) * amount + +/proc/Mean(...) + var/values = 0 + var/sum = 0 + for(var/val in args) + values++ + sum += val + return sum / values + +// The quadratic formula. Returns a list with the solutions, or an empty list +// if they are imaginary. +/proc/SolveQuadratic(a, b, c) + ASSERT(a) + . = list() + var/d = b*b - 4 * a * c + var/bottom = 2 * a + if(d < 0) return + var/root = sqrt(d) + . += (-b + root) / bottom + if(!d) return + . += (-b - root) / bottom + +// Will filter out extra rotations and negative rotations +// E.g: 540 becomes 180. -180 becomes 180. +/proc/SimplifyDegrees(degrees) + degrees = degrees % 360 + if(degrees < 0) + degrees += 360 + return degrees + +// min is inclusive, max is exclusive +/proc/Wrap(val, min, max) + var/d = max - min + var/t = Floor((val - min) / d) + return val - (t * d) + +//A logarithm that converts an integer to a number scaled between 0 and 1 (can be tweaked to be higher). +//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions. +/proc/TransformUsingVariable(input, inputmaximum, scaling_modifier = 0) + + var/inputToDegrees = (input/inputmaximum)*180 //Converting from a 0 -> 100 scale to a 0 -> 180 scale. The 0 -> 180 scale corresponds to degrees + var/size_factor = ((-cos(inputToDegrees) +1) /2) //returns a value from 0 to 1 + + return size_factor + scaling_modifier //scale mod of 0 results in a number from 0 to 1. A scale modifier of +0.5 returns 0.5 to 1.5 + //world<< "Transform multiplier of [src] is [size_factor + scaling_modifer]" + +/proc/RaiseToPower(num, power) + if(!power) return 1 + return (power-- > 1 ? num * RaiseToPower(num, power) : num) + +//converts a uniform distributed random number into a normal distributed one +//since this method produces two random numbers, one is saved for subsequent calls +//(making the cost negligble for every second call) +//This will return +/- decimals, situated about mean with standard deviation stddev +//68% chance that the number is within 1stddev +//95% chance that the number is within 2stddev +//98% chance that the number is within 3stddev...etc +GLOBAL_VAR(gaussian_next) +#define ACCURACY 10000 +/proc/gaussian(mean, stddev) + var/R1;var/R2;var/working + if(GLOB.gaussian_next != null) + R1 = GLOB.gaussian_next + GLOB.gaussian_next = null + else + do + R1 = rand(-ACCURACY,ACCURACY)/ACCURACY + R2 = rand(-ACCURACY,ACCURACY)/ACCURACY + working = R1*R1 + R2*R2 + while(working >= 1 || working==0) + working = sqrt(-2 * log(working) / working) + R1 *= working + GLOB.gaussian_next = R2 * working + return (mean + stddev * R1) +#undef ACCURACY + + + +// oof, what a mouthful +// Used in status_procs' "adjust" to let them modify a status effect by a given +// amount, without inadverdently increasing it in the wrong direction +/proc/directional_bounded_sum(orig_val, modifier, bound_lower, bound_upper) + var/new_val = orig_val + modifier + if(modifier > 0) + if(new_val > bound_upper) + new_val = max(orig_val, bound_upper) + else if(modifier < 0) + if(new_val < bound_lower) + new_val = min(orig_val, bound_lower) + return new_val + +// sqrt, but if you give it a negative number, you get 0 instead of a runtime +/proc/sqrtor0(num) + if(num < 0) + return 0 + return sqrt(num) + +/proc/round_down(num) + if(round(num) != num) + return round(num--) + else return num + +// Returns a list where [1] is all x values and [2] is all y values that overlap between the given pair of rectangles +/proc/get_overlap(x1, y1, x2, y2, x3, y3, x4, y4) + var/list/region_x1 = list() + var/list/region_y1 = list() + var/list/region_x2 = list() + var/list/region_y2 = list() + + // These loops create loops filled with x/y values that the boundaries inhabit + // ex: list(5, 6, 7, 8, 9) + for(var/i in min(x1, x2) to max(x1, x2)) + region_x1["[i]"] = TRUE + for(var/i in min(y1, y2) to max(y1, y2)) + region_y1["[i]"] = TRUE + for(var/i in min(x3, x4) to max(x3, x4)) + region_x2["[i]"] = TRUE + for(var/i in min(y3, y4) to max(y3, y4)) + region_y2["[i]"] = TRUE + + return list(region_x1 & region_x2, region_y1 & region_y2) diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm index 3b370f8a945..0520ab6b077 100644 --- a/code/__HELPERS/matrices.dm +++ b/code/__HELPERS/matrices.dm @@ -59,4 +59,4 @@ //The Y pixel offset of this matrix /matrix/proc/get_y_shift() - . = f \ No newline at end of file + . = f diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 7c61cf832b5..f437c409088 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -1,578 +1,618 @@ -proc/GetOppositeDir(var/dir) - switch(dir) - if(NORTH) return SOUTH - if(SOUTH) return NORTH - if(EAST) return WEST - if(WEST) return EAST - if(SOUTHWEST) return NORTHEAST - if(NORTHWEST) return SOUTHEAST - if(NORTHEAST) return SOUTHWEST - if(SOUTHEAST) return NORTHWEST - return 0 - -proc/random_underwear(gender, species = "Human") - var/list/pick_list = list() - switch(gender) - if(MALE) pick_list = GLOB.underwear_m - if(FEMALE) pick_list = GLOB.underwear_f - else pick_list = GLOB.underwear_list - return pick_species_allowed_underwear(pick_list, species) - -proc/random_undershirt(gender, species = "Human") - var/list/pick_list = list() - switch(gender) - if(MALE) pick_list = GLOB.undershirt_m - if(FEMALE) pick_list = GLOB.undershirt_f - else pick_list = GLOB.undershirt_list - return pick_species_allowed_underwear(pick_list, species) - -proc/random_socks(gender, species = "Human") - var/list/pick_list = list() - switch(gender) - if(MALE) pick_list = GLOB.socks_m - if(FEMALE) pick_list = GLOB.socks_f - else pick_list = GLOB.socks_list - return pick_species_allowed_underwear(pick_list, species) - -proc/pick_species_allowed_underwear(list/all_picks, species) - var/list/valid_picks = list() - for(var/test in all_picks) - var/datum/sprite_accessory/S = all_picks[test] - if(!(species in S.species_allowed)) - continue - valid_picks += test - - if(!valid_picks.len) valid_picks += "Nude" - - return pick(valid_picks) - -proc/random_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) - var/h_style = "Bald" - var/list/valid_hairstyles = list() - for(var/hairstyle in GLOB.hair_styles_public_list) - var/datum/sprite_accessory/S = GLOB.hair_styles_public_list[hairstyle] - - if(hairstyle == "Bald") //Just in case. - valid_hairstyles += hairstyle - continue - if((gender == MALE && S.gender == FEMALE) || (gender == FEMALE && S.gender == MALE)) - continue - if(species == "Machine") //If the user is a species who can have a robotic head... - if(!robohead) - robohead = all_robolimbs["Morpheus Cyberkinetics"] - if((species in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. - valid_hairstyles += hairstyle //Give them their hairstyles if they do. - else - if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it. - But if the user has a robotic humanoid head and the hairstyle can fit humans, let them use it as a wig. */ - valid_hairstyles += hairstyle - else //If the user is not a species who can have robotic heads, use the default handling. - if(species in S.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list. - valid_hairstyles += hairstyle - - if(valid_hairstyles.len) - h_style = pick(valid_hairstyles) - - return h_style - -proc/random_facial_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) - var/f_style = "Shaved" - var/list/valid_facial_hairstyles = list() - for(var/facialhairstyle in GLOB.facial_hair_styles_list) - var/datum/sprite_accessory/S = GLOB.facial_hair_styles_list[facialhairstyle] - - if(facialhairstyle == "Shaved") //Just in case. - valid_facial_hairstyles += facialhairstyle - continue - if((gender == MALE && S.gender == FEMALE) || (gender == FEMALE && S.gender == MALE)) - continue - if(species == "Machine") //If the user is a species who can have a robotic head... - if(!robohead) - robohead = all_robolimbs["Morpheus Cyberkinetics"] - if((species in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. - valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do. - else - if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the facial hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it. - But if the user has a robotic humanoid head and the facial hairstyle can fit humans, let them use it as a wig. */ - valid_facial_hairstyles += facialhairstyle - else //If the user is not a species who can have robotic heads, use the default handling. - if(species in S.species_allowed) //If the user's head is of a species the facial hair style allows, add it to the list. - valid_facial_hairstyles += facialhairstyle - - if(valid_facial_hairstyles.len) - f_style = pick(valid_facial_hairstyles) - - return f_style - -proc/random_head_accessory(species = "Human") - var/ha_style = "None" - var/list/valid_head_accessories = list() - for(var/head_accessory in GLOB.head_accessory_styles_list) - var/datum/sprite_accessory/S = GLOB.head_accessory_styles_list[head_accessory] - - if(!(species in S.species_allowed)) - continue - valid_head_accessories += head_accessory - - if(valid_head_accessories.len) - ha_style = pick(valid_head_accessories) - - return ha_style - -proc/random_marking_style(var/location = "body", species = "Human", var/datum/robolimb/robohead, var/body_accessory, var/alt_head) - var/m_style = "None" - var/list/valid_markings = list() - for(var/marking in GLOB.marking_styles_list) - var/datum/sprite_accessory/body_markings/S = GLOB.marking_styles_list[marking] - if(S.name == "None") - valid_markings += marking - continue - if(S.marking_location != location) //If the marking isn't for the location we desire, skip. - continue - if(!(species in S.species_allowed)) //If the user's head is not of a species the marking style allows, skip it. Otherwise, add it to the list. - continue - if(location == "tail") - if(!body_accessory) - if(S.tails_allowed) - continue - else - if(!S.tails_allowed || !(body_accessory in S.tails_allowed)) - continue - if(location == "head") - var/datum/sprite_accessory/body_markings/head/M = GLOB.marking_styles_list[S.name] - if(species == "Machine")//If the user is a species that can have a robotic head... - if(!robohead) - robohead = all_robolimbs["Morpheus Cyberkinetics"] - if(!(S.models_allowed && (robohead.company in S.models_allowed))) //Make sure they don't get markings incompatible with their head. - continue - else if(alt_head && alt_head != "None") //If the user's got an alt head, validate markings for that head. - if(!("All" in M.heads_allowed) && !(alt_head in M.heads_allowed)) - continue - else - if(M.heads_allowed && !("All" in M.heads_allowed)) - continue - valid_markings += marking - - if(valid_markings.len) - m_style = pick(valid_markings) - - return m_style - -proc/random_body_accessory(species = "Vulpkanin") - var/body_accessory = null - var/list/valid_body_accessories = list() - for(var/B in body_accessory_by_name) - var/datum/body_accessory/A = body_accessory_by_name[B] - if(!istype(A)) - valid_body_accessories += "None" //The only null entry should be the "None" option. - continue - if(species in A.allowed_species) //If the user is not of a species the body accessory style allows, skip it. Otherwise, add it to the list. - valid_body_accessories += B - - if(valid_body_accessories.len) - body_accessory = pick(valid_body_accessories) - - return body_accessory - -proc/random_name(gender, species = "Human") - - var/datum/species/current_species - if(species) - current_species = GLOB.all_species[species] - - if(!current_species || current_species.name == "Human") - if(gender==FEMALE) - return capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names)) - else - return capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) - else - return current_species.get_random_name(gender) - -proc/random_skin_tone(species = "Human") - if(species == "Human" || species == "Drask") - switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) - if("caucasian") . = -10 - if("afroamerican") . = -115 - if("african") . = -165 - if("latino") . = -55 - if("albino") . = 34 - else . = rand(-185, 34) - return min(max(. + rand(-25, 25), -185), 34) - else if(species == "Vox") - . = rand(1, 6) - return . - -proc/skintone2racedescription(tone, species = "Human") - if(species == "Human") - switch(tone) - if(30 to INFINITY) return "albino" - if(20 to 30) return "pale" - if(5 to 15) return "light skinned" - if(-10 to 5) return "white" - if(-25 to -10) return "tan" - if(-45 to -25) return "darker skinned" - if(-65 to -45) return "brown" - if(-INFINITY to -65) return "black" - else return "unknown" - else if(species == "Vox") - switch(tone) - if(2) return "dark green" - if(3) return "brown" - if(4) return "gray" - if(5) return "emerald" - if(6) return "azure" - else return "green" - else - return "unknown" - -proc/age2agedescription(age) - switch(age) - if(0 to 1) return "infant" - if(1 to 3) return "toddler" - if(3 to 13) return "child" - if(13 to 19) return "teenager" - if(19 to 30) return "young adult" - if(30 to 45) return "adult" - if(45 to 60) return "middle-aged" - if(60 to 70) return "aging" - if(70 to INFINITY) return "elderly" - else return "unknown" - -/proc/set_criminal_status(mob/living/user, datum/data/record/target_records , criminal_status, comment, user_rank, list/authcard_access = list()) - var/status = criminal_status - var/their_name = target_records.fields["name"] - var/their_rank = target_records.fields["rank"] - switch(criminal_status) - if("arrest") - status = "*Arrest*" - if("none") - status = "None" - if("execute") - if((access_magistrate in authcard_access) || (access_armory in authcard_access)) - status = "*Execute*" - message_admins("[ADMIN_FULLMONTY(usr)] authorized EXECUTION for [their_rank] [their_name], with comment: [comment]") - else - return 0 - if("incarcerated") - status = "Incarcerated" - if("parolled") - status = "Parolled" - if("released") - status = "Released" - target_records.fields["criminal"] = status - log_admin("[key_name_admin(user)] set secstatus of [their_rank] [their_name] to [status], comment: [comment]") - target_records.fields["comments"] += "Set to [status] by [user.name] ([user_rank]) on [current_date_string] [station_time_timestamp()], comment: [comment]" - update_all_mob_security_hud() - return 1 - -/* -Proc for attack log creation, because really why not -1 argument is the actor -2 argument is the target of action -3 is the full description of the action -4 is whether or not to message admins -This is always put in the attack log. -*/ - -/proc/add_attack_logs(mob/user, mob/target, what_done, custom_level) - if(islist(target)) // Multi-victim adding - var/list/targets = target - for(var/mob/M in targets) - add_attack_logs(user, M, what_done, custom_level) - return - - var/user_str = key_name_log(user) + COORD(user) - var/target_str = key_name_log(target) + COORD(target) - - if(istype(user)) - user.create_attack_log("Attacked [target_str]: [what_done]") - if(istype(target)) - target.create_attack_log("Attacked by [user_str]: [what_done]") - log_attack(user_str, target_str, what_done) - - var/loglevel = ATKLOG_MOST - if(!isnull(custom_level)) - loglevel = custom_level - else if(istype(target)) - if(istype(user) && !user.ckey && !target.ckey) // Attacks between NPCs are only shown to admins with ATKLOG_ALL - loglevel = ATKLOG_ALL - else if(!user.ckey || !target.ckey || (user.ckey == target.ckey)) // Player v NPC combat is de-prioritized. Also no self-harm, nobody cares - loglevel = ATKLOG_ALMOSTALL - else - var/area/A = get_area(target) - if(A && A.hide_attacklogs) - loglevel = ATKLOG_ALMOSTALL - if(isLivingSSD(target)) // Attacks on SSDs are shown to admins with any log level except ATKLOG_NONE. Overrides custom level - loglevel = ATKLOG_FEW - - msg_admin_attack("[key_name_admin(user)] vs [key_name_admin(target)]: [what_done]", loglevel) - -/proc/do_mob(var/mob/user, var/mob/target, var/time = 30, var/uninterruptible = 0, progress = 1, datum/callback/extra_checks = null) - if(!user || !target) - return 0 - var/user_loc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/target_loc = target.loc - - var/holding = user.get_active_hand() - var/datum/progressbar/progbar - if(progress) - progbar = new(user, time, target) - - var/endtime = world.time+time - var/starttime = world.time - . = 1 - while(world.time < endtime) - sleep(1) - if(progress) - progbar.update(world.time - starttime) - if(!user || !target) - . = 0 - break - if(uninterruptible) - continue - - if(drifting && !user.inertia_dir) - drifting = 0 - user_loc = user.loc - - if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_hand() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) - . = 0 - break - if(progress) - qdel(progbar) - -/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null) - if(!user) - return 0 - var/atom/Tloc = null - if(target) - Tloc = target.loc - - var/atom/Uloc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/holding = user.get_active_hand() - - var/holdingnull = 1 //User's hand started out empty, check for an empty hand - if(holding) - holdingnull = 0 //Users hand started holding something, check to see if it's still holding that - - var/datum/progressbar/progbar - if(progress) - progbar = new(user, delay, target) - - var/endtime = world.time + delay - var/starttime = world.time - . = 1 - while(world.time < endtime) - sleep(1) - if(progress) - progbar.update(world.time - starttime) - - if(drifting && !user.inertia_dir) - drifting = 0 - Uloc = user.loc - - if(!user || user.stat || user.IsWeakened() || user.stunned || (!drifting && user.loc != Uloc)|| (extra_checks && !extra_checks.Invoke())) - . = 0 - break - - if(Tloc && (!target || Tloc != target.loc)) - . = 0 - break - - if(needhand) - //This might seem like an odd check, but you can still need a hand even when it's empty - //i.e the hand is used to pull some item/tool out of the construction - if(!holdingnull) - if(!holding) - . = 0 - break - if(user.get_active_hand() != holding) - . = 0 - break - if(progress) - qdel(progbar) - -#define DOAFTERONCE_MAGIC "Magic~~" -GLOBAL_LIST_INIT(do_after_once_tracker, list()) -/proc/do_after_once(mob/user, delay, needhand = 1, atom/target = null, progress = 1, attempt_cancel_message = "Attempt cancelled.") - if(!user || !target) - return - - var/cache_key = "[user.UID()][target.UID()]" - if(GLOB.do_after_once_tracker[cache_key]) - GLOB.do_after_once_tracker[cache_key] = DOAFTERONCE_MAGIC - to_chat(user, "[attempt_cancel_message]") - return FALSE - GLOB.do_after_once_tracker[cache_key] = TRUE - . = do_after(user, delay, needhand, target, progress, extra_checks = CALLBACK(GLOBAL_PROC, .proc/do_after_once_checks, cache_key)) - GLOB.do_after_once_tracker[cache_key] = FALSE - -/proc/do_after_once_checks(cache_key) - if(GLOB.do_after_once_tracker[cache_key] && GLOB.do_after_once_tracker[cache_key] == DOAFTERONCE_MAGIC) - GLOB.do_after_once_tracker[cache_key] = FALSE - return FALSE - return TRUE - -/proc/is_species(A, species_datum) - . = FALSE - if(ishuman(A)) - var/mob/living/carbon/human/H = A - if(H.dna && istype(H.dna.species, species_datum)) - . = TRUE - -/proc/spawn_atom_to_turf(spawn_type, target, amount, admin_spawn=FALSE, list/extra_args) - var/turf/T = get_turf(target) - if(!T) - CRASH("attempt to spawn atom type: [spawn_type] in nullspace") - - var/list/new_args = list(T) - if(extra_args) - new_args += extra_args - - for(var/j in 1 to amount) - var/atom/X = new spawn_type(arglist(new_args)) - X.admin_spawned = admin_spawn - -/proc/admin_mob_info(mob/M, mob/user = usr) - if(!ismob(M)) - to_chat(user, "This can only be used on instances of type /mob") - return - - var/location_description = "" - var/special_role_description = "" - var/health_description = "" - var/gender_description = "" - var/turf/T = get_turf(M) - - //Location - if(isturf(T)) - if(isarea(T.loc)) - location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])" - else - location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])" - - //Job + antagonist - if(M.mind) - special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [M.mind.special_role]; Has been rev: [(M.mind.has_been_rev)?"Yes":"No"]" - else - special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing; Has been rev: Mind datum missing;" - - //Health - if(isliving(M)) - var/mob/living/L = M - var/status - switch(M.stat) - if(CONSCIOUS) - status = "Alive" - if(UNCONSCIOUS) - status = "Unconscious" - if(DEAD) - status = "Dead" - health_description = "Status = [status]" - health_description += "
Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getBrainLoss()]" - else - health_description = "This mob type has no health to speak of." - - //Gener - switch(M.gender) - if(MALE, FEMALE) - gender_description = "[M.gender]" - else - gender_description = "[M.gender]" - - to_chat(user, "Info about [M.name]: ") - to_chat(user, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]") - to_chat(user, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];") - to_chat(user, "Location = [location_description];") - to_chat(user, "[special_role_description]") - to_chat(user, "(PM) ([ADMIN_PP(M,"PP")]) ([ADMIN_VV(M,"VV")]) ([ADMIN_TP(M,"TP")]) ([ADMIN_SM(M,"SM")]) ([ADMIN_FLW(M,"FLW")])") - -// Gets the first mob contained in an atom, and warns the user if there's not exactly one -/proc/get_mob_in_atom_with_warning(atom/A, mob/user = usr) - if(!istype(A)) - return null - if(ismob(A)) - return A - - . = null - for(var/mob/M in A) - if(!.) - . = M - else - to_chat(user, "Multiple mobs in [A], using first mob found...") - break - if(!.) - to_chat(user, "No mob located in [A].") - -// Suppress the mouse macros -/client/var/next_mouse_macro_warning -/mob/proc/LogMouseMacro(verbused, params) - if(!client) - return - if(!client.next_mouse_macro_warning) // Log once - log_admin("[key_name(usr)] attempted to use a mouse macro: [verbused] [params]") - message_admins("[key_name_admin(usr)] attempted to use a mouse macro: [verbused] [html_encode(params)]") - if(client.next_mouse_macro_warning < world.time) // Warn occasionally - usr << 'sound/misc/sadtrombone.ogg' - client.next_mouse_macro_warning = world.time + 600 -/mob/verb/ClickSubstitute(params as command_text) - set hidden = 1 - set name = ".click" - LogMouseMacro(".click", params) -/mob/verb/DblClickSubstitute(params as command_text) - set hidden = 1 - set name = ".dblclick" - LogMouseMacro(".dblclick", params) -/mob/verb/MouseSubstitute(params as command_text) - set hidden = 1 - set name = ".mouse" - LogMouseMacro(".mouse", params) - -/proc/update_all_mob_security_hud() - for(var/mob/living/carbon/human/H in GLOB.mob_list) - H.sec_hud_set_security_status() - -/proc/getviewsize(view) - var/viewX - var/viewY - if(isnum(view)) - var/totalviewrange = 1 + 2 * view - viewX = totalviewrange - viewY = totalviewrange - else - var/list/viewrangelist = splittext(view, "x") - viewX = text2num(viewrangelist[1]) - viewY = text2num(viewrangelist[2]) - return list(viewX, viewY) - -//Used in chemical_mob_spawn. Generates a random mob based on a given gold_core_spawnable value. -/proc/create_random_mob(spawn_location, mob_class = HOSTILE_SPAWN) - var/static/list/mob_spawn_meancritters = list() // list of possible hostile mobs - var/static/list/mob_spawn_nicecritters = list() // and possible friendly mobs - - if(mob_spawn_meancritters.len <= 0 || mob_spawn_nicecritters.len <= 0) - for(var/T in typesof(/mob/living/simple_animal)) - var/mob/living/simple_animal/SA = T - switch(initial(SA.gold_core_spawnable)) - if(HOSTILE_SPAWN) - mob_spawn_meancritters += T - if(FRIENDLY_SPAWN) - mob_spawn_nicecritters += T - - var/chosen - if(mob_class == FRIENDLY_SPAWN) - chosen = pick(mob_spawn_nicecritters) - else - chosen = pick(mob_spawn_meancritters) - var/mob/living/simple_animal/C = new chosen(spawn_location) - return C \ No newline at end of file +proc/GetOppositeDir(var/dir) + switch(dir) + if(NORTH) return SOUTH + if(SOUTH) return NORTH + if(EAST) return WEST + if(WEST) return EAST + if(SOUTHWEST) return NORTHEAST + if(NORTHWEST) return SOUTHEAST + if(NORTHEAST) return SOUTHWEST + if(SOUTHEAST) return NORTHWEST + return 0 + +proc/random_underwear(gender, species = "Human") + var/list/pick_list = list() + switch(gender) + if(MALE) pick_list = GLOB.underwear_m + if(FEMALE) pick_list = GLOB.underwear_f + else pick_list = GLOB.underwear_list + return pick_species_allowed_underwear(pick_list, species) + +proc/random_undershirt(gender, species = "Human") + var/list/pick_list = list() + switch(gender) + if(MALE) pick_list = GLOB.undershirt_m + if(FEMALE) pick_list = GLOB.undershirt_f + else pick_list = GLOB.undershirt_list + return pick_species_allowed_underwear(pick_list, species) + +proc/random_socks(gender, species = "Human") + var/list/pick_list = list() + switch(gender) + if(MALE) pick_list = GLOB.socks_m + if(FEMALE) pick_list = GLOB.socks_f + else pick_list = GLOB.socks_list + return pick_species_allowed_underwear(pick_list, species) + +proc/pick_species_allowed_underwear(list/all_picks, species) + var/list/valid_picks = list() + for(var/test in all_picks) + var/datum/sprite_accessory/S = all_picks[test] + if(!(species in S.species_allowed)) + continue + valid_picks += test + + if(!valid_picks.len) valid_picks += "Nude" + + return pick(valid_picks) + +proc/random_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) + var/h_style = "Bald" + var/list/valid_hairstyles = list() + for(var/hairstyle in GLOB.hair_styles_public_list) + var/datum/sprite_accessory/S = GLOB.hair_styles_public_list[hairstyle] + + if(hairstyle == "Bald") //Just in case. + valid_hairstyles += hairstyle + continue + if((gender == MALE && S.gender == FEMALE) || (gender == FEMALE && S.gender == MALE)) + continue + if(species == "Machine") //If the user is a species who can have a robotic head... + if(!robohead) + robohead = GLOB.all_robolimbs["Morpheus Cyberkinetics"] + if((species in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. + valid_hairstyles += hairstyle //Give them their hairstyles if they do. + else + if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it. + But if the user has a robotic humanoid head and the hairstyle can fit humans, let them use it as a wig. */ + valid_hairstyles += hairstyle + else //If the user is not a species who can have robotic heads, use the default handling. + if(species in S.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list. + valid_hairstyles += hairstyle + + if(valid_hairstyles.len) + h_style = pick(valid_hairstyles) + + return h_style + +proc/random_facial_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) + var/f_style = "Shaved" + var/list/valid_facial_hairstyles = list() + for(var/facialhairstyle in GLOB.facial_hair_styles_list) + var/datum/sprite_accessory/S = GLOB.facial_hair_styles_list[facialhairstyle] + + if(facialhairstyle == "Shaved") //Just in case. + valid_facial_hairstyles += facialhairstyle + continue + if((gender == MALE && S.gender == FEMALE) || (gender == FEMALE && S.gender == MALE)) + continue + if(species == "Machine") //If the user is a species who can have a robotic head... + if(!robohead) + robohead = GLOB.all_robolimbs["Morpheus Cyberkinetics"] + if((species in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. + valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do. + else + if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the facial hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it. + But if the user has a robotic humanoid head and the facial hairstyle can fit humans, let them use it as a wig. */ + valid_facial_hairstyles += facialhairstyle + else //If the user is not a species who can have robotic heads, use the default handling. + if(species in S.species_allowed) //If the user's head is of a species the facial hair style allows, add it to the list. + valid_facial_hairstyles += facialhairstyle + + if(valid_facial_hairstyles.len) + f_style = pick(valid_facial_hairstyles) + + return f_style + +proc/random_head_accessory(species = "Human") + var/ha_style = "None" + var/list/valid_head_accessories = list() + for(var/head_accessory in GLOB.head_accessory_styles_list) + var/datum/sprite_accessory/S = GLOB.head_accessory_styles_list[head_accessory] + + if(!(species in S.species_allowed)) + continue + valid_head_accessories += head_accessory + + if(valid_head_accessories.len) + ha_style = pick(valid_head_accessories) + + return ha_style + +proc/random_marking_style(var/location = "body", species = "Human", var/datum/robolimb/robohead, var/body_accessory, var/alt_head) + var/m_style = "None" + var/list/valid_markings = list() + for(var/marking in GLOB.marking_styles_list) + var/datum/sprite_accessory/body_markings/S = GLOB.marking_styles_list[marking] + if(S.name == "None") + valid_markings += marking + continue + if(S.marking_location != location) //If the marking isn't for the location we desire, skip. + continue + if(!(species in S.species_allowed)) //If the user's head is not of a species the marking style allows, skip it. Otherwise, add it to the list. + continue + if(location == "tail") + if(!body_accessory) + if(S.tails_allowed) + continue + else + if(!S.tails_allowed || !(body_accessory in S.tails_allowed)) + continue + if(location == "head") + var/datum/sprite_accessory/body_markings/head/M = GLOB.marking_styles_list[S.name] + if(species == "Machine")//If the user is a species that can have a robotic head... + if(!robohead) + robohead = GLOB.all_robolimbs["Morpheus Cyberkinetics"] + if(!(S.models_allowed && (robohead.company in S.models_allowed))) //Make sure they don't get markings incompatible with their head. + continue + else if(alt_head && alt_head != "None") //If the user's got an alt head, validate markings for that head. + if(!("All" in M.heads_allowed) && !(alt_head in M.heads_allowed)) + continue + else + if(M.heads_allowed && !("All" in M.heads_allowed)) + continue + valid_markings += marking + + if(valid_markings.len) + m_style = pick(valid_markings) + + return m_style + +proc/random_body_accessory(species = "Vulpkanin") + var/body_accessory = null + var/list/valid_body_accessories = list() + for(var/B in GLOB.body_accessory_by_name) + var/datum/body_accessory/A = GLOB.body_accessory_by_name[B] + if(!istype(A)) + valid_body_accessories += "None" //The only null entry should be the "None" option. + continue + if(species in A.allowed_species) //If the user is not of a species the body accessory style allows, skip it. Otherwise, add it to the list. + valid_body_accessories += B + + if(valid_body_accessories.len) + body_accessory = pick(valid_body_accessories) + + return body_accessory + +proc/random_name(gender, species = "Human") + + var/datum/species/current_species + if(species) + current_species = GLOB.all_species[species] + + if(!current_species || current_species.name == "Human") + if(gender==FEMALE) + return capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names)) + else + return capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) + else + return current_species.get_random_name(gender) + +proc/random_skin_tone(species = "Human") + if(species == "Human" || species == "Drask") + switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) + if("caucasian") . = -10 + if("afroamerican") . = -115 + if("african") . = -165 + if("latino") . = -55 + if("albino") . = 34 + else . = rand(-185, 34) + return min(max(. + rand(-25, 25), -185), 34) + else if(species == "Vox") + . = rand(1, 6) + return . + +proc/skintone2racedescription(tone, species = "Human") + if(species == "Human") + switch(tone) + if(30 to INFINITY) return "albino" + if(20 to 30) return "pale" + if(5 to 15) return "light skinned" + if(-10 to 5) return "white" + if(-25 to -10) return "tan" + if(-45 to -25) return "darker skinned" + if(-65 to -45) return "brown" + if(-INFINITY to -65) return "black" + else return "unknown" + else if(species == "Vox") + switch(tone) + if(2) return "dark green" + if(3) return "brown" + if(4) return "gray" + if(5) return "emerald" + if(6) return "azure" + else return "green" + else + return "unknown" + +proc/age2agedescription(age) + switch(age) + if(0 to 1) return "infant" + if(1 to 3) return "toddler" + if(3 to 13) return "child" + if(13 to 19) return "teenager" + if(19 to 30) return "young adult" + if(30 to 45) return "adult" + if(45 to 60) return "middle-aged" + if(60 to 70) return "aging" + if(70 to INFINITY) return "elderly" + else return "unknown" + +/proc/set_criminal_status(mob/living/user, datum/data/record/target_records , criminal_status, comment, user_rank, list/authcard_access = list()) + var/status = criminal_status + var/their_name = target_records.fields["name"] + var/their_rank = target_records.fields["rank"] + switch(criminal_status) + if("arrest") + status = SEC_RECORD_STATUS_ARREST + if("none") + status = SEC_RECORD_STATUS_NONE + if("execute") + if((ACCESS_MAGISTRATE in authcard_access) || (ACCESS_ARMORY in authcard_access)) + status = SEC_RECORD_STATUS_EXECUTE + message_admins("[ADMIN_FULLMONTY(usr)] authorized EXECUTION for [their_rank] [their_name], with comment: [comment]") + else + return 0 + if("search") + status = SEC_RECORD_STATUS_SEARCH + if("monitor") + status = SEC_RECORD_STATUS_MONITOR + if ("demote") + status = SEC_RECORD_STATUS_DEMOTE + if("incarcerated") + status = SEC_RECORD_STATUS_INCARCERATED + if("parolled") + status = SEC_RECORD_STATUS_PAROLLED + if("released") + status = SEC_RECORD_STATUS_RELEASED + target_records.fields["criminal"] = status + log_admin("[key_name_admin(user)] set secstatus of [their_rank] [their_name] to [status], comment: [comment]") + target_records.fields["comments"] += "Set to [status] by [user.name] ([user_rank]) on [GLOB.current_date_string] [station_time_timestamp()], comment: [comment]" + update_all_mob_security_hud() + return 1 + +/* +Proc for attack log creation, because really why not +1 argument is the actor +2 argument is the target of action +3 is the full description of the action +4 is whether or not to message admins +This is always put in the attack log. +*/ + +/proc/add_attack_logs(atom/user, target, what_done, custom_level) + if(islist(target)) // Multi-victim adding + var/list/targets = target + for(var/t in targets) + add_attack_logs(user, t, what_done, custom_level) + return + + var/user_str = key_name_log(user) + COORD(user) + var/target_str + if(isatom(target)) + var/atom/AT = target + target_str = key_name_log(AT) + COORD(AT) + else + target_str = target + var/mob/MU = user + var/mob/MT = target + if(istype(MU)) + MU.create_log(ATTACK_LOG, what_done, target, get_turf(user)) + MU.create_attack_log("Attacked [target_str]: [what_done]") + if(istype(MT)) + MT.create_log(DEFENSE_LOG, what_done, user, get_turf(MT)) + MT.create_attack_log("Attacked by [user_str]: [what_done]") + log_attack(user_str, target_str, what_done) + + var/loglevel = ATKLOG_MOST + if(!isnull(custom_level)) + loglevel = custom_level + else if(istype(MT)) + if(istype(MU)) + if(!MU.ckey && !MT.ckey) // Attacks between NPCs are only shown to admins with ATKLOG_ALL + loglevel = ATKLOG_ALL + else if(!MU.ckey || !MT.ckey || (MU.ckey == MT.ckey)) // Player v NPC combat is de-prioritized. Also no self-harm, nobody cares + loglevel = ATKLOG_ALMOSTALL + else + var/area/A = get_area(MT) + if(A && A.hide_attacklogs) + loglevel = ATKLOG_ALMOSTALL + if(isLivingSSD(target)) // Attacks on SSDs are shown to admins with any log level except ATKLOG_NONE. Overrides custom level + loglevel = ATKLOG_FEW + + msg_admin_attack("[key_name_admin(user)] vs [key_name_admin(target)]: [what_done]", loglevel) + +/proc/do_mob(mob/user, mob/target, time = 30, uninterruptible = 0, progress = 1, list/extra_checks = list()) + if(!user || !target) + return 0 + var/user_loc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/target_loc = target.loc + + var/holding = user.get_active_hand() + var/datum/progressbar/progbar + if(progress) + progbar = new(user, time, target) + + var/endtime = world.time+time + var/starttime = world.time + . = 1 + while(world.time < endtime) + sleep(1) + if(progress) + progbar.update(world.time - starttime) + if(!user || !target) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = 0 + user_loc = user.loc + + if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_hand() != holding || user.incapacitated() || user.lying || check_for_true_callbacks(extra_checks)) + . = 0 + break + if(progress) + qdel(progbar) + +/* Use this proc when you want to have code under it execute after a delay, and ensure certain conditions are met during that delay... + * Such as the user not being interrupted via getting stunned or by moving off the tile they're currently on. + * + * Example usage: + * + * if(do_after(user, 50, target = sometarget, extra_checks = list(callback_check1, callback_check2))) + * do_stuff() + * + * This will create progress bar that lasts for 5 seconds. If the user doesn't move or otherwise do something that would cause the checks to fail in those 5 seconds, do_stuff() would execute. + * The Proc returns TRUE upon success (the progress bar reached the end), or FALSE upon failure (the user moved or some other check failed) + */ +/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, list/extra_checks = list(), use_default_checks = TRUE) + if(!user) + return FALSE + var/atom/Tloc = null + if(target) + Tloc = target.loc + + var/atom/Uloc = user.loc + + var/drifting = FALSE + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = TRUE + + var/holding = user.get_active_hand() + + var/holdingnull = TRUE //User's hand started out empty, check for an empty hand + if(holding) + holdingnull = FALSE //Users hand started holding something, check to see if it's still holding that + + var/datum/progressbar/progbar + if(progress) + progbar = new(user, delay, target) + + var/endtime = world.time + delay + var/starttime = world.time + . = TRUE + + // By default, checks for weakness and stunned get added to the extra_checks list. + // Setting `use_default_checks` to FALSE means that you don't want the do_after to check for these statuses, or that you will be supplying your own checks. + if(use_default_checks) + extra_checks += CALLBACK(user, /mob.proc/IsWeakened) + extra_checks += CALLBACK(user, /mob.proc/IsStunned) + + while(world.time < endtime) + sleep(1) + if(progress) + progbar.update(world.time - starttime) + + if(drifting && !user.inertia_dir) + drifting = FALSE + Uloc = user.loc + + if(!user || user.stat || (!drifting && user.loc != Uloc) || check_for_true_callbacks(extra_checks)) + . = FALSE + break + + if(Tloc && (!target || Tloc != target.loc)) + . = FALSE + break + + if(needhand) + //This might seem like an odd check, but you can still need a hand even when it's empty + //i.e the hand is used to pull some item/tool out of the construction + if(!holdingnull) + if(!holding) + . = FALSE + break + if(user.get_active_hand() != holding) + . = FALSE + break + if(progress) + qdel(progbar) + +// Upon any of the callbacks in the list returning TRUE, the proc will return TRUE. +/proc/check_for_true_callbacks(list/extra_checks) + for(var/datum/callback/CB in extra_checks) + if(CB.Invoke()) + return TRUE + return FALSE + +#define DOAFTERONCE_MAGIC "Magic~~" +GLOBAL_LIST_INIT(do_after_once_tracker, list()) +/proc/do_after_once(mob/user, delay, needhand = 1, atom/target = null, progress = 1, attempt_cancel_message = "Attempt cancelled.") + if(!user || !target) + return + + var/cache_key = "[user.UID()][target.UID()]" + if(GLOB.do_after_once_tracker[cache_key]) + GLOB.do_after_once_tracker[cache_key] = DOAFTERONCE_MAGIC + to_chat(user, "[attempt_cancel_message]") + return FALSE + GLOB.do_after_once_tracker[cache_key] = TRUE + . = do_after(user, delay, needhand, target, progress, extra_checks = list(CALLBACK(GLOBAL_PROC, .proc/do_after_once_checks, cache_key))) + GLOB.do_after_once_tracker[cache_key] = FALSE + +/proc/do_after_once_checks(cache_key) + if(GLOB.do_after_once_tracker[cache_key] && GLOB.do_after_once_tracker[cache_key] == DOAFTERONCE_MAGIC) + GLOB.do_after_once_tracker[cache_key] = FALSE + return TRUE + return FALSE + +/proc/is_species(A, species_datum) + . = FALSE + if(ishuman(A)) + var/mob/living/carbon/human/H = A + if(H.dna && istype(H.dna.species, species_datum)) + . = TRUE + +/proc/spawn_atom_to_turf(spawn_type, target, amount, admin_spawn=FALSE, list/extra_args) + var/turf/T = get_turf(target) + if(!T) + CRASH("attempt to spawn atom type: [spawn_type] in nullspace") + + var/list/new_args = list(T) + if(extra_args) + new_args += extra_args + + for(var/j in 1 to amount) + var/atom/X = new spawn_type(arglist(new_args)) + X.admin_spawned = admin_spawn + +/proc/admin_mob_info(mob/M, mob/user = usr) + if(!ismob(M)) + to_chat(user, "This can only be used on instances of type /mob") + return + + var/location_description = "" + var/special_role_description = "" + var/health_description = "" + var/gender_description = "" + var/turf/T = get_turf(M) + + //Location + if(isturf(T)) + if(isarea(T.loc)) + location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])" + else + location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])" + + //Job + antagonist + if(M.mind) + special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [M.mind.special_role]; Has been rev: [(M.mind.has_been_rev)?"Yes":"No"]" + else + special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing; Has been rev: Mind datum missing;" + + //Health + if(isliving(M)) + var/mob/living/L = M + var/status + switch(M.stat) + if(CONSCIOUS) + status = "Alive" + if(UNCONSCIOUS) + status = "Unconscious" + if(DEAD) + status = "Dead" + health_description = "Status = [status]" + health_description += "
Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getBrainLoss()]" + else + health_description = "This mob type has no health to speak of." + + //Gener + switch(M.gender) + if(MALE, FEMALE) + gender_description = "[M.gender]" + else + gender_description = "[M.gender]" + + to_chat(user, "Info about [M.name]: ") + to_chat(user, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]") + to_chat(user, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];") + to_chat(user, "Location = [location_description];") + to_chat(user, "[special_role_description]") + to_chat(user, "(PM) ([ADMIN_PP(M,"PP")]) ([ADMIN_VV(M,"VV")]) ([ADMIN_TP(M,"TP")]) ([ADMIN_SM(M,"SM")]) ([ADMIN_FLW(M,"FLW")])") + +// Gets the first mob contained in an atom, and warns the user if there's not exactly one +/proc/get_mob_in_atom_with_warning(atom/A, mob/user = usr) + if(!istype(A)) + return null + if(ismob(A)) + return A + + . = null + for(var/mob/M in A) + if(!.) + . = M + else + to_chat(user, "Multiple mobs in [A], using first mob found...") + break + if(!.) + to_chat(user, "No mob located in [A].") + +// Suppress the mouse macros +/client/var/next_mouse_macro_warning +/mob/proc/LogMouseMacro(verbused, params) + if(!client) + return + if(!client.next_mouse_macro_warning) // Log once + log_admin("[key_name(usr)] attempted to use a mouse macro: [verbused] [params]") + message_admins("[key_name_admin(usr)] attempted to use a mouse macro: [verbused] [html_encode(params)]") + if(client.next_mouse_macro_warning < world.time) // Warn occasionally + usr << 'sound/misc/sadtrombone.ogg' + client.next_mouse_macro_warning = world.time + 600 +/mob/verb/ClickSubstitute(params as command_text) + set hidden = 1 + set name = ".click" + LogMouseMacro(".click", params) +/mob/verb/DblClickSubstitute(params as command_text) + set hidden = 1 + set name = ".dblclick" + LogMouseMacro(".dblclick", params) +/mob/verb/MouseSubstitute(params as command_text) + set hidden = 1 + set name = ".mouse" + LogMouseMacro(".mouse", params) + +/proc/update_all_mob_security_hud() + for(var/mob/living/carbon/human/H in GLOB.mob_list) + H.sec_hud_set_security_status() + +/proc/getviewsize(view) + var/viewX + var/viewY + if(isnum(view)) + var/totalviewrange = 1 + 2 * view + viewX = totalviewrange + viewY = totalviewrange + else + var/list/viewrangelist = splittext(view, "x") + viewX = text2num(viewrangelist[1]) + viewY = text2num(viewrangelist[2]) + return list(viewX, viewY) + +//Used in chemical_mob_spawn. Generates a random mob based on a given gold_core_spawnable value. +/proc/create_random_mob(spawn_location, mob_class = HOSTILE_SPAWN) + var/static/list/mob_spawn_meancritters = list() // list of possible hostile mobs + var/static/list/mob_spawn_nicecritters = list() // and possible friendly mobs + + if(mob_spawn_meancritters.len <= 0 || mob_spawn_nicecritters.len <= 0) + for(var/T in typesof(/mob/living/simple_animal)) + var/mob/living/simple_animal/SA = T + switch(initial(SA.gold_core_spawnable)) + if(HOSTILE_SPAWN) + mob_spawn_meancritters += T + if(FRIENDLY_SPAWN) + mob_spawn_nicecritters += T + + var/chosen + if(mob_class == FRIENDLY_SPAWN) + chosen = pick(mob_spawn_nicecritters) + else + chosen = pick(mob_spawn_meancritters) + var/mob/living/simple_animal/C = new chosen(spawn_location) + return C diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index d5a1ad8cbfd..29e0f9b52b9 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -1,282 +1,282 @@ -var/church_name = null -/proc/church_name() - if(church_name) - return church_name - - var/name = "" - - name += pick("Holy", "United", "First", "Second", "Last") - - if(prob(20)) - name += " Space" - - name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses") - name += " of [religion_name()]" - - return name - -var/command_name = null -/proc/command_name() - return using_map.dock_name - -var/religion_name = null -/proc/religion_name() - if(religion_name) - return religion_name - - var/name = "" - - name += pick("bee", "science", "edu", "captain", "civilian", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob") - name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity") - - return capitalize(name) - -/proc/system_name() - return using_map.starsys_name - -/proc/station_name() - return using_map.station_name - -/proc/new_station_name() - var/random = rand(1,5) - var/name = "" - var/new_station_name = "" - - //Rare: Pre-Prefix - if(prob(10)) - name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented") - new_station_name = name + " " - name = "" - - // Prefix - for(var/holiday_name in SSholiday.holidays) - if(holiday_name == "Friday the 13th") - random = 13 - var/datum/holiday/holiday = SSholiday.holidays[holiday_name] - name = holiday.getStationPrefix() - //get normal name - if(!name) - name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza") - if(name) - new_station_name += name + " " - - // Suffix - name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp") - new_station_name += name + " " - - // ID Number - switch(random) - if(1) - new_station_name += "[rand(1, 99)]" - if(2) - new_station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega") - if(3) - new_station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX") - if(4) - new_station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") - if(5) - new_station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen") - if(13) - new_station_name += pick("13","XIII","Thirteen") - return new_station_name - -var/syndicate_name = null -/proc/syndicate_name() - if(syndicate_name) - return syndicate_name - - var/name = "" - - // Prefix - name += pick("Clandestine", "Prima", "Blue", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Bonk", "Gene", "Gib") - - // Suffix - if(prob(80)) - name += " " - - // Full - if(prob(60)) - name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive") - // Broken - else - name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive") - name += pick("", "-") - name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Code") - // Small - else - name += pick("-", "*", "") - name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Gen", "Star", "Dyne", "Code", "Hive") - - syndicate_name = name - return name - - -//Traitors and traitor silicons will get these. Revs will not. -GLOBAL_VAR(syndicate_code_phrase) //Code phrase for traitors. -GLOBAL_VAR(syndicate_code_response) //Code response for traitors. - - /* - Should be expanded. - How this works: - Instead of "I'm looking for James Smith," the traitor would say "James Smith" as part of a conversation. - Another traitor may then respond with: "They enjoy running through the void-filled vacuum of the derelict." - The phrase should then have the words: James Smith. - The response should then have the words: run, void, and derelict. - This way assures that the code is suited to the conversation and is unpredicatable. - Obviously, some people will be better at this than others but in theory, everyone should be able to do it and it only enhances roleplay. - Can probably be done through "{ }" but I don't really see the practical benefit. - One example of an earlier system is commented below. - /N - */ - -/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm - - var/code_phrase = ""//What is returned when the proc finishes. - var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely. - 50; 2, - 200; 3, - 50; 4, - 25; 5 - ) - - var/safety[] = list(1,2,3)//Tells the proc which options to remove later on. - var/nouns[] = list("love","hate","anger","peace","pride","sympathy","bravery","loyalty","honesty","integrity","compassion","charity","success","courage","deceit","skill","beauty","brilliance","pain","misery","beliefs","dreams","justice","truth","faith","liberty","knowledge","thought","information","culture","trust","dedication","progress","education","hospitality","leisure","trouble","friendships", "relaxation") - var/drinks[] = list("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequila sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","wine","moonshine") - var/locations[] = teleportlocs.len ? teleportlocs : drinks//if null, defaults to drinks instead. - - var/names[] = list() - for(var/datum/data/record/t in data_core.general)//Picks from crew manifest. - names += t.fields["name"] - - var/maxwords = words//Extra var to check for duplicates. - - for(words,words>0,words--)//Randomly picks from one of the choices below. - - if(words==1&&(1 in safety)&&(2 in safety))//If there is only one word remaining and choice 1 or 2 have not been selected. - safety = list(pick(1,2))//Select choice 1 or 2. - else if(words==1&&maxwords==2)//Else if there is only one word remaining (and there were two originally), and 1 or 2 were chosen, - safety = list(3)//Default to list 3 - - switch(pick(safety))//Chance based on the safety list. - if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc. - switch(rand(1,2))//Mainly to add more options later. - if(1) - if(names.len) - code_phrase += pick(names) - if(2) - code_phrase += pick(GLOB.joblist)//Returns a job. - safety -= 1 - if(2) - switch(rand(1,2))//Places or things. - if(1) - code_phrase += pick(drinks) - if(2) - code_phrase += pick(locations) - safety -= 2 - if(3) - switch(rand(1,3))//Nouns, adjectives, verbs. Can be selected more than once. - if(1) - code_phrase += pick(nouns) - if(2) - code_phrase += pick(GLOB.adjectives) - if(3) - code_phrase += pick(GLOB.verbs) - if(words==1) - code_phrase += "." - else - code_phrase += ", " - - return code_phrase - -/proc/GenerateKey() - var/newKey - newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le") - newKey += pick("diamond", "beer", "mushroom", "civilian", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") - newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") - return newKey - -/* -//This proc tests the gen above. -/client/verb/test_code_phrase() - set name = "Generate Code Phrase" - set category = "Debug" - - to_chat(world, "Code Phrase is: [generate_code_phrase()]") - return - - - This was an earlier attempt at code phrase system, aside from an even earlier attempt (and failure). - This system more or less works as intended--aside from being unfinished--but it's still very predictable. - Particularly, the phrase opening statements are pretty easy to recognize and identify when metagaming. - I think the above-used method solves this issue by using words in a sequence, providing for much greater flexibility. - /N - - switch(choice) - if(1) - syndicate_code_phrase += pick("I'm looking for","Have you seen","Maybe you've seen","I'm trying to find","I'm tracking") - syndicate_code_phrase += " " - syndicate_code_phrase += pick(pick(GLOB.first_names_male,GLOB.first_names_female)) - syndicate_code_phrase += " " - syndicate_code_phrase += pick(GLOB.last_names) - syndicate_code_phrase += "." - if(2) - syndicate_code_phrase += pick("How do I get to","How do I find","Where is","Where do I find") - syndicate_code_phrase += " " - syndicate_code_phrase += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentComm","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict") - syndicate_code_phrase += "?" - if(3) - if(prob(70)) - syndicate_code_phrase += pick("Get me","I want","I'd like","Make me") - syndicate_code_phrase += " a " - else - syndicate_code_phrase += pick("One") - syndicate_code_phrase += " " - syndicate_code_phrase += pick("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequila sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","wine","moonshine") - syndicate_code_phrase += "." - if(4) - syndicate_code_phrase += pick("I wish I was","My dad was","His mom was","Where do I find","The hero this station needs is","I'd fuck","I wouldn't trust","Someone caught","HoS caught","Someone found","I'd wrestle","I wanna kill") - syndicate_code_phrase += " [pick("a","the")] " - syndicate_code_phrase += pick("wizard","ninja","xeno","lizard","slime","monkey","syndicate","cyborg","clown","space carp","singularity","singulo","mime") - syndicate_code_phrase += "." - if(5) - syndicate_code_phrase += pick("Do we have","Is there","Where is","Where's","Who's") - syndicate_code_phrase += " " - syndicate_code_phrase += "[pick(GLOB.joblist)]" - syndicate_code_phrase += "?" - - switch(choice) - if(1) - if(prob(80)) - syndicate_code_response += pick("Try looking for them near","I they ran off to","Yes. I saw them near","Nope. I'm heading to","Try searching") - syndicate_code_response += " " - syndicate_code_response += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentComm","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict") - syndicate_code_response += "." - else if(prob(60)) - syndicate_code_response += pick("No. I'm busy, sorry.","I don't have the time.","Not sure, maybe?","There is no time.") - else - syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*") - if(2) - if(prob(80)) - syndicate_code_response += pick("Go to","Navigate to","Try","Sure, run to","Try searching","It's near","It's around") - syndicate_code_response += " the " - syndicate_code_response += pick("[pick("south","north","east","west")] maitenance door","nearby maitenance","teleporter","[pick("cold","dead")] space","morgue","vacuum","[pick("south","north","east","west")] hall ","[pick("south","north","east","west")] hallway","[pick("white","black","red","green","blue","pink","purple")] [pick("rabbit","frog","lion","tiger","panther","snake","facehugger")]") - syndicate_code_response += "." - else if(prob(60)) - syndicate_code_response += pick("Try asking","Ask","Talk to","Go see","Follow","Hunt down") - syndicate_code_response += " " - if(prob(50)) - syndicate_code_response += pick(pick(GLOB.first_names_male,GLOB.first_names_female)) - syndicate_code_response += " " - syndicate_code_response += pick(GLOB.last_names) - else - syndicate_code_response += " the " - syndicate_code_response += "[pic(GLOB.joblist)]" - syndicate_code_response += "." - else - syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*") - if(3) - if(4) - if(5) - - return -*/ +GLOBAL_VAR(church_name) +/proc/church_name() + if(GLOB.church_name) + return GLOB.church_name + + var/name = "" + + name += pick("Holy", "United", "First", "Second", "Last") + + if(prob(20)) + name += " Space" + + name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses") + name += " of [religion_name()]" + + return name + +GLOBAL_VAR(command_name) +/proc/command_name() + return GLOB.using_map.dock_name + +GLOBAL_VAR(religion_name) +/proc/religion_name() + if(GLOB.religion_name) + return GLOB.religion_name + + var/name = "" + + name += pick("bee", "science", "edu", "captain", "civilian", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob") + name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity") + + return capitalize(name) + +/proc/system_name() + return GLOB.using_map.starsys_name + +/proc/station_name() + return GLOB.using_map.station_name + +/proc/new_station_name() + var/random = rand(1,5) + var/name = "" + var/new_station_name = "" + + //Rare: Pre-Prefix + if(prob(10)) + name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented") + new_station_name = name + " " + name = "" + + // Prefix + for(var/holiday_name in SSholiday.holidays) + if(holiday_name == "Friday the 13th") + random = 13 + var/datum/holiday/holiday = SSholiday.holidays[holiday_name] + name = holiday.getStationPrefix() + //get normal name + if(!name) + name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza") + if(name) + new_station_name += name + " " + + // Suffix + name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp") + new_station_name += name + " " + + // ID Number + switch(random) + if(1) + new_station_name += "[rand(1, 99)]" + if(2) + new_station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega") + if(3) + new_station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX") + if(4) + new_station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") + if(5) + new_station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen") + if(13) + new_station_name += pick("13","XIII","Thirteen") + return new_station_name + +GLOBAL_VAR(syndicate_name) +/proc/syndicate_name() + if(GLOB.syndicate_name) + return GLOB.syndicate_name + + var/name = "" + + // Prefix + name += pick("Clandestine", "Prima", "Blue", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Bonk", "Gene", "Gib") + + // Suffix + if(prob(80)) + name += " " + + // Full + if(prob(60)) + name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive") + // Broken + else + name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive") + name += pick("", "-") + name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Code") + // Small + else + name += pick("-", "*", "") + name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Gen", "Star", "Dyne", "Code", "Hive") + + GLOB.syndicate_name = name + return name + + +//Traitors and traitor silicons will get these. Revs will not. +GLOBAL_VAR(syndicate_code_phrase) //Code phrase for traitors. +GLOBAL_VAR(syndicate_code_response) //Code response for traitors. + + /* + Should be expanded. + How this works: + Instead of "I'm looking for James Smith," the traitor would say "James Smith" as part of a conversation. + Another traitor may then respond with: "They enjoy running through the void-filled vacuum of the derelict." + The phrase should then have the words: James Smith. + The response should then have the words: run, void, and derelict. + This way assures that the code is suited to the conversation and is unpredicatable. + Obviously, some people will be better at this than others but in theory, everyone should be able to do it and it only enhances roleplay. + Can probably be done through "{ }" but I don't really see the practical benefit. + One example of an earlier system is commented below. + /N + */ + +/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm + + var/code_phrase = ""//What is returned when the proc finishes. + var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely. + 50; 2, + 200; 3, + 50; 4, + 25; 5 + ) + + var/safety[] = list(1,2,3)//Tells the proc which options to remove later on. + var/nouns[] = list("love","hate","anger","peace","pride","sympathy","bravery","loyalty","honesty","integrity","compassion","charity","success","courage","deceit","skill","beauty","brilliance","pain","misery","beliefs","dreams","justice","truth","faith","liberty","knowledge","thought","information","culture","trust","dedication","progress","education","hospitality","leisure","trouble","friendships", "relaxation") + var/drinks[] = list("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequila sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","wine","moonshine") + var/locations[] = GLOB.teleportlocs.len ? GLOB.teleportlocs : drinks//if null, defaults to drinks instead. + + var/names[] = list() + for(var/datum/data/record/t in GLOB.data_core.general)//Picks from crew manifest. + names += t.fields["name"] + + var/maxwords = words//Extra var to check for duplicates. + + for(words,words>0,words--)//Randomly picks from one of the choices below. + + if(words==1&&(1 in safety)&&(2 in safety))//If there is only one word remaining and choice 1 or 2 have not been selected. + safety = list(pick(1,2))//Select choice 1 or 2. + else if(words==1&&maxwords==2)//Else if there is only one word remaining (and there were two originally), and 1 or 2 were chosen, + safety = list(3)//Default to list 3 + + switch(pick(safety))//Chance based on the safety list. + if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc. + switch(rand(1,2))//Mainly to add more options later. + if(1) + if(names.len) + code_phrase += pick(names) + if(2) + code_phrase += pick(GLOB.joblist)//Returns a job. + safety -= 1 + if(2) + switch(rand(1,2))//Places or things. + if(1) + code_phrase += pick(drinks) + if(2) + code_phrase += pick(locations) + safety -= 2 + if(3) + switch(rand(1,3))//Nouns, adjectives, verbs. Can be selected more than once. + if(1) + code_phrase += pick(nouns) + if(2) + code_phrase += pick(GLOB.adjectives) + if(3) + code_phrase += pick(GLOB.verbs) + if(words==1) + code_phrase += "." + else + code_phrase += ", " + + return code_phrase + +/proc/GenerateKey() + var/newKey + newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le") + newKey += pick("diamond", "beer", "mushroom", "civilian", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") + newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") + return newKey + +/* +//This proc tests the gen above. +/client/verb/test_code_phrase() + set name = "Generate Code Phrase" + set category = "Debug" + + to_chat(world, "Code Phrase is: [generate_code_phrase()]") + return + + + This was an earlier attempt at code phrase system, aside from an even earlier attempt (and failure). + This system more or less works as intended--aside from being unfinished--but it's still very predictable. + Particularly, the phrase opening statements are pretty easy to recognize and identify when metagaming. + I think the above-used method solves this issue by using words in a sequence, providing for much greater flexibility. + /N + + switch(choice) + if(1) + syndicate_code_phrase += pick("I'm looking for","Have you seen","Maybe you've seen","I'm trying to find","I'm tracking") + syndicate_code_phrase += " " + syndicate_code_phrase += pick(pick(GLOB.first_names_male,GLOB.first_names_female)) + syndicate_code_phrase += " " + syndicate_code_phrase += pick(GLOB.last_names) + syndicate_code_phrase += "." + if(2) + syndicate_code_phrase += pick("How do I get to","How do I find","Where is","Where do I find") + syndicate_code_phrase += " " + syndicate_code_phrase += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentComm","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict") + syndicate_code_phrase += "?" + if(3) + if(prob(70)) + syndicate_code_phrase += pick("Get me","I want","I'd like","Make me") + syndicate_code_phrase += " a " + else + syndicate_code_phrase += pick("One") + syndicate_code_phrase += " " + syndicate_code_phrase += pick("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequila sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","wine","moonshine") + syndicate_code_phrase += "." + if(4) + syndicate_code_phrase += pick("I wish I was","My dad was","His mom was","Where do I find","The hero this station needs is","I'd fuck","I wouldn't trust","Someone caught","HoS caught","Someone found","I'd wrestle","I wanna kill") + syndicate_code_phrase += " [pick("a","the")] " + syndicate_code_phrase += pick("wizard","ninja","xeno","lizard","slime","monkey","syndicate","cyborg","clown","space carp","singularity","singulo","mime") + syndicate_code_phrase += "." + if(5) + syndicate_code_phrase += pick("Do we have","Is there","Where is","Where's","Who's") + syndicate_code_phrase += " " + syndicate_code_phrase += "[pick(GLOB.joblist)]" + syndicate_code_phrase += "?" + + switch(choice) + if(1) + if(prob(80)) + syndicate_code_response += pick("Try looking for them near","I they ran off to","Yes. I saw them near","Nope. I'm heading to","Try searching") + syndicate_code_response += " " + syndicate_code_response += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentComm","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict") + syndicate_code_response += "." + else if(prob(60)) + syndicate_code_response += pick("No. I'm busy, sorry.","I don't have the time.","Not sure, maybe?","There is no time.") + else + syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*") + if(2) + if(prob(80)) + syndicate_code_response += pick("Go to","Navigate to","Try","Sure, run to","Try searching","It's near","It's around") + syndicate_code_response += " the " + syndicate_code_response += pick("[pick("south","north","east","west")] maitenance door","nearby maitenance","teleporter","[pick("cold","dead")] space","morgue","vacuum","[pick("south","north","east","west")] hall ","[pick("south","north","east","west")] hallway","[pick("white","black","red","green","blue","pink","purple")] [pick("rabbit","frog","lion","tiger","panther","snake","facehugger")]") + syndicate_code_response += "." + else if(prob(60)) + syndicate_code_response += pick("Try asking","Ask","Talk to","Go see","Follow","Hunt down") + syndicate_code_response += " " + if(prob(50)) + syndicate_code_response += pick(pick(GLOB.first_names_male,GLOB.first_names_female)) + syndicate_code_response += " " + syndicate_code_response += pick(GLOB.last_names) + else + syndicate_code_response += " the " + syndicate_code_response += "[pic(GLOB.joblist)]" + syndicate_code_response += "." + else + syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*") + if(3) + if(4) + if(5) + + return +*/ diff --git a/code/__HELPERS/qdel.dm b/code/__HELPERS/qdel.dm index 136e573a25e..020077a4e5c 100644 --- a/code/__HELPERS/qdel.dm +++ b/code/__HELPERS/qdel.dm @@ -7,4 +7,4 @@ #define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); } /proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly. - QDEL_LIST(L) \ No newline at end of file + QDEL_LIST(L) diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm index 6777f43ff91..34e4c96da6a 100644 --- a/code/__HELPERS/sanitize_values.dm +++ b/code/__HELPERS/sanitize_values.dm @@ -1,52 +1,52 @@ -//general stuff -/proc/sanitize_integer(number, min=0, max=1, default=0) - if(isnum(number)) - number = round(number) - if(min <= number && number <= max) - return number - return default - -/proc/sanitize_text(text, default="") - if(istext(text)) - return text - return default - -/proc/sanitize_inlist(value, list/List, default) - if(value in List) return value - if(default) return default - if(List && List.len)return pick(List) - - - -//more specialised stuff -/proc/sanitize_gender(gender,neuter=0,plural=0, default="male") - switch(gender) - if(MALE, FEMALE)return gender - if(NEUTER) - if(neuter) return gender - else return default - if(PLURAL) - if(plural) return gender - else return default - return default - -/proc/sanitize_hexcolor(color, default="#000000") - if(!istext(color)) return default - var/len = length(color) - if(len != 7 && len !=4) return default - if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#" - . = "#" - for(var/i=2,i<=len,i++) - var/ascii = text2ascii(color,i) - switch(ascii) - if(48 to 57) . += ascii2text(ascii) //numbers 0 to 9 - if(97 to 102) . += ascii2text(ascii) //letters a to f - if(65 to 70) . += ascii2text(ascii+32) //letters A to F - translates to lowercase - else return default - return . - -/proc/sanitize_ooccolor(color) - var/list/HSL = rgb2hsl(hex2num(copytext(color,2,4)),hex2num(copytext(color,4,6)),hex2num(copytext(color,6,8))) - HSL[3] = min(HSL[3],0.4) - var/list/RGB = hsl2rgb(arglist(HSL)) - return "#[num2hex(RGB[1],2)][num2hex(RGB[2],2)][num2hex(RGB[3],2)]" +//general stuff +/proc/sanitize_integer(number, min=0, max=1, default=0) + if(isnum(number)) + number = round(number) + if(min <= number && number <= max) + return number + return default + +/proc/sanitize_text(text, default="") + if(istext(text)) + return text + return default + +/proc/sanitize_inlist(value, list/List, default) + if(value in List) return value + if(default) return default + if(List && List.len)return pick(List) + + + +//more specialised stuff +/proc/sanitize_gender(gender,neuter=0,plural=0, default="male") + switch(gender) + if(MALE, FEMALE)return gender + if(NEUTER) + if(neuter) return gender + else return default + if(PLURAL) + if(plural) return gender + else return default + return default + +/proc/sanitize_hexcolor(color, default="#000000") + if(!istext(color)) return default + var/len = length(color) + if(len != 7 && len !=4) return default + if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#" + . = "#" + for(var/i=2,i<=len,i++) + var/ascii = text2ascii(color,i) + switch(ascii) + if(48 to 57) . += ascii2text(ascii) //numbers 0 to 9 + if(97 to 102) . += ascii2text(ascii) //letters a to f + if(65 to 70) . += ascii2text(ascii+32) //letters A to F - translates to lowercase + else return default + return . + +/proc/sanitize_ooccolor(color) + var/list/HSL = rgb2hsl(hex2num(copytext(color,2,4)),hex2num(copytext(color,4,6)),hex2num(copytext(color,6,8))) + HSL[3] = min(HSL[3],0.4) + var/list/RGB = hsl2rgb(arglist(HSL)) + return "#[num2hex(RGB[1],2)][num2hex(RGB[2],2)][num2hex(RGB[3],2)]" diff --git a/code/__HELPERS/sorts/InsertSort.dm b/code/__HELPERS/sorts/InsertSort.dm index decc1c29ff2..641cf120822 100644 --- a/code/__HELPERS/sorts/InsertSort.dm +++ b/code/__HELPERS/sorts/InsertSort.dm @@ -16,4 +16,4 @@ SI.associative = associative SI.binarySort(fromIndex, toIndex, fromIndex) - return L \ No newline at end of file + return L diff --git a/code/__HELPERS/sorts/MergeSort.dm b/code/__HELPERS/sorts/MergeSort.dm index d8c3d1477d0..811017f017f 100644 --- a/code/__HELPERS/sorts/MergeSort.dm +++ b/code/__HELPERS/sorts/MergeSort.dm @@ -16,4 +16,4 @@ SI.associative = associative SI.mergeSort(fromIndex, toIndex) - return L \ No newline at end of file + return L diff --git a/code/__HELPERS/sorts/TimSort.dm b/code/__HELPERS/sorts/TimSort.dm index b3aca97ea99..03063f683a6 100644 --- a/code/__HELPERS/sorts/TimSort.dm +++ b/code/__HELPERS/sorts/TimSort.dm @@ -17,4 +17,4 @@ SI.associative = associative SI.timSort(fromIndex, toIndex) - return L \ No newline at end of file + return L diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index db41b411d12..ecbebf813df 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -1,619 +1,619 @@ -/* - * Holds procs designed to help with filtering text - * Contains groups: - * SQL sanitization - * Text sanitization - * Text searches - * Text modification - * Misc - */ - - -/* - * SQL sanitization - */ - -// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. -/proc/sanitizeSQL(var/t as text) - if(isnull(t)) - return null - if(!istext(t)) - t = "[t]" // Just quietly assume any non-texts are supposed to be text - var/sqltext = dbcon.Quote(t); - return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that - -/proc/format_table_name(table as text) - return sqlfdbktableprefix + table - -/* - * Text sanitization - */ -// Can be used almost the same way as normal input for text -/proc/clean_input(Message, Title, Default, mob/user=usr) - var/txt = input(user, Message, Title, Default) as text | null - if(txt) - return html_encode(txt) - -//Simply removes < and > and limits the length of the message -/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) - var/list/strip_chars = list("<",">") - t = copytext(t,1,limit) - for(var/char in strip_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + copytext(t, index+1) - index = findtext(t, char) - return t - -//Removes a few problematic characters -/proc/sanitize_simple(var/t,var/list/repl_chars = list("\n"="#","\t"="#")) - for(var/char in repl_chars) - t = replacetext(t, char, repl_chars[char]) - return t - -/proc/readd_quotes(var/t) - var/list/repl_chars = list(""" = "\"") - for(var/char in repl_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5) - index = findtext(t, char) - return t - -//Runs byond's sanitization proc along-side sanitize_simple -/proc/sanitize(var/t,var/list/repl_chars = null) - return html_encode(sanitize_simple(t,repl_chars)) - -// Gut ANYTHING that isnt alphanumeric, or brackets -/proc/paranoid_sanitize(t) - var/regex/alphanum_only = regex("\[^a-zA-Z0-9# ,.?!:;()]", "g") - return alphanum_only.Replace(t, "#") - -// Less agressive, to allow discord features, such as <>, / and @ -/proc/not_as_paranoid_sanitize(t) - var/regex/alphanum_slashes_only = regex("\[^a-zA-Z0-9# ,.?!:;()/<>@]", "g") - return alphanum_slashes_only.Replace(t, "#") - -//Runs sanitize and strip_html_simple -//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' after sanitize() calls byond's html_encode() -/proc/strip_html(var/t,var/limit=MAX_MESSAGE_LEN) - return copytext((sanitize(strip_html_simple(t))),1,limit) - -// Used to get a properly sanitized multiline input, of max_length -/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/name = input(user, message, title, default) as message|null - if(no_trim) - return copytext(html_encode(name), 1, max_length) - else - return trim(html_encode(name), max_length) - -//Runs byond's sanitization proc along-side strip_html_simple -//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause -/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) - return copytext((html_encode(strip_html_simple(t))),1,limit) - - -//Returns null if there is any bad text in the string -/proc/reject_bad_text(var/text, var/max_length=512) - if(length(text) > max_length) return //message too long - var/non_whitespace = 0 - for(var/i=1, i<=length(text), i++) - switch(text2ascii(text,i)) - if(62,60,92,47) return //rejects the text if it contains these bad characters: <, >, \ or / - if(127 to 255) return //rejects weird letters like � - if(0 to 31) return //more weird stuff - if(32) continue //whitespace - else non_whitespace = 1 - if(non_whitespace) return text //only accepts the text if it has some non-spaces - -// Used to get a sanitized input. -/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/name = html_encode(input(user, message, title, default) as text|null) - if(!no_trim) - name = trim(name) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) - return copytext(name, 1, max_length) - -// Uses client.typing to check if the popup should appear or not -/proc/typing_input(mob/user, message = "", title = "", default = "") - if(user.client.checkTyping()) // Prevent double windows - return null - var/client/C = user.client // Save it in a var in case the client disconnects from the mob - C.typing = TRUE - var/msg = input(user, message, title, default) as text|null - if(!C) - return null - C.typing = FALSE - if(!user || C != user.client) // User got out of the mob for some reason or the mob is gone - return null - return msg - -//Filters out undesirable characters from names -/proc/reject_bad_name(var/t_in, var/allow_numbers=0, var/max_length=MAX_NAME_LEN) - // Decode so that names with characters like < are still rejected - t_in = html_decode(t_in) - if(!t_in || length(t_in) > max_length) - return //Rejects the input if it is null or if it is longer than the max length allowed - - var/number_of_alphanumeric = 0 - var/last_char_group = 0 - var/t_out = "" - - for(var/i=1, i<=length(t_in), i++) - var/ascii_char = text2ascii(t_in,i) - switch(ascii_char) - // A .. Z - if(65 to 90) //Uppercase Letters - t_out += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // a .. z - if(97 to 122) //Lowercase Letters - if(last_char_group<2) t_out += ascii2text(ascii_char-32) //Force uppercase first character - else t_out += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // 0 .. 9 - if(48 to 57) //Numbers - if(!last_char_group) continue //suppress at start of string - if(!allow_numbers) continue - t_out += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 3 - - // ' - . , - if(39, 45, 46, 44) //Common name punctuation - if(!last_char_group) continue - t_out += ascii2text(ascii_char) - last_char_group = 2 - - // ~ | @ : # $ % & * + ! - if(126, 124, 64, 58, 35, 36, 37, 38, 42, 43, 33) //Other symbols that we'll allow (mainly for AI) - if(!last_char_group) continue //suppress at start of string - if(!allow_numbers) continue - t_out += ascii2text(ascii_char) - last_char_group = 2 - - //Space - if(32) - if(last_char_group <= 1) continue //suppress double-spaces and spaces at start of string - t_out += ascii2text(ascii_char) - last_char_group = 1 - else - return - - if(number_of_alphanumeric < 2) return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" - - if(last_char_group == 1) - t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space) - - for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai","plating")) //prevents these common metagamey names - if(cmptext(t_out,bad_name)) return //(not case sensitive) - - return t_out - -//checks text for html tags -//if tag is not in whitelist (var/list/paper_tag_whitelist in global.dm) -//relpaces < with < -proc/checkhtml(var/t) - t = sanitize_simple(t, list("&#"=".")) - var/p = findtext(t,"<",1) - while(p) //going through all the tags - var/start = p++ - var/tag = copytext(t,p, p+1) - if(tag != "/") - while(reject_bad_text(copytext(t, p, p+1), 1)) - tag = copytext(t,start, p) - p++ - tag = copytext(t,start+1, p) - if(!(tag in paper_tag_whitelist)) //if it's unkown tag, disarming it - t = copytext(t,1,start-1) + "<" + copytext(t,start+1) - p = findtext(t,"<",p) - return t -/* - * Text searches - */ - -//Checks the beginning of a string for a specified sub-string -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtext(text, prefix, start, end) - -//Checks the beginning of a string for a specified sub-string. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix_case(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtextEx(text, prefix, start, end) - -//Checks the end of a string for a specified substring. -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtext(text, suffix, start, null) - return - -//Checks the end of a string for a specified substring. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix_case(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtextEx(text, suffix, start, null) - -/* - * Text modification - */ -// See bygex.dm -/proc/replace_characters(var/t,var/list/repl_chars) - for(var/char in repl_chars) - t = replacetext(t, char, repl_chars[char]) - return t - -//Strips the first char and returns it and the new string as a list -/proc/strip_first(t) - return list(copytext(t, 1, 2), copytext(t, 2, 0)) - -//Strips the last char and returns it and the new string as a list -/proc/strip_last(t) - return list(copytext(t, 1, length(t)), copytext(t, length(t))) - -//Adds 'u' number of zeros ahead of the text 't' -/proc/add_zero(t, u) - while(length(t) < u) - t = "0[t]" - return t - -//Adds 'u' number of spaces ahead of the text 't' -/proc/add_lspace(t, u) - while(length(t) < u) - t = " [t]" - return t - -//Adds 'u' number of spaces behind the text 't' -/proc/add_tspace(t, u) - while(length(t) < u) - t = "[t] " - return t - -//Returns a string with reserved characters and spaces before the first letter removed -/proc/trim_left(text) - for(var/i = 1 to length(text)) - if(text2ascii(text, i) > 32) - return copytext(text, i) - return "" - -//Returns a string with reserved characters and spaces after the last letter removed -/proc/trim_right(text) - for(var/i = length(text), i > 0, i--) - if(text2ascii(text, i) > 32) - return copytext(text, 1, i + 1) - - return "" - -//Returns a string with reserved characters and spaces before the first word and after the last word removed. -/proc/trim(text) - return trim_left(trim_right(text)) - -//Returns a string with the first element of the string capitalized. -/proc/capitalize(var/t as text) - return uppertext(copytext(t, 1, 2)) + copytext(t, 2) - -//Centers text by adding spaces to either side of the string. -/proc/dd_centertext(message, length) - var/new_message = message - var/size = length(message) - var/delta = length - size - if(size == length) - return new_message - if(size > length) - return copytext(new_message, 1, length + 1) - if(delta == 1) - return new_message + " " - if(delta % 2) - new_message = " " + new_message - delta-- - var/spaces = add_lspace("",delta/2-1) - return spaces + new_message + spaces - -//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose -/proc/dd_limittext(message, length) - var/size = length(message) - if(size <= length) - return message - return copytext(message, 1, length + 1) - - -/proc/stringmerge(var/text,var/compare,replace = "*") -//This proc fills in all spaces with the "replace" var (* by default) with whatever -//is in the other string at the same spot (assuming it is not a replace char). -//This is used for fingerprints - var/newtext = text - if(length(text) != length(compare)) - return 0 - for(var/i = 1, i < length(text), i++) - var/a = copytext(text,i,i+1) - var/b = copytext(compare,i,i+1) -//if it isn't both the same letter, or if they are both the replacement character -//(no way to know what it was supposed to be) - if(a != b) - if(a == replace) //if A is the replacement char - newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1) - else if(b == replace) //if B is the replacement char - newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) - else //The lists disagree, Uh-oh! - return 0 - return newtext - -/proc/stringpercent(var/text,character = "*") -//This proc returns the number of chars of the string that is the character -//This is used for detective work to determine fingerprint completion. - if(!text || !character) - return 0 - var/count = 0 - for(var/i = 1, i <= length(text), i++) - var/a = copytext(text,i,i+1) - if(a == character) - count++ - return count - -/proc/reverse_text(var/text = "") - var/new_text = "" - for(var/i = length(text); i > 0; i--) - new_text += copytext(text, i, i+1) - return new_text - -//This proc strips html properly, but it's not lazy like the other procs. -//This means that it doesn't just remove < and > and call it a day. -//Also limit the size of the input, if specified. -/proc/strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN, allow_lines = 0) - if(!input) - return - var/opentag = 1 //These store the position of < and > respectively. - var/closetag = 1 - while(1) - opentag = findtext(input, "<") - closetag = findtext(input, ">") - if(closetag && opentag) - if(closetag < opentag) - input = copytext(input, (closetag + 1)) - else - input = copytext(input, 1, opentag) + copytext(input, (closetag + 1)) - else if(closetag || opentag) - if(opentag) - input = copytext(input, 1, opentag) - else - input = copytext(input, (closetag + 1)) - else - break - if(max_length) - input = copytext(input,1,max_length) - return sanitize(input, allow_lines ? list("\t" = " ") : list("\n" = " ", "\t" = " ")) - -/proc/trim_strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN, allow_lines = 0) - return trim(strip_html_properly(input, max_length, allow_lines)) - -//Used in preferences' SetFlavorText and human's set_flavor verb -//Previews a string of len or less length -/proc/TextPreview(var/string,var/len=40) - if(length(string) <= len) - if(!length(string)) - return "\[...\]" - else - return html_encode(string) //NO DECODED HTML YOU CHUCKLEFUCKS - else - return "[copytext_preserve_html(string, 1, 37)]..." - -//alternative copytext() for encoded text, doesn't break html entities (" and other) -/proc/copytext_preserve_html(var/text, var/first, var/last) - return html_encode(copytext(html_decode(text), first, last)) - -//Run sanitize(), but remove <, >, " first to prevent displaying them as > < &34; in some places, after html_encode(). -//Best used for sanitize object names, window titles. -//If you have a problem with sanitize() in chat, when quotes and >, < are displayed as html entites - -//this is a problem of double-encode(when & becomes &), use sanitize() with encode=0, but not the sanitizeSafe()! -/proc/sanitizeSafe(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) - return sanitize(replace_characters(input, list(">"=" ","<"=" ", "\""="'")), max_length, encode, trim, extra) - - -//Replace BYOND text macros with span classes for to_chat -/proc/replace_text_macro(match, code, rest) - var/regex/text_macro = new("(\\xFF.)(.*)$") - return text_macro.Replace(rest, /proc/replace_text_macro) - -/proc/macro2html(text) - var/static/regex/text_macro = new("(\\xFF.)(.*)$") - return text_macro.Replace(text, /proc/replace_text_macro) - -/proc/dmm_encode(text) - // First, go through and nix out any of our escape sequences so we don't leave ourselves open to some escape sequence attack - // Some coder will probably despise me for this, years down the line - - var/list/repl_chars = list("#?qt;", "#?lbr;", "#?rbr;") - for(var/char in repl_chars) - var/index = findtext(text, char) - var/keylength = length(char) - while(index) - log_runtime(EXCEPTION("Bad string given to dmm encoder! [text]")) - // Replace w/ underscore to prevent "{4;" from cheesing the radar - // Should probably also use canon text replacing procs - text = copytext(text, 1, index) + "_" + copytext(text, index+keylength) - index = findtext(text, char) - - // Then, replace characters as normal - var/list/repl_chars_2 = list("\"" = "#?qt;", "{" = "#?lbr;", "}" = "#?rbr;") - for(var/char in repl_chars_2) - var/index = findtext(text, char) - var/keylength = length(char) - while(index) - text = copytext(text, 1, index) + repl_chars_2[char] + copytext(text, index+keylength) - index = findtext(text, char) - return text - - -/proc/dmm_decode(text) - // Replace what we extracted above - var/list/repl_chars = list("#?qt;" = "\"", "#?lbr;" = "{", "#?rbr;" = "}") - for(var/char in repl_chars) - var/index = findtext(text, char) - var/keylength = length(char) - while(index) - text = copytext(text, 1, index) + repl_chars[char] + copytext(text, index+keylength) - index = findtext(text, char) - return text - -//Checks if any of a given list of needles is in the haystack -/proc/text_in_list(haystack, list/needle_list, start=1, end=0) - for(var/needle in needle_list) - if(findtext(haystack, needle, start, end)) - return 1 - return 0 - -//Like above, but case sensitive -/proc/text_in_list_case(haystack, list/needle_list, start=1, end=0) - for(var/needle in needle_list) - if(findtextEx(haystack, needle, start, end)) - return 1 - return 0 - - -// Pencode -/proc/pencode_to_html(text, mob/user, obj/item/pen/P = null, format = 1, sign = 1, fields = 1, deffont = PEN_FONT, signfont = SIGNFONT, crayonfont = CRAYON_FONT, no_font = FALSE) - text = replacetext(text, "\[b\]", "") - text = replacetext(text, "\[/b\]", "") - text = replacetext(text, "\[i\]", "") - text = replacetext(text, "\[/i\]", "") - text = replacetext(text, "\[u\]", "") - text = replacetext(text, "\[/u\]", "") - if(sign) - text = replacetext(text, "\[sign\]", "[user ? user.real_name : "Anonymous"]") - if(fields) - text = replacetext(text, "\[field\]", "") - if(format) - text = replacetext(text, "\[h1\]", "

") - text = replacetext(text, "\[/h1\]", "

") - text = replacetext(text, "\[h2\]", "

") - text = replacetext(text, "\[/h2\]", "

") - text = replacetext(text, "\[h3\]", "

") - text = replacetext(text, "\[/h3\]", "

") - text = replacetext(text, "\n", "
") - text = replacetext(text, "\[center\]", "
") - text = replacetext(text, "\[/center\]", "
") - text = replacetext(text, "\[br\]", "
") - text = replacetext(text, "\[large\]", "") - text = replacetext(text, "\[/large\]", "") - - if(istype(P, /obj/item/toy/crayon) || !format) // If it is a crayon, and he still tries to use these, make them empty! - text = replacetext(text, "\[*\]", "") - text = replacetext(text, "\[hr\]", "") - text = replacetext(text, "\[small\]", "") - text = replacetext(text, "\[/small\]", "") - text = replacetext(text, "\[list\]", "") - text = replacetext(text, "\[/list\]", "") - text = replacetext(text, "\[table\]", "") - text = replacetext(text, "\[/table\]", "") - text = replacetext(text, "\[row\]", "") - text = replacetext(text, "\[cell\]", "") - text = replacetext(text, "\[logo\]", "") - if(istype(P, /obj/item/toy/crayon)) - text = "[text]" - else // They are using "not a crayon" - formatting is OK and such - text = replacetext(text, "\[*\]", "
  • ") - text = replacetext(text, "\[hr\]", "
    ") - text = replacetext(text, "\[small\]", "") - text = replacetext(text, "\[/small\]", "") - text = replacetext(text, "\[list\]", "
      ") - text = replacetext(text, "\[/list\]", "
    ") - text = replacetext(text, "\[table\]", "") - text = replacetext(text, "\[/table\]", "
    ") - text = replacetext(text, "\[grid\]", "") - text = replacetext(text, "\[/grid\]", "
    ") - text = replacetext(text, "\[row\]", "") - text = replacetext(text, "\[cell\]", "") - text = replacetext(text, "\[logo\]", "") - text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO - if(!no_font) - if(P) - text = "[text]" - else - text = "[text]" - - text = copytext(text, 1, MAX_PAPER_MESSAGE_LEN) - return text - -/proc/convert_pencode_arg(text, tag, arg) - arg = sanitize_simple(html_encode(arg), list("''"="","\""="", "?"="")) - // https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-4---css-escape-and-strictly-validate-before-inserting-untrusted-data-into-html-style-property-values - var/list/style_attacks = list("javascript:", "expression", "byond:", "file:") - - for(var/style_attack in style_attacks) - if(findtext(arg, style_attack)) - // Do not attempt to render dangerous things - return text - - if(tag == "class") - return "" - - if(tag == "style") - return "" - - if(tag == "img") - var/list/img_props = splittext(arg, ";") - if(img_props.len == 3) - return "" - if(img_props.len == 2) - return "" - return "" - - return text - -/proc/admin_pencode_to_html() - var/text = pencode_to_html(arglist(args)) - var/regex/R = new(@"\[(.*?) (.*?)\]", "ge") - text = R.Replace(text, /proc/convert_pencode_arg) - - text = replacetext(text, "\[/class\]", "") - text = replacetext(text, "\[/style\]", "") - text = replacetext(text, "\[/img\]", "") - - return text - -/proc/html_to_pencode(text) - text = replacetext(text, "
    ", "\n") - text = replacetext(text, "
    ", "\[center\]") - text = replacetext(text, "
    ", "\[/center\]") - text = replacetext(text, "
    ", "\[br\]") - text = replacetext(text, "", "\[b\]") - text = replacetext(text, "", "\[/b\]") - text = replacetext(text, "", "\[i\]") - text = replacetext(text, "", "\[/i\]") - text = replacetext(text, "", "\[u\]") - text = replacetext(text, "", "\[/u\]") - text = replacetext(text, "", "\[large\]") - text = replacetext(text, "", "\[field\]") - - text = replacetext(text, "

    ", "\[h1\]") - text = replacetext(text, "

    ", "\[/h1\]") - text = replacetext(text, "

    ", "\[h2\]") - text = replacetext(text, "

    ", "\[/h2\]") - text = replacetext(text, "

    ", "\[h3\]") - text = replacetext(text, "

    ", "\[/h3\]") - - text = replacetext(text, "
  • ", "\[*\]") - text = replacetext(text, "
    ", "\[hr\]") - text = replacetext(text, "", "\[small\]") - text = replacetext(text, "
      ", "\[list\]") - text = replacetext(text, "
    ", "\[/list\]") - text = replacetext(text, "", "\[table\]") - text = replacetext(text, "
    ", "\[/table\]") - text = replacetext(text, "", "\[grid\]") - text = replacetext(text, "
    ", "\[/grid\]") - text = replacetext(text, "", "\[row\]") - text = replacetext(text, "", "\[cell\]") - text = replacetext(text, "", "\[logo\]") - return text - -#define string2charlist(string) (splittext(string, regex("(\\x0A|.)")) - splittext(string, "")) +/* + * Holds procs designed to help with filtering text + * Contains groups: + * SQL sanitization + * Text sanitization + * Text searches + * Text modification + * Misc + */ + + +/* + * SQL sanitization + */ + +// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. +/proc/sanitizeSQL(var/t as text) + if(isnull(t)) + return null + if(!istext(t)) + t = "[t]" // Just quietly assume any non-texts are supposed to be text + var/sqltext = GLOB.dbcon.Quote(t); + return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that + +/proc/format_table_name(table as text) + return sqlfdbktableprefix + table + +/* + * Text sanitization + */ +// Can be used almost the same way as normal input for text +/proc/clean_input(Message, Title, Default, mob/user=usr) + var/txt = input(user, Message, Title, Default) as text | null + if(txt) + return html_encode(txt) + +//Simply removes < and > and limits the length of the message +/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) + var/list/strip_chars = list("<",">") + t = copytext(t,1,limit) + for(var/char in strip_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + copytext(t, index+1) + index = findtext(t, char) + return t + +//Removes a few problematic characters +/proc/sanitize_simple(var/t,var/list/repl_chars = list("\n"="#","\t"="#")) + for(var/char in repl_chars) + t = replacetext(t, char, repl_chars[char]) + return t + +/proc/readd_quotes(var/t) + var/list/repl_chars = list(""" = "\"") + for(var/char in repl_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5) + index = findtext(t, char) + return t + +//Runs byond's sanitization proc along-side sanitize_simple +/proc/sanitize(var/t,var/list/repl_chars = null) + return html_encode(sanitize_simple(t,repl_chars)) + +// Gut ANYTHING that isnt alphanumeric, or brackets +/proc/paranoid_sanitize(t) + var/regex/alphanum_only = regex("\[^a-zA-Z0-9# ,.?!:;()]", "g") + return alphanum_only.Replace(t, "#") + +// Less agressive, to allow discord features, such as <>, / and @ +/proc/not_as_paranoid_sanitize(t) + var/regex/alphanum_slashes_only = regex("\[^a-zA-Z0-9# ,.?!:;()/<>@]", "g") + return alphanum_slashes_only.Replace(t, "#") + +//Runs sanitize and strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' after sanitize() calls byond's html_encode() +/proc/strip_html(var/t,var/limit=MAX_MESSAGE_LEN) + return copytext((sanitize(strip_html_simple(t))),1,limit) + +// Used to get a properly sanitized multiline input, of max_length +/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/name = input(user, message, title, default) as message|null + if(no_trim) + return copytext(html_encode(name), 1, max_length) + else + return trim(html_encode(name), max_length) + +//Runs byond's sanitization proc along-side strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause +/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) + return copytext((html_encode(strip_html_simple(t))),1,limit) + + +//Returns null if there is any bad text in the string +/proc/reject_bad_text(var/text, var/max_length=512) + if(length(text) > max_length) return //message too long + var/non_whitespace = 0 + for(var/i=1, i<=length(text), i++) + switch(text2ascii(text,i)) + if(62,60,92,47) return //rejects the text if it contains these bad characters: <, >, \ or / + if(127 to 255) return //rejects weird letters like � + if(0 to 31) return //more weird stuff + if(32) continue //whitespace + else non_whitespace = 1 + if(non_whitespace) return text //only accepts the text if it has some non-spaces + +// Used to get a sanitized input. +/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/name = html_encode(input(user, message, title, default) as text|null) + if(!no_trim) + name = trim(name) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) + return copytext(name, 1, max_length) + +// Uses client.typing to check if the popup should appear or not +/proc/typing_input(mob/user, message = "", title = "", default = "") + if(user.client.checkTyping()) // Prevent double windows + return null + var/client/C = user.client // Save it in a var in case the client disconnects from the mob + C.typing = TRUE + var/msg = input(user, message, title, default) as text|null + if(!C) + return null + C.typing = FALSE + if(!user || C != user.client) // User got out of the mob for some reason or the mob is gone + return null + return msg + +//Filters out undesirable characters from names +/proc/reject_bad_name(var/t_in, var/allow_numbers=0, var/max_length=MAX_NAME_LEN) + // Decode so that names with characters like < are still rejected + t_in = html_decode(t_in) + if(!t_in || length(t_in) > max_length) + return //Rejects the input if it is null or if it is longer than the max length allowed + + var/number_of_alphanumeric = 0 + var/last_char_group = 0 + var/t_out = "" + + for(var/i=1, i<=length(t_in), i++) + var/ascii_char = text2ascii(t_in,i) + switch(ascii_char) + // A .. Z + if(65 to 90) //Uppercase Letters + t_out += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // a .. z + if(97 to 122) //Lowercase Letters + if(last_char_group<2) t_out += ascii2text(ascii_char-32) //Force uppercase first character + else t_out += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // 0 .. 9 + if(48 to 57) //Numbers + if(!last_char_group) continue //suppress at start of string + if(!allow_numbers) continue + t_out += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 3 + + // ' - . , + if(39, 45, 46, 44) //Common name punctuation + if(!last_char_group) continue + t_out += ascii2text(ascii_char) + last_char_group = 2 + + // ~ | @ : # $ % & * + ! + if(126, 124, 64, 58, 35, 36, 37, 38, 42, 43, 33) //Other symbols that we'll allow (mainly for AI) + if(!last_char_group) continue //suppress at start of string + if(!allow_numbers) continue + t_out += ascii2text(ascii_char) + last_char_group = 2 + + //Space + if(32) + if(last_char_group <= 1) continue //suppress double-spaces and spaces at start of string + t_out += ascii2text(ascii_char) + last_char_group = 1 + else + return + + if(number_of_alphanumeric < 2) return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" + + if(last_char_group == 1) + t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space) + + for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai","plating")) //prevents these common metagamey names + if(cmptext(t_out,bad_name)) return //(not case sensitive) + + return t_out + +//checks text for html tags +//if tag is not in whitelist (var/list/paper_tag_whitelist in global.dm) +//relpaces < with < +proc/checkhtml(var/t) + t = sanitize_simple(t, list("&#"=".")) + var/p = findtext(t,"<",1) + while(p) //going through all the tags + var/start = p++ + var/tag = copytext(t,p, p+1) + if(tag != "/") + while(reject_bad_text(copytext(t, p, p+1), 1)) + tag = copytext(t,start, p) + p++ + tag = copytext(t,start+1, p) + if(!(tag in GLOB.paper_tag_whitelist)) //if it's unkown tag, disarming it + t = copytext(t,1,start-1) + "<" + copytext(t,start+1) + p = findtext(t,"<",p) + return t +/* + * Text searches + */ + +//Checks the beginning of a string for a specified sub-string +//Returns the position of the substring or 0 if it was not found +/proc/dd_hasprefix(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtext(text, prefix, start, end) + +//Checks the beginning of a string for a specified sub-string. This proc is case sensitive +//Returns the position of the substring or 0 if it was not found +/proc/dd_hasprefix_case(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtextEx(text, prefix, start, end) + +//Checks the end of a string for a specified substring. +//Returns the position of the substring or 0 if it was not found +/proc/dd_hassuffix(text, suffix) + var/start = length(text) - length(suffix) + if(start) + return findtext(text, suffix, start, null) + return + +//Checks the end of a string for a specified substring. This proc is case sensitive +//Returns the position of the substring or 0 if it was not found +/proc/dd_hassuffix_case(text, suffix) + var/start = length(text) - length(suffix) + if(start) + return findtextEx(text, suffix, start, null) + +/* + * Text modification + */ +// See bygex.dm +/proc/replace_characters(var/t,var/list/repl_chars) + for(var/char in repl_chars) + t = replacetext(t, char, repl_chars[char]) + return t + +//Strips the first char and returns it and the new string as a list +/proc/strip_first(t) + return list(copytext(t, 1, 2), copytext(t, 2, 0)) + +//Strips the last char and returns it and the new string as a list +/proc/strip_last(t) + return list(copytext(t, 1, length(t)), copytext(t, length(t))) + +//Adds 'u' number of zeros ahead of the text 't' +/proc/add_zero(t, u) + while(length(t) < u) + t = "0[t]" + return t + +//Adds 'u' number of spaces ahead of the text 't' +/proc/add_lspace(t, u) + while(length(t) < u) + t = " [t]" + return t + +//Adds 'u' number of spaces behind the text 't' +/proc/add_tspace(t, u) + while(length(t) < u) + t = "[t] " + return t + +//Returns a string with reserved characters and spaces before the first letter removed +/proc/trim_left(text) + for(var/i = 1 to length(text)) + if(text2ascii(text, i) > 32) + return copytext(text, i) + return "" + +//Returns a string with reserved characters and spaces after the last letter removed +/proc/trim_right(text) + for(var/i = length(text), i > 0, i--) + if(text2ascii(text, i) > 32) + return copytext(text, 1, i + 1) + + return "" + +//Returns a string with reserved characters and spaces before the first word and after the last word removed. +/proc/trim(text) + return trim_left(trim_right(text)) + +//Returns a string with the first element of the string capitalized. +/proc/capitalize(var/t as text) + return uppertext(copytext(t, 1, 2)) + copytext(t, 2) + +//Centers text by adding spaces to either side of the string. +/proc/dd_centertext(message, length) + var/new_message = message + var/size = length(message) + var/delta = length - size + if(size == length) + return new_message + if(size > length) + return copytext(new_message, 1, length + 1) + if(delta == 1) + return new_message + " " + if(delta % 2) + new_message = " " + new_message + delta-- + var/spaces = add_lspace("",delta/2-1) + return spaces + new_message + spaces + +//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose +/proc/dd_limittext(message, length) + var/size = length(message) + if(size <= length) + return message + return copytext(message, 1, length + 1) + + +/proc/stringmerge(var/text,var/compare,replace = "*") +//This proc fills in all spaces with the "replace" var (* by default) with whatever +//is in the other string at the same spot (assuming it is not a replace char). +//This is used for fingerprints + var/newtext = text + if(length(text) != length(compare)) + return 0 + for(var/i = 1, i < length(text), i++) + var/a = copytext(text,i,i+1) + var/b = copytext(compare,i,i+1) +//if it isn't both the same letter, or if they are both the replacement character +//(no way to know what it was supposed to be) + if(a != b) + if(a == replace) //if A is the replacement char + newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1) + else if(b == replace) //if B is the replacement char + newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) + else //The lists disagree, Uh-oh! + return 0 + return newtext + +/proc/stringpercent(var/text,character = "*") +//This proc returns the number of chars of the string that is the character +//This is used for detective work to determine fingerprint completion. + if(!text || !character) + return 0 + var/count = 0 + for(var/i = 1, i <= length(text), i++) + var/a = copytext(text,i,i+1) + if(a == character) + count++ + return count + +/proc/reverse_text(var/text = "") + var/new_text = "" + for(var/i = length(text); i > 0; i--) + new_text += copytext(text, i, i+1) + return new_text + +//This proc strips html properly, but it's not lazy like the other procs. +//This means that it doesn't just remove < and > and call it a day. +//Also limit the size of the input, if specified. +/proc/strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN, allow_lines = 0) + if(!input) + return + var/opentag = 1 //These store the position of < and > respectively. + var/closetag = 1 + while(1) + opentag = findtext(input, "<") + closetag = findtext(input, ">") + if(closetag && opentag) + if(closetag < opentag) + input = copytext(input, (closetag + 1)) + else + input = copytext(input, 1, opentag) + copytext(input, (closetag + 1)) + else if(closetag || opentag) + if(opentag) + input = copytext(input, 1, opentag) + else + input = copytext(input, (closetag + 1)) + else + break + if(max_length) + input = copytext(input,1,max_length) + return sanitize(input, allow_lines ? list("\t" = " ") : list("\n" = " ", "\t" = " ")) + +/proc/trim_strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN, allow_lines = 0) + return trim(strip_html_properly(input, max_length, allow_lines)) + +//Used in preferences' SetFlavorText and human's set_flavor verb +//Previews a string of len or less length +/proc/TextPreview(var/string,var/len=40) + if(length(string) <= len) + if(!length(string)) + return "\[...\]" + else + return html_encode(string) //NO DECODED HTML YOU CHUCKLEFUCKS + else + return "[copytext_preserve_html(string, 1, 37)]..." + +//alternative copytext() for encoded text, doesn't break html entities (" and other) +/proc/copytext_preserve_html(var/text, var/first, var/last) + return html_encode(copytext(html_decode(text), first, last)) + +//Run sanitize(), but remove <, >, " first to prevent displaying them as > < &34; in some places, after html_encode(). +//Best used for sanitize object names, window titles. +//If you have a problem with sanitize() in chat, when quotes and >, < are displayed as html entites - +//this is a problem of double-encode(when & becomes &), use sanitize() with encode=0, but not the sanitizeSafe()! +/proc/sanitizeSafe(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) + return sanitize(replace_characters(input, list(">"=" ","<"=" ", "\""="'")), max_length, encode, trim, extra) + + +//Replace BYOND text macros with span classes for to_chat +/proc/replace_text_macro(match, code, rest) + var/regex/text_macro = new("(\\xFF.)(.*)$") + return text_macro.Replace(rest, /proc/replace_text_macro) + +/proc/macro2html(text) + var/static/regex/text_macro = new("(\\xFF.)(.*)$") + return text_macro.Replace(text, /proc/replace_text_macro) + +/proc/dmm_encode(text) + // First, go through and nix out any of our escape sequences so we don't leave ourselves open to some escape sequence attack + // Some coder will probably despise me for this, years down the line + + var/list/repl_chars = list("#?qt;", "#?lbr;", "#?rbr;") + for(var/char in repl_chars) + var/index = findtext(text, char) + var/keylength = length(char) + while(index) + log_runtime(EXCEPTION("Bad string given to dmm encoder! [text]")) + // Replace w/ underscore to prevent "{4;" from cheesing the radar + // Should probably also use canon text replacing procs + text = copytext(text, 1, index) + "_" + copytext(text, index+keylength) + index = findtext(text, char) + + // Then, replace characters as normal + var/list/repl_chars_2 = list("\"" = "#?qt;", "{" = "#?lbr;", "}" = "#?rbr;") + for(var/char in repl_chars_2) + var/index = findtext(text, char) + var/keylength = length(char) + while(index) + text = copytext(text, 1, index) + repl_chars_2[char] + copytext(text, index+keylength) + index = findtext(text, char) + return text + + +/proc/dmm_decode(text) + // Replace what we extracted above + var/list/repl_chars = list("#?qt;" = "\"", "#?lbr;" = "{", "#?rbr;" = "}") + for(var/char in repl_chars) + var/index = findtext(text, char) + var/keylength = length(char) + while(index) + text = copytext(text, 1, index) + repl_chars[char] + copytext(text, index+keylength) + index = findtext(text, char) + return text + +//Checks if any of a given list of needles is in the haystack +/proc/text_in_list(haystack, list/needle_list, start=1, end=0) + for(var/needle in needle_list) + if(findtext(haystack, needle, start, end)) + return 1 + return 0 + +//Like above, but case sensitive +/proc/text_in_list_case(haystack, list/needle_list, start=1, end=0) + for(var/needle in needle_list) + if(findtextEx(haystack, needle, start, end)) + return 1 + return 0 + + +// Pencode +/proc/pencode_to_html(text, mob/user, obj/item/pen/P = null, format = 1, sign = 1, fields = 1, deffont = PEN_FONT, signfont = SIGNFONT, crayonfont = CRAYON_FONT, no_font = FALSE) + text = replacetext(text, "\[b\]", "") + text = replacetext(text, "\[/b\]", "") + text = replacetext(text, "\[i\]", "") + text = replacetext(text, "\[/i\]", "") + text = replacetext(text, "\[u\]", "") + text = replacetext(text, "\[/u\]", "") + if(sign) + text = replacetext(text, "\[sign\]", "[user ? user.real_name : "Anonymous"]") + if(fields) + text = replacetext(text, "\[field\]", "") + if(format) + text = replacetext(text, "\[h1\]", "

    ") + text = replacetext(text, "\[/h1\]", "

    ") + text = replacetext(text, "\[h2\]", "

    ") + text = replacetext(text, "\[/h2\]", "

    ") + text = replacetext(text, "\[h3\]", "

    ") + text = replacetext(text, "\[/h3\]", "

    ") + text = replacetext(text, "\n", "
    ") + text = replacetext(text, "\[center\]", "
    ") + text = replacetext(text, "\[/center\]", "
    ") + text = replacetext(text, "\[br\]", "
    ") + text = replacetext(text, "\[large\]", "") + text = replacetext(text, "\[/large\]", "") + + if(istype(P, /obj/item/toy/crayon) || !format) // If it is a crayon, and he still tries to use these, make them empty! + text = replacetext(text, "\[*\]", "") + text = replacetext(text, "\[hr\]", "") + text = replacetext(text, "\[small\]", "") + text = replacetext(text, "\[/small\]", "") + text = replacetext(text, "\[list\]", "") + text = replacetext(text, "\[/list\]", "") + text = replacetext(text, "\[table\]", "") + text = replacetext(text, "\[/table\]", "") + text = replacetext(text, "\[row\]", "") + text = replacetext(text, "\[cell\]", "") + text = replacetext(text, "\[logo\]", "") + if(istype(P, /obj/item/toy/crayon)) + text = "[text]" + else // They are using "not a crayon" - formatting is OK and such + text = replacetext(text, "\[*\]", "
  • ") + text = replacetext(text, "\[hr\]", "
    ") + text = replacetext(text, "\[small\]", "") + text = replacetext(text, "\[/small\]", "") + text = replacetext(text, "\[list\]", "
      ") + text = replacetext(text, "\[/list\]", "
    ") + text = replacetext(text, "\[table\]", "") + text = replacetext(text, "\[/table\]", "
    ") + text = replacetext(text, "\[grid\]", "") + text = replacetext(text, "\[/grid\]", "
    ") + text = replacetext(text, "\[row\]", "") + text = replacetext(text, "\[cell\]", "") + text = replacetext(text, "\[logo\]", "") + text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO + if(!no_font) + if(P) + text = "[text]" + else + text = "[text]" + + text = copytext(text, 1, MAX_PAPER_MESSAGE_LEN) + return text + +/proc/convert_pencode_arg(text, tag, arg) + arg = sanitize_simple(html_encode(arg), list("''"="","\""="", "?"="")) + // https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-4---css-escape-and-strictly-validate-before-inserting-untrusted-data-into-html-style-property-values + var/list/style_attacks = list("javascript:", "expression", "byond:", "file:") + + for(var/style_attack in style_attacks) + if(findtext(arg, style_attack)) + // Do not attempt to render dangerous things + return text + + if(tag == "class") + return "" + + if(tag == "style") + return "" + + if(tag == "img") + var/list/img_props = splittext(arg, ";") + if(img_props.len == 3) + return "" + if(img_props.len == 2) + return "" + return "" + + return text + +/proc/admin_pencode_to_html() + var/text = pencode_to_html(arglist(args)) + var/regex/R = new(@"\[(.*?) (.*?)\]", "ge") + text = R.Replace(text, /proc/convert_pencode_arg) + + text = replacetext(text, "\[/class\]", "") + text = replacetext(text, "\[/style\]", "") + text = replacetext(text, "\[/img\]", "") + + return text + +/proc/html_to_pencode(text) + text = replacetext(text, "
    ", "\n") + text = replacetext(text, "
    ", "\[center\]") + text = replacetext(text, "
    ", "\[/center\]") + text = replacetext(text, "
    ", "\[br\]") + text = replacetext(text, "", "\[b\]") + text = replacetext(text, "", "\[/b\]") + text = replacetext(text, "", "\[i\]") + text = replacetext(text, "", "\[/i\]") + text = replacetext(text, "", "\[u\]") + text = replacetext(text, "", "\[/u\]") + text = replacetext(text, "", "\[large\]") + text = replacetext(text, "", "\[field\]") + + text = replacetext(text, "

    ", "\[h1\]") + text = replacetext(text, "

    ", "\[/h1\]") + text = replacetext(text, "

    ", "\[h2\]") + text = replacetext(text, "

    ", "\[/h2\]") + text = replacetext(text, "

    ", "\[h3\]") + text = replacetext(text, "

    ", "\[/h3\]") + + text = replacetext(text, "
  • ", "\[*\]") + text = replacetext(text, "
    ", "\[hr\]") + text = replacetext(text, "", "\[small\]") + text = replacetext(text, "
      ", "\[list\]") + text = replacetext(text, "
    ", "\[/list\]") + text = replacetext(text, "", "\[table\]") + text = replacetext(text, "
    ", "\[/table\]") + text = replacetext(text, "", "\[grid\]") + text = replacetext(text, "
    ", "\[/grid\]") + text = replacetext(text, "", "\[row\]") + text = replacetext(text, "", "\[cell\]") + text = replacetext(text, "", "\[logo\]") + return text + +#define string2charlist(string) (splittext(string, regex("(\\x0A|.)")) - splittext(string, "")) diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm index d13bf08c58b..340d7a3b134 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -50,10 +50,24 @@ return "[date_portion]T[time_portion]" /proc/gameTimestamp(format = "hh:mm:ss", wtime=null) - if(!wtime) + if(wtime == null) wtime = world.time return time2text(wtime - GLOB.timezoneOffset, format) +// max hh:mm:ss supported +/proc/timeStampToNum(timestamp) + var/list/splits = text2numlist(timestamp, ":") + . = 0 + var/split_len = length(splits) + for(var/i = 1 to length(splits)) + switch(split_len - i) + if(2) + . += splits[i] HOURS + if(1) + . += splits[i] MINUTES + if(0) + . += splits[i] SECONDS + /* This is used for displaying the "station time" equivelent of a world.time value Calling it with no args will give you the current time, but you can specify a world.time-based value as an argument - You can use this, for example, to do "This will expire at [station_time_at(world.time + 500)]" to display a "station time" expiration date diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm index 7c1d2257e27..ee6c5d10fe0 100644 --- a/code/__HELPERS/traits.dm +++ b/code/__HELPERS/traits.dm @@ -62,7 +62,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai //mob traits #define TRAIT_PACIFISM "pacifism" -#define TRAIT_WATERBREATH "waterbreathing" +#define TRAIT_WATERBREATH "waterbreathing" +#define TRAIT_BLOODCRAWL "bloodcrawl" +#define TRAIT_BLOODCRAWL_EAT "bloodcrawl_eat" // common trait sources -#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention \ No newline at end of file +#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index ae7704459b5..bf44f6a4bc9 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -1,393 +1,394 @@ -/* - * Holds procs designed to change one type of value, into another. - * Contains: - * hex2num & num2hex - * file2list - * angle2dir - * angle2text - * worldtime2text - */ - -//Returns an integer given a hex input -/proc/hex2num(hex) - if(!(istext(hex))) - return - - var/num = 0 - var/power = 0 - var/i = null - i = length(hex) - while(i > 0) - var/char = copytext(hex, i, i + 1) - switch(char) - if("0") - //Apparently, switch works with empty statements, yay! If that doesn't work, blame me, though. -- Urist - if("9", "8", "7", "6", "5", "4", "3", "2", "1") - num += text2num(char) * 16 ** power - if("a", "A") - num += 16 ** power * 10 - if("b", "B") - num += 16 ** power * 11 - if("c", "C") - num += 16 ** power * 12 - if("d", "D") - num += 16 ** power * 13 - if("e", "E") - num += 16 ** power * 14 - if("f", "F") - num += 16 ** power * 15 - else - return - power++ - i-- - return num - -//Returns the hex value of a number given a value assumed to be a base-ten value -/proc/num2hex(num, placeholder) - if(!isnum(num)) return - if(placeholder == null) placeholder = 2 - - var/hex = "" - while(num) - var/val = num % 16 - num = round(num / 16) - - if(val > 9) - val = ascii2text(55 + val) // 65 - 70 correspond to "A" - "F" - hex = "[val][hex]" - while(length(hex) < placeholder) - hex = "0[hex]" - return hex || "0" - -//Returns an integer value for R of R/G/B given a hex color input. -/proc/color2R(hex) - if(!(istext(hex))) - return - - return hex2num(copytext(hex, 2, 4)) //Returning R - -//Returns an integer value for G of R/G/B given a hex color input. -/proc/color2G(hex) - if(!(istext(hex))) - return - - return hex2num(copytext(hex, 4, 6)) //Returning G - -//Returns an integer value for B of R/G/B given a hex color input. -/proc/color2B(hex) - if(!(istext(hex))) - return - - return hex2num(copytext(hex, 6, 8)) //Returning B - -/proc/text2numlist(text, delimiter="\n") - var/list/num_list = list() - for(var/x in splittext(text, delimiter)) - num_list += text2num(x) - return num_list - -//Splits the text of a file at seperator and returns them in a list. -/proc/file2list(filename, seperator="\n") - return splittext(return_file_text(filename),seperator) - - -//Turns a direction into text - -/proc/num2dir(direction) - switch(direction) - if(1.0) return NORTH - if(2.0) return SOUTH - if(4.0) return EAST - if(8.0) return WEST - else - log_runtime(EXCEPTION("UNKNOWN DIRECTION: [direction]")) - -/proc/dir2text(direction) - switch(direction) - if(1.0) - return "north" - if(2.0) - return "south" - if(4.0) - return "east" - if(8.0) - return "west" - if(5.0) - return "northeast" - if(6.0) - return "southeast" - if(9.0) - return "northwest" - if(10.0) - return "southwest" - else - return - -//Turns text into proper directions -/proc/text2dir(direction) - switch(uppertext(direction)) - if("NORTH") - return 1 - if("SOUTH") - return 2 - if("EAST") - return 4 - if("WEST") - return 8 - if("NORTHEAST") - return 5 - if("NORTHWEST") - return 9 - if("SOUTHEAST") - return 6 - if("SOUTHWEST") - return 10 - else - return - -//Converts an angle (degrees) into an ss13 direction -/proc/angle2dir(var/degree) - degree = ((degree+22.5)%365) - if(degree < 45) return NORTH - if(degree < 90) return NORTHEAST - if(degree < 135) return EAST - if(degree < 180) return SOUTHEAST - if(degree < 225) return SOUTH - if(degree < 270) return SOUTHWEST - if(degree < 315) return WEST - return NORTH|WEST - -/proc/angle2dir_cardinal(angle) - switch(round(angle, 0.1)) - if(315.5 to 360, 0 to 45.5) - return NORTH - if(45.6 to 135.5) - return EAST - if(135.6 to 225.5) - return SOUTH - if(225.6 to 315.5) - return WEST - -//returns the north-zero clockwise angle in degrees, given a direction - -/proc/dir2angle(var/D) - switch(D) - if(NORTH) return 0 - if(SOUTH) return 180 - if(EAST) return 90 - if(WEST) return 270 - if(NORTHEAST) return 45 - if(SOUTHEAST) return 135 - if(NORTHWEST) return 315 - if(SOUTHWEST) return 225 - else return null - -//Returns the angle in english -/proc/angle2text(var/degree) - return dir2text(angle2dir(degree)) - -//Converts a blend_mode constant to one acceptable to icon.Blend() -/proc/blendMode2iconMode(blend_mode) - switch(blend_mode) - if(BLEND_MULTIPLY) return ICON_MULTIPLY - if(BLEND_ADD) return ICON_ADD - if(BLEND_SUBTRACT) return ICON_SUBTRACT - else return ICON_OVERLAY - -//Converts a rights bitfield into a string -/proc/rights2text(rights,seperator="") - if(rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" - if(rights & R_ADMIN) . += "[seperator]+ADMIN" - if(rights & R_BAN) . += "[seperator]+BAN" - if(rights & R_EVENT) . += "[seperator]+EVENT" - if(rights & R_SERVER) . += "[seperator]+SERVER" - if(rights & R_DEBUG) . += "[seperator]+DEBUG" - if(rights & R_POSSESS) . += "[seperator]+POSSESS" - if(rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" - if(rights & R_STEALTH) . += "[seperator]+STEALTH" - if(rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" - if(rights & R_VAREDIT) . += "[seperator]+VAREDIT" - if(rights & R_SOUNDS) . += "[seperator]+SOUND" - if(rights & R_SPAWN) . += "[seperator]+SPAWN" - if(rights & R_PROCCALL) . += "[seperator]+PROCCALL" - if(rights & R_MOD) . += "[seperator]+MODERATOR" - if(rights & R_MENTOR) . += "[seperator]+MENTOR" - return . - -/proc/ui_style2icon(ui_style) - switch(ui_style) - if("Retro") - return 'icons/mob/screen_retro.dmi' - if("Plasmafire") - return 'icons/mob/screen_plasmafire.dmi' - if("Slimecore") - return 'icons/mob/screen_slimecore.dmi' - if("Operative") - return 'icons/mob/screen_operative.dmi' - if("White") - return 'icons/mob/screen_white.dmi' - else - return 'icons/mob/screen_midnight.dmi' - -//colour formats -/proc/rgb2hsl(red, green, blue) - red /= 255;green /= 255;blue /= 255; - var/max = max(red,green,blue) - var/min = min(red,green,blue) - var/range = max-min - - var/hue=0;var/saturation=0;var/lightness=0; - lightness = (max + min)/2 - if(range != 0) - if(lightness < 0.5) saturation = range/(max+min) - else saturation = range/(2-max-min) - - var/dred = ((max-red)/(6*max)) + 0.5 - var/dgreen = ((max-green)/(6*max)) + 0.5 - var/dblue = ((max-blue)/(6*max)) + 0.5 - - if(max==red) hue = dblue - dgreen - else if(max==green) hue = dred - dblue + (1/3) - else hue = dgreen - dred + (2/3) - if(hue < 0) hue++ - else if(hue > 1) hue-- - - return list(hue, saturation, lightness) - -/proc/hsl2rgb(hue, saturation, lightness) - var/red;var/green;var/blue; - if(saturation == 0) - red = lightness * 255 - green = red - blue = red - else - var/a;var/b; - if(lightness < 0.5) b = lightness*(1+saturation) - else b = (lightness+saturation) - (saturation*lightness) - a = 2*lightness - b - - red = round(255 * hue2rgb(a, b, hue+(1/3))) - green = round(255 * hue2rgb(a, b, hue)) - blue = round(255 * hue2rgb(a, b, hue-(1/3))) - - return list(red, green, blue) - -/proc/hue2rgb(a, b, hue) - if(hue < 0) hue++ - else if(hue > 1) hue-- - if(6*hue < 1) return (a+(b-a)*6*hue) - if(2*hue < 1) return b - if(3*hue < 2) return (a+(b-a)*((2/3)-hue)*6) - return a - -/proc/num2septext(var/theNum, var/sigFig = 7,var/sep=",") // default sigFig (1,000,000) - var/finalNum = num2text(theNum, sigFig) - - // Start from the end, or from the decimal point - var/end = findtextEx(finalNum, ".") || length(finalNum) + 1 - - // Moving towards start of string, insert comma every 3 characters - for(var/pos = end - 3, pos > 1, pos -= 3) - finalNum = copytext(finalNum, 1, pos) + sep + copytext(finalNum, pos) - - return finalNum - - -// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ -/proc/heat2color(temp) - return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) - -/proc/heat2color_r(temp) - temp /= 100 - if(temp <= 66) - . = 255 - else - . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) - -/proc/heat2color_g(temp) - temp /= 100 - if(temp <= 66) - . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) - else - . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) - -/proc/heat2color_b(temp) - temp /= 100 - if(temp >= 66) - . = 255 - else - if(temp <= 16) - . = 0 - else - . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) - -//Argument: Give this a space-separated string consisting of 6 numbers. Returns null if you don't -/proc/text2matrix(var/matrixtext) - var/list/matrixtext_list = splittext(matrixtext, " ") - var/list/matrix_list = list() - for(var/item in matrixtext_list) - var/entry = text2num(item) - if(entry == null) - return null - matrix_list += entry - if(matrix_list.len < 6) - return null - var/a = matrix_list[1] - var/b = matrix_list[2] - var/c = matrix_list[3] - var/d = matrix_list[4] - var/e = matrix_list[5] - var/f = matrix_list[6] - return matrix(a, b, c, d, e, f) - - -//This is a weird one: -//It returns a list of all var names found in the string -//These vars must be in the [var_name] format -//It's only a proc because it's used in more than one place - -//Takes a string and a datum -//The string is well, obviously the string being checked -//The datum is used as a source for var names, to check validity -//Otherwise every single word could technically be a variable! -/proc/string2listofvars(var/t_string, var/datum/var_source) - if(!t_string || !var_source) - return list() - - . = list() - - var/var_found = findtext(t_string, "\[") //Not the actual variables, just a generic "should we even bother" check - if(var_found) - //Find var names - - // "A dog said hi [name]!" - // splittext() --> list("A dog said hi ","name]!" - // jointext() --> "A dog said hi name]!" - // splittext() --> list("A","dog","said","hi","name]!") - - t_string = replacetext(t_string, "\[", "\[ ")//Necessary to resolve "word[var_name]" scenarios - var/list/list_value = splittext(t_string, "\[") - var/intermediate_stage = jointext(list_value, null) - - list_value = splittext(intermediate_stage, " ") - for(var/value in list_value) - if(findtext(value, "]")) - value = splittext(value, "]") //"name]!" --> list("name","!") - for(var/A in value) - if(var_source.vars.Find(A)) - . += A - -/proc/type2parent(child) - var/string_type = "[child]" - var/last_slash = findlasttext(string_type, "/") - if(last_slash == 1) - switch(child) - if(/datum) - return null - if(/obj || /mob) - return /atom/movable - if(/area || /turf) - return /atom - else - return /datum - return text2path(copytext(string_type, 1, last_slash)) \ No newline at end of file +/* + * Holds procs designed to change one type of value, into another. + * Contains: + * hex2num & num2hex + * file2list + * angle2dir + * angle2text + * worldtime2text + */ + +//Returns an integer given a hex input +/proc/hex2num(hex) + if(!(istext(hex))) + return + + var/num = 0 + var/power = 0 + var/i = null + i = length(hex) + while(i > 0) + var/char = copytext(hex, i, i + 1) + switch(char) + if("0") + //Apparently, switch works with empty statements, yay! If that doesn't work, blame me, though. -- Urist + if("9", "8", "7", "6", "5", "4", "3", "2", "1") + num += text2num(char) * 16 ** power + if("a", "A") + num += 16 ** power * 10 + if("b", "B") + num += 16 ** power * 11 + if("c", "C") + num += 16 ** power * 12 + if("d", "D") + num += 16 ** power * 13 + if("e", "E") + num += 16 ** power * 14 + if("f", "F") + num += 16 ** power * 15 + else + return + power++ + i-- + return num + +//Returns the hex value of a number given a value assumed to be a base-ten value +/proc/num2hex(num, placeholder) + if(!isnum(num)) return + if(placeholder == null) placeholder = 2 + + var/hex = "" + while(num) + var/val = num % 16 + num = round(num / 16) + + if(val > 9) + val = ascii2text(55 + val) // 65 - 70 correspond to "A" - "F" + hex = "[val][hex]" + while(length(hex) < placeholder) + hex = "0[hex]" + return hex || "0" + +//Returns an integer value for R of R/G/B given a hex color input. +/proc/color2R(hex) + if(!(istext(hex))) + return + + return hex2num(copytext(hex, 2, 4)) //Returning R + +//Returns an integer value for G of R/G/B given a hex color input. +/proc/color2G(hex) + if(!(istext(hex))) + return + + return hex2num(copytext(hex, 4, 6)) //Returning G + +//Returns an integer value for B of R/G/B given a hex color input. +/proc/color2B(hex) + if(!(istext(hex))) + return + + return hex2num(copytext(hex, 6, 8)) //Returning B + +/proc/text2numlist(text, delimiter="\n") + var/list/num_list = list() + for(var/x in splittext(text, delimiter)) + num_list += text2num(x) + return num_list + +//Splits the text of a file at seperator and returns them in a list. +/proc/file2list(filename, seperator="\n") + return splittext(return_file_text(filename),seperator) + + +//Turns a direction into text + +/proc/num2dir(direction) + switch(direction) + if(1.0) return NORTH + if(2.0) return SOUTH + if(4.0) return EAST + if(8.0) return WEST + else + log_runtime(EXCEPTION("UNKNOWN DIRECTION: [direction]")) + +/proc/dir2text(direction) + switch(direction) + if(1.0) + return "north" + if(2.0) + return "south" + if(4.0) + return "east" + if(8.0) + return "west" + if(5.0) + return "northeast" + if(6.0) + return "southeast" + if(9.0) + return "northwest" + if(10.0) + return "southwest" + else + return + +//Turns text into proper directions +/proc/text2dir(direction) + switch(uppertext(direction)) + if("NORTH") + return 1 + if("SOUTH") + return 2 + if("EAST") + return 4 + if("WEST") + return 8 + if("NORTHEAST") + return 5 + if("NORTHWEST") + return 9 + if("SOUTHEAST") + return 6 + if("SOUTHWEST") + return 10 + else + return + +//Converts an angle (degrees) into an ss13 direction +/proc/angle2dir(var/degree) + degree = ((degree+22.5)%365) + if(degree < 45) return NORTH + if(degree < 90) return NORTHEAST + if(degree < 135) return EAST + if(degree < 180) return SOUTHEAST + if(degree < 225) return SOUTH + if(degree < 270) return SOUTHWEST + if(degree < 315) return WEST + return NORTH|WEST + +/proc/angle2dir_cardinal(angle) + switch(round(angle, 0.1)) + if(315.5 to 360, 0 to 45.5) + return NORTH + if(45.6 to 135.5) + return EAST + if(135.6 to 225.5) + return SOUTH + if(225.6 to 315.5) + return WEST + +//returns the north-zero clockwise angle in degrees, given a direction + +/proc/dir2angle(var/D) + switch(D) + if(NORTH) return 0 + if(SOUTH) return 180 + if(EAST) return 90 + if(WEST) return 270 + if(NORTHEAST) return 45 + if(SOUTHEAST) return 135 + if(NORTHWEST) return 315 + if(SOUTHWEST) return 225 + else return null + +//Returns the angle in english +/proc/angle2text(var/degree) + return dir2text(angle2dir(degree)) + +//Converts a blend_mode constant to one acceptable to icon.Blend() +/proc/blendMode2iconMode(blend_mode) + switch(blend_mode) + if(BLEND_MULTIPLY) return ICON_MULTIPLY + if(BLEND_ADD) return ICON_ADD + if(BLEND_SUBTRACT) return ICON_SUBTRACT + else return ICON_OVERLAY + +//Converts a rights bitfield into a string +/proc/rights2text(rights,seperator="") + if(rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" + if(rights & R_ADMIN) . += "[seperator]+ADMIN" + if(rights & R_BAN) . += "[seperator]+BAN" + if(rights & R_EVENT) . += "[seperator]+EVENT" + if(rights & R_SERVER) . += "[seperator]+SERVER" + if(rights & R_DEBUG) . += "[seperator]+DEBUG" + if(rights & R_POSSESS) . += "[seperator]+POSSESS" + if(rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" + if(rights & R_STEALTH) . += "[seperator]+STEALTH" + if(rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" + if(rights & R_VAREDIT) . += "[seperator]+VAREDIT" + if(rights & R_SOUNDS) . += "[seperator]+SOUND" + if(rights & R_SPAWN) . += "[seperator]+SPAWN" + if(rights & R_PROCCALL) . += "[seperator]+PROCCALL" + if(rights & R_MOD) . += "[seperator]+MODERATOR" + if(rights & R_MENTOR) . += "[seperator]+MENTOR" + if(rights & R_VIEWRUNTIMES) . += "[seperator]+VIEWRUNTIMES" + return . + +/proc/ui_style2icon(ui_style) + switch(ui_style) + if("Retro") + return 'icons/mob/screen_retro.dmi' + if("Plasmafire") + return 'icons/mob/screen_plasmafire.dmi' + if("Slimecore") + return 'icons/mob/screen_slimecore.dmi' + if("Operative") + return 'icons/mob/screen_operative.dmi' + if("White") + return 'icons/mob/screen_white.dmi' + else + return 'icons/mob/screen_midnight.dmi' + +//colour formats +/proc/rgb2hsl(red, green, blue) + red /= 255;green /= 255;blue /= 255; + var/max = max(red,green,blue) + var/min = min(red,green,blue) + var/range = max-min + + var/hue=0;var/saturation=0;var/lightness=0; + lightness = (max + min)/2 + if(range != 0) + if(lightness < 0.5) saturation = range/(max+min) + else saturation = range/(2-max-min) + + var/dred = ((max-red)/(6*max)) + 0.5 + var/dgreen = ((max-green)/(6*max)) + 0.5 + var/dblue = ((max-blue)/(6*max)) + 0.5 + + if(max==red) hue = dblue - dgreen + else if(max==green) hue = dred - dblue + (1/3) + else hue = dgreen - dred + (2/3) + if(hue < 0) hue++ + else if(hue > 1) hue-- + + return list(hue, saturation, lightness) + +/proc/hsl2rgb(hue, saturation, lightness) + var/red;var/green;var/blue; + if(saturation == 0) + red = lightness * 255 + green = red + blue = red + else + var/a;var/b; + if(lightness < 0.5) b = lightness*(1+saturation) + else b = (lightness+saturation) - (saturation*lightness) + a = 2*lightness - b + + red = round(255 * hue2rgb(a, b, hue+(1/3))) + green = round(255 * hue2rgb(a, b, hue)) + blue = round(255 * hue2rgb(a, b, hue-(1/3))) + + return list(red, green, blue) + +/proc/hue2rgb(a, b, hue) + if(hue < 0) hue++ + else if(hue > 1) hue-- + if(6*hue < 1) return (a+(b-a)*6*hue) + if(2*hue < 1) return b + if(3*hue < 2) return (a+(b-a)*((2/3)-hue)*6) + return a + +/proc/num2septext(var/theNum, var/sigFig = 7,var/sep=",") // default sigFig (1,000,000) + var/finalNum = num2text(theNum, sigFig) + + // Start from the end, or from the decimal point + var/end = findtextEx(finalNum, ".") || length(finalNum) + 1 + + // Moving towards start of string, insert comma every 3 characters + for(var/pos = end - 3, pos > 1, pos -= 3) + finalNum = copytext(finalNum, 1, pos) + sep + copytext(finalNum, pos) + + return finalNum + + +// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ +/proc/heat2color(temp) + return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) + +/proc/heat2color_r(temp) + temp /= 100 + if(temp <= 66) + . = 255 + else + . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) + +/proc/heat2color_g(temp) + temp /= 100 + if(temp <= 66) + . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) + else + . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) + +/proc/heat2color_b(temp) + temp /= 100 + if(temp >= 66) + . = 255 + else + if(temp <= 16) + . = 0 + else + . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) + +//Argument: Give this a space-separated string consisting of 6 numbers. Returns null if you don't +/proc/text2matrix(var/matrixtext) + var/list/matrixtext_list = splittext(matrixtext, " ") + var/list/matrix_list = list() + for(var/item in matrixtext_list) + var/entry = text2num(item) + if(entry == null) + return null + matrix_list += entry + if(matrix_list.len < 6) + return null + var/a = matrix_list[1] + var/b = matrix_list[2] + var/c = matrix_list[3] + var/d = matrix_list[4] + var/e = matrix_list[5] + var/f = matrix_list[6] + return matrix(a, b, c, d, e, f) + + +//This is a weird one: +//It returns a list of all var names found in the string +//These vars must be in the [var_name] format +//It's only a proc because it's used in more than one place + +//Takes a string and a datum +//The string is well, obviously the string being checked +//The datum is used as a source for var names, to check validity +//Otherwise every single word could technically be a variable! +/proc/string2listofvars(var/t_string, var/datum/var_source) + if(!t_string || !var_source) + return list() + + . = list() + + var/var_found = findtext(t_string, "\[") //Not the actual variables, just a generic "should we even bother" check + if(var_found) + //Find var names + + // "A dog said hi [name]!" + // splittext() --> list("A dog said hi ","name]!" + // jointext() --> "A dog said hi name]!" + // splittext() --> list("A","dog","said","hi","name]!") + + t_string = replacetext(t_string, "\[", "\[ ")//Necessary to resolve "word[var_name]" scenarios + var/list/list_value = splittext(t_string, "\[") + var/intermediate_stage = jointext(list_value, null) + + list_value = splittext(intermediate_stage, " ") + for(var/value in list_value) + if(findtext(value, "]")) + value = splittext(value, "]") //"name]!" --> list("name","!") + for(var/A in value) + if(var_source.vars.Find(A)) + . += A + +/proc/type2parent(child) + var/string_type = "[child]" + var/last_slash = findlasttext(string_type, "/") + if(last_slash == 1) + switch(child) + if(/datum) + return null + if(/obj || /mob) + return /atom/movable + if(/area || /turf) + return /atom + else + return /datum + return text2path(copytext(string_type, 1, last_slash)) diff --git a/code/__HELPERS/typelists.dm b/code/__HELPERS/typelists.dm index f271b9204de..3519eb60f3a 100644 --- a/code/__HELPERS/typelists.dm +++ b/code/__HELPERS/typelists.dm @@ -40,4 +40,4 @@ GLOBAL_LIST_EMPTY(typelistkeys) for (var/saving in savings) to_chat(world, "Savings for [saving]: [savings[saving]] lists, [saveditems[saving]] items") -#endif \ No newline at end of file +#endif diff --git a/code/__HELPERS/unique_ids.dm b/code/__HELPERS/unique_ids.dm index a66de0b5a88..549f5c71016 100644 --- a/code/__HELPERS/unique_ids.dm +++ b/code/__HELPERS/unique_ids.dm @@ -14,7 +14,7 @@ // var/myUID = mydatum.UID() // var/datum/D = locateUID(myUID) -var/global/next_unique_datum_id = 1 +GLOBAL_VAR_INIT(next_unique_datum_id, 1) // /client/var/tmp/unique_datum_id = null @@ -22,7 +22,7 @@ var/global/next_unique_datum_id = 1 if(!unique_datum_id) var/tag_backup = tag tag = null // Grab the raw ref, not the tag - unique_datum_id = "\ref[src]_[next_unique_datum_id++]" + unique_datum_id = "\ref[src]_[GLOB.next_unique_datum_id++]" tag = tag_backup return unique_datum_id diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 45283498ebd..aedc16c4162 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1129,9 +1129,9 @@ proc/get_mob_with_client_list() //For objects that should embed, but make no sense being is_sharp or is_pointed() //e.g: rods -var/list/can_embed_types = typecacheof(list( +GLOBAL_LIST_INIT(can_embed_types, typecacheof(list( /obj/item/stack/rods, - /obj/item/pipe)) + /obj/item/pipe))) /proc/can_embed(obj/item/W) if(is_sharp(W)) @@ -1139,7 +1139,7 @@ var/list/can_embed_types = typecacheof(list( if(is_pointed(W)) return 1 - if(is_type_in_typecache(W, can_embed_types)) + if(is_type_in_typecache(W, GLOB.can_embed_types)) return 1 /proc/is_hot(obj/item/W as obj) @@ -1221,18 +1221,18 @@ var/list/can_embed_types = typecacheof(list( /* Checks if that loc and dir has a item on the wall */ -var/list/static/global/wall_items = typecacheof(list(/obj/machinery/power/apc, /obj/machinery/alarm, +GLOBAL_LIST_INIT(wall_items, typecacheof(list(/obj/machinery/power/apc, /obj/machinery/alarm, /obj/item/radio/intercom, /obj/structure/extinguisher_cabinet, /obj/structure/reagent_dispensers/peppertank, /obj/machinery/status_display, /obj/machinery/requests_console, /obj/machinery/light_switch, /obj/structure/sign, /obj/machinery/newscaster, /obj/machinery/firealarm, /obj/structure/noticeboard, /obj/machinery/door_control, /obj/machinery/computer/security/telescreen, /obj/machinery/embedded_controller/radio/airlock, /obj/item/storage/secure/safe, /obj/machinery/door_timer, /obj/machinery/flasher, /obj/machinery/keycard_auth, /obj/structure/mirror, /obj/structure/closet/fireaxecabinet, /obj/machinery/computer/security/telescreen/entertainment, - /obj/structure/sign)) + /obj/structure/sign))) /proc/gotwallitem(loc, dir) for(var/obj/O in loc) - if(is_type_in_typecache(O, wall_items)) + if(is_type_in_typecache(O, GLOB.wall_items)) //Direction works sometimes if(O.dir == dir) return 1 @@ -1254,7 +1254,7 @@ var/list/static/global/wall_items = typecacheof(list(/obj/machinery/power/apc, / //Some stuff is placed directly on the wallturf (signs) for(var/obj/O in get_step(loc, dir)) - if(is_type_in_typecache(O, wall_items)) + if(is_type_in_typecache(O, GLOB.wall_items)) if(abs(O.pixel_x) <= 10 && abs(O.pixel_y) <= 10) return 1 return 0 @@ -1381,7 +1381,7 @@ atom/proc/GetTypeInAllContents(typepath) var/initial_chance = chance while(steps > 0) if(prob(chance)) - step(AM, pick(alldirs)) + step(AM, pick(GLOB.alldirs)) chance = max(chance - (initial_chance / steps), 0) steps-- @@ -1415,19 +1415,19 @@ atom/proc/GetTypeInAllContents(typepath) return locate(dest_x,dest_y,dest_z) -var/mob/dview/dview_mob = new +GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) //Version of view() which ignores darkness, because BYOND doesn't have it. /proc/dview(var/range = world.view, var/center, var/invis_flags = 0) if(!center) return - dview_mob.loc = center + GLOB.dview_mob.loc = center - dview_mob.see_invisible = invis_flags + GLOB.dview_mob.see_invisible = invis_flags - . = view(range, dview_mob) - dview_mob.loc = null + . = view(range, GLOB.dview_mob) + GLOB.dview_mob.loc = null /mob/dview invisibility = 101 @@ -1895,8 +1895,15 @@ var/mob/dview/dview_mob = new #undef DELTA_CALC -// This proc gets a list of all "points of interest" (poi's) that can be used by admins to track valuable mobs or atoms (such as the nuke disk). -/proc/getpois(mobs_only=0,skip_mindless=0) +/* + * This proc gets a list of all "points of interest" (poi's) that can be used by admins to track valuable mobs or atoms (such as the nuke disk). + * @param mobs_only if set to TRUE it won't include locations to the returned list + * @param skip_mindless if set to TRUE it will skip mindless mobs + * @param force_include_bots if set to TRUE it will include bots even if skip_mindless is set to TRUE + * @param force_include_cameras if set to TRUE it will include camera eyes even if skip_mindless is set to TRUE + * @return returns a list with the found points of interest +*/ +/proc/getpois(mobs_only = FALSE, skip_mindless = FALSE, force_include_bots = FALSE, force_include_cameras = FALSE) var/list/mobs = sortmobs() var/list/names = list() var/list/pois = list() @@ -1904,7 +1911,7 @@ var/mob/dview/dview_mob = new for(var/mob/M in mobs) if(skip_mindless && (!M.mind && !M.ckey)) - if(!isbot(M) && !istype(M, /mob/camera/)) + if(!(force_include_bots && isbot(M)) && !(force_include_cameras && istype(M, /mob/camera))) continue if(M.client && M.client.holder && M.client.holder.fakekey) //stealthmins continue diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 968a2f4d577..536ea01cc96 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -28,4 +28,4 @@ This may require updating to a beta release. #endif // Macros that must exist before world.dm -#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat \ No newline at end of file +// #define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat diff --git a/code/_globalvars/configuration.dm b/code/_globalvars/configuration.dm index ee3f6a73a92..ea2cf1290fd 100644 --- a/code/_globalvars/configuration.dm +++ b/code/_globalvars/configuration.dm @@ -1,45 +1,44 @@ -var/datum/configuration/config = null +GLOBAL_REAL(config, /datum/configuration) -var/host = null -var/join_motd = null +GLOBAL_VAR(host) +GLOBAL_VAR(join_motd) GLOBAL_VAR(join_tos) -var/game_version = "ParaCode" -var/changelog_hash = md5('html/changelog.html') //used to check if the CL changed -var/game_year = (text2num(time2text(world.realtime, "YYYY")) + 544) +GLOBAL_VAR_INIT(game_version, "ParaCode") +GLOBAL_VAR_INIT(game_year, (text2num(time2text(world.realtime, "YYYY")) + 544)) -var/aliens_allowed = 1 -var/traitor_scaling = 1 -//var/goonsay_allowed = 0 -var/dna_ident = 1 -var/abandon_allowed = 0 -var/enter_allowed = 1 -var/guests_allowed = 1 -var/shuttle_frozen = 0 -var/shuttle_left = 0 -var/tinted_weldhelh = 1 -var/mouse_respawn_time = 5 //Amount of time that must pass between a player dying as a mouse and repawning as a mouse. In minutes. - -// Command to run if shutting down (SHUTDOWN_ON_REBOOT) instead of rebooting -// It's defined here as a global because this is a hilariously bad thing to have on the easily-edited config datum -var/global/shutdown_shell_command - -// Also global to prevent easy edits -var/global/python_path = "" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix +GLOBAL_VAR_INIT(aliens_allowed, 1) +GLOBAL_VAR_INIT(traitor_scaling, 1) +//GLOBAL_VAR_INIT(goonsay_allowed, 0) +GLOBAL_VAR_INIT(dna_ident, 1) +GLOBAL_VAR_INIT(abandon_allowed, 0) +GLOBAL_VAR_INIT(enter_allowed, 1) +GLOBAL_VAR_INIT(guests_allowed, 1) +GLOBAL_VAR_INIT(shuttle_frozen, 0) +GLOBAL_VAR_INIT(shuttle_left, 0) +GLOBAL_VAR_INIT(tinted_weldhelh, 1) +GLOBAL_VAR_INIT(mouse_respawn_time, 5) //Amount of time that must pass between a player dying as a mouse and repawning as a mouse. In minutes. // Debug is used exactly once (in living.dm) but is commented out in a lot of places. It is not set anywhere and only checked. // Debug2 is used in conjunction with a lot of admin verbs and therefore is actually legit. -var/Debug = 0 // global debug switch -var/Debug2 = 1 // enables detailed job debug file in secrets +GLOBAL_VAR_INIT(debug, 0) // global debug switch +GLOBAL_VAR_INIT(debug2, 1) // enables detailed job debug file in secrets //This was a define, but I changed it to a variable so it can be changed in-game.(kept the all-caps definition because... code...) -Errorage -var/MAX_EX_DEVASTATION_RANGE = 3 -var/MAX_EX_HEAVY_RANGE = 7 -var/MAX_EX_LIGHT_RANGE = 14 -var/MAX_EX_FLASH_RANGE = 14 -var/MAX_EX_FLAME_RANGE = 14 +GLOBAL_VAR_INIT(max_ex_devastation_range, 3) +GLOBAL_VAR_INIT(max_ex_heavy_range, 7) +GLOBAL_VAR_INIT(max_ex_light_range, 14) +GLOBAL_VAR_INIT(max_ex_flash_range, 14) +GLOBAL_VAR_INIT(max_ex_flame_range, 14) //Random event stuff, apparently used -var/eventchance = 10 //% per 5 mins -var/event = 0 -var/hadevent = 0 -var/blobevent = 0 +GLOBAL_VAR_INIT(eventchance, 10) //% per 5 mins +GLOBAL_VAR_INIT(event, 0) +GLOBAL_VAR_INIT(hadevent, 0) +GLOBAL_VAR_INIT(blobevent, 0) + +// These vars are protected because changing them could pose a security risk, though they are fine to be read since they are just system paths +GLOBAL_VAR(shutdown_shell_command) // Command to run if shutting down (SHUTDOWN_ON_REBOOT) instead of rebooting +GLOBAL_PROTECT(shutdown_shell_command) + +GLOBAL_VAR(python_path) //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix +GLOBAL_PROTECT(python_path) diff --git a/code/_globalvars/database.dm b/code/_globalvars/database.dm deleted file mode 100644 index 5c5b4e93f6a..00000000000 --- a/code/_globalvars/database.dm +++ /dev/null @@ -1,11 +0,0 @@ -// MySQL configuration -var/sqladdress = "localhost" -var/sqlport = "3306" -var/sqlfdbkdb = "test" -var/sqlfdbklogin = "root" -var/sqlfdbkpass = "" -var/sqlfdbktableprefix = "erro_" //backwords compatibility with downstream server hosts - -//Database connections -//A connection is established on world creation. Ideally, the connection dies when the server restarts (After feedback logging.). -var/DBConnection/dbcon = new() //Feedback database (New database) diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm index 3f8ec321470..3088270579c 100644 --- a/code/_globalvars/game_modes.dm +++ b/code/_globalvars/game_modes.dm @@ -1,10 +1,11 @@ -var/master_mode = "extended"//"extended" -var/secret_force_mode = "secret" // if this is anything but "secret", the secret rotation will forceably choose this mode -var/wavesecret = 0 // meteor mode, delays wave progression, terrible name -var/datum/station_state/start_state = null // Used in round-end report +GLOBAL_VAR_INIT(master_mode, "extended") //"extended" +GLOBAL_VAR_INIT(secret_force_mode, "secret") // if this is anything but "secret", the secret rotation will forceably choose this mode -var/custom_event_msg = null -var/custom_event_admin_msg = null +GLOBAL_VAR_INIT(wavesecret, 0) // meteor mode, delays wave progression, terrible name +GLOBAL_DATUM(start_state, /datum/station_state) // Used in round-end report. Dont ask why it inits as null -var/list/summon_spots = list() +GLOBAL_VAR(custom_event_msg) +GLOBAL_VAR(custom_event_admin_msg) + +GLOBAL_LIST_EMPTY(summon_spots) diff --git a/code/_globalvars/genetics.dm b/code/_globalvars/genetics.dm index 6a4a118f866..1119ac364cc 100644 --- a/code/_globalvars/genetics.dm +++ b/code/_globalvars/genetics.dm @@ -1,68 +1,68 @@ /////////// -var/BLINDBLOCK = 0 -var/COLOURBLINDBLOCK = 0 -var/DEAFBLOCK = 0 -var/HULKBLOCK = 0 -var/TELEBLOCK = 0 -var/FIREBLOCK = 0 -var/XRAYBLOCK = 0 -var/CLUMSYBLOCK = 0 -var/FAKEBLOCK = 0 -var/COUGHBLOCK = 0 -var/GLASSESBLOCK = 0 -var/EPILEPSYBLOCK = 0 -var/TWITCHBLOCK = 0 -var/NERVOUSBLOCK = 0 -var/WINGDINGSBLOCK = 0 -var/MONKEYBLOCK = 50 // Monkey block will always be the DNA_SE_LENGTH +GLOBAL_VAR_INIT(blindblock, 0) +GLOBAL_VAR_INIT(colourblindblock, 0) +GLOBAL_VAR_INIT(deafblock, 0) +GLOBAL_VAR_INIT(hulkblock, 0) +GLOBAL_VAR_INIT(teleblock, 0) +GLOBAL_VAR_INIT(fireblock, 0) +GLOBAL_VAR_INIT(xrayblock, 0) +GLOBAL_VAR_INIT(clumsyblock, 0) +GLOBAL_VAR_INIT(fakeblock, 0) +GLOBAL_VAR_INIT(coughblock, 0) +GLOBAL_VAR_INIT(glassesblock, 0) +GLOBAL_VAR_INIT(epilepsyblock, 0) +GLOBAL_VAR_INIT(twitchblock, 0) +GLOBAL_VAR_INIT(nervousblock, 0) +GLOBAL_VAR_INIT(wingdingsblock, 0) +GLOBAL_VAR_INIT(monkeyblock, DNA_SE_LENGTH) // Monkey block will always be the DNA_SE_LENGTH -var/BLOCKADD = 0 -var/DIFFMUT = 0 +GLOBAL_VAR_INIT(blockadd, 0) +GLOBAL_VAR_INIT(diffmut, 0) -var/BREATHLESSBLOCK = 0 -var/REMOTEVIEWBLOCK = 0 -var/REGENERATEBLOCK = 0 -var/INCREASERUNBLOCK = 0 -var/REMOTETALKBLOCK = 0 -var/MORPHBLOCK = 0 -var/COLDBLOCK = 0 -var/HALLUCINATIONBLOCK = 0 -var/NOPRINTSBLOCK = 0 -var/SHOCKIMMUNITYBLOCK = 0 -var/SMALLSIZEBLOCK = 0 +GLOBAL_VAR_INIT(breathlessblock, 0) +GLOBAL_VAR_INIT(remoteviewblock, 0) +GLOBAL_VAR_INIT(regenerateblock, 0) +GLOBAL_VAR_INIT(increaserunblock, 0) +GLOBAL_VAR_INIT(remotetalkblock, 0) +GLOBAL_VAR_INIT(morphblock, 0) +GLOBAL_VAR_INIT(coldblock, 0) +GLOBAL_VAR_INIT(hallucinationblock, 0) +GLOBAL_VAR_INIT(noprintsblock, 0) +GLOBAL_VAR_INIT(shockimmunityblock, 0) +GLOBAL_VAR_INIT(smallsizeblock, 0) /////////////////////////////// // Goon Stuff /////////////////////////////// // Disabilities -var/LISPBLOCK = 0 -var/MUTEBLOCK = 0 -var/RADBLOCK = 0 -var/FATBLOCK = 0 -var/CHAVBLOCK = 0 -var/SWEDEBLOCK = 0 -var/SCRAMBLEBLOCK = 0 -var/STRONGBLOCK = 0 -var/HORNSBLOCK = 0 -var/COMICBLOCK = 0 +GLOBAL_VAR_INIT(lispblock, 0) +GLOBAL_VAR_INIT(muteblock, 0) +GLOBAL_VAR_INIT(radblock, 0) +GLOBAL_VAR_INIT(fatblock, 0) +GLOBAL_VAR_INIT(chavblock, 0) +GLOBAL_VAR_INIT(swedeblock, 0) +GLOBAL_VAR_INIT(scrambleblock, 0) +GLOBAL_VAR_INIT(strongblock, 0) +GLOBAL_VAR_INIT(hornsblock, 0) +GLOBAL_VAR_INIT(comicblock, 0) // Powers -var/SOBERBLOCK = 0 -var/PSYRESISTBLOCK = 0 -var/SHADOWBLOCK = 0 -var/CHAMELEONBLOCK = 0 -var/CRYOBLOCK = 0 -var/EATBLOCK = 0 -var/JUMPBLOCK = 0 -var/EMPATHBLOCK = 0 -var/IMMOLATEBLOCK = 0 -var/POLYMORPHBLOCK = 0 +GLOBAL_VAR_INIT(soberblock, 0) +GLOBAL_VAR_INIT(psyresistblock, 0) +GLOBAL_VAR_INIT(shadowblock, 0) +GLOBAL_VAR_INIT(chameleonblock, 0) +GLOBAL_VAR_INIT(cryoblock, 0) +GLOBAL_VAR_INIT(eatblock, 0) +GLOBAL_VAR_INIT(jumpblock, 0) +GLOBAL_VAR_INIT(empathblock, 0) +GLOBAL_VAR_INIT(immolateblock, 0) +GLOBAL_VAR_INIT(polymorphblock, 0) /////////////////////////////// // /vg/ Mutations /////////////////////////////// -var/LOUDBLOCK = 0 -var/DIZZYBLOCK = 0 +GLOBAL_VAR_INIT(loudblock, 0) +GLOBAL_VAR_INIT(dizzyblock, 0) -var/list/reg_dna = list( ) //this appears to be a list of UE == real_name correlations -var/list/global_mutations = list() // list of hidden mutation things +GLOBAL_LIST_EMPTY(reg_dna) +GLOBAL_LIST_EMPTY(global_mutations) diff --git a/code/_globalvars/lists/devil.dm b/code/_globalvars/lists/devil.dm index 2c941069fe4..54528709573 100644 --- a/code/_globalvars/lists/devil.dm +++ b/code/_globalvars/lists/devil.dm @@ -1,8 +1,8 @@ //what could possibly go wrong -var/list/devil_machines = typecacheof(/obj/item/circuitboard, TRUE) - list( +GLOBAL_LIST_INIT(devil_machines, typecacheof(/obj/item/circuitboard, TRUE) - list( /obj/item/circuitboard/shuttle, /obj/item/circuitboard/swfdoor, /obj/item/circuitboard/olddoor, /obj/item/circuitboard/computer, /obj/item/circuitboard/machine - ) +)) diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm index feadeece28f..744e595ad3b 100644 --- a/code/_globalvars/lists/names.dm +++ b/code/_globalvars/lists/names.dm @@ -21,4 +21,4 @@ GLOBAL_LIST_INIT(nightmare_strings, file2list("config/names/nightmares.txt")) //loaded on startup because of " //would include in rsc if ' was used -GLOBAL_LIST_INIT(vox_name_syllables, list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) \ No newline at end of file +GLOBAL_LIST_INIT(vox_name_syllables, list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index f383ba3fc33..931a0e394e8 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -51,4 +51,4 @@ GLOBAL_LIST_EMPTY(mob_spawners) // All mob_spawn objects GLOBAL_LIST_EMPTY(explosive_walls) -GLOBAL_LIST_EMPTY(engine_beacon_list) \ No newline at end of file +GLOBAL_LIST_EMPTY(engine_beacon_list) diff --git a/code/_globalvars/lists/reagents.dm b/code/_globalvars/lists/reagents.dm index 981eb63aa7e..abd878ff936 100644 --- a/code/_globalvars/lists/reagents.dm +++ b/code/_globalvars/lists/reagents.dm @@ -63,4 +63,6 @@ GLOBAL_LIST_INIT(blocked_chems, list("polonium", "initropidril", "concentrated_i GLOBAL_LIST_INIT(safe_chem_list, list("antihol", "charcoal", "epinephrine", "insulin", "teporone","silver_sulfadiazine", "salbutamol", "omnizine", "stimulants", "synaptizine", "potass_iodide", "oculine", "mannitol", "styptic_powder", "spaceacillin", "salglu_solution", "sal_acid", "cryoxadone", "blood", "synthflesh", "hydrocodone", - "mitocholide", "rezadone")) \ No newline at end of file + "mitocholide", "rezadone")) + +GLOBAL_LIST_INIT(safe_chem_applicator_list, list("silver_sulfadiazine", "styptic_powder", "synthflesh")) diff --git a/code/_globalvars/lists/typecache.dm b/code/_globalvars/lists/typecache.dm index 2600e5c287f..96807214f2f 100644 --- a/code/_globalvars/lists/typecache.dm +++ b/code/_globalvars/lists/typecache.dm @@ -9,4 +9,4 @@ GLOBAL_LIST_INIT(typecache_living, typecacheof(/mob/living)) GLOBAL_LIST_INIT(typecache_stack, typecacheof(/obj/item/stack)) -GLOBAL_LIST_INIT(typecache_machine_or_structure, typecacheof(list(/obj/machinery, /obj/structure))) \ No newline at end of file +GLOBAL_LIST_INIT(typecache_machine_or_structure, typecacheof(list(/obj/machinery, /obj/structure))) diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 10c7d74c752..0ea513708a1 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -1,3 +1,4 @@ +// Everything in this file should be protected GLOBAL_VAR(log_directory) GLOBAL_PROTECT(log_directory) GLOBAL_VAR(world_game_log) @@ -15,14 +16,20 @@ GLOBAL_PROTECT(world_asset_log) GLOBAL_VAR(runtime_summary_log) GLOBAL_PROTECT(runtime_summary_log) -var/list/jobMax = list() -var/list/admin_log = list ( ) -var/list/lastsignalers = list( ) //keeps last 100 signals here in format: "[src] used \ref[src] @ location [src.loc]: [freq]/[code]" -var/list/lawchanges = list( ) //Stores who uploaded laws to which silicon-based lifeform, and what the law was +GLOBAL_LIST_EMPTY(jobMax) +GLOBAL_PROTECT(jobMax) +GLOBAL_LIST_EMPTY(admin_log) +GLOBAL_PROTECT(admin_log) +GLOBAL_LIST_EMPTY(lastsignalers) +GLOBAL_PROTECT(lastsignalers) +GLOBAL_LIST_EMPTY(lawchanges) +GLOBAL_PROTECT(lawchanges) -var/list/combatlog = list() -var/list/IClog = list() -var/list/OOClog = list() -var/list/adminlog = list() +GLOBAL_LIST_EMPTY(combatlog) +GLOBAL_PROTECT(combatlog) +GLOBAL_LIST_EMPTY(IClog) +GLOBAL_PROTECT(IClog) +GLOBAL_LIST_EMPTY(OOClog) +GLOBAL_PROTECT(OOClog) -var/list/investigate_log_subjects = list("notes", "watchlist", "hrefs") +GLOBAL_LIST_INIT(investigate_log_subjects, list("notes", "watchlist", "hrefs")) diff --git a/code/_globalvars/mapping.dm b/code/_globalvars/mapping.dm index c1efb29075d..ca1c899e301 100644 --- a/code/_globalvars/mapping.dm +++ b/code/_globalvars/mapping.dm @@ -3,12 +3,14 @@ #define Z_SOUTH 3 #define Z_WEST 4 -var/list/cardinal = list( NORTH, SOUTH, EAST, WEST ) -var/list/alldirs = list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) -var/list/diagonals = list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) +GLOBAL_LIST_INIT(cardinal, list( NORTH, SOUTH, EAST, WEST )) +GLOBAL_LIST_INIT(alldirs, list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) +GLOBAL_LIST_INIT(diagonals, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) -var/global/list/global_map = null - //list/global_map = list(list(1,5),list(4,3))//an array of map Z levels. +// This must exist early on or shit breaks bad +GLOBAL_DATUM_INIT(using_map, /datum/map, new USING_MAP_DATUM) + +GLOBAL_LIST(global_map) // This is the array of zlevels | list(list(1,5),list(4,3)) | becomes a 2D array of zlevels //Resulting sector map looks like //|_1_|_4_| //|_5_|_3_| @@ -18,38 +20,37 @@ var/global/list/global_map = null //3 - AI satellite //5 - empty space -var/list/wizardstart = list() -var/list/newplayer_start = list() -var/list/latejoin = list() -var/list/latejoin_gateway = list() -var/list/latejoin_cryo = list() -var/list/latejoin_cyborg = list() -var/list/prisonwarp = list() //prisoners go to these -var/list/xeno_spawn = list()//Aliens spawn at these. -var/list/ertdirector = list() -var/list/emergencyresponseteamspawn = list() -var/list/tdome1 = list() -var/list/tdome2 = list() -var/list/team_alpha = list() -var/list/team_bravo = list() -var/list/tdomeobserve = list() -var/list/tdomeadmin = list() -var/list/aroomwarp = list() -var/list/prisonsecuritywarp = list() //prison security goes to these -var/list/prisonwarped = list() //list of players already warped -var/list/blobstart = list() -var/list/ninjastart = list() -var/list/carplist = list() //list of all carp-spawn landmarks -var/list/syndicateofficer = list() +GLOBAL_LIST_EMPTY(wizardstart) +GLOBAL_LIST_EMPTY(newplayer_start) +GLOBAL_LIST_EMPTY(latejoin) +GLOBAL_LIST_EMPTY(latejoin_gateway) +GLOBAL_LIST_EMPTY(latejoin_cryo) +GLOBAL_LIST_EMPTY(latejoin_cyborg) +GLOBAL_LIST_EMPTY(prisonwarp) //prisoners go to these +GLOBAL_LIST_EMPTY(xeno_spawn)//Aliens spawn at these. +GLOBAL_LIST_EMPTY(ertdirector) +GLOBAL_LIST_EMPTY(emergencyresponseteamspawn) +GLOBAL_LIST_EMPTY(tdome1) +GLOBAL_LIST_EMPTY(tdome2) +GLOBAL_LIST_EMPTY(team_alpha) +GLOBAL_LIST_EMPTY(team_bravo) +GLOBAL_LIST_EMPTY(tdomeobserve) +GLOBAL_LIST_EMPTY(tdomeadmin) +GLOBAL_LIST_EMPTY(aroomwarp) +GLOBAL_LIST_EMPTY(prisonsecuritywarp) //prison security goes to these +GLOBAL_LIST_EMPTY(prisonwarped) //list of players already warped +GLOBAL_LIST_EMPTY(blobstart) +GLOBAL_LIST_EMPTY(ninjastart) +GLOBAL_LIST_EMPTY(carplist) //list of all carp-spawn landmarks +GLOBAL_LIST_EMPTY(syndicateofficer) //away missions -var/list/awaydestinations = list() //a list of landmarks that the warpgate can take you to +GLOBAL_LIST_EMPTY(awaydestinations) //a list of landmarks that the warpgate can take you to //List of preloaded templates -var/list/datum/map_template/map_templates = list() - -var/list/datum/map_template/ruins_templates = list() -var/list/datum/map_template/space_ruins_templates = list() -var/list/datum/map_template/lava_ruins_templates = list() -var/list/datum/map_template/shelter_templates = list() -var/list/datum/map_template/shuttle_templates = list() \ No newline at end of file +GLOBAL_LIST_EMPTY(map_templates) +GLOBAL_LIST_EMPTY(ruins_templates) +GLOBAL_LIST_EMPTY(space_ruins_templates) +GLOBAL_LIST_EMPTY(lava_ruins_templates) +GLOBAL_LIST_EMPTY(shelter_templates) +GLOBAL_LIST_EMPTY(shuttle_templates) diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index f4cdd766cb9..32d6370f3d0 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -1,93 +1,103 @@ -var/global/obj/effect/overlay/plmaster = null -var/global/obj/effect/overlay/slmaster = null +GLOBAL_DATUM(plmaster, /obj/effect/overlay) +GLOBAL_DATUM(slmaster, /obj/effect/overlay) GLOBAL_VAR_INIT(CELLRATE, 0.002) // conversion ratio between a watt-tick and kilojoule GLOBAL_VAR_INIT(CHARGELEVEL, 0.001) // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second) // Announcer intercom, because too much stuff creates an intercom for one message then hard del()s it. -var/global/obj/item/radio/intercom/global_announcer = create_global_announcer() -var/global/obj/item/radio/intercom/command/command_announcer = create_command_announcer() +GLOBAL_DATUM_INIT(global_announcer, /obj/item/radio/intercom, create_global_announcer()) +GLOBAL_DATUM_INIT(command_announcer, /obj/item/radio/intercom/command, create_command_announcer()) + // Load order issues means this can't be new'd until other code runs // This is probably not the way I should be doing this, but I don't know how to do it right! proc/create_global_announcer() spawn(0) - global_announcer = new(null) + GLOB.global_announcer = new(null) return proc/create_command_announcer() spawn(0) - command_announcer = new(null) + GLOB.command_announcer = new(null) return -var/list/paper_tag_whitelist = list("center","p","div","span","h1","h2","h3","h4","h5","h6","hr","pre", \ +GLOBAL_LIST_INIT(paper_tag_whitelist, list("center","p","div","span","h1","h2","h3","h4","h5","h6","hr","pre", \ "big","small","font","i","u","b","s","sub","sup","tt","br","hr","ol","ul","li","caption","col", \ - "table","td","th","tr") -var/list/paper_blacklist = list("java","onblur","onchange","onclick","ondblclick","onfocus","onkeydown", \ + "table","td","th","tr")) +GLOBAL_LIST_INIT(paper_blacklist, list("java","onblur","onchange","onclick","ondblclick","onfocus","onkeydown", \ "onkeypress","onkeyup","onload","onmousedown","onmousemove","onmouseout","onmouseover", \ - "onmouseup","onreset","onselect","onsubmit","onunload") + "onmouseup","onreset","onselect","onsubmit","onunload")) //Reverse of dir -var/list/reverse_dir = list(2, 1, 3, 8, 10, 9, 11, 4, 6, 5, 7, 12, 14, 13, 15, 32, 34, 33, 35, 40, 42, 41, 43, 36, 38, 37, 39, 44, 46, 45, 47, 16, 18, 17, 19, 24, 26, 25, 27, 20, 22, 21, 23, 28, 30, 29, 31, 48, 50, 49, 51, 56, 58, 57, 59, 52, 54, 53, 55, 60, 62, 61, 63) -var/gravity_is_on = 1 //basically unused, just one admin verb.. +GLOBAL_LIST_INIT(reverse_dir, list(2, 1, 3, 8, 10, 9, 11, 4, 6, 5, 7, 12, 14, 13, 15, 32, 34, 33, 35, 40, 42, 41, 43, 36, 38, 37, 39, 44, 46, 45, 47, 16, 18, 17, 19, 24, 26, 25, 27, 20, 22, 21, 23, 28, 30, 29, 31, 48, 50, 49, 51, 56, 58, 57, 59, 52, 54, 53, 55, 60, 62, 61, 63)) +GLOBAL_VAR_INIT(gravity_is_on, 1) //basically unused, just one admin verb.. // Recall time limit: 2 hours -var/recall_time_limit=72000 //apparently used for the comm console +GLOBAL_VAR_INIT(recall_time_limit, 72000) //apparently used for the comm console //////SCORE STUFF //Goonstyle scoreboard -var/score_crewscore = 0 // this is the overall var/score for the whole round -var/score_stuffshipped = 0 // how many useful items have cargo shipped out? -var/score_stuffharvested = 0 // how many harvests have hydroponics done? -var/score_oremined = 0 // obvious -var/score_researchdone = 0 -var/score_eventsendured = 0 // how many random events did the station survive? -var/score_powerloss = 0 // how many APCs have poor charge? -var/score_escapees = 0 // how many people got out alive? -var/score_deadcrew = 0 // dead bodies on the station, oh no -var/score_mess = 0 // how much poo, puke, gibs, etc went uncleaned -var/score_meals = 0 -var/score_disease = 0 // how many rampant, uncured diseases are on board the station -var/score_deadcommand = 0 // used during rev, how many command staff perished -var/score_arrested = 0 // how many traitors/revs/whatever are alive in the brig -var/score_traitorswon = 0 // how many traitors were successful? -var/score_allarrested = 0 // did the crew catch all the enemies alive? -var/score_opkilled = 0 // used during nuke mode, how many operatives died? -var/score_disc = 0 // is the disc safe and secure? -var/score_nuked = 0 // was the station blown into little bits? -var/score_nuked_penalty = 0 //penalty for getting blown to bits +GLOBAL_VAR_INIT(score_crewscore, 0) // this is the overall var/score for the whole round +GLOBAL_VAR_INIT(score_stuffshipped, 0) // how many useful items have cargo shipped out? +GLOBAL_VAR_INIT(score_stuffharvested, 0) // how many harvests have hydroponics done? +GLOBAL_VAR_INIT(score_oremined, 0) // obvious +GLOBAL_VAR_INIT(score_researchdone, 0) +GLOBAL_VAR_INIT(score_eventsendured, 0) // how many random events did the station survive? +GLOBAL_VAR_INIT(score_powerloss, 0) // how many APCs have poor charge? +GLOBAL_VAR_INIT(score_escapees, 0) // how many people got out alive? +GLOBAL_VAR_INIT(score_deadcrew, 0) // dead bodies on the station, oh no +GLOBAL_VAR_INIT(score_mess, 0) // how much poo, puke, gibs, etc went uncleaned +GLOBAL_VAR_INIT(score_meals, 0) +GLOBAL_VAR_INIT(score_disease, 0) // how many rampant, uncured diseases are on board the station +GLOBAL_VAR_INIT(score_deadcommand, 0) // used during rev, how many command staff perished +GLOBAL_VAR_INIT(score_arrested, 0) // how many traitors/revs/whatever are alive in the brig +GLOBAL_VAR_INIT(score_traitorswon, 0) // how many traitors were successful? +GLOBAL_VAR_INIT(score_allarrested, 0) // did the crew catch all the enemies alive? +GLOBAL_VAR_INIT(score_opkilled, 0) // used during nuke mode, how many operatives died? +GLOBAL_VAR_INIT(score_disc, 0) // is the disc safe and secure? +GLOBAL_VAR_INIT(score_nuked, 0) // was the station blown into little bits? +GLOBAL_VAR_INIT(score_nuked_penalty, 0) //penalty for getting blown to bits // these ones are mainly for the stat panel -var/score_powerbonus = 0 // if all APCs on the station are running optimally, big bonus -var/score_messbonus = 0 // if there are no messes on the station anywhere, huge bonus -var/score_deadaipenalty = 0 // is the AI dead? if so, big penalty -var/score_foodeaten = 0 // nom nom nom -var/score_clownabuse = 0 // how many times a clown was punched, struck or otherwise maligned -var/score_richestname = null // this is all stuff to show who was the richest alive on the shuttle -var/score_richestjob = null // kinda pointless if you dont have a money system i guess -var/score_richestcash = 0 -var/score_richestkey = null -var/score_dmgestname = null // who had the most damage on the shuttle (but was still alive) -var/score_dmgestjob = null -var/score_dmgestdamage = 0 -var/score_dmgestkey = null +GLOBAL_VAR_INIT(score_powerbonus, 0) // if all APCs on the station are running optimally, big bonus +GLOBAL_VAR_INIT(score_messbonus, 0) // if there are no messes on the station anywhere, huge bonus +GLOBAL_VAR_INIT(score_deadaipenalty, 0) // is the AI dead? if so, big penalty +GLOBAL_VAR_INIT(score_foodeaten, 0) // nom nom nom +GLOBAL_VAR_INIT(score_clownabuse, 0) // how many times a clown was punched, struck or otherwise maligned +GLOBAL_VAR(score_richestname) // this is all stuff to show who was the richest alive on the shuttle +GLOBAL_VAR(score_richestjob) // kinda pointless if you dont have a money system i guess +GLOBAL_VAR_INIT(score_richestcash, 0) +GLOBAL_VAR(score_richestkey) +GLOBAL_VAR(score_dmgestname) // who had the most damage on the shuttle (but was still alive) +GLOBAL_VAR(score_dmgestjob) +GLOBAL_VAR_INIT(score_dmgestdamage, 0) +GLOBAL_VAR(score_dmgestkey) -var/TAB = "    " +#define TAB "    " GLOBAL_VAR_INIT(timezoneOffset, 0) // The difference betwen midnight (of the host computer) and 0 world.ticks. // For FTP requests. (i.e. downloading runtime logs.) // However it'd be ok to use for accessing attack logs and such too, which are even laggier. -var/fileaccess_timer = 0 +GLOBAL_VAR_INIT(fileaccess_timer, 0) GLOBAL_VAR_INIT(gametime_offset, 432000) // 12:00 in seconds //printers shutdown if too much shit printed -var/copier_items_printed = 0 -var/copier_max_items = 300 -var/copier_items_printed_logged = FALSE +GLOBAL_VAR_INIT(copier_items_printed, 0) +GLOBAL_VAR_INIT(copier_max_items, 300) +GLOBAL_VAR_INIT(copier_items_printed_logged, FALSE) GLOBAL_VAR(map_name) // Self explanatory -var/global/datum/datacore/data_core = null // Station datacore, manifest, etc +GLOBAL_DATUM(data_core, /datum/datacore) // Station datacore, manifest, etc -GLOBAL_VAR_INIT(panic_bunker_enabled, 0) // Is the panic bunker enabled \ No newline at end of file +GLOBAL_VAR_INIT(panic_bunker_enabled, FALSE) // Is the panic bunker enabled + +//Database connections +//A connection is established on world creation. Ideally, the connection dies when the server restarts (After feedback logging.). +// This is a protected datum which means its read only. I hope the reason for protecting this is obvious +GLOBAL_DATUM_INIT(dbcon, /DBConnection, new) //Feedback database (New database) +GLOBAL_PROTECT(dbcon) + +GLOBAL_LIST_EMPTY(ability_verbs) // Create-level abilities +GLOBAL_LIST_INIT(pipe_colors, list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE)) diff --git a/code/_globalvars/sensitive.dm b/code/_globalvars/sensitive.dm new file mode 100644 index 00000000000..b1e6751c4dd --- /dev/null +++ b/code/_globalvars/sensitive.dm @@ -0,0 +1,12 @@ +// All global vars in this file require a different handler as we dont want their data to be exposed, not even to admins with permission to view globals +// They write as native BYOND globals, which means they cant be edited at all, and also use a format +// Please only throw absolutely crucial do-not-view shit in here please. -aa07 + +// MySQL configuration +GLOBAL_REAL_VAR(sqladdress) = "localhost" +GLOBAL_REAL_VAR(sqlport) = "3306" +GLOBAL_REAL_VAR(sqlfdbkdb) = "test" +GLOBAL_REAL_VAR(sqlfdbklogin) = "root" +GLOBAL_REAL_VAR(sqlfdbkpass) = "" +GLOBAL_REAL_VAR(sqlfdbktableprefix) = "erro_" + diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index d545a2aac1a..ccc31e5c501 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -5,7 +5,10 @@ */ GLOBAL_LIST_INIT(traits_by_type, list( /mob = list( - "TRAIT_PACIFISM" = TRAIT_PACIFISM + "TRAIT_PACIFISM" = TRAIT_PACIFISM, + "TRAIT_WATERBREATH" = TRAIT_WATERBREATH, + "BLOODCRAWL" = TRAIT_BLOODCRAWL, + "BLOODCRAWL_EAT" = TRAIT_BLOODCRAWL_EAT ))) /// value -> trait name, generated on use from trait_by_type global @@ -16,4 +19,4 @@ GLOBAL_LIST(trait_name_map) for(var/key in GLOB.traits_by_type) for(var/tname in GLOB.traits_by_type[key]) var/val = GLOB.traits_by_type[key][tname] - .[val] = tname \ No newline at end of file + .[val] = tname diff --git a/code/_globalvars/unused.dm b/code/_globalvars/unused.dm deleted file mode 100644 index 2e3f8f9c6dc..00000000000 --- a/code/_globalvars/unused.dm +++ /dev/null @@ -1 +0,0 @@ -var/going = 1.0 \ No newline at end of file diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm index 038174388ff..58cfdf477f2 100644 --- a/code/_onclick/adjacent.dm +++ b/code/_onclick/adjacent.dm @@ -96,4 +96,4 @@ else if( !border_only ) // dense, not on border, cannot pass over return 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 7c2214e2287..fbb04d4c021 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -42,7 +42,7 @@ if(control_disabled || stat) return - + var/turf/pixel_turf = isturf(A) ? A : get_turf_pixel(A) if(isnull(pixel_turf)) return @@ -57,7 +57,7 @@ var/turf_visible if(pixel_turf) - turf_visible = cameranet.checkTurfVis(pixel_turf) + turf_visible = GLOB.cameranet.checkTurfVis(pixel_turf) if(!turf_visible) if(istype(loc, /obj/item/aicard) && (pixel_turf in view(client.view, loc))) turf_visible = TRUE @@ -228,4 +228,4 @@ // /mob/living/silicon/ai/TurfAdjacent(var/turf/T) - return (cameranet && cameranet.checkTurfVis(T)) + return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index ea9f1663ed0..20e50bb3054 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -261,12 +261,12 @@ if(incapacitated()) return var/face_dir = get_cardinal_dir(src, A) - if(forced_look == face_dir) + if(!face_dir || forced_look == face_dir || A == src) forced_look = null - to_chat(src, "You are no longer facing any direction.") + to_chat(src, "Cancelled direction lock.") return forced_look = face_dir - to_chat(src, "You are now facing [dir2text(forced_look)].") + to_chat(src, "You are now facing [dir2text(forced_look)]. To cancel this, shift-middleclick yourself.") /* Middle shift-control-click @@ -274,12 +274,12 @@ */ /mob/proc/MiddleShiftControlClickOn(atom/A) var/face_uid = A.UID() - if(forced_look == face_uid) + if(forced_look == face_uid || A == src) forced_look = null - to_chat(src, "You are no longer facing [A].") + to_chat(src, "Cancelled direction lock.") return forced_look = face_uid - to_chat(src, "You are now facing [A].") + to_chat(src, "You are now facing [A]. To cancel this, shift-middleclick yourself.") // In case of use break glass /* @@ -447,4 +447,4 @@ params += "&catcher=1" if(T) T.Click(location, control, params) - . = 1 \ No newline at end of file + . = 1 diff --git a/code/_onclick/click_override.dm b/code/_onclick/click_override.dm index a6042386b3d..481d30da4b6 100644 --- a/code/_onclick/click_override.dm +++ b/code/_onclick/click_override.dm @@ -89,4 +89,4 @@ A = target_atom next_shocked.Cut() - P.last_shocked = world.time \ No newline at end of file + P.last_shocked = world.time diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index dc98d887279..ada0ce8cd30 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -262,4 +262,4 @@ using = new /obj/screen/act_intent/robot/AI() using.icon_state = mymob.a_intent static_inventory += using - action_intent = using \ No newline at end of file + action_intent = using diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm index c1f14fc625d..01940be1d8b 100644 --- a/code/_onclick/hud/blob_overmind.dm +++ b/code/_onclick/hud/blob_overmind.dm @@ -196,4 +196,4 @@ using = new /obj/screen/blob/Split() using.screen_loc = ui_acti - static_inventory += using \ No newline at end of file + static_inventory += using diff --git a/code/_onclick/hud/bot.dm b/code/_onclick/hud/bot.dm index 5f4ddbd7622..12320f2da3e 100644 --- a/code/_onclick/hud/bot.dm +++ b/code/_onclick/hud/bot.dm @@ -30,4 +30,4 @@ mymob.pullin.icon = 'icons/mob/screen_bot.dmi' mymob.pullin.update_icon(mymob) mymob.pullin.screen_loc = ui_bot_pull - static_inventory += mymob.pullin \ No newline at end of file + static_inventory += mymob.pullin diff --git a/code/_onclick/hud/constructs.dm b/code/_onclick/hud/constructs.dm index 7ed1d66724f..0effd812b2d 100644 --- a/code/_onclick/hud/constructs.dm +++ b/code/_onclick/hud/constructs.dm @@ -60,4 +60,4 @@ mymob.pullin.icon = 'icons/mob/screen_construct.dmi' mymob.pullin.icon_state = "pull0" mymob.pullin.name = "pull" - mymob.pullin.screen_loc = ui_construct_pull \ No newline at end of file + mymob.pullin.screen_loc = ui_construct_pull diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm index cc1ac13931f..d628124e052 100644 --- a/code/_onclick/hud/guardian.dm +++ b/code/_onclick/hud/guardian.dm @@ -1,94 +1,94 @@ -/mob/living/simple_animal/hostile/guardian/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/guardian(src) - -/datum/hud/guardian/New(mob/owner) - ..() - var/obj/screen/using - - guardianhealthdisplay = new /obj/screen/healths/guardian() - infodisplay += guardianhealthdisplay - - using = new /obj/screen/act_intent/guardian() - using.icon_state = mymob.a_intent - static_inventory += using - action_intent = using - - using = new /obj/screen/guardian/Manifest() - using.screen_loc = ui_rhand - static_inventory += using - - using = new /obj/screen/guardian/Recall() - using.screen_loc = ui_lhand - static_inventory += using - - using = new /obj/screen/guardian/ToggleMode() - using.screen_loc = ui_storage1 - static_inventory += using - - using = new /obj/screen/guardian/ToggleLight() - using.screen_loc = ui_inventory - static_inventory += using - - using = new /obj/screen/guardian/Communicate() - using.screen_loc = ui_back - static_inventory += using - - -//HUD BUTTONS - -/obj/screen/guardian - icon = 'icons/mob/guardian.dmi' - icon_state = "base" - -/obj/screen/guardian/Manifest - icon_state = "manifest" - name = "Manifest" - desc = "Spring forth into battle!" - -/obj/screen/guardian/Manifest/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.Manifest() - - -/obj/screen/guardian/Recall - icon_state = "recall" - name = "Recall" - desc = "Return to your user." - -/obj/screen/guardian/Recall/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.Recall() - -/obj/screen/guardian/ToggleMode - icon_state = "toggle" - name = "Toggle Mode" - desc = "Switch between ability modes." - -/obj/screen/guardian/ToggleMode/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.ToggleMode() - -/obj/screen/guardian/Communicate - icon_state = "communicate" - name = "Communicate" - desc = "Communicate telepathically with your user." - -/obj/screen/guardian/Communicate/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.Communicate() - - -/obj/screen/guardian/ToggleLight - icon_state = "light" - name = "Toggle Light" - desc = "Glow like star dust." - -/obj/screen/guardian/ToggleLight/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.ToggleLight() \ No newline at end of file +/mob/living/simple_animal/hostile/guardian/create_mob_hud() + if(client && !hud_used) + hud_used = new /datum/hud/guardian(src) + +/datum/hud/guardian/New(mob/owner) + ..() + var/obj/screen/using + + guardianhealthdisplay = new /obj/screen/healths/guardian() + infodisplay += guardianhealthdisplay + + using = new /obj/screen/act_intent/guardian() + using.icon_state = mymob.a_intent + static_inventory += using + action_intent = using + + using = new /obj/screen/guardian/Manifest() + using.screen_loc = ui_rhand + static_inventory += using + + using = new /obj/screen/guardian/Recall() + using.screen_loc = ui_lhand + static_inventory += using + + using = new /obj/screen/guardian/ToggleMode() + using.screen_loc = ui_storage1 + static_inventory += using + + using = new /obj/screen/guardian/ToggleLight() + using.screen_loc = ui_inventory + static_inventory += using + + using = new /obj/screen/guardian/Communicate() + using.screen_loc = ui_back + static_inventory += using + + +//HUD BUTTONS + +/obj/screen/guardian + icon = 'icons/mob/guardian.dmi' + icon_state = "base" + +/obj/screen/guardian/Manifest + icon_state = "manifest" + name = "Manifest" + desc = "Spring forth into battle!" + +/obj/screen/guardian/Manifest/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.Manifest() + + +/obj/screen/guardian/Recall + icon_state = "recall" + name = "Recall" + desc = "Return to your user." + +/obj/screen/guardian/Recall/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.Recall() + +/obj/screen/guardian/ToggleMode + icon_state = "toggle" + name = "Toggle Mode" + desc = "Switch between ability modes." + +/obj/screen/guardian/ToggleMode/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.ToggleMode() + +/obj/screen/guardian/Communicate + icon_state = "communicate" + name = "Communicate" + desc = "Communicate telepathically with your user." + +/obj/screen/guardian/Communicate/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.Communicate() + + +/obj/screen/guardian/ToggleLight + icon_state = "light" + name = "Toggle Light" + desc = "Glow like star dust." + +/obj/screen/guardian/ToggleLight/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.ToggleLight() diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index beb6949ce2c..00b23ff3cdd 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -214,4 +214,4 @@ to_chat(usr, "This mob type does not use a HUD.") /datum/hud/proc/update_locked_slots() - return \ No newline at end of file + return diff --git a/code/_onclick/hud/other_mobs.dm b/code/_onclick/hud/other_mobs.dm index a82c847a246..f03d436bcaf 100644 --- a/code/_onclick/hud/other_mobs.dm +++ b/code/_onclick/hud/other_mobs.dm @@ -24,4 +24,4 @@ mymob.pullin.icon = 'icons/mob/screen_corgi.dmi' mymob.pullin.update_icon(mymob) mymob.pullin.screen_loc = ui_construct_pull - static_inventory += mymob.pullin \ No newline at end of file + static_inventory += mymob.pullin diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index 429344e6142..68847cbd97d 100644 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -310,4 +310,4 @@ 1, 1, 1, 1, 0, 0, 0, 0 ) - screen_loc = "CENTER-7,CENTER-7" \ No newline at end of file + screen_loc = "CENTER-7,CENTER-7" diff --git a/code/_onclick/hud/picture_in_picture.dm b/code/_onclick/hud/picture_in_picture.dm index e011aea037c..872659d714b 100644 --- a/code/_onclick/hud/picture_in_picture.dm +++ b/code/_onclick/hud/picture_in_picture.dm @@ -141,4 +141,4 @@ /obj/screen/movable/pic_in_pic/proc/unshow_to(client/C) if(C) shown_to -= C - C.screen -= src \ No newline at end of file + C.screen -= src diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index 9c0f498cd83..fc3e926b396 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -51,4 +51,4 @@ /obj/screen/plane_master/lighting/backdrop(mob/mymob) mymob.overlay_fullscreen("lighting_backdrop_lit", /obj/screen/fullscreen/lighting_backdrop/lit) - mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit) \ No newline at end of file + mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit) diff --git a/code/_onclick/hud/slime.dm b/code/_onclick/hud/slime.dm index f49228ddf66..273991a83c8 100644 --- a/code/_onclick/hud/slime.dm +++ b/code/_onclick/hud/slime.dm @@ -5,4 +5,4 @@ /mob/living/simple_animal/slime/create_mob_hud() if(client && !hud_used) - hud_used = new /datum/hud/slime(src) \ No newline at end of file + hud_used = new /datum/hud/slime(src) diff --git a/code/_onclick/hud/swarmer.dm b/code/_onclick/hud/swarmer.dm index 84f5811cc8d..2042442642a 100644 --- a/code/_onclick/hud/swarmer.dm +++ b/code/_onclick/hud/swarmer.dm @@ -1,97 +1,97 @@ - -////HUD NONSENSE//// -/obj/screen/swarmer - icon = 'icons/mob/swarmer.dmi' - -/obj/screen/swarmer/FabricateTrap - icon_state = "ui_trap" - name = "Create trap (Costs 5 Resources)" - desc = "Creates a trap that will nonlethally shock any non-swarmer that attempts to cross it. (Costs 5 resources)" - -/obj/screen/swarmer/FabricateTrap/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.CreateTrap() - -/obj/screen/swarmer/Barricade - icon_state = "ui_barricade" - name = "Create barricade (Costs 5 Resources)" - desc = "Creates a destructible barricade that will stop any non swarmer from passing it. Also allows disabler beams to pass through. (Costs 5 resources)" - -/obj/screen/swarmer/Barricade/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.CreateBarricade() - -/obj/screen/swarmer/Replicate - icon_state = "ui_replicate" - name = "Replicate (Costs 50 Resources)" - desc = "Creates a another of our kind." - -/obj/screen/swarmer/Replicate/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.CreateSwarmer() - -/obj/screen/swarmer/RepairSelf - icon_state = "ui_self_repair" - name = "Repair self" - desc = "Repairs damage to our body." - -/obj/screen/swarmer/RepairSelf/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.RepairSelf() - -/obj/screen/swarmer/ToggleLight - icon_state = "ui_light" - name = "Toggle light" - desc = "Toggles our inbuilt light on or off." - -/obj/screen/swarmer/ToggleLight/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.ToggleLight() - -/obj/screen/swarmer/ContactSwarmers - icon_state = "ui_contact_swarmers" - name = "Contact swarmers" - desc = "Sends a message to all other swarmers, should they exist." - -/obj/screen/swarmer/ContactSwarmers/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.ContactSwarmers() - -/mob/living/simple_animal/hostile/swarmer/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/swarmer(src) - -/datum/hud/swarmer/New(mob/owner) - ..() - var/obj/screen/using - - using = new /obj/screen/swarmer/FabricateTrap() - using.screen_loc = ui_rhand - static_inventory += using - - using = new /obj/screen/swarmer/Barricade() - using.screen_loc = ui_lhand - static_inventory += using - - using = new /obj/screen/swarmer/Replicate() - using.screen_loc = ui_zonesel - static_inventory += using - - using = new /obj/screen/swarmer/RepairSelf() - using.screen_loc = ui_storage1 - static_inventory += using - - using = new /obj/screen/swarmer/ToggleLight() - using.screen_loc = ui_back - static_inventory += using - - using = new /obj/screen/swarmer/ContactSwarmers() - using.screen_loc = ui_inventory - static_inventory += using - + +////HUD NONSENSE//// +/obj/screen/swarmer + icon = 'icons/mob/swarmer.dmi' + +/obj/screen/swarmer/FabricateTrap + icon_state = "ui_trap" + name = "Create trap (Costs 5 Resources)" + desc = "Creates a trap that will nonlethally shock any non-swarmer that attempts to cross it. (Costs 5 resources)" + +/obj/screen/swarmer/FabricateTrap/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.CreateTrap() + +/obj/screen/swarmer/Barricade + icon_state = "ui_barricade" + name = "Create barricade (Costs 5 Resources)" + desc = "Creates a destructible barricade that will stop any non swarmer from passing it. Also allows disabler beams to pass through. (Costs 5 resources)" + +/obj/screen/swarmer/Barricade/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.CreateBarricade() + +/obj/screen/swarmer/Replicate + icon_state = "ui_replicate" + name = "Replicate (Costs 50 Resources)" + desc = "Creates a another of our kind." + +/obj/screen/swarmer/Replicate/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.CreateSwarmer() + +/obj/screen/swarmer/RepairSelf + icon_state = "ui_self_repair" + name = "Repair self" + desc = "Repairs damage to our body." + +/obj/screen/swarmer/RepairSelf/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.RepairSelf() + +/obj/screen/swarmer/ToggleLight + icon_state = "ui_light" + name = "Toggle light" + desc = "Toggles our inbuilt light on or off." + +/obj/screen/swarmer/ToggleLight/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.ToggleLight() + +/obj/screen/swarmer/ContactSwarmers + icon_state = "ui_contact_swarmers" + name = "Contact swarmers" + desc = "Sends a message to all other swarmers, should they exist." + +/obj/screen/swarmer/ContactSwarmers/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.ContactSwarmers() + +/mob/living/simple_animal/hostile/swarmer/create_mob_hud() + if(client && !hud_used) + hud_used = new /datum/hud/swarmer(src) + +/datum/hud/swarmer/New(mob/owner) + ..() + var/obj/screen/using + + using = new /obj/screen/swarmer/FabricateTrap() + using.screen_loc = ui_rhand + static_inventory += using + + using = new /obj/screen/swarmer/Barricade() + using.screen_loc = ui_lhand + static_inventory += using + + using = new /obj/screen/swarmer/Replicate() + using.screen_loc = ui_zonesel + static_inventory += using + + using = new /obj/screen/swarmer/RepairSelf() + using.screen_loc = ui_storage1 + static_inventory += using + + using = new /obj/screen/swarmer/ToggleLight() + using.screen_loc = ui_back + static_inventory += using + + using = new /obj/screen/swarmer/ContactSwarmers() + using.screen_loc = ui_inventory + static_inventory += using + diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index d2a91da4020..e27002cf118 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -58,7 +58,7 @@ else return 1 var/obj/item/organ/external/O = M.get_organ(user.zone_selected) - if((is_sharp(src) || (isscrewdriver(src) && O.is_robotic())) && user.a_intent == INTENT_HELP) + if((is_sharp(src) || (isscrewdriver(src) && O?.is_robotic())) && user.a_intent == INTENT_HELP) if(!attempt_initiate_surgery(src, M, user)) return FALSE else diff --git a/code/_onclick/overmind.dm b/code/_onclick/overmind.dm index 8ace273dd8f..419524c8711 100644 --- a/code/_onclick/overmind.dm +++ b/code/_onclick/overmind.dm @@ -32,4 +32,4 @@ /mob/camera/blob/AltClickOn(atom/A) //Remove a blob var/turf/T = get_turf(A) if(T) - remove_blob(T) \ No newline at end of file + remove_blob(T) diff --git a/code/_onclick/telekinesis.dm b/code/_onclick/telekinesis.dm index 6ef7a797136..e684707d460 100644 --- a/code/_onclick/telekinesis.dm +++ b/code/_onclick/telekinesis.dm @@ -3,7 +3,7 @@ This needs more thinking out, but I might as well. */ -var/const/tk_maxrange = 15 +#define TK_MAXRANGE 15 /* Telekinetic attack: @@ -118,7 +118,7 @@ var/const/tk_maxrange = 15 var/d = get_dist(user, target) if(focus) d = max(d,get_dist(user,focus)) // whichever is further - if(d > tk_maxrange) + if(d > TK_MAXRANGE) to_chat(user, "Your mind won't reach that far.") return diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index d1a3861676b..2f4811de380 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -1,893 +1,897 @@ -/datum/configuration - var/server_name = null // server name (for world name / status) - var/server_tag_line = null // server tagline (for showing on hub entry) - var/server_extra_features = null // server-specific extra features (for hub entry) - var/server_suffix = 0 // generate numeric suffix based on server port - - var/minimum_client_build = 1421 // Build 1421 due to the middle mouse button exploit - - var/nudge_script_path = "nudge.py" // where the nudge.py script is located - - var/log_ooc = 0 // log OOC channel - var/log_access = 0 // log login/logout - var/log_say = 0 // log client say - var/log_admin = 0 // log admin actions - var/log_debug = 1 // log debug output - var/log_game = 0 // log game events - var/log_vote = 0 // log voting - var/log_whisper = 0 // log client whisper - var/log_emote = 0 // log emotes - var/log_attack = 0 // log attack messages - var/log_adminchat = 0 // log admin chat messages - var/log_adminwarn = 0 // log warnings admins get about bomb construction and such - var/log_pda = 0 // log pda messages - var/log_world_output = 0 // log world.log << messages - var/log_runtimes = 0 // logs world.log to a file - var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits - var/sql_enabled = 0 // for sql switching - var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour - var/pregame_timestart = 240 // Time it takes for the server to start the game - var/allow_vote_restart = 0 // allow votes to restart - var/allow_vote_mode = 0 // allow votes to change mode - var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) - var/vote_period = 600 // length of voting period (deciseconds, default 1 minute) - var/vote_autotransfer_initial = 72000 // Length of time before the first autotransfer vote is called - var/vote_autotransfer_interval = 18000 // length of time before next sequential autotransfer vote - var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) - var/vote_no_dead = 0 // dead people can't vote (tbi) -// var/enable_authentication = 0 // goon authentication - var/del_new_on_log = 1 // del's new players if they log before they spawn in - var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard - var/traitor_scaling = 0 //if amount of traitors scales based on amount of players - var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other - var/continuous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. - var/allow_Metadata = 0 // Metadata is supported. - var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. - var/Ticklag = 0.5 - var/socket_talk = 0 // use socket_talk to communicate with other processes - var/list/resource_urls = null - var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. - var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. - var/list/mode_names = list() - var/list/modes = list() // allowed modes - var/list/votable_modes = list() // votable modes - var/list/probabilities = list() // relative probability of each mode - var/humans_need_surnames = 0 - var/allow_random_events = 0 // enables random events mid-round when set to 1 - var/allow_ai = 1 // allow ai job - var/respawn = 0 - var/guest_jobban = 1 - var/usewhitelist = 0 - var/mods_are_mentors = 0 - var/load_jobs_from_txt = 0 - var/ToRban = 0 - var/automute_on = 0 //enables automuting/spam prevention - var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. - var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized - var/medal_hub_address = null - var/medal_hub_password = null - - var/reactionary_explosions = 0 //If we use reactionary explosions, explosions that react to walls and doors - - var/assistantlimit = 0 //enables assistant limiting - var/assistantratio = 2 //how many assistants to security members - - // The AFK subsystem will not be activated if any of the below config values are equal or less than 0 - var/warn_afk_minimum = 0 // How long till you get a warning while being AFK - var/auto_cryo_afk = 0 // How long till you get put into cryo when you're AFK - var/auto_despawn_afk = 0 // How long till you actually despawn in cryo when you're AFK (Not ssd so not automatic) - - var/auto_cryo_ssd_mins = 0 - var/ssd_warning = 0 - - var/list_afk_minimum = 5 // How long people have to be AFK before it's listed on the "List AFK players" verb - - var/traitor_objectives_amount = 2 - var/shadowling_max_age = 0 - - var/max_maint_drones = 5 //This many drones can spawn, - var/allow_drone_spawn = 1 //assuming the admin allow them to. - var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. - - var/usealienwhitelist = 0 - var/limitalienplayers = 0 - var/alien_to_human_ratio = 0.5 - - var/server - var/banappeals - var/wikiurl = "http://example.org" - var/forumurl = "http://example.org" - var/rulesurl = "http://example.org" - var/githuburl = "http://example.org" - var/donationsurl = "http://example.org" - var/repositoryurl = "http://example.org" - var/discordurl = "http://example.org" - var/discordforumurl = "http://example.org" - - var/overflow_server_url - var/forbid_singulo_possession = 0 - - var/check_randomizer = 0 - - //game_options.txt configs - - var/bones_can_break = 1 - - var/revival_pod_plants = 1 - var/revival_cloning = 1 - var/revival_brain_life = -1 - - var/auto_toggle_ooc_during_round = 0 - - var/shuttle_refuel_delay = 12000 - - //Used for modifying movement speed for mobs. - //Unversal modifiers - var/run_speed = 0 - var/walk_speed = 0 - - //Mob specific modifiers. NOTE: These will affect different mob types in different ways - var/human_delay = 0 - var/robot_delay = 0 - var/monkey_delay = 0 - var/alien_delay = 0 - var/slime_delay = 0 - var/animal_delay = 0 - - //IP Intel vars - var/ipintel_email - var/ipintel_rating_bad = 1 - var/ipintel_save_good = 12 - var/ipintel_save_bad = 1 - var/ipintel_domain = "check.getipintel.net" - var/ipintel_maxplaytime = 0 - var/ipintel_whitelist = 0 - var/ipintel_detailsurl = "https://iphub.info/?ip=" - - var/forum_link_url - var/forum_playerinfo_url - - var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt - var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt - var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database - var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database - - var/use_exp_tracking = 0 - var/use_exp_restrictions = 0 - var/use_exp_restrictions_admin_bypass = 0 - - var/simultaneous_pm_warning_timeout = 100 - - var/assistant_maint = 0 //Do assistants get maint access? - var/gateway_delay = 6000 //How long the gateway takes before it activates. Default is half an hour. - var/ghost_interaction = 0 - - var/comms_password = "" - - var/use_irc_bot = 0 - var/list/irc_bot_host = list() - var/main_irc = "" - var/admin_irc = "" - var/admin_notify_irc = "" - var/cidrandomizer_irc = "" - - var/default_laws = 0 //Controls what laws the AI spawns with. - - var/list/station_levels = list(1) // Defines which Z-levels the station exists on. - var/list/admin_levels= list(2) // Defines which Z-levels which are for admin functionality, for example including such areas as Central Command and the Syndicate Shuttle - var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect - var/list/player_levels = list(1, 3, 4, 5, 6, 7) // Defines all Z-levels a character can typically reach - - var/const/minutes_to_ticks = 60 * 10 - // Event settings - var/expected_round_length = 60 * 2 * minutes_to_ticks // 2 hours - // If the first delay has a custom start time - // No custom time, no custom time, between 80 to 100 minutes respectively. - var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) - // The lowest delay until next event - // 10, 30, 50 minutes respectively - var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) - // The upper delay until next event - // 15, 45, 70 minutes respectively - var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) - - var/starlight = 0 // Whether space turfs have ambient light or not - var/allow_holidays = 0 - var/player_overflow_cap = 0 //number of players before the server starts rerouting - var/list/overflow_whitelist = list() //whitelist for overflow - - var/disable_away_missions = 0 // disable away missions - var/disable_space_ruins = 0 //disable space ruins - - var/extra_space_ruin_levels_min = 2 - var/extra_space_ruin_levels_max = 4 - - var/ooc_allowed = 1 - var/looc_allowed = 1 - var/dooc_allowed = 1 - var/dsay_allowed = 1 - - var/disable_lobby_music = 0 // Disables the lobby music - var/disable_cid_warn_popup = 0 //disables the annoying "You have already logged in this round, disconnect or be banned" popup, because it annoys the shit out of me when testing. - - var/max_loadout_points = 5 // How many points can be spent on extra items in character setup - - var/disable_ooc_emoji = 0 // prevents people from using emoji in OOC - - var/shutdown_on_reboot = 0 // Whether to shut down the world instead of rebooting it - - var/disable_karma = 0 // Disable all karma functions and unlock karma jobs by default - - // StonedMC - var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling - - // Highpop tickrates - var/base_mc_tick_rate = 1 - var/high_pop_mc_tick_rate = 1.1 - - var/high_pop_mc_mode_amount = 65 - var/disable_high_pop_mc_mode_amount = 60 - - // Nightshift - var/randomize_shift_time = FALSE - var/enable_night_shifts = FALSE - - // Developer - var/developer_express_start = 0 - - // Automatic localhost admin disable - var/disable_localhost_admin = 0 - - //Start now warning - var/start_now_confirmation = 0 - - // Lavaland - var/lavaland_budget = 60 - - //cube monkey limit - var/cubemonkeycap = 20 - - // Makes gamemodes respect player limits - var/enable_gamemode_player_limit = 0 - -/datum/configuration/New() - for(var/T in subtypesof(/datum/game_mode)) - var/datum/game_mode/M = T - - if(initial(M.config_tag)) - if(!(initial(M.config_tag) in modes)) // ensure each mode is added only once - src.modes += initial(M.config_tag) - src.mode_names[initial(M.config_tag)] = initial(M.name) - src.probabilities[initial(M.config_tag)] = initial(M.probability) - if(initial(M.votable)) - src.votable_modes += initial(M.config_tag) - src.votable_modes += "secret" - -/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist - var/list/Lines = file2list(filename) - - for(var/t in Lines) - if(!t) continue - - t = trim(t) - if(length(t) == 0) - continue - else if(copytext(t, 1, 2) == "#") - continue - - var/pos = findtext(t, " ") - var/name = null - var/value = null - - if(pos) - name = lowertext(copytext(t, 1, pos)) - value = copytext(t, pos + 1) - else - name = lowertext(t) - - if(!name) - continue - - if(type == "config") - switch(name) - if("resource_urls") - config.resource_urls = splittext(value, " ") - - if("admin_legacy_system") - config.admin_legacy_system = 1 - - if("ban_legacy_system") - config.ban_legacy_system = 1 - - if("use_age_restriction_for_jobs") - config.use_age_restriction_for_jobs = 1 - - if("use_age_restriction_for_antags") - config.use_age_restriction_for_antags = 1 - - if("use_exp_tracking") - config.use_exp_tracking = 1 - - if("use_exp_restrictions") - config.use_exp_restrictions = 1 - - if("use_exp_restrictions_admin_bypass") - config.use_exp_restrictions_admin_bypass = 1 - - if("jobs_have_minimal_access") - config.jobs_have_minimal_access = 1 - - if("shadowling_max_age") - config.shadowling_max_age = text2num(value) - - if("warn_afk_minimum") - config.warn_afk_minimum = text2num(value) - if("auto_cryo_afk") - config.auto_cryo_afk = text2num(value) - if("auto_despawn_afk") - config.auto_despawn_afk = text2num(value) - - if("auto_cryo_ssd_mins") - config.auto_cryo_ssd_mins = text2num(value) - if("ssd_warning") - config.ssd_warning = 1 - - if("list_afk_minimum") - config.list_afk_minimum = text2num(value) - - if("ipintel_email") - if(value != "ch@nge.me") - config.ipintel_email = value - if("ipintel_rating_bad") - config.ipintel_rating_bad = text2num(value) - if("ipintel_domain") - config.ipintel_domain = value - if("ipintel_save_good") - config.ipintel_save_good = text2num(value) - if("ipintel_save_bad") - config.ipintel_save_bad = text2num(value) - if("ipintel_maxplaytime") - config.ipintel_maxplaytime = text2num(value) - if("ipintel_whitelist") - config.ipintel_whitelist = 1 - if("ipintel_detailsurl") - config.ipintel_detailsurl = value - - if("forum_link_url") - config.forum_link_url = value - - if("forum_playerinfo_url") - config.forum_playerinfo_url = value - - if("log_ooc") - config.log_ooc = 1 - - if("log_access") - config.log_access = 1 - - if("log_say") - config.log_say = 1 - - if("log_admin") - config.log_admin = 1 - - if("log_debug") - config.log_debug = 1 - - if("log_game") - config.log_game = 1 - - if("log_vote") - config.log_vote = 1 - - if("log_whisper") - config.log_whisper = 1 - - if("log_attack") - config.log_attack = 1 - - if("log_emote") - config.log_emote = 1 - - if("log_adminchat") - config.log_adminchat = 1 - - if("log_adminwarn") - config.log_adminwarn = 1 - - if("log_pda") - config.log_pda = 1 - - if("log_world_output") - config.log_world_output = 1 - - if("log_hrefs") - config.log_hrefs = 1 - - if("log_runtime") - config.log_runtimes = 1 - - if("mentors") - config.mods_are_mentors = 1 - - if("allow_admin_ooccolor") - config.allow_admin_ooccolor = 1 - - if("pregame_timestart") - config.pregame_timestart = text2num(value) - - if("allow_vote_restart") - config.allow_vote_restart = 1 - - if("allow_vote_mode") - config.allow_vote_mode = 1 - - if("no_dead_vote") - config.vote_no_dead = 1 - - if("vote_autotransfer_initial") - config.vote_autotransfer_initial = text2num(value) - - if("vote_autotransfer_interval") - config.vote_autotransfer_interval = text2num(value) - - if("default_no_vote") - config.vote_no_default = 1 - - if("vote_delay") - config.vote_delay = text2num(value) - - if("vote_period") - config.vote_period = text2num(value) - - if("allow_ai") - config.allow_ai = 1 - -// if("authentication") -// config.enable_authentication = 1 - - if("norespawn") - config.respawn = 0 - - if("servername") - config.server_name = value - - if("server_tag_line") - config.server_tag_line = value - - if("server_extra_features") - config.server_extra_features = value - - if("serversuffix") - config.server_suffix = 1 - - if("minimum_client_build") - config.minimum_client_build = text2num(value) - - if("nudge_script_path") - config.nudge_script_path = value - - if("server") - config.server = value - - if("banappeals") - config.banappeals = value - - if("wikiurl") - config.wikiurl = value - - if("forumurl") - config.forumurl = value - - if("rulesurl") - config.rulesurl = value - - if("githuburl") - config.githuburl = value - - if("discordurl") - config.discordurl = value - - if("discordforumurl") - config.discordforumurl = value - - if("donationsurl") - config.donationsurl = value - - if("repositoryurl") - config.repositoryurl = value - - if("guest_jobban") - config.guest_jobban = 1 - - if("guest_ban") - guests_allowed = 0 - - if("usewhitelist") - config.usewhitelist = 1 - - if("feature_object_spell_system") - config.feature_object_spell_system = 1 - - if("allow_metadata") - config.allow_Metadata = 1 - - if("traitor_scaling") - config.traitor_scaling = 1 - - if("protect_roles_from_antagonist") - config.protect_roles_from_antagonist = 1 - - if("probability") - var/prob_pos = findtext(value, " ") - var/prob_name = null - var/prob_value = null - - if(prob_pos) - prob_name = lowertext(copytext(value, 1, prob_pos)) - prob_value = copytext(value, prob_pos + 1) - if(prob_name in config.modes) - config.probabilities[prob_name] = text2num(prob_value) - else - log_config("Unknown game mode probability configuration definition: [prob_name].") - else - log_config("Incorrect probability configuration definition: [prob_name] [prob_value].") - - if("allow_random_events") - config.allow_random_events = 1 - - if("load_jobs_from_txt") - load_jobs_from_txt = 1 - - if("forbid_singulo_possession") - forbid_singulo_possession = 1 - - if("check_randomizer") - check_randomizer = 1 - - if("popup_admin_pm") - config.popup_admin_pm = 1 - - if("allow_holidays") - config.allow_holidays = 1 - - if("use_irc_bot") - use_irc_bot = 1 - - if("ticklag") - Ticklag = text2num(value) - - if("socket_talk") - socket_talk = text2num(value) - - if("allow_antag_hud") - config.antag_hud_allowed = 1 - - if("antag_hud_restricted") - config.antag_hud_restricted = 1 - - if("humans_need_surnames") - humans_need_surnames = 1 - - if("tor_ban") - ToRban = 1 - - if("automute_on") - automute_on = 1 - - if("usealienwhitelist") - usealienwhitelist = 1 - - if("alien_player_ratio") - limitalienplayers = 1 - alien_to_human_ratio = text2num(value) - - if("assistant_maint") - config.assistant_maint = 1 - - if("gateway_delay") - config.gateway_delay = text2num(value) - - if("continuous_rounds") - config.continuous_rounds = 1 - - if("ghost_interaction") - config.ghost_interaction = 1 - - if("comms_password") - config.comms_password = value - - if("irc_bot_host") - config.irc_bot_host = splittext(value, ";") - - if("main_irc") - config.main_irc = value - - if("admin_irc") - config.admin_irc = value - - if("admin_notify_irc") - config.admin_notify_irc = value - - if("cidrandomizer_irc") - config.cidrandomizer_irc = value - - if("python_path") - if(value) - python_path = value - else - if(world.system_type == UNIX) - python_path = "/usr/bin/env python2" - else //probably windows, if not this should work anyway - python_path = "pythonw" - - if("assistant_limit") - config.assistantlimit = 1 - - if("assistant_ratio") - config.assistantratio = text2num(value) - - if("allow_drone_spawn") - config.allow_drone_spawn = text2num(value) - - if("drone_build_time") - config.drone_build_time = text2num(value) - - if("max_maint_drones") - config.max_maint_drones = text2num(value) - - if("expected_round_length") - config.expected_round_length = MinutesToTicks(text2num(value)) - - if("event_custom_start_mundane") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_custom_start_moderate") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_custom_start_major") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_delay_lower") - var/values = text2numlist(value, ";") - config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) - config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) - config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) - - if("event_delay_upper") - var/values = text2numlist(value, ";") - config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) - config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) - config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) - - if("starlight") - config.starlight = 1 - - if("player_reroute_cap") - var/vvalue = text2num(value) - config.player_overflow_cap = vvalue >= 0 ? vvalue : 0 - - if("overflow_server_url") - config.overflow_server_url = value - - if("disable_away_missions") - config.disable_away_missions = 1 - - if("disable_space_ruins") - config.disable_space_ruins = 1 - - if("disable_lobby_music") - config.disable_lobby_music = 1 - - if("disable_cid_warn_popup") - config.disable_cid_warn_popup = 1 - - if("extra_space_ruin_levels_min") - var/vvalue = text2num(value) - config.extra_space_ruin_levels_min = max(vvalue, 0) - - if("extra_space_ruin_levels_max") - var/vvalue = text2num(value) - config.extra_space_ruin_levels_max = max(vvalue, 0) - - if("max_loadout_points") - config.max_loadout_points = text2num(value) - - if("round_abandon_penalty_period") - config.round_abandon_penalty_period = MinutesToTicks(text2num(value)) - - if("medal_hub_address") - config.medal_hub_address = value - - if("medal_hub_password") - config.medal_hub_password = value - - if("disable_ooc_emoji") - config.disable_ooc_emoji = 1 - - if("shutdown_on_reboot") - config.shutdown_on_reboot = 1 - - if("shutdown_shell_command") - shutdown_shell_command = value - - if("disable_karma") - config.disable_karma = 1 - - if("start_now_confirmation") - config.start_now_confirmation = 1 - - if("tick_limit_mc_init") - config.tick_limit_mc_init = text2num(value) - if("base_mc_tick_rate") - config.base_mc_tick_rate = text2num(value) - if("high_pop_mc_tick_rate") - config.high_pop_mc_tick_rate = text2num(value) - if("high_pop_mc_mode_amount") - config.high_pop_mc_mode_amount = text2num(value) - if("disable_high_pop_mc_mode_amount") - config.disable_high_pop_mc_mode_amount = text2num(value) - if("developer_express_start") - config.developer_express_start = 1 - if("disable_localhost_admin") - config.disable_localhost_admin = 1 - if("enable_gamemode_player_limit") - config.enable_gamemode_player_limit = 1 - else - log_config("Unknown setting in configuration: '[name]'") - - - else if(type == "game_options") - value = text2num(value) - - switch(name) - if("revival_pod_plants") - config.revival_pod_plants = value - if("revival_cloning") - config.revival_cloning = value - if("revival_brain_life") - config.revival_brain_life = value - if("auto_toggle_ooc_during_round") - config.auto_toggle_ooc_during_round = 1 - if("run_speed") - config.run_speed = value - if("walk_speed") - config.walk_speed = value - if("human_delay") - config.human_delay = value - if("robot_delay") - config.robot_delay = value - if("monkey_delay") - config.monkey_delay = value - if("alien_delay") - config.alien_delay = value - if("slime_delay") - config.slime_delay = value - if("animal_delay") - config.animal_delay = value - if("bones_can_break") - config.bones_can_break = value - if("shuttle_refuel_delay") - config.shuttle_refuel_delay = text2num(value) - if("traitor_objectives_amount") - config.traitor_objectives_amount = text2num(value) - if("reactionary_explosions") - config.reactionary_explosions = 1 - if("bombcap") - var/BombCap = text2num(value) - if(!BombCap) - continue - if(BombCap < 4) - BombCap = 4 - if(BombCap > 128) - BombCap = 128 - - MAX_EX_DEVASTATION_RANGE = round(BombCap/4) - MAX_EX_HEAVY_RANGE = round(BombCap/2) - MAX_EX_LIGHT_RANGE = BombCap - MAX_EX_FLASH_RANGE = BombCap - MAX_EX_FLAME_RANGE = BombCap - if("default_laws") - config.default_laws = text2num(value) - if("randomize_shift_time") - config.randomize_shift_time = TRUE - if("enable_night_shifts") - config.enable_night_shifts = TRUE - if("lavaland_budget") - config.lavaland_budget = text2num(value) - if("cubemonkey_cap") - config.cubemonkeycap = text2num(value) - else - log_config("Unknown setting in configuration: '[name]'") - -/datum/configuration/proc/loadsql(filename) // -- TLE - var/list/Lines = file2list(filename) - var/db_version = 0 - for(var/t in Lines) - if(!t) continue - - t = trim(t) - if(length(t) == 0) - continue - else if(copytext(t, 1, 2) == "#") - continue - - var/pos = findtext(t, " ") - var/name = null - var/value = null - - if(pos) - name = lowertext(copytext(t, 1, pos)) - value = copytext(t, pos + 1) - else - name = lowertext(t) - - if(!name) - continue - - switch(name) - if("sql_enabled") - config.sql_enabled = 1 - if("address") - sqladdress = value - if("port") - sqlport = value - if("feedback_database") - sqlfdbkdb = value - if("feedback_login") - sqlfdbklogin = value - if("feedback_password") - sqlfdbkpass = value - if("feedback_tableprefix") - sqlfdbktableprefix = value - if("db_version") - db_version = text2num(value) - else - log_config("Unknown setting in configuration: '[name]'") - if(config.sql_enabled && db_version != SQL_VERSION) - config.sql_enabled = 0 - log_config("WARNING: DB_CONFIG DEFINITION MISMATCH!") - spawn(60) - if(SSticker.current_state == GAME_STATE_PREGAME) - going = 0 - spawn(600) - to_chat(world, "DB_CONFIG MISMATCH, ROUND START DELAYED.
    Please check database version for recent upstream changes!
    ") - -/datum/configuration/proc/loadoverflowwhitelist(filename) - var/list/Lines = file2list(filename) - for(var/t in Lines) - if(!t) continue - - t = trim(t) - if(length(t) == 0) - continue - else if(copytext(t, 1, 2) == "#") - continue - - config.overflow_whitelist += t - -/datum/configuration/proc/pick_mode(mode_name) - for(var/T in subtypesof(/datum/game_mode)) - var/datum/game_mode/M = T - if(initial(M.config_tag) && initial(M.config_tag) == mode_name) - return new T() - return new /datum/game_mode/extended() - -/datum/configuration/proc/get_runnable_modes() - var/list/datum/game_mode/runnable_modes = new - for(var/T in subtypesof(/datum/game_mode)) - var/datum/game_mode/M = new T() -// to_chat(world, "DEBUG: [T], tag=[M.config_tag], prob=[probabilities[M.config_tag]]") - if(!(M.config_tag in modes)) - qdel(M) - continue - if(probabilities[M.config_tag]<=0) - qdel(M) - continue - if(M.can_start()) - runnable_modes[M] = probabilities[M.config_tag] -// to_chat(world, "DEBUG: runnable_mode\[[runnable_modes.len]\] = [M.config_tag]") - return runnable_modes +/datum/configuration + var/server_name = null // server name (for world name / status) + var/server_tag_line = null // server tagline (for showing on hub entry) + var/server_extra_features = null // server-specific extra features (for hub entry) + var/server_suffix = 0 // generate numeric suffix based on server port + + var/minimum_client_build = 1421 // Build 1421 due to the middle mouse button exploit + + var/nudge_script_path = "nudge.py" // where the nudge.py script is located + + var/log_ooc = 0 // log OOC channel + var/log_access = 0 // log login/logout + var/log_say = 0 // log client say + var/log_admin = 0 // log admin actions + var/log_debug = 1 // log debug output + var/log_game = 0 // log game events + var/log_vote = 0 // log voting + var/log_whisper = 0 // log client whisper + var/log_emote = 0 // log emotes + var/log_attack = 0 // log attack messages + var/log_adminchat = 0 // log admin chat messages + var/log_adminwarn = 0 // log warnings admins get about bomb construction and such + var/log_pda = 0 // log pda messages + var/log_world_output = 0 // log world.log << messages + var/log_runtimes = 0 // logs world.log to a file + var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits + var/sql_enabled = 0 // for sql switching + var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour + var/pregame_timestart = 240 // Time it takes for the server to start the game + var/allow_vote_restart = 0 // allow votes to restart + var/allow_vote_mode = 0 // allow votes to change mode + var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) + var/vote_period = 600 // length of voting period (deciseconds, default 1 minute) + var/vote_autotransfer_initial = 72000 // Length of time before the first autotransfer vote is called + var/vote_autotransfer_interval = 18000 // length of time before next sequential autotransfer vote + var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) + var/vote_no_dead = 0 // dead people can't vote (tbi) +// var/enable_authentication = 0 // goon authentication + var/del_new_on_log = 1 // del's new players if they log before they spawn in + var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard + var/traitor_scaling = 0 //if amount of traitors scales based on amount of players + var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other + var/continuous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. + var/allow_Metadata = 0 // Metadata is supported. + var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. + var/Ticklag = 0.5 + var/socket_talk = 0 // use socket_talk to communicate with other processes + var/list/resource_urls = null + var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. + var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. + var/list/mode_names = list() + var/list/modes = list() // allowed modes + var/list/votable_modes = list() // votable modes + var/list/probabilities = list() // relative probability of each mode + var/humans_need_surnames = 0 + var/allow_random_events = 0 // enables random events mid-round when set to 1 + var/allow_ai = 1 // allow ai job + var/respawn = 0 + var/guest_jobban = 1 + var/panic_bunker_threshold = 150 // above this player count threshold, never-before-seen players are blocked from connecting + var/usewhitelist = 0 + var/mods_are_mentors = 0 + var/load_jobs_from_txt = 0 + var/ToRban = 0 + var/automute_on = 0 //enables automuting/spam prevention + var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. + var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized + var/medal_hub_address = null + var/medal_hub_password = null + + var/reactionary_explosions = 0 //If we use reactionary explosions, explosions that react to walls and doors + + var/assistantlimit = 0 //enables assistant limiting + var/assistantratio = 2 //how many assistants to security members + + // The AFK subsystem will not be activated if any of the below config values are equal or less than 0 + var/warn_afk_minimum = 0 // How long till you get a warning while being AFK + var/auto_cryo_afk = 0 // How long till you get put into cryo when you're AFK + var/auto_despawn_afk = 0 // How long till you actually despawn in cryo when you're AFK (Not ssd so not automatic) + + var/auto_cryo_ssd_mins = 0 + var/ssd_warning = 0 + + var/list_afk_minimum = 5 // How long people have to be AFK before it's listed on the "List AFK players" verb + + var/traitor_objectives_amount = 2 + var/shadowling_max_age = 0 + + var/max_maint_drones = 5 //This many drones can spawn, + var/allow_drone_spawn = 1 //assuming the admin allow them to. + var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. + + var/usealienwhitelist = 0 + var/limitalienplayers = 0 + var/alien_to_human_ratio = 0.5 + + var/server + var/banappeals + var/wikiurl = "http://example.org" + var/forumurl = "http://example.org" + var/rulesurl = "http://example.org" + var/githuburl = "http://example.org" + var/donationsurl = "http://example.org" + var/repositoryurl = "http://example.org" + var/discordurl = "http://example.org" + var/discordforumurl = "http://example.org" + + var/overflow_server_url + var/forbid_singulo_possession = 0 + + var/check_randomizer = 0 + + //game_options.txt configs + + var/bones_can_break = 1 + + var/revival_pod_plants = 1 + var/revival_cloning = 1 + var/revival_brain_life = -1 + + var/auto_toggle_ooc_during_round = 0 + + var/shuttle_refuel_delay = 12000 + + //Used for modifying movement speed for mobs. + //Unversal modifiers + var/run_speed = 0 + var/walk_speed = 0 + + //Mob specific modifiers. NOTE: These will affect different mob types in different ways + var/human_delay = 0 + var/robot_delay = 0 + var/monkey_delay = 0 + var/alien_delay = 0 + var/slime_delay = 0 + var/animal_delay = 0 + + //IP Intel vars + var/ipintel_email + var/ipintel_rating_bad = 1 + var/ipintel_save_good = 12 + var/ipintel_save_bad = 1 + var/ipintel_domain = "check.getipintel.net" + var/ipintel_maxplaytime = 0 + var/ipintel_whitelist = 0 + var/ipintel_detailsurl = "https://iphub.info/?ip=" + + var/forum_link_url + var/forum_playerinfo_url + + var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt + var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt + var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database + var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database + + var/use_exp_tracking = 0 + var/use_exp_restrictions = 0 + var/use_exp_restrictions_admin_bypass = 0 + + var/simultaneous_pm_warning_timeout = 100 + + var/assistant_maint = 0 //Do assistants get maint access? + var/gateway_delay = 6000 //How long the gateway takes before it activates. Default is half an hour. + var/ghost_interaction = 0 + + var/comms_password = "" + + var/use_irc_bot = 0 + var/list/irc_bot_host = list() + var/main_irc = "" + var/admin_irc = "" + var/admin_notify_irc = "" + var/cidrandomizer_irc = "" + + var/default_laws = 0 //Controls what laws the AI spawns with. + + var/list/station_levels = list(1) // Defines which Z-levels the station exists on. + var/list/admin_levels= list(2) // Defines which Z-levels which are for admin functionality, for example including such areas as Central Command and the Syndicate Shuttle + var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect + var/list/player_levels = list(1, 3, 4, 5, 6, 7) // Defines all Z-levels a character can typically reach + + var/const/minutes_to_ticks = 60 * 10 + // Event settings + var/expected_round_length = 60 * 2 * minutes_to_ticks // 2 hours + // If the first delay has a custom start time + // No custom time, no custom time, between 80 to 100 minutes respectively. + var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) + // The lowest delay until next event + // 10, 30, 50 minutes respectively + var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) + // The upper delay until next event + // 15, 45, 70 minutes respectively + var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) + + var/starlight = 0 // Whether space turfs have ambient light or not + var/allow_holidays = 0 + var/player_overflow_cap = 0 //number of players before the server starts rerouting + var/list/overflow_whitelist = list() //whitelist for overflow + + var/disable_away_missions = 0 // disable away missions + var/disable_space_ruins = 0 //disable space ruins + + var/extra_space_ruin_levels_min = 2 + var/extra_space_ruin_levels_max = 4 + + var/ooc_allowed = 1 + var/looc_allowed = 1 + var/dooc_allowed = 1 + var/dsay_allowed = 1 + + var/disable_lobby_music = 0 // Disables the lobby music + var/disable_cid_warn_popup = 0 //disables the annoying "You have already logged in this round, disconnect or be banned" popup, because it annoys the shit out of me when testing. + + var/max_loadout_points = 5 // How many points can be spent on extra items in character setup + + var/disable_ooc_emoji = 0 // prevents people from using emoji in OOC + + var/shutdown_on_reboot = 0 // Whether to shut down the world instead of rebooting it + + var/disable_karma = 0 // Disable all karma functions and unlock karma jobs by default + + // StonedMC + var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling + + // Highpop tickrates + var/base_mc_tick_rate = 1 + var/high_pop_mc_tick_rate = 1.1 + + var/high_pop_mc_mode_amount = 65 + var/disable_high_pop_mc_mode_amount = 60 + + // Nightshift + var/randomize_shift_time = FALSE + var/enable_night_shifts = FALSE + + // Developer + var/developer_express_start = 0 + + // Automatic localhost admin disable + var/disable_localhost_admin = 0 + + //Start now warning + var/start_now_confirmation = 0 + + // Lavaland + var/lavaland_budget = 60 + + //cube monkey limit + var/cubemonkeycap = 20 + + // Makes gamemodes respect player limits + var/enable_gamemode_player_limit = 0 + +/datum/configuration/New() + for(var/T in subtypesof(/datum/game_mode)) + var/datum/game_mode/M = T + + if(initial(M.config_tag)) + if(!(initial(M.config_tag) in modes)) // ensure each mode is added only once + src.modes += initial(M.config_tag) + src.mode_names[initial(M.config_tag)] = initial(M.name) + src.probabilities[initial(M.config_tag)] = initial(M.probability) + if(initial(M.votable)) + src.votable_modes += initial(M.config_tag) + src.votable_modes += "secret" + +/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist + var/list/Lines = file2list(filename) + + for(var/t in Lines) + if(!t) continue + + t = trim(t) + if(length(t) == 0) + continue + else if(copytext(t, 1, 2) == "#") + continue + + var/pos = findtext(t, " ") + var/name = null + var/value = null + + if(pos) + name = lowertext(copytext(t, 1, pos)) + value = copytext(t, pos + 1) + else + name = lowertext(t) + + if(!name) + continue + + if(type == "config") + switch(name) + if("resource_urls") + config.resource_urls = splittext(value, " ") + + if("admin_legacy_system") + config.admin_legacy_system = 1 + + if("ban_legacy_system") + config.ban_legacy_system = 1 + + if("use_age_restriction_for_jobs") + config.use_age_restriction_for_jobs = 1 + + if("use_age_restriction_for_antags") + config.use_age_restriction_for_antags = 1 + + if("use_exp_tracking") + config.use_exp_tracking = 1 + + if("use_exp_restrictions") + config.use_exp_restrictions = 1 + + if("use_exp_restrictions_admin_bypass") + config.use_exp_restrictions_admin_bypass = 1 + + if("jobs_have_minimal_access") + config.jobs_have_minimal_access = 1 + + if("shadowling_max_age") + config.shadowling_max_age = text2num(value) + + if("warn_afk_minimum") + config.warn_afk_minimum = text2num(value) + if("auto_cryo_afk") + config.auto_cryo_afk = text2num(value) + if("auto_despawn_afk") + config.auto_despawn_afk = text2num(value) + + if("auto_cryo_ssd_mins") + config.auto_cryo_ssd_mins = text2num(value) + if("ssd_warning") + config.ssd_warning = 1 + + if("list_afk_minimum") + config.list_afk_minimum = text2num(value) + + if("ipintel_email") + if(value != "ch@nge.me") + config.ipintel_email = value + if("ipintel_rating_bad") + config.ipintel_rating_bad = text2num(value) + if("ipintel_domain") + config.ipintel_domain = value + if("ipintel_save_good") + config.ipintel_save_good = text2num(value) + if("ipintel_save_bad") + config.ipintel_save_bad = text2num(value) + if("ipintel_maxplaytime") + config.ipintel_maxplaytime = text2num(value) + if("ipintel_whitelist") + config.ipintel_whitelist = 1 + if("ipintel_detailsurl") + config.ipintel_detailsurl = value + + if("forum_link_url") + config.forum_link_url = value + + if("forum_playerinfo_url") + config.forum_playerinfo_url = value + + if("log_ooc") + config.log_ooc = 1 + + if("log_access") + config.log_access = 1 + + if("log_say") + config.log_say = 1 + + if("log_admin") + config.log_admin = 1 + + if("log_debug") + config.log_debug = 1 + + if("log_game") + config.log_game = 1 + + if("log_vote") + config.log_vote = 1 + + if("log_whisper") + config.log_whisper = 1 + + if("log_attack") + config.log_attack = 1 + + if("log_emote") + config.log_emote = 1 + + if("log_adminchat") + config.log_adminchat = 1 + + if("log_adminwarn") + config.log_adminwarn = 1 + + if("log_pda") + config.log_pda = 1 + + if("log_world_output") + config.log_world_output = 1 + + if("log_hrefs") + config.log_hrefs = 1 + + if("log_runtime") + config.log_runtimes = 1 + + if("mentors") + config.mods_are_mentors = 1 + + if("allow_admin_ooccolor") + config.allow_admin_ooccolor = 1 + + if("pregame_timestart") + config.pregame_timestart = text2num(value) + + if("allow_vote_restart") + config.allow_vote_restart = 1 + + if("allow_vote_mode") + config.allow_vote_mode = 1 + + if("no_dead_vote") + config.vote_no_dead = 1 + + if("vote_autotransfer_initial") + config.vote_autotransfer_initial = text2num(value) + + if("vote_autotransfer_interval") + config.vote_autotransfer_interval = text2num(value) + + if("default_no_vote") + config.vote_no_default = 1 + + if("vote_delay") + config.vote_delay = text2num(value) + + if("vote_period") + config.vote_period = text2num(value) + + if("allow_ai") + config.allow_ai = 1 + +// if("authentication") +// config.enable_authentication = 1 + + if("norespawn") + config.respawn = 0 + + if("servername") + config.server_name = value + + if("server_tag_line") + config.server_tag_line = value + + if("server_extra_features") + config.server_extra_features = value + + if("serversuffix") + config.server_suffix = 1 + + if("minimum_client_build") + config.minimum_client_build = text2num(value) + + if("nudge_script_path") + config.nudge_script_path = value + + if("server") + config.server = value + + if("banappeals") + config.banappeals = value + + if("wikiurl") + config.wikiurl = value + + if("forumurl") + config.forumurl = value + + if("rulesurl") + config.rulesurl = value + + if("githuburl") + config.githuburl = value + + if("discordurl") + config.discordurl = value + + if("discordforumurl") + config.discordforumurl = value + + if("donationsurl") + config.donationsurl = value + + if("repositoryurl") + config.repositoryurl = value + + if("guest_jobban") + config.guest_jobban = 1 + + if("guest_ban") + GLOB.guests_allowed = 0 + + if("panic_bunker_threshold") + config.panic_bunker_threshold = text2num(value) + + if("usewhitelist") + config.usewhitelist = 1 + + if("feature_object_spell_system") + config.feature_object_spell_system = 1 + + if("allow_metadata") + config.allow_Metadata = 1 + + if("traitor_scaling") + config.traitor_scaling = 1 + + if("protect_roles_from_antagonist") + config.protect_roles_from_antagonist = 1 + + if("probability") + var/prob_pos = findtext(value, " ") + var/prob_name = null + var/prob_value = null + + if(prob_pos) + prob_name = lowertext(copytext(value, 1, prob_pos)) + prob_value = copytext(value, prob_pos + 1) + if(prob_name in config.modes) + config.probabilities[prob_name] = text2num(prob_value) + else + log_config("Unknown game mode probability configuration definition: [prob_name].") + else + log_config("Incorrect probability configuration definition: [prob_name] [prob_value].") + + if("allow_random_events") + config.allow_random_events = 1 + + if("load_jobs_from_txt") + load_jobs_from_txt = 1 + + if("forbid_singulo_possession") + forbid_singulo_possession = 1 + + if("check_randomizer") + check_randomizer = 1 + + if("popup_admin_pm") + config.popup_admin_pm = 1 + + if("allow_holidays") + config.allow_holidays = 1 + + if("use_irc_bot") + use_irc_bot = 1 + + if("ticklag") + Ticklag = text2num(value) + + if("socket_talk") + socket_talk = text2num(value) + + if("allow_antag_hud") + config.antag_hud_allowed = 1 + + if("antag_hud_restricted") + config.antag_hud_restricted = 1 + + if("humans_need_surnames") + humans_need_surnames = 1 + + if("tor_ban") + ToRban = 1 + + if("automute_on") + automute_on = 1 + + if("usealienwhitelist") + usealienwhitelist = 1 + + if("alien_player_ratio") + limitalienplayers = 1 + alien_to_human_ratio = text2num(value) + + if("assistant_maint") + config.assistant_maint = 1 + + if("gateway_delay") + config.gateway_delay = text2num(value) + + if("continuous_rounds") + config.continuous_rounds = 1 + + if("ghost_interaction") + config.ghost_interaction = 1 + + if("comms_password") + config.comms_password = value + + if("irc_bot_host") + config.irc_bot_host = splittext(value, ";") + + if("main_irc") + config.main_irc = value + + if("admin_irc") + config.admin_irc = value + + if("admin_notify_irc") + config.admin_notify_irc = value + + if("cidrandomizer_irc") + config.cidrandomizer_irc = value + + if("python_path") + if(value) + GLOB.python_path = value + else + if(world.system_type == UNIX) + GLOB.python_path = "/usr/bin/env python2" + else //probably windows, if not this should work anyway + GLOB.python_path = "pythonw" + + if("assistant_limit") + config.assistantlimit = 1 + + if("assistant_ratio") + config.assistantratio = text2num(value) + + if("allow_drone_spawn") + config.allow_drone_spawn = text2num(value) + + if("drone_build_time") + config.drone_build_time = text2num(value) + + if("max_maint_drones") + config.max_maint_drones = text2num(value) + + if("expected_round_length") + config.expected_round_length = MinutesToTicks(text2num(value)) + + if("event_custom_start_mundane") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_custom_start_moderate") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_custom_start_major") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_delay_lower") + var/values = text2numlist(value, ";") + config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) + config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) + config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) + + if("event_delay_upper") + var/values = text2numlist(value, ";") + config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) + config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) + config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) + + if("starlight") + config.starlight = 1 + + if("player_reroute_cap") + var/vvalue = text2num(value) + config.player_overflow_cap = vvalue >= 0 ? vvalue : 0 + + if("overflow_server_url") + config.overflow_server_url = value + + if("disable_away_missions") + config.disable_away_missions = 1 + + if("disable_space_ruins") + config.disable_space_ruins = 1 + + if("disable_lobby_music") + config.disable_lobby_music = 1 + + if("disable_cid_warn_popup") + config.disable_cid_warn_popup = 1 + + if("extra_space_ruin_levels_min") + var/vvalue = text2num(value) + config.extra_space_ruin_levels_min = max(vvalue, 0) + + if("extra_space_ruin_levels_max") + var/vvalue = text2num(value) + config.extra_space_ruin_levels_max = max(vvalue, 0) + + if("max_loadout_points") + config.max_loadout_points = text2num(value) + + if("round_abandon_penalty_period") + config.round_abandon_penalty_period = MinutesToTicks(text2num(value)) + + if("medal_hub_address") + config.medal_hub_address = value + + if("medal_hub_password") + config.medal_hub_password = value + + if("disable_ooc_emoji") + config.disable_ooc_emoji = 1 + + if("shutdown_on_reboot") + config.shutdown_on_reboot = 1 + + if("shutdown_shell_command") + GLOB.shutdown_shell_command = value + + if("disable_karma") + config.disable_karma = 1 + + if("start_now_confirmation") + config.start_now_confirmation = 1 + + if("tick_limit_mc_init") + config.tick_limit_mc_init = text2num(value) + if("base_mc_tick_rate") + config.base_mc_tick_rate = text2num(value) + if("high_pop_mc_tick_rate") + config.high_pop_mc_tick_rate = text2num(value) + if("high_pop_mc_mode_amount") + config.high_pop_mc_mode_amount = text2num(value) + if("disable_high_pop_mc_mode_amount") + config.disable_high_pop_mc_mode_amount = text2num(value) + if("developer_express_start") + config.developer_express_start = 1 + if("disable_localhost_admin") + config.disable_localhost_admin = 1 + if("enable_gamemode_player_limit") + config.enable_gamemode_player_limit = 1 + else + log_config("Unknown setting in configuration: '[name]'") + + + else if(type == "game_options") + value = text2num(value) + + switch(name) + if("revival_pod_plants") + config.revival_pod_plants = value + if("revival_cloning") + config.revival_cloning = value + if("revival_brain_life") + config.revival_brain_life = value + if("auto_toggle_ooc_during_round") + config.auto_toggle_ooc_during_round = 1 + if("run_speed") + config.run_speed = value + if("walk_speed") + config.walk_speed = value + if("human_delay") + config.human_delay = value + if("robot_delay") + config.robot_delay = value + if("monkey_delay") + config.monkey_delay = value + if("alien_delay") + config.alien_delay = value + if("slime_delay") + config.slime_delay = value + if("animal_delay") + config.animal_delay = value + if("bones_can_break") + config.bones_can_break = value + if("shuttle_refuel_delay") + config.shuttle_refuel_delay = text2num(value) + if("traitor_objectives_amount") + config.traitor_objectives_amount = text2num(value) + if("reactionary_explosions") + config.reactionary_explosions = 1 + if("bombcap") + var/BombCap = text2num(value) + if(!BombCap) + continue + if(BombCap < 4) + BombCap = 4 + if(BombCap > 128) + BombCap = 128 + + GLOB.max_ex_devastation_range = round(BombCap/4) + GLOB.max_ex_heavy_range = round(BombCap/2) + GLOB.max_ex_light_range = BombCap + GLOB.max_ex_flash_range = BombCap + GLOB.max_ex_flame_range = BombCap + if("default_laws") + config.default_laws = text2num(value) + if("randomize_shift_time") + config.randomize_shift_time = TRUE + if("enable_night_shifts") + config.enable_night_shifts = TRUE + if("lavaland_budget") + config.lavaland_budget = text2num(value) + if("cubemonkey_cap") + config.cubemonkeycap = text2num(value) + else + log_config("Unknown setting in configuration: '[name]'") + +/datum/configuration/proc/loadsql(filename) // -- TLE + var/list/Lines = file2list(filename) + var/db_version = 0 + for(var/t in Lines) + if(!t) continue + + t = trim(t) + if(length(t) == 0) + continue + else if(copytext(t, 1, 2) == "#") + continue + + var/pos = findtext(t, " ") + var/name = null + var/value = null + + if(pos) + name = lowertext(copytext(t, 1, pos)) + value = copytext(t, pos + 1) + else + name = lowertext(t) + + if(!name) + continue + + switch(name) + if("sql_enabled") + config.sql_enabled = 1 + if("address") + sqladdress = value + if("port") + sqlport = value + if("feedback_database") + sqlfdbkdb = value + if("feedback_login") + sqlfdbklogin = value + if("feedback_password") + sqlfdbkpass = value + if("feedback_tableprefix") + sqlfdbktableprefix = value + if("db_version") + db_version = text2num(value) + else + log_config("Unknown setting in configuration: '[name]'") + if(config.sql_enabled && db_version != SQL_VERSION) + config.sql_enabled = 0 + log_config("WARNING: DB_CONFIG DEFINITION MISMATCH!") + spawn(60) + if(SSticker.current_state == GAME_STATE_PREGAME) + SSticker.ticker_going = FALSE + spawn(600) + to_chat(world, "DB_CONFIG MISMATCH, ROUND START DELAYED.
    Please check database version for recent upstream changes!
    ") + +/datum/configuration/proc/loadoverflowwhitelist(filename) + var/list/Lines = file2list(filename) + for(var/t in Lines) + if(!t) continue + + t = trim(t) + if(length(t) == 0) + continue + else if(copytext(t, 1, 2) == "#") + continue + + config.overflow_whitelist += t + +/datum/configuration/proc/pick_mode(mode_name) + for(var/T in subtypesof(/datum/game_mode)) + var/datum/game_mode/M = T + if(initial(M.config_tag) && initial(M.config_tag) == mode_name) + return new T() + return new /datum/game_mode/extended() + +/datum/configuration/proc/get_runnable_modes() + var/list/datum/game_mode/runnable_modes = new + for(var/T in subtypesof(/datum/game_mode)) + var/datum/game_mode/M = new T() +// to_chat(world, "DEBUG: [T], tag=[M.config_tag], prob=[probabilities[M.config_tag]]") + if(!(M.config_tag in modes)) + qdel(M) + continue + if(probabilities[M.config_tag]<=0) + qdel(M) + continue + if(M.can_start()) + runnable_modes[M] = probabilities[M.config_tag] +// to_chat(world, "DEBUG: runnable_mode\[[runnable_modes.len]\] = [M.config_tag]") + return runnable_modes diff --git a/code/controllers/controller.dm b/code/controllers/controller.dm index 06547d120d5..c9d5f1e5650 100644 --- a/code/controllers/controller.dm +++ b/code/controllers/controller.dm @@ -16,4 +16,4 @@ /datum/controller/proc/Recover() -/datum/controller/proc/stat_entry() \ No newline at end of file +/datum/controller/proc/stat_entry() diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index 8b0f0b1987e..bebcdc7a897 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -1,102 +1,102 @@ - /** - * Failsafe - * - * Pretty much pokes the MC to make sure it's still alive. - **/ - -GLOBAL_REAL(Failsafe, /datum/controller/failsafe) - -/datum/controller/failsafe // This thing pretty much just keeps poking the master controller - name = "Failsafe" - - // The length of time to check on the MC (in deciseconds). - // Set to 0 to disable. - var/processing_interval = 20 - // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC. - var/defcon = 5 - //the world.time of the last check, so the mc can restart US if we hang. - // (Real friends look out for *eachother*) - var/lasttick = 0 - - // Track the MC iteration to make sure its still on track. - var/master_iteration = 0 - var/running = TRUE - -/datum/controller/failsafe/New() - // Highlander-style: there can only be one! Kill off the old and replace it with the new. - if(Failsafe != src) - if(istype(Failsafe)) - qdel(Failsafe) - Failsafe = src - Initialize() - -/datum/controller/failsafe/Initialize() - set waitfor = 0 - Failsafe.Loop() - if(!QDELETED(src)) - qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us - -/datum/controller/failsafe/Destroy() - running = FALSE - ..() - return QDEL_HINT_HARDDEL_NOW - -/datum/controller/failsafe/proc/Loop() - while(running) - lasttick = world.time - if(!Master) - // Replace the missing Master! This should never, ever happen. - new /datum/controller/master() - // Only poke it if overrides are not in effect. - if(processing_interval > 0) - if(Master.processing && Master.iteration) - // Check if processing is done yet. - if(Master.iteration == master_iteration) - switch(defcon) - if(4,5) - --defcon - if(3) - message_admins("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks.") - --defcon - if(2) - to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.") - --defcon - if(1) - - to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5 - defcon) * processing_interval] ticks. Killing and restarting...") - --defcon - var/rtn = Recreate_MC() - if(rtn > 0) - defcon = 4 - master_iteration = 0 - to_chat(GLOB.admins, "MC restarted successfully") - else if(rtn < 0) - log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") - to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.") - //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again - //no need to handle that specially when defcon 0 can handle it - if(0) //DEFCON 0! (mc failed to restart) - var/rtn = Recreate_MC() - if(rtn > 0) - defcon = 4 - master_iteration = 0 - to_chat(GLOB.admins, "MC restarted successfully") - else - defcon = min(defcon + 1,5) - master_iteration = Master.iteration - if(defcon <= 1) - sleep(processing_interval * 2) - else - sleep(processing_interval) - else - defcon = 5 - sleep(initial(processing_interval)) - -/datum/controller/failsafe/proc/defcon_pretty() - return defcon - -/datum/controller/failsafe/stat_entry() - if(!statclick) - statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) - - stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])")) + /** + * Failsafe + * + * Pretty much pokes the MC to make sure it's still alive. + **/ + +GLOBAL_REAL(Failsafe, /datum/controller/failsafe) + +/datum/controller/failsafe // This thing pretty much just keeps poking the master controller + name = "Failsafe" + + // The length of time to check on the MC (in deciseconds). + // Set to 0 to disable. + var/processing_interval = 20 + // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC. + var/defcon = 5 + //the world.time of the last check, so the mc can restart US if we hang. + // (Real friends look out for *eachother*) + var/lasttick = 0 + + // Track the MC iteration to make sure its still on track. + var/master_iteration = 0 + var/running = TRUE + +/datum/controller/failsafe/New() + // Highlander-style: there can only be one! Kill off the old and replace it with the new. + if(Failsafe != src) + if(istype(Failsafe)) + qdel(Failsafe) + Failsafe = src + Initialize() + +/datum/controller/failsafe/Initialize() + set waitfor = 0 + Failsafe.Loop() + if(!QDELETED(src)) + qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us + +/datum/controller/failsafe/Destroy() + running = FALSE + ..() + return QDEL_HINT_HARDDEL_NOW + +/datum/controller/failsafe/proc/Loop() + while(running) + lasttick = world.time + if(!Master) + // Replace the missing Master! This should never, ever happen. + new /datum/controller/master() + // Only poke it if overrides are not in effect. + if(processing_interval > 0) + if(Master.processing && Master.iteration) + // Check if processing is done yet. + if(Master.iteration == master_iteration) + switch(defcon) + if(4,5) + --defcon + if(3) + message_admins("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks.") + --defcon + if(2) + to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.") + --defcon + if(1) + + to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5 - defcon) * processing_interval] ticks. Killing and restarting...") + --defcon + var/rtn = Recreate_MC() + if(rtn > 0) + defcon = 4 + master_iteration = 0 + to_chat(GLOB.admins, "MC restarted successfully") + else if(rtn < 0) + log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") + to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.") + //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again + //no need to handle that specially when defcon 0 can handle it + if(0) //DEFCON 0! (mc failed to restart) + var/rtn = Recreate_MC() + if(rtn > 0) + defcon = 4 + master_iteration = 0 + to_chat(GLOB.admins, "MC restarted successfully") + else + defcon = min(defcon + 1,5) + master_iteration = Master.iteration + if(defcon <= 1) + sleep(processing_interval * 2) + else + sleep(processing_interval) + else + defcon = 5 + sleep(initial(processing_interval)) + +/datum/controller/failsafe/proc/defcon_pretty() + return defcon + +/datum/controller/failsafe/stat_entry() + if(!statclick) + statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) + + stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])")) diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm index 543f0aed4b2..5a520b547e1 100644 --- a/code/controllers/globals.dm +++ b/code/controllers/globals.dm @@ -64,4 +64,4 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars) call(src, I)() var/end_tick = world.time if(end_tick - start_tick) - warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!") \ No newline at end of file + warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!") diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 10c6b42a369..fea79c2f7a5 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -62,10 +62,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/static/current_ticklimit = TICK_LIMIT_RUNNING /datum/controller/master/New() - //temporary file used to record errors with loading config, moved to log directory once logging is set up - GLOB.config_error_log = GLOB.world_game_log = GLOB.world_runtime_log = "data/logs/config_error.log" - load_configuration() - // Highlander-style: there can only be one! Kill off the old and replace it with the new. if(!random_seed) random_seed = rand(1, 1e9) @@ -73,6 +69,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/list/_subsystems = list() subsystems = _subsystems + // Highlander-style: there can only be one! Kill off the old and replace it with the new. if(Master != src) if(istype(Master)) Recover() @@ -146,7 +143,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new msg = "The [BadBoy.name] subsystem was the last to fire for 2 controller restarts. It will be recovered now and disabled if it happens again." FireHim = TRUE if(3) - msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined." + msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined. The following implications are now in effect: [BadBoy.offline_implications]" BadBoy.flags |= SS_NO_FIRE if(msg) to_chat(GLOB.admins, "[msg]") @@ -615,4 +612,4 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(client_count < config.disable_high_pop_mc_mode_amount) processing = config.base_mc_tick_rate else if(client_count > config.high_pop_mc_mode_amount) - processing = config.high_pop_mc_tick_rate \ No newline at end of file + processing = config.high_pop_mc_tick_rate diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 7ba1b68d4a6..4a125003389 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -35,6 +35,8 @@ var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out! + var/offline_implications = "None" // What are the implications of this SS being offlined? + //Do not override ///datum/controller/subsystem/New() @@ -179,7 +181,7 @@ var/title = name if(can_fire) - title = "\[[state_letter()]][title]" + title = "[state_colour()]\[[state_letter()]][title]
    " stat(title, statclick.update(msg)) @@ -196,6 +198,18 @@ if(SS_IDLE) . = " " +/datum/controller/subsystem/proc/state_colour() + switch(state) + if(SS_RUNNING) // If its actively processing, colour it green + . = "" + if(SS_QUEUED) // If its in the running queue, but delayed, colour it orange + . = "" + if(SS_PAUSED, SS_PAUSING) // If its being paused due to lag, colour it red + . = "" + if(SS_SLEEPING) // If fire() slept, colour it blue + . = "" + if(SS_IDLE) // Leave it default if the SS is idle + . = "" //could be used to postpone a costly subsystem for (default one) var/cycles, cycles //for instance, during cpu intensive operations like explosions /datum/controller/subsystem/proc/postpone(cycles = 1) @@ -214,4 +228,4 @@ next_fire = world.time + wait if("queued_priority") //editing this breaks things. return 0 - . = ..() \ No newline at end of file + . = ..() diff --git a/code/controllers/subsystem/acid.dm b/code/controllers/subsystem/acid.dm index f86c8540af5..119d82113d7 100644 --- a/code/controllers/subsystem/acid.dm +++ b/code/controllers/subsystem/acid.dm @@ -3,6 +3,7 @@ SUBSYSTEM_DEF(acid) priority = FIRE_PRIORITY_ACID flags = SS_NO_INIT|SS_BACKGROUND runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + offline_implications = "Objects will no longer react to acid. No immediate action is needed." var/list/currentrun = list() var/list/processing = list() @@ -33,4 +34,4 @@ SUBSYSTEM_DEF(acid) processing -= O if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/afk.dm b/code/controllers/subsystem/afk.dm index 9f409fd35b4..9122e6a16c6 100644 --- a/code/controllers/subsystem/afk.dm +++ b/code/controllers/subsystem/afk.dm @@ -5,6 +5,7 @@ SUBSYSTEM_DEF(afk) name = "AFK Watcher" wait = 300 flags = SS_BACKGROUND + offline_implications = "Players will no longer be marked as AFK. No immediate action is needed." var/list/afk_players = list() // Associative list. ckey as key and AFK state as value @@ -17,27 +18,27 @@ SUBSYSTEM_DEF(afk) for(var/mob/living/carbon/human/H in GLOB.living_mob_list) if(!H.ckey) // Useless non ckey creatures continue - - var/turf/T + + var/turf/T // Only players and players with the AFK watch enabled // No dead, unconcious, restrained, people without jobs, people on other Z levels than the station or antags if(!H.client || !H.client.prefs.afk_watch || !H.mind || \ H.stat || H.restrained() || !H.job || H.mind.special_role || \ !is_station_level((T = get_turf(H)).z)) // Assign the turf as last. Small optimization - if(afk_players[H.ckey]) + if(afk_players[H.ckey]) toRemove += H.ckey continue - + var/mins_afk = round(H.client.inactivity / 600) if(mins_afk < config.warn_afk_minimum) if(afk_players[H.ckey]) toRemove += H.ckey continue - + if(!afk_players[H.ckey]) afk_players[H.ckey] = AFK_WARNED warn(H, "You are AFK for [mins_afk] minutes. You will be cryod after [config.auto_cryo_afk] total minutes and fully despawned after [config.auto_despawn_afk] total minutes. Please move or click in game if you want to avoid being despawned.") - else + else var/area/A = T.loc // Turfs loc is the area if(afk_players[H.ckey] == AFK_WARNED) if(mins_afk >= config.auto_cryo_afk && A.can_get_auto_cryod) @@ -50,14 +51,14 @@ SUBSYSTEM_DEF(afk) afk_players[H.ckey] = AFK_CRYOD msg_admins(H, mins_afk, T, "put into cryostorage") warn(H, "You are AFK for [mins_afk] minutes and have been moved to cryostorage. After being AFK for [config.auto_despawn_afk] total minutes you will be fully despawned. Please eject yourself (right click, eject) out of the cryostorage if you want to avoid being despawned.") - + else if(mins_afk >= config.auto_despawn_afk) var/obj/machinery/cryopod/P = H.loc msg_admins(H, mins_afk, T, "forcefully despawned") warn(H, "You are have been despawned after being AFK for [mins_afk] minutes.") toRemove += H.ckey P.despawn_occupant() - + removeFromWatchList(toRemove) /datum/controller/subsystem/afk/proc/warn(mob/living/carbon/human/H, text) @@ -78,4 +79,4 @@ SUBSYSTEM_DEF(afk) break #undef AFK_WARNED -#undef AFK_CRYOD \ No newline at end of file +#undef AFK_CRYOD diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index fe48494f321..80ce66d13ec 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -14,6 +14,7 @@ SUBSYSTEM_DEF(air) wait = 5 flags = SS_BACKGROUND runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + offline_implications = "Turfs will no longer process atmos, and all atmospheric machines (including cryotubes) will no longer function. Shuttle call recommended." var/cost_turfs = 0 var/cost_groups = 0 var/cost_highpressure = 0 @@ -275,7 +276,7 @@ SUBSYSTEM_DEF(air) if(blockchanges && T.excited_group) T.excited_group.garbage_collect() else - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(T.atmos_adjacent_turfs & direction)) continue var/turf/simulated/S = get_step(T, direction) @@ -360,21 +361,21 @@ SUBSYSTEM_DEF(air) return count /datum/controller/subsystem/air/proc/setup_overlays() - plmaster = new /obj/effect/overlay() - plmaster.icon = 'icons/effects/tile_effects.dmi' - plmaster.icon_state = "plasma" - plmaster.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - plmaster.anchored = TRUE // should only appear in vis_contents, but to be safe - plmaster.layer = FLY_LAYER - plmaster.appearance_flags = TILE_BOUND + GLOB.plmaster = new /obj/effect/overlay() + GLOB.plmaster.icon = 'icons/effects/tile_effects.dmi' + GLOB.plmaster.icon_state = "plasma" + GLOB.plmaster.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + GLOB.plmaster.anchored = TRUE // should only appear in vis_contents, but to be safe + GLOB.plmaster.layer = FLY_LAYER + GLOB.plmaster.appearance_flags = TILE_BOUND - slmaster = new /obj/effect/overlay() - slmaster.icon = 'icons/effects/tile_effects.dmi' - slmaster.icon_state = "sleeping_agent" - slmaster.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - slmaster.anchored = TRUE // should only appear in vis_contents, but to be safe - slmaster.layer = FLY_LAYER - slmaster.appearance_flags = TILE_BOUND + GLOB.slmaster = new /obj/effect/overlay() + GLOB.slmaster.icon = 'icons/effects/tile_effects.dmi' + GLOB.slmaster.icon_state = "sleeping_agent" + GLOB.slmaster.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + GLOB.slmaster.anchored = TRUE // should only appear in vis_contents, but to be safe + GLOB.slmaster.layer = FLY_LAYER + GLOB.slmaster.appearance_flags = TILE_BOUND #undef SSAIR_PIPENETS #undef SSAIR_ATMOSMACHINERY @@ -382,4 +383,4 @@ SUBSYSTEM_DEF(air) #undef SSAIR_EXCITEDGROUPS #undef SSAIR_HIGHPRESSURE #undef SSAIR_HOTSPOTS -#undef SSAIR_SUPERCONDUCTIVITY \ No newline at end of file +#undef SSAIR_SUPERCONDUCTIVITY diff --git a/code/controllers/subsystem/alarm.dm b/code/controllers/subsystem/alarm.dm index 90e3ff960f1..289adf6de40 100644 --- a/code/controllers/subsystem/alarm.dm +++ b/code/controllers/subsystem/alarm.dm @@ -1,30 +1,31 @@ -SUBSYSTEM_DEF(alarms) - name = "Alarms" - init_order = INIT_ORDER_ALARMS // 2 - var/datum/alarm_handler/atmosphere/atmosphere_alarm = new() - var/datum/alarm_handler/burglar/burglar_alarm = new() - var/datum/alarm_handler/camera/camera_alarm = new() - var/datum/alarm_handler/fire/fire_alarm = new() - var/datum/alarm_handler/motion/motion_alarm = new() - var/datum/alarm_handler/power/power_alarm = new() - var/list/datum/alarm/all_handlers - -/datum/controller/subsystem/alarms/Initialize(start_timeofday) - all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm) - return ..() - -/datum/controller/subsystem/alarms/fire() - for(var/datum/alarm_handler/AH in all_handlers) - AH.process() - -/datum/controller/subsystem/alarms/proc/active_alarms() - var/list/all_alarms = new () - for(var/datum/alarm_handler/AH in all_handlers) - var/list/alarms = AH.alarms - all_alarms += alarms - - return all_alarms - -/datum/controller/subsystem/alarms/proc/number_of_active_alarms() - var/list/alarms = active_alarms() - return alarms.len +SUBSYSTEM_DEF(alarms) + name = "Alarms" + init_order = INIT_ORDER_ALARMS // 2 + offline_implications = "Alarms (Power, camera, fire, etc) will no longer be checked. No immediate action is needed." + var/datum/alarm_handler/atmosphere/atmosphere_alarm = new() + var/datum/alarm_handler/burglar/burglar_alarm = new() + var/datum/alarm_handler/camera/camera_alarm = new() + var/datum/alarm_handler/fire/fire_alarm = new() + var/datum/alarm_handler/motion/motion_alarm = new() + var/datum/alarm_handler/power/power_alarm = new() + var/list/datum/alarm/all_handlers + +/datum/controller/subsystem/alarms/Initialize(start_timeofday) + all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm) + return ..() + +/datum/controller/subsystem/alarms/fire() + for(var/datum/alarm_handler/AH in all_handlers) + AH.process() + +/datum/controller/subsystem/alarms/proc/active_alarms() + var/list/all_alarms = new () + for(var/datum/alarm_handler/AH in all_handlers) + var/list/alarms = AH.alarms + all_alarms += alarms + + return all_alarms + +/datum/controller/subsystem/alarms/proc/number_of_active_alarms() + var/list/alarms = active_alarms() + return alarms.len diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm index cd531db614a..7a9a3d355f3 100644 --- a/code/controllers/subsystem/assets.dm +++ b/code/controllers/subsystem/assets.dm @@ -14,4 +14,4 @@ SUBSYSTEM_DEF(assets) for(var/client/C in GLOB.clients) addtimer(CALLBACK(GLOBAL_PROC, .proc/getFilesSlow, C, preload, FALSE), 10) - return ..() \ No newline at end of file + return ..() diff --git a/code/controllers/subsystem/changelog.dm b/code/controllers/subsystem/changelog.dm new file mode 100644 index 00000000000..98188599abd --- /dev/null +++ b/code/controllers/subsystem/changelog.dm @@ -0,0 +1,263 @@ +/* + Subsystem core for ParadiseSS13 changelogs + Author: AffectedArc07 + + Basically this SS extracts changelogs from the past 30 days from the database, and cleanly formats them into HTML that the players can see + It only runs the extraction on initialize to ensure that the changelog doesnt change mid round, and to reduce the amount of DB calls that need to be done + The changelog entries are generated from the PHP scripts in tools/githubChangelogProcessor.php + + This SS also handles the checking of player CL dates and informing them if it has changed + +*/ + +SUBSYSTEM_DEF(changelog) + name = "Changelog" + flags = SS_NO_FIRE + var/current_cl_timestamp = "0" // Timestamp is seconds since UNIX epoch (1st January 1970). ITs also a string because BYOND doesnt like big numbers. + var/ss_ready = FALSE // Is the SS ready? We dont want to run procs if we have not generated yet + var/list/startup_clients_button = list() // Clients who connected before initialization who need their button color updating + var/list/startup_clients_open = list() // Clients who connected before initialization who need the CL opening + var/changelogHTML = "" // HTML that the changelog will use to display + +/datum/controller/subsystem/changelog/Initialize() + // This entire subsystem relies on SQL being here. + if(!GLOB.dbcon.IsConnected()) + return ..() + + var/DBQuery/latest_cl_date = GLOB.dbcon.NewQuery("SELECT UNIX_TIMESTAMP(date_merged) AS ut FROM [format_table_name("changelog")] ORDER BY date_merged DESC LIMIT 1") + if(!latest_cl_date.Execute()) + var/err = latest_cl_date.ErrorMsg() + log_game("SQL ERROR during SSchangelog initialization L24. Error: \[[err]\]\n") + message_admins("SQL ERROR during SSchangelog initialization L24. Error: \[[err]\]\n") + // Abort if we cant do this + return ..() + + while(latest_cl_date.NextRow()) + current_cl_timestamp = latest_cl_date.item[1] + + if(!GenerateChangelogHTML()) // if this failed to generate + to_chat(world, "WARNING: Changelog failed to generate. Please inform a coder/server dev") + return ..() + + ss_ready = TRUE + // Now we can alert anyone who wanted to check the changelog + for(var/x in startup_clients_button) + var/client/C = x + UpdatePlayerChangelogButton(C) + + // Now we can alert anyone who wanted to check the changelog + for(var/client/C in startup_clients_open) + OpenChangelog(C) + + return ..() + + +/datum/controller/subsystem/changelog/proc/UpdatePlayerChangelogDate(client/C) + if(!ss_ready) + return // Only return here, we dont have to worry about a queue list because this will be called from ShowChangelog() + // Technically this is only for the date but we can also do the UI button at the same time + var/datum/preferences/P = GLOB.preferences_datums[C.ckey] + if(P.toggles & UI_DARKMODE) + winset(C, "rpane.changelog", "background-color=#40628a;font-color=#ffffff;font-style=none") + else + winset(C, "rpane.changelog", "background-color=none;font-style=none") + C.prefs.lastchangelog = current_cl_timestamp + var/DBQuery/updatePlayerCLTime = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastchangelog='[sanitizeSQL(current_cl_timestamp)]' WHERE ckey='[C.ckey]'") + if(!updatePlayerCLTime.Execute()) + var/err = updatePlayerCLTime.ErrorMsg() + log_game("SQL ERROR during lastchangelog updating. Error: \[[err]\]\n") + message_admins("SQL ERROR during lastchangelog updating. Error: \[[err]\]\n") + to_chat(C, "Couldn't update your last seen changelog, please try again later.") + return FALSE + return TRUE + +/datum/controller/subsystem/changelog/proc/UpdatePlayerChangelogButton(client/C) + // If SQL aint even enabled, just set the button to default style + if(!GLOB.dbcon.IsConnected()) + if(C.prefs.toggles & UI_DARKMODE) + winset(C, "rpane.changelog", "background-color=#40628a;text-color=#FFFFFF") + else + winset(C, "rpane.changelog", "background-color=none;text-color=#000000") + return + + // If SQL is enabled but we aint ready, queue them up, and use the default style + if(!ss_ready) + startup_clients_button |= C + if(C.prefs.toggles & UI_DARKMODE) + winset(C, "rpane.changelog", "background-color=#40628a;text-color=#FFFFFF") + else + winset(C, "rpane.changelog", "background-color=none;text-color=#000000") + return + + // Sanity check to ensure clients still exist (If a client DCs mid startup this would runtime) + if(C && C.prefs) + // If we are ready, process the button style + if(C.prefs.lastchangelog != current_cl_timestamp) + winset(C, "rpane.changelog", "background-color=#bb7700;text-color=#FFFFFF;font-style=bold") + to_chat(C, "Changelog has changed since your last visit.") + else + if(C.prefs.toggles & UI_DARKMODE) + winset(C, "rpane.changelog", "background-color=#40628a;text-color=#FFFFFF") + else + winset(C, "rpane.changelog", "background-color=none;text-color=#000000") + + +/datum/controller/subsystem/changelog/proc/OpenChangelog(client/C) + // If SQL isnt enabled, dont even queue them, just tell them it wont work + if(!GLOB.dbcon.IsConnected()) + to_chat(C, "This server is not running with an SQL backend. Changelog is unavailable.") + return + + // If SQL is enabled but we aint ready, queue them up + if(!ss_ready) + startup_clients_open |= C + to_chat(C, "The changelog system is still initializing. The changelog will open for you once it has initialized.") + return + + UpdatePlayerChangelogDate(C) + UpdatePlayerChangelogButton(C) + + var/datum/browser/cl_popup = new(C.mob, "changelog", "Changelog", 700, 800) + cl_popup.set_content(changelogHTML) + cl_popup.open() + +/client/verb/changes() + set name = "Changelog" + set desc = "View the changelog." + set category = "OOC" + // Just invoke the actual CL thing + SSchangelog.OpenChangelog(src) + +// Helper to turn CL types into a fontawesome icon instead of an image +// The colors are #28a745 for green, #fd7e14 for orange, and #dc3545 for red. +// These colours are from bootstrap and look good with black and white +/datum/controller/subsystem/changelog/proc/Text2Icon(text) + switch(text) + if("FIX") + return "" // Fixes are white because while they are good, they have no negative coutnerpart + if("WIP") + return "" // WIP stuff is orange because new code is good but its not done yet + if("TWEAK") + return "" // Tweaks are white because they could be good or bad, and theres no specific add or remove + if("SOUNDADD") + return "" // Sound additions are green because its something new + if("SOUNDDEL") + return "" // Sound removals are red because something has been removed + if("CODEADD") + return "" // Code additions are green because its something new + if("CODEDEL") + return "" // Code removals are red becuase someting has been removed + if("IMAGEADD") + return "" // Image additions are green because something has been added + if("IMAGEDEL") + return "" // Image removals are red because something has been removed + if("SPELLCHECK") + return "" // Spellcheck is white because theres no dedicated negative to it, so theres no red for it to collate with + if("EXPERIMENT") + return "" // Experimental stuff is orange because while its a new feature, its unstable + else // Just incase the DB somehow breaks + return "" // Same here + +// This proc is the star of the show +/datum/controller/subsystem/changelog/proc/GenerateChangelogHTML() + // Modify the code below to modify the header of the changelog + var/changelog_header = {" + + ParadiseSS13 Changelog + +
    +

    Paradise Station Changelog

    +

    Forum - Wiki - GitHub

    +
    + "} + + var/list/prs_to_process = list() + // Grab all from last 30 days + var/DBQuery/pr_list_query = GLOB.dbcon.NewQuery("SELECT DISTINCT pr_number FROM changelog WHERE date_merged BETWEEN NOW() - INTERVAL 30 DAY AND NOW() ORDER BY date_merged DESC") + if(!pr_list_query.Execute()) + var/err = pr_list_query.ErrorMsg() + log_game("SQL ERROR during CL generation L143. Error: \[[err]\]\n") + message_admins("SQL ERROR during CL generation L143. Error: \[[err]\]\n") + return FALSE + + while(pr_list_query.NextRow()) + prs_to_process += text2num(pr_list_query.item[1]) + + // Load in the header + changelogHTML += changelog_header + + // Make blocks for all the PRs + for(var/pr_number in prs_to_process) + // Initial declarations + var/pr_block = "" // HTML for the changelog section + var/author = "" // Author of the PR + var/merge_date = "" // Timestamp of when the PR was merged + + // Now we gather the data from the DB + // Also we probably dont need to sanitize the PR number but you never know + var/DBQuery/pr_meta = GLOB.dbcon.NewQuery("SELECT author,DATE(date_merged) AS date FROM changelog WHERE pr_number = [sanitizeSQL(pr_number)] LIMIT 1") + if(!pr_meta.Execute()) + var/err = pr_meta.ErrorMsg() + log_game("SQL ERROR during CL generation L190. Error: \[[err]\]\n") + message_admins("SQL ERROR during CL generation L190. Error: \[[err]\]\n") + return FALSE + + while(pr_meta.NextRow()) + author = pr_meta.item[1] + merge_date = pr_meta.item[2] + + // Now for each actual entry + var/DBQuery/db_entries = GLOB.dbcon.NewQuery("SELECT cl_type, cl_entry FROM changelog WHERE pr_number = [sanitizeSQL(pr_number)]") + if(!db_entries.Execute()) + var/err = db_entries.ErrorMsg() + log_game("SQL ERROR during CL generation L204. Error: \[[err]\]\n") + message_admins("SQL ERROR during CL generation L204. Error: \[[err]\]\n") + return FALSE + + + // Now we make a changelog block + pr_block += "
    " + // If the github URL in the config has a trailing slash, it doesnt matter here, thankfully github accepts having a double slash: https://github.com/org/repo//pull/1 + pr_block += "

    #[pr_number] by [author] (Merged on [merge_date])" + + while(db_entries.NextRow()) + pr_block += "

    [Text2Icon(db_entries.item[1])] [db_entries.item[2]]

    " + + pr_block += "

    " + + changelogHTML += pr_block + + // Make sure we return TRUE so we know it worked + return TRUE + + +// Topic handler so that PRs and forums and stuff open in another window +/datum/controller/subsystem/changelog/Topic(href, href_list) + // Handler to open pages in your browser instead of inside the CL window + // Yes usr.client is gross here but src is the subsystem + // Takes the page to open as an argument + if(href_list["openPage"]) + switch(href_list["openPage"]) + if("forum") + usr.client.forum() + if("wiki") + // Wiki needs snowflake because it has no cancel button + if(config.wikiurl) + if(alert("This will open the wiki in your browser. Are you sure?",,"Yes","No")=="No") + return + usr.client.wiki("") // Blank arg is important here + else + to_chat(usr, "The Wiki URL is not set in the server configuration. Please inform the server host.") + + if("github") + usr.client.github() + // Takes a PR number as argument + if(href_list["openPR"]) + if(config.githuburl) + if(alert("This will open PR #[href_list["openPR"]] in your browser. Are you sure?",,"Yes","No")=="No") + return + var/url = "[config.githuburl]/pull/[href_list["openPR"]]" + usr << link(url) + else + to_chat(usr, "The GitHub URL is not set in the server configuration. PRs cannot be opened from changelog view. Please inform the server host.") + diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index d2f87c93e06..4eb468a0952 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -4,6 +4,7 @@ SUBSYSTEM_DEF(chat) wait = 1 priority = FIRE_PRIORITY_CHAT init_order = INIT_ORDER_CHAT + offline_implications = "Chat messages will no longer be cleanly queued. No immediate action is needed." var/list/payload = list() @@ -63,4 +64,4 @@ SUBSYSTEM_DEF(chat) C.chatOutput.messageQueue += message return - payload[C] += twiceEncoded \ No newline at end of file + payload[C] += twiceEncoded diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index 2f415efa38d..ad77904bac7 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -1,299 +1,300 @@ -SUBSYSTEM_DEF(events) - name = "Events" - init_order = INIT_ORDER_EVENTS - runlevels = RUNLEVEL_GAME - // Report events at the end of the rouund - var/report_at_round_end = 0 - - // UI vars - var/window_x = 700 - var/window_y = 600 - var/table_options = " align='center'" - var/head_options = " style='font-weight:bold;'" - var/row_options1 = " width='85px'" - var/row_options2 = " width='260px'" - var/row_options3 = " width='150px'" - - // Event vars - var/datum/event_container/selected_event_container = null - var/list/active_events = list() - var/list/finished_events = list() - var/list/allEvents - var/list/event_containers = list( - EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane, - EVENT_LEVEL_MODERATE = new/datum/event_container/moderate, - EVENT_LEVEL_MAJOR = new/datum/event_container/major - ) - - var/datum/event_meta/new_event = new - -/datum/controller/subsystem/events/Initialize() - allEvents = subtypesof(/datum/event) - return ..() - -/datum/controller/subsystem/events/fire() - for(var/datum/event/E in active_events) - E.process() - - for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/list/datum/event_container/EC = event_containers[i] - EC.process() - -/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E) - if(!E.event_meta) // datum/event is used here and there for random reasons, maintaining "backwards compatibility" - log_debug("Event of '[E.type]' with missing meta-data has completed.") - return - - finished_events += E - - var/theseverity - - if(!E.severity) - theseverity = EVENT_LEVEL_MODERATE - - if(!E.severity == EVENT_LEVEL_MUNDANE && !E.severity == EVENT_LEVEL_MODERATE && !E.severity == EVENT_LEVEL_MAJOR) - theseverity = EVENT_LEVEL_MODERATE //just to be careful - - if(E.severity) - theseverity = E.severity - - // Add the event back to the list of available events - var/datum/event_container/EC = event_containers[theseverity] - var/datum/event_meta/EM = E.event_meta - EC.available_events += EM - - log_debug("Event '[EM.name]' has completed at [station_time_timestamp()].") - -/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay) - var/list/datum/event_container/EC = event_containers[severity] - EC.next_event_time += delay - -/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) - - var/html = GetInteractWindow() - - var/datum/browser/popup = new(user, "event_manager", "Event Manager", window_x, window_y) - popup.set_content(html) - popup.open() - -/datum/controller/subsystem/events/proc/RoundEnd() - if(!report_at_round_end) - return - - to_chat(world, "


    Random Events This Round:") - for(var/datum/event/E in active_events|finished_events) - var/datum/event_meta/EM = E.event_meta - if(EM.name == "Nothing") - continue - var/message = "'[EM.name]' began at [station_time_timestamp("hh:mm:ss", E.startedAt)] " - if(E.isRunning) - message += "and is still running." - else - if(E.endedAt - E.startedAt > MinutesToTicks(5)) // Only mention end time if the entire duration was more than 5 minutes - message += "and ended at [station_time_timestamp("hh:mm:ss", E.endedAt)]." - else - message += "and ran to completion." - - to_chat(world, message) - -/datum/controller/subsystem/events/proc/GetInteractWindow() - var/html = "Refresh" - - if(selected_event_container) - var/event_time = max(0, selected_event_container.next_event_time - world.time) - html += "Back
    " - html += "Time till start: [round(event_time / 600, 0.1)]
    " - html += "
    " - html += "

    Available [severity_to_string[selected_event_container.severity]] Events (queued & running events will not be displayed)

    " - html += "" - html += "Name Weight MinWeight MaxWeight OneShot Enabled CurrWeight Remove" - for(var/datum/event_meta/EM in selected_event_container.available_events) - html += "" - html += "[EM.name]" - html += "[EM.weight]" - html += "[EM.min_weight]" - html += "[EM.max_weight]" - html += "[EM.one_shot]" - html += "[EM.enabled]" - html += "[EM.get_weight(number_active_with_role())]" - html += "Remove" - html += "" - html += "" - html += "
    " - - html += "
    " - html += "

    Add Event

    " - html += "" - html += "NameTypeWeightOneShot" - html += "" - html += "[new_event.name ? new_event.name : "Enter Event"]" - html += "[new_event.event_type ? new_event.event_type : "Select Type"]" - html += "[new_event.weight ? new_event.weight : 0]" - html += "[new_event.one_shot]" - html += "" - html += "" - html += "Add
    " - html += "
    " - else - html += "Round End Report: [report_at_round_end ? "On": "Off"]
    " - html += "
    " - html += "

    Event Start

    " - - html += "" - html += "SeverityStarts AtStarts InAdjust StartPauseInterval Mod" - for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/datum/event_container/EC = event_containers[severity] - var/next_event_at = max(0, EC.next_event_time - world.time) - html += "" - html += "[severity_to_string[severity]]" - html += "[station_time_timestamp("hh:mm:ss", max(EC.next_event_time, world.time))]" - html += "[round(next_event_at / 600, 0.1)]" - html += "" - html += "--" - html += "-" - html += "+" - html += "++" - html += "" - html += "" - html += "[EC.delayed ? "Resume" : "Pause"]" - html += "" - html += "" - html += "[EC.delay_modifier]" - html += "" - html += "" - html += "" - html += "
    " - - html += "
    " - html += "

    Next Event

    " - html += "" - html += "SeverityNameEvent RotationClear" - for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/datum/event_container/EC = event_containers[severity] - var/datum/event_meta/EM = EC.next_event - html += "" - html += "[severity_to_string[severity]]" - html += "[EM ? EM.name : "Random"]" - html += "View" - html += "Clear" - html += "" - html += "" - html += "
    " - - html += "
    " - html += "

    Running Events

    " - html += "Estimated times, affected by master controller delays." - html += "" - html += "SeverityNameEnds AtEnds InStop" - for(var/datum/event/E in active_events) - if(!E.event_meta) - continue - var/datum/event_meta/EM = E.event_meta - var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the manager processes - var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1)) - var/no_end = E.noAutoEnd - html += "" - html += "[severity_to_string[EM.severity]]" - html += "[EM.name]" - html += "[no_end ? "N/A" : station_time_timestamp("hh:mm:ss", ends_at)]" - html += "[no_end ? "N/A" : ends_in]" - html += "Stop" - html += "" - html += "" - html += "
    " - - return html - -/datum/controller/subsystem/events/Topic(href, href_list) - if(..()) - return - - - if(href_list["toggle_report"]) - report_at_round_end = !report_at_round_end - admin_log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") - else if(href_list["dec_timer"]) - var/datum/event_container/EC = locate(href_list["event"]) - var/decrease = (60 * RaiseToPower(10, text2num(href_list["dec_timer"]))) - EC.next_event_time -= decrease - admin_log_and_message_admins("decreased timer for [severity_to_string[EC.severity]] events by [decrease/600] minute(s).") - else if(href_list["inc_timer"]) - var/datum/event_container/EC = locate(href_list["event"]) - var/increase = (60 * RaiseToPower(10, text2num(href_list["inc_timer"]))) - EC.next_event_time += increase - admin_log_and_message_admins("increased timer for [severity_to_string[EC.severity]] events by [increase/600] minute(s).") - else if(href_list["select_event"]) - var/datum/event_container/EC = locate(href_list["select_event"]) - var/datum/event_meta/EM = EC.SelectEvent() - if(EM) - admin_log_and_message_admins("has queued the [severity_to_string[EC.severity]] event '[EM.name]'.") - else if(href_list["pause"]) - var/datum/event_container/EC = locate(href_list["pause"]) - EC.delayed = !EC.delayed - admin_log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [severity_to_string[EC.severity]] events.") - else if(href_list["interval"]) - var/delay = input("Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") as num|null - if(delay && delay > 0) - var/datum/event_container/EC = locate(href_list["interval"]) - EC.delay_modifier = delay - admin_log_and_message_admins("has set the interval modifier for [severity_to_string[EC.severity]] events to [EC.delay_modifier].") - else if(href_list["stop"]) - if(alert("Stopping an event may have unintended side-effects. Continue?","Stopping Event!","Yes","No") != "Yes") - return - var/datum/event/E = locate(href_list["stop"]) - var/datum/event_meta/EM = E.event_meta - admin_log_and_message_admins("has stopped the [severity_to_string[EM.severity]] event '[EM.name]'.") - E.kill() - else if(href_list["view_events"]) - selected_event_container = locate(href_list["view_events"]) - else if(href_list["back"]) - selected_event_container = null - else if(href_list["set_name"]) - var/name = clean_input("Enter event name.", "Set Name") - if(name) - var/datum/event_meta/EM = locate(href_list["set_name"]) - EM.name = name - else if(href_list["set_type"]) - var/type = input("Select event type.", "Select") as null|anything in allEvents - if(type) - var/datum/event_meta/EM = locate(href_list["set_type"]) - EM.event_type = type - else if(href_list["set_weight"]) - var/weight = input("Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") as num|null - if(weight && weight > 0) - var/datum/event_meta/EM = locate(href_list["set_weight"]) - EM.weight = weight - if(EM != new_event) - admin_log_and_message_admins("has changed the weight of the [severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") - else if(href_list["toggle_oneshot"]) - var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) - EM.one_shot = !EM.one_shot - if(EM != new_event) - admin_log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["toggle_enabled"]) - var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) - EM.enabled = !EM.enabled - admin_log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["remove"]) - if(alert("This will remove the event from rotation. Continue?","Removing Event!","Yes","No") != "Yes") - return - var/datum/event_meta/EM = locate(href_list["remove"]) - var/datum/event_container/EC = locate(href_list["EC"]) - EC.available_events -= EM - admin_log_and_message_admins("has removed the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["add"]) - if(!new_event.name || !new_event.event_type) - return - if(alert("This will add a new event to the rotation. Continue?","Add Event!","Yes","No") != "Yes") - return - new_event.severity = selected_event_container.severity - selected_event_container.available_events += new_event - admin_log_and_message_admins("has added \a [severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") - new_event = new - else if(href_list["clear"]) - var/datum/event_container/EC = locate(href_list["clear"]) - if(EC.next_event) - admin_log_and_message_admins("has dequeued the [severity_to_string[EC.severity]] event '[EC.next_event.name]'.") - EC.next_event = null - - Interact(usr) +SUBSYSTEM_DEF(events) + name = "Events" + init_order = INIT_ORDER_EVENTS + runlevels = RUNLEVEL_GAME + offline_implications = "Random events will no longer happen. No immediate action is needed." + // Report events at the end of the rouund + var/report_at_round_end = 0 + + // UI vars + var/window_x = 700 + var/window_y = 600 + var/table_options = " align='center'" + var/head_options = " style='font-weight:bold;'" + var/row_options1 = " width='85px'" + var/row_options2 = " width='260px'" + var/row_options3 = " width='150px'" + + // Event vars + var/datum/event_container/selected_event_container = null + var/list/active_events = list() + var/list/finished_events = list() + var/list/allEvents + var/list/event_containers = list( + EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane, + EVENT_LEVEL_MODERATE = new/datum/event_container/moderate, + EVENT_LEVEL_MAJOR = new/datum/event_container/major + ) + + var/datum/event_meta/new_event = new + +/datum/controller/subsystem/events/Initialize() + allEvents = subtypesof(/datum/event) + return ..() + +/datum/controller/subsystem/events/fire() + for(var/datum/event/E in active_events) + E.process() + + for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/datum/event_container/EC = event_containers[i] + EC.process() + +/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E) + if(!E.event_meta) // datum/event is used here and there for random reasons, maintaining "backwards compatibility" + log_debug("Event of '[E.type]' with missing meta-data has completed.") + return + + finished_events += E + + var/theseverity + + if(!E.severity) + theseverity = EVENT_LEVEL_MODERATE + + if(!E.severity == EVENT_LEVEL_MUNDANE && !E.severity == EVENT_LEVEL_MODERATE && !E.severity == EVENT_LEVEL_MAJOR) + theseverity = EVENT_LEVEL_MODERATE //just to be careful + + if(E.severity) + theseverity = E.severity + + // Add the event back to the list of available events + var/datum/event_container/EC = event_containers[theseverity] + var/datum/event_meta/EM = E.event_meta + EC.available_events += EM + + log_debug("Event '[EM.name]' has completed at [station_time_timestamp()].") + +/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay) + var/datum/event_container/EC = event_containers[severity] + EC.next_event_time += delay + +/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) + + var/html = GetInteractWindow() + + var/datum/browser/popup = new(user, "event_manager", "Event Manager", window_x, window_y) + popup.set_content(html) + popup.open() + +/datum/controller/subsystem/events/proc/RoundEnd() + if(!report_at_round_end) + return + + to_chat(world, "


    Random Events This Round:") + for(var/datum/event/E in active_events|finished_events) + var/datum/event_meta/EM = E.event_meta + if(EM.name == "Nothing") + continue + var/message = "'[EM.name]' began at [station_time_timestamp("hh:mm:ss", E.startedAt)] " + if(E.isRunning) + message += "and is still running." + else + if(E.endedAt - E.startedAt > MinutesToTicks(5)) // Only mention end time if the entire duration was more than 5 minutes + message += "and ended at [station_time_timestamp("hh:mm:ss", E.endedAt)]." + else + message += "and ran to completion." + + to_chat(world, message) + +/datum/controller/subsystem/events/proc/GetInteractWindow() + var/html = "Refresh" + + if(selected_event_container) + var/event_time = max(0, selected_event_container.next_event_time - world.time) + html += "Back
    " + html += "Time till start: [round(event_time / 600, 0.1)]
    " + html += "
    " + html += "

    Available [GLOB.severity_to_string[selected_event_container.severity]] Events (queued & running events will not be displayed)

    " + html += "" + html += "Name Weight MinWeight MaxWeight OneShot Enabled CurrWeight Remove" + for(var/datum/event_meta/EM in selected_event_container.available_events) + html += "" + html += "[EM.name]" + html += "[EM.weight]" + html += "[EM.min_weight]" + html += "[EM.max_weight]" + html += "[EM.one_shot]" + html += "[EM.enabled]" + html += "[EM.get_weight(number_active_with_role())]" + html += "Remove" + html += "" + html += "" + html += "
    " + + html += "
    " + html += "

    Add Event

    " + html += "" + html += "NameTypeWeightOneShot" + html += "" + html += "[new_event.name ? new_event.name : "Enter Event"]" + html += "[new_event.event_type ? new_event.event_type : "Select Type"]" + html += "[new_event.weight ? new_event.weight : 0]" + html += "[new_event.one_shot]" + html += "" + html += "" + html += "Add
    " + html += "
    " + else + html += "Round End Report: [report_at_round_end ? "On": "Off"]
    " + html += "
    " + html += "

    Event Start

    " + + html += "" + html += "SeverityStarts AtStarts InAdjust StartPauseInterval Mod" + for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/datum/event_container/EC = event_containers[severity] + var/next_event_at = max(0, EC.next_event_time - world.time) + html += "" + html += "[GLOB.severity_to_string[severity]]" + html += "[station_time_timestamp("hh:mm:ss", max(EC.next_event_time, world.time))]" + html += "[round(next_event_at / 600, 0.1)]" + html += "" + html += "--" + html += "-" + html += "+" + html += "++" + html += "" + html += "" + html += "[EC.delayed ? "Resume" : "Pause"]" + html += "" + html += "" + html += "[EC.delay_modifier]" + html += "" + html += "" + html += "" + html += "
    " + + html += "
    " + html += "

    Next Event

    " + html += "" + html += "SeverityNameEvent RotationClear" + for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/datum/event_container/EC = event_containers[severity] + var/datum/event_meta/EM = EC.next_event + html += "" + html += "[GLOB.severity_to_string[severity]]" + html += "[EM ? EM.name : "Random"]" + html += "View" + html += "Clear" + html += "" + html += "" + html += "
    " + + html += "
    " + html += "

    Running Events

    " + html += "Estimated times, affected by master controller delays." + html += "" + html += "SeverityNameEnds AtEnds InStop" + for(var/datum/event/E in active_events) + if(!E.event_meta) + continue + var/datum/event_meta/EM = E.event_meta + var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the manager processes + var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1)) + var/no_end = E.noAutoEnd + html += "" + html += "[GLOB.severity_to_string[EM.severity]]" + html += "[EM.name]" + html += "[no_end ? "N/A" : station_time_timestamp("hh:mm:ss", ends_at)]" + html += "[no_end ? "N/A" : ends_in]" + html += "Stop" + html += "" + html += "" + html += "
    " + + return html + +/datum/controller/subsystem/events/Topic(href, href_list) + if(..()) + return + + + if(href_list["toggle_report"]) + report_at_round_end = !report_at_round_end + admin_log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") + else if(href_list["dec_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/decrease = (60 * RaiseToPower(10, text2num(href_list["dec_timer"]))) + EC.next_event_time -= decrease + admin_log_and_message_admins("decreased timer for [GLOB.severity_to_string[EC.severity]] events by [decrease/600] minute(s).") + else if(href_list["inc_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/increase = (60 * RaiseToPower(10, text2num(href_list["inc_timer"]))) + EC.next_event_time += increase + admin_log_and_message_admins("increased timer for [GLOB.severity_to_string[EC.severity]] events by [increase/600] minute(s).") + else if(href_list["select_event"]) + var/datum/event_container/EC = locate(href_list["select_event"]) + var/datum/event_meta/EM = EC.SelectEvent() + if(EM) + admin_log_and_message_admins("has queued the [GLOB.severity_to_string[EC.severity]] event '[EM.name]'.") + else if(href_list["pause"]) + var/datum/event_container/EC = locate(href_list["pause"]) + EC.delayed = !EC.delayed + admin_log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [GLOB.severity_to_string[EC.severity]] events.") + else if(href_list["interval"]) + var/delay = input("Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") as num|null + if(delay && delay > 0) + var/datum/event_container/EC = locate(href_list["interval"]) + EC.delay_modifier = delay + admin_log_and_message_admins("has set the interval modifier for [GLOB.severity_to_string[EC.severity]] events to [EC.delay_modifier].") + else if(href_list["stop"]) + if(alert("Stopping an event may have unintended side-effects. Continue?","Stopping Event!","Yes","No") != "Yes") + return + var/datum/event/E = locate(href_list["stop"]) + var/datum/event_meta/EM = E.event_meta + admin_log_and_message_admins("has stopped the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + E.kill() + else if(href_list["view_events"]) + selected_event_container = locate(href_list["view_events"]) + else if(href_list["back"]) + selected_event_container = null + else if(href_list["set_name"]) + var/name = clean_input("Enter event name.", "Set Name") + if(name) + var/datum/event_meta/EM = locate(href_list["set_name"]) + EM.name = name + else if(href_list["set_type"]) + var/type = input("Select event type.", "Select") as null|anything in allEvents + if(type) + var/datum/event_meta/EM = locate(href_list["set_type"]) + EM.event_type = type + else if(href_list["set_weight"]) + var/weight = input("Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") as num|null + if(weight && weight > 0) + var/datum/event_meta/EM = locate(href_list["set_weight"]) + EM.weight = weight + if(EM != new_event) + admin_log_and_message_admins("has changed the weight of the [GLOB.severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") + else if(href_list["toggle_oneshot"]) + var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) + EM.one_shot = !EM.one_shot + if(EM != new_event) + admin_log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["toggle_enabled"]) + var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) + EM.enabled = !EM.enabled + admin_log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["remove"]) + if(alert("This will remove the event from rotation. Continue?","Removing Event!","Yes","No") != "Yes") + return + var/datum/event_meta/EM = locate(href_list["remove"]) + var/datum/event_container/EC = locate(href_list["EC"]) + EC.available_events -= EM + admin_log_and_message_admins("has removed the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["add"]) + if(!new_event.name || !new_event.event_type) + return + if(alert("This will add a new event to the rotation. Continue?","Add Event!","Yes","No") != "Yes") + return + new_event.severity = selected_event_container.severity + selected_event_container.available_events += new_event + admin_log_and_message_admins("has added \a [GLOB.severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") + new_event = new + else if(href_list["clear"]) + var/datum/event_container/EC = locate(href_list["clear"]) + if(EC.next_event) + admin_log_and_message_admins("has dequeued the [GLOB.severity_to_string[EC.severity]] event '[EC.next_event.name]'.") + EC.next_event = null + + Interact(usr) diff --git a/code/controllers/subsystem/fires.dm b/code/controllers/subsystem/fires.dm index aafc5ed48d5..dc419fffeb5 100644 --- a/code/controllers/subsystem/fires.dm +++ b/code/controllers/subsystem/fires.dm @@ -3,6 +3,7 @@ SUBSYSTEM_DEF(fires) priority = FIRE_PRIOTITY_BURNING flags = SS_NO_INIT|SS_BACKGROUND runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + offline_implications = "Objects will no longer react to fires. No immediate action is needed." var/list/currentrun = list() var/list/processing = list() diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index 4c397636cab..cbf17d05fbf 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -5,6 +5,7 @@ SUBSYSTEM_DEF(garbage) flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY init_order = INIT_ORDER_GARBAGE + offline_implications = "Garbage collection is no longer functional, and objects will not be qdel'd. Immediate server restart recommended." var/list/collection_timeout = list(0, 2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level @@ -432,4 +433,4 @@ SUBSYSTEM_DEF(garbage) CHECK_TICK #endif -#endif \ No newline at end of file +#endif diff --git a/code/controllers/subsystem/holiday.dm b/code/controllers/subsystem/holiday.dm index be9d6d4fc7d..ddde9cd62be 100644 --- a/code/controllers/subsystem/holiday.dm +++ b/code/controllers/subsystem/holiday.dm @@ -1,31 +1,31 @@ -SUBSYSTEM_DEF(holiday) - name = "Holiday" - init_order = INIT_ORDER_HOLIDAY // 3 - flags = SS_NO_FIRE - var/list/holidays - -/datum/controller/subsystem/holiday/Initialize(start_timeofday) - if(!config.allow_holidays) - return ..() //Holiday stuff was not enabled in the config! - - var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year - var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month - var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day - - for(var/H in subtypesof(/datum/holiday)) - var/datum/holiday/holiday = new H() - if(holiday.shouldCelebrate(DD, MM, YY)) - holiday.celebrate() - if(!holidays) - holidays = list() - holidays[holiday.name] = holiday - - if(holidays) - holidays = shuffle(holidays) - world.update_status() - for(var/datum/holiday/H in holidays) - if(H.eventChance) - if(prob(H.eventChance)) - H.handle_event() - - return ..() \ No newline at end of file +SUBSYSTEM_DEF(holiday) + name = "Holiday" + init_order = INIT_ORDER_HOLIDAY // 3 + flags = SS_NO_FIRE + var/list/holidays + +/datum/controller/subsystem/holiday/Initialize(start_timeofday) + if(!config.allow_holidays) + return ..() //Holiday stuff was not enabled in the config! + + var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year + var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month + var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day + + for(var/H in subtypesof(/datum/holiday)) + var/datum/holiday/holiday = new H() + if(holiday.shouldCelebrate(DD, MM, YY)) + holiday.celebrate() + if(!holidays) + holidays = list() + holidays[holiday.name] = holiday + + if(holidays) + holidays = shuffle(holidays) + world.update_status() + for(var/datum/holiday/H in holidays) + if(H.eventChance) + if(prob(H.eventChance)) + H.handle_event() + + return ..() diff --git a/code/controllers/subsystem/icon_smooth.dm b/code/controllers/subsystem/icon_smooth.dm index 531b6af2e78..289505a153c 100644 --- a/code/controllers/subsystem/icon_smooth.dm +++ b/code/controllers/subsystem/icon_smooth.dm @@ -4,6 +4,7 @@ SUBSYSTEM_DEF(icon_smooth) wait = 1 priority = FIRE_PRIOTITY_SMOOTHING flags = SS_TICKER + offline_implications = "Objects will no longer smooth together properly. No immediate action is needed." var/list/smooth_queue = list() diff --git a/code/controllers/subsystem/idlenpcpool.dm b/code/controllers/subsystem/idlenpcpool.dm index 3757237243f..d61b90f1239 100644 --- a/code/controllers/subsystem/idlenpcpool.dm +++ b/code/controllers/subsystem/idlenpcpool.dm @@ -4,6 +4,7 @@ SUBSYSTEM_DEF(idlenpcpool) priority = FIRE_PRIORITY_IDLE_NPC wait = 60 runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + offline_implications = "Idle simple animals will no longer process. Shuttle call recommended." var/list/currentrun = list() var/static/list/idle_mobs_by_zlevel[][] @@ -39,4 +40,4 @@ SUBSYSTEM_DEF(idlenpcpool) if(SA.stat != DEAD) SA.consider_wakeup() if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index f379deec72b..9dd6fce1554 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -5,6 +5,7 @@ SUBSYSTEM_DEF(input) flags = SS_TICKER priority = FIRE_PRIORITY_INPUT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY + offline_implications = "Player input will no longer be recognised. Immediate server restart recommended." var/list/macro_sets var/list/movement_keys @@ -24,7 +25,7 @@ SUBSYSTEM_DEF(input) // This is for when macro sets are eventualy datumized /datum/controller/subsystem/input/proc/setup_default_macro_sets() var/list/static/default_macro_sets - + if(default_macro_sets) macro_sets = default_macro_sets return diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm index d7e7a3cc33e..1d6fd410f9b 100644 --- a/code/controllers/subsystem/jobs.dm +++ b/code/controllers/subsystem/jobs.dm @@ -3,6 +3,7 @@ SUBSYSTEM_DEF(jobs) init_order = INIT_ORDER_JOBS // 12 wait = 3000 // 5 minutes (Deciseconds) runlevels = RUNLEVEL_GAME + offline_implications = "Job playtime hours will no longer be logged. No immediate action is needed." //List of all jobs var/list/occupations = list() @@ -10,7 +11,7 @@ SUBSYSTEM_DEF(jobs) var/list/type_occupations = list() //Dict of all jobs, keys are types var/list/prioritized_jobs = list() // List of jobs set to priority by HoP/Captain var/list/id_change_records = list() // List of all job transfer records - var/list/id_change_counter = 1 + var/id_change_counter = 1 //Players who need jobs var/list/unassigned = list() //Debug info @@ -39,8 +40,6 @@ SUBSYSTEM_DEF(jobs) var/datum/job/job = new J() if(!job) continue - if(!job.faction in faction) - continue occupations += job name_occupations[job.title] = job type_occupations[J] = job @@ -49,7 +48,7 @@ SUBSYSTEM_DEF(jobs) /datum/controller/subsystem/jobs/proc/Debug(var/text) - if(!Debug2) + if(!GLOB.debug2) return 0 job_debug.Add(text) return 1 @@ -138,7 +137,7 @@ SUBSYSTEM_DEF(jobs) if(flag && !(flag in player.client.prefs.be_special)) Debug("FOC flag failed, Player: [player], Flag: [flag], ") continue - if(player.mind && job.title in player.mind.restricted_roles) + if(player.mind && (job.title in player.mind.restricted_roles)) Debug("FOC incompatbile with antagonist role, Player: [player]") continue if(player.client.prefs.GetJobDepartment(job, level) & job.flag) @@ -155,10 +154,10 @@ SUBSYSTEM_DEF(jobs) if(istype(job, GetJob("Civilian"))) // We don't want to give him assistant, that's boring! continue - if(job.title in command_positions) //If you want a command position, select it! + if(job.title in GLOB.command_positions) //If you want a command position, select it! continue - if(job.title in whitelisted_positions) // No random whitelisted job, sorry! + if(job.title in GLOB.whitelisted_positions) // No random whitelisted job, sorry! continue if(job.admin_only) // No admin positions either. @@ -180,7 +179,7 @@ SUBSYSTEM_DEF(jobs) Debug("GRJ player has disability rendering them ineligible for job, Player: [player]") continue - if(player.mind && job.title in player.mind.restricted_roles) + if(player.mind && (job.title in player.mind.restricted_roles)) Debug("GRJ incompatible with antagonist role, Player: [player], Job: [job.title]") continue @@ -203,7 +202,7 @@ SUBSYSTEM_DEF(jobs) ///This proc is called before the level loop of DivideOccupations() and will try to select a head, ignoring ALL non-head preferences for every level until it locates a head or runs out of levels to check /datum/controller/subsystem/jobs/proc/FillHeadPosition() for(var/level = 1 to 3) - for(var/command_position in command_positions) + for(var/command_position in GLOB.command_positions) var/datum/job/job = GetJob(command_position) if(!job) continue @@ -231,7 +230,7 @@ SUBSYSTEM_DEF(jobs) ///This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level /datum/controller/subsystem/jobs/proc/CheckHeadPositions(var/level) - for(var/command_position in command_positions) + for(var/command_position in GLOB.command_positions) var/datum/job/job = GetJob(command_position) if(!job) continue @@ -357,7 +356,7 @@ SUBSYSTEM_DEF(jobs) Debug("DO player has disability rendering them ineligible for job, Player: [player], Job:[job.title]") continue - if(player.mind && job.title in player.mind.restricted_roles) + if(player.mind && (job.title in player.mind.restricted_roles)) Debug("DO incompatible with antagonist role, Player: [player], Job:[job.title]") continue @@ -389,8 +388,11 @@ SUBSYSTEM_DEF(jobs) // Antags, who have to get in, come first for(var/mob/new_player/player in unassigned) if(player.mind.special_role) - GiveRandomJob(player) - if(player in unassigned) + if(player.client.prefs.alternate_option != BE_ASSISTANT) + GiveRandomJob(player) + if(player in unassigned) + AssignRole(player, "Civilian") + else AssignRole(player, "Civilian") // Then we assign what we can to everyone else. @@ -597,7 +599,7 @@ SUBSYSTEM_DEF(jobs) // If they're head, give them the account info for their department if(job && job.head_position) remembered_info = "" - var/datum/money_account/department_account = department_accounts[job.department] + var/datum/money_account/department_account = GLOB.department_accounts[job.department] if(department_account) remembered_info += "Your department's account number is: #[department_account.account_number]
    " @@ -619,7 +621,9 @@ SUBSYSTEM_DEF(jobs) if(tgtcard.assignment && tgtcard.assignment == job.title) jobs_to_formats[job.title] = "disabled" // the job they already have is pre-selected else if(!job.would_accept_job_transfer_from_player(M)) - jobs_to_formats[job.title] = "linkDiscourage" // karma jobs they don't have available are discouraged + jobs_to_formats[job.title] = "linkDiscourage" // jobs which are karma-locked and not unlocked for this player are discouraged + else if((job.title in GLOB.command_positions) && istype(M) && M.client && job.available_in_playtime(M.client)) + jobs_to_formats[job.title] = "linkDiscourage" // command jobs which are playtime-locked and not unlocked for this player are discouraged else if(job.total_positions && !job.current_positions && job.title != "Civilian") jobs_to_formats[job.title] = "linkEncourage" // jobs with nobody doing them at all are encouraged else if(job.total_positions >= 0 && job.current_positions >= job.total_positions) @@ -635,7 +639,7 @@ SUBSYSTEM_DEF(jobs) var/datum/job/oldjobdatum = SSjobs.GetJob(oldtitle) var/datum/job/newjobdatum = SSjobs.GetJob(newtitle) if(istype(oldjobdatum) && oldjobdatum.current_positions > 0 && istype(newjobdatum)) - if(!(oldjobdatum.title in command_positions) && !(newjobdatum.title in command_positions)) + if(!(oldjobdatum.title in GLOB.command_positions) && !(newjobdatum.title in GLOB.command_positions)) oldjobdatum.current_positions-- newjobdatum.current_positions++ diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index 82bbd52471a..3fc92a3346a 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -7,6 +7,7 @@ SUBSYSTEM_DEF(lighting) wait = 2 init_order = INIT_ORDER_LIGHTING flags = SS_TICKER + offline_implications = "Lighting will no longer update. Shuttle call recommended." /datum/controller/subsystem/lighting/stat_entry() ..("L:[GLOB.lighting_update_lights.len]|C:[GLOB.lighting_update_corners.len]|O:[GLOB.lighting_update_objects.len]") @@ -84,4 +85,4 @@ SUBSYSTEM_DEF(lighting) /datum/controller/subsystem/lighting/Recover() initialized = SSlighting.initialized - ..() \ No newline at end of file + ..() diff --git a/code/controllers/subsystem/machinery.dm b/code/controllers/subsystem/machinery.dm index 9a73c1dbabb..7e78e2b9df1 100644 --- a/code/controllers/subsystem/machinery.dm +++ b/code/controllers/subsystem/machinery.dm @@ -6,6 +6,7 @@ SUBSYSTEM_DEF(machines) name = "Machines" init_order = INIT_ORDER_MACHINES flags = SS_KEEP_TIMING + offline_implications = "Machinery will no longer process. Shuttle call recommended." var/list/processing = list() var/list/currentrun = list() diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 75fd7519357..67e2b5c5e88 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -13,24 +13,109 @@ SUBSYSTEM_DEF(mapping) if(!config.disable_space_ruins) var/timer = start_watch() log_startup_progress("Creating random space levels...") - seedRuins(list(level_name_to_num(EMPTY_AREA)), rand(0, 3), /area/space, space_ruins_templates) + seedRuins(list(level_name_to_num(EMPTY_AREA)), rand(0, 3), /area/space, GLOB.space_ruins_templates) log_startup_progress("Loaded random space levels in [stop_watch(timer)]s.") // load in extra levels of space ruins var/num_extra_space = rand(config.extra_space_ruin_levels_min, config.extra_space_ruin_levels_max) for(var/i = 1, i <= num_extra_space, i++) - var/zlev = space_manager.add_new_zlevel("[EMPTY_AREA] #[i]", linkage = CROSSLINKED, traits = list(REACHABLE)) - seedRuins(list(zlev), rand(0, 3), /area/space, space_ruins_templates) + var/zlev = GLOB.space_manager.add_new_zlevel("[EMPTY_AREA] #[i]", linkage = CROSSLINKED, traits = list(REACHABLE)) + seedRuins(list(zlev), rand(0, 3), /area/space, GLOB.space_ruins_templates) // Setup the Z-level linkage - space_manager.do_transition_setup() + GLOB.space_manager.do_transition_setup() // Spawn Lavaland ruins and rivers. - seedRuins(list(level_name_to_num(MINING)), config.lavaland_budget, /area/lavaland/surface/outdoors/unexplored, lava_ruins_templates) + seedRuins(list(level_name_to_num(MINING)), config.lavaland_budget, /area/lavaland/surface/outdoors/unexplored, GLOB.lava_ruins_templates) spawn_rivers(list(level_name_to_num(MINING))) return ..() + +/datum/controller/subsystem/mapping/proc/seedRuins(list/z_levels = null, budget = 0, whitelist = /area/space, list/potentialRuins) + if(!z_levels || !z_levels.len) + WARNING("No Z levels provided - Not generating ruins") + return + + for(var/zl in z_levels) + var/turf/T = locate(1, 1, zl) + if(!T) + WARNING("Z level [zl] does not exist - Not generating ruins") + return + + var/list/ruins = potentialRuins.Copy() + + var/list/forced_ruins = list() //These go first on the z level associated (same random one by default) + var/list/ruins_availible = list() //we can try these in the current pass + var/forced_z //If set we won't pick z level and use this one instead. + + //Set up the starting ruin list + for(var/key in ruins) + var/datum/map_template/ruin/R = ruins[key] + if(R.cost > budget) //Why would you do that + continue + if(R.always_place) + forced_ruins[R] = -1 + if(R.unpickable) + continue + ruins_availible[R] = R.placement_weight + + while(budget > 0 && (ruins_availible.len || forced_ruins.len)) + var/datum/map_template/ruin/current_pick + var/forced = FALSE + if(forced_ruins.len) //We have something we need to load right now, so just pick it + for(var/ruin in forced_ruins) + current_pick = ruin + if(forced_ruins[ruin] > 0) //Load into designated z + forced_z = forced_ruins[ruin] + forced = TRUE + break + else //Otherwise just pick random one + current_pick = pickweight(ruins_availible) + + var/placement_tries = PLACEMENT_TRIES + var/failed_to_place = TRUE + var/z_placed = 0 + while(placement_tries > 0) + placement_tries-- + z_placed = pick(z_levels) + if(!current_pick.try_to_place(forced_z ? forced_z : z_placed,whitelist)) + continue + else + failed_to_place = FALSE + break + + //That's done remove from priority even if it failed + if(forced) + //TODO : handle forced ruins with multiple variants + forced_ruins -= current_pick + forced = FALSE + + if(failed_to_place) + for(var/datum/map_template/ruin/R in ruins_availible) + if(R.id == current_pick.id) + ruins_availible -= R + log_world("Failed to place [current_pick.name] ruin.") + else + budget -= current_pick.cost + if(!current_pick.allow_duplicates) + for(var/datum/map_template/ruin/R in ruins_availible) + if(R.id == current_pick.id) + ruins_availible -= R + if(current_pick.never_spawn_with) + for(var/blacklisted_type in current_pick.never_spawn_with) + for(var/possible_exclusion in ruins_availible) + if(istype(possible_exclusion,blacklisted_type)) + ruins_availible -= possible_exclusion + forced_z = 0 + + //Update the availible list + for(var/datum/map_template/ruin/R in ruins_availible) + if(R.cost > budget) + ruins_availible -= R + + log_world("Ruin loader finished with [budget] left to spend.") + /datum/controller/subsystem/mapping/Recover() - flags |= SS_NO_INIT \ No newline at end of file + flags |= SS_NO_INIT diff --git a/code/controllers/subsystem/medals.dm b/code/controllers/subsystem/medals.dm index f3c2752ddf2..5de6cdeb27a 100644 --- a/code/controllers/subsystem/medals.dm +++ b/code/controllers/subsystem/medals.dm @@ -83,4 +83,4 @@ SUBSYSTEM_DEF(medals) /datum/controller/subsystem/medals/proc/ClearScore(client/player) if(isnull(world.SetScores(player.ckey, "", config.medal_hub_address, config.medal_hub_password))) log_game("MEDAL ERROR: Could not contact hub to clear scores for [player.ckey].") - message_admins("Error! Failed to contact hub to clear scores for [player.ckey]!") \ No newline at end of file + message_admins("Error! Failed to contact hub to clear scores for [player.ckey]!") diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm index a8a1fb72ceb..8345822ca80 100644 --- a/code/controllers/subsystem/mobs.dm +++ b/code/controllers/subsystem/mobs.dm @@ -3,6 +3,7 @@ SUBSYSTEM_DEF(mobs) priority = FIRE_PRIORITY_MOBS flags = SS_KEEP_TIMING runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + offline_implications = "Mobs will no longer process. Immediate server restart recommended." var/list/currentrun = list() var/static/list/clients_by_zlevel[][] @@ -33,4 +34,4 @@ SUBSYSTEM_DEF(mobs) else GLOB.mob_list.Remove(M) if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/nano_mob_hunter.dm b/code/controllers/subsystem/nano_mob_hunter.dm index a634f091013..9094bf9fe2b 100644 --- a/code/controllers/subsystem/nano_mob_hunter.dm +++ b/code/controllers/subsystem/nano_mob_hunter.dm @@ -2,6 +2,7 @@ SUBSYSTEM_DEF(mob_hunt) name = "Nano-Mob Hunter GO Server" init_order = INIT_ORDER_NANOMOB priority = FIRE_PRIORITY_NANOMOB // Low priority, no need for MC_TICK_CHECK due to extremely low performance impact. + offline_implications = "Nano-Mob Hunter will no longer spawn mobs. No immediate action is needed." var/max_normal_spawns = 15 //change this to adjust the number of normal spawns that can exist at one time. trapped spawns (from traitors) don't count towards this var/list/normal_spawns = list() var/max_trap_spawns = 15 //change this to adjust the number of trap spawns that can exist at one time. traps spawned beyond this point clear the oldest traps diff --git a/code/controllers/subsystem/nanoui.dm b/code/controllers/subsystem/nanoui.dm index c8cbbc62013..ef1b6ee03fe 100644 --- a/code/controllers/subsystem/nanoui.dm +++ b/code/controllers/subsystem/nanoui.dm @@ -4,6 +4,7 @@ SUBSYSTEM_DEF(nanoui) flags = SS_NO_INIT priority = FIRE_PRIORITY_NANOUI runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + offline_implications = "All NanoUIs will no longer process. Shuttle call recommended." var/list/currentrun = list() var/list/open_uis = list() // A list of open UIs, grouped by src_object and ui_key. diff --git a/code/controllers/subsystem/nightshift.dm b/code/controllers/subsystem/nightshift.dm index 4ebe1dceb0e..deffcb6bddf 100644 --- a/code/controllers/subsystem/nightshift.dm +++ b/code/controllers/subsystem/nightshift.dm @@ -4,6 +4,7 @@ SUBSYSTEM_DEF(nightshift) priority = FIRE_PRIORITY_NIGHTSHIFT wait = 600 flags = SS_NO_TICK_CHECK + offline_implications = "The game will no longer shift between day and night lighting. No immediate action is needed." var/nightshift_active = FALSE var/nightshift_start_time = 702000 //7:30 PM, station time @@ -25,12 +26,12 @@ SUBSYSTEM_DEF(nightshift) check_nightshift() /datum/controller/subsystem/nightshift/proc/announce(message) - priority_announcement.Announce(message, new_sound = 'sound/misc/notice2.ogg', new_title = "Automated Lighting System Announcement") + GLOB.priority_announcement.Announce(message, new_sound = 'sound/misc/notice2.ogg', new_title = "Automated Lighting System Announcement") /datum/controller/subsystem/nightshift/proc/check_nightshift(check_canfire=FALSE) if(check_canfire && !can_fire) return - var/emergency = security_level >= SEC_LEVEL_RED + var/emergency = GLOB.security_level >= SEC_LEVEL_RED var/announcing = TRUE var/time = station_time() var/night_time = (time < nightshift_end_time) || (time > nightshift_start_time) @@ -41,7 +42,7 @@ SUBSYSTEM_DEF(nightshift) if(!emergency) announce("Restoring night lighting configuration to normal operation.") else - announce("Disabling night lighting: Station is in a state of emergency.") + announce("Disabling night lighting: Station is in a state of emergency.") if(emergency) night_time = FALSE if(nightshift_active != night_time) diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm index f2e159404fd..79541c199a4 100644 --- a/code/controllers/subsystem/npcpool.dm +++ b/code/controllers/subsystem/npcpool.dm @@ -3,6 +3,7 @@ SUBSYSTEM_DEF(npcpool) flags = SS_POST_FIRE_TIMING|SS_NO_INIT|SS_BACKGROUND priority = FIRE_PRIORITY_NPC runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + offline_implications = "Simple animals will no longer process. Shuttle call recommended." var/list/currentrun = list() @@ -34,4 +35,4 @@ SUBSYSTEM_DEF(npcpool) if(SA.stat != DEAD) SA.handle_automated_speech() if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm index c35f1adee65..95b9e925b5d 100644 --- a/code/controllers/subsystem/overlays.dm +++ b/code/controllers/subsystem/overlays.dm @@ -4,6 +4,7 @@ SUBSYSTEM_DEF(overlays) wait = 1 priority = FIRE_PRIORITY_OVERLAYS init_order = INIT_ORDER_OVERLAY + offline_implications = "Overlays may look strange. No immediate action is needed." var/list/queue var/list/stats diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm index 039b6d2b008..9135f62b03f 100644 --- a/code/controllers/subsystem/parallax.dm +++ b/code/controllers/subsystem/parallax.dm @@ -4,6 +4,7 @@ SUBSYSTEM_DEF(parallax) flags = SS_POST_FIRE_TIMING | SS_BACKGROUND priority = FIRE_PRIORITY_PARALLAX runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + offline_implications = "Space parallax will no longer move around. No immediate action is needed." var/list/currentrun var/planet_x_offset = 128 var/planet_y_offset = 128 diff --git a/code/controllers/subsystem/processing/fastprocess.dm b/code/controllers/subsystem/processing/fastprocess.dm index 732c5a3ba55..9622e021469 100644 --- a/code/controllers/subsystem/processing/fastprocess.dm +++ b/code/controllers/subsystem/processing/fastprocess.dm @@ -3,4 +3,4 @@ PROCESSING_SUBSYSTEM_DEF(fastprocess) name = "Fast Processing" wait = 2 - stat_tag = "FP" \ No newline at end of file + stat_tag = "FP" diff --git a/code/controllers/subsystem/processing/obj.dm b/code/controllers/subsystem/processing/obj.dm index 7ee2bb1f0f3..26021fb267a 100644 --- a/code/controllers/subsystem/processing/obj.dm +++ b/code/controllers/subsystem/processing/obj.dm @@ -2,4 +2,4 @@ PROCESSING_SUBSYSTEM_DEF(obj) name = "Objects" priority = FIRE_PRIORITY_OBJ flags = SS_NO_INIT - wait = 20 \ No newline at end of file + wait = 20 diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm index 2e06d7ebcc6..a8bc823bbbe 100644 --- a/code/controllers/subsystem/processing/processing.dm +++ b/code/controllers/subsystem/processing/processing.dm @@ -34,4 +34,4 @@ SUBSYSTEM_DEF(processing) /datum/proc/process() set waitfor = 0 - return PROCESS_KILL \ No newline at end of file + return PROCESS_KILL diff --git a/code/controllers/subsystem/radio.dm b/code/controllers/subsystem/radio.dm index 93b5152ee45..49b30062786 100644 --- a/code/controllers/subsystem/radio.dm +++ b/code/controllers/subsystem/radio.dm @@ -1,99 +1,99 @@ -SUBSYSTEM_DEF(radio) - name = "Radio" - flags = SS_NO_INIT | SS_NO_FIRE - - var/list/radiochannels = list( - "Common" = PUB_FREQ, - "Science" = SCI_FREQ, - "Command" = COMM_FREQ, - "Medical" = MED_FREQ, - "Engineering" = ENG_FREQ, - "Security" = SEC_FREQ, - "Response Team" = ERT_FREQ, - "Special Ops" = DTH_FREQ, - "Syndicate" = SYND_FREQ, - "SyndTeam" = SYNDTEAM_FREQ, - "Supply" = SUP_FREQ, - "Service" = SRV_FREQ, - "AI Private" = AI_FREQ, - "Medical(I)" = MED_I_FREQ, - "Security(I)" = SEC_I_FREQ - ) - var/list/CENT_FREQS = list(ERT_FREQ, DTH_FREQ) - var/list/ANTAG_FREQS = list(SYND_FREQ, SYNDTEAM_FREQ) - var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, MED_FREQ, SEC_FREQ, SCI_FREQ, SRV_FREQ, SUP_FREQ) - var/list/datum/radio_frequency/frequencies = list() - -// This is fucking disgusting and needs to die -/datum/controller/subsystem/radio/proc/frequency_span_class(var/frequency) - // Antags! - if(frequency in ANTAG_FREQS) - return "syndradio" - // centcomm channels (deathsquid and ert) - if(frequency in CENT_FREQS) - return "centradio" - // This switch used to be a shit tonne of if statements. I am gonna find who made this and give them a kind talking to - switch(frequency) - if(COMM_FREQ) - return "comradio" - if(AI_FREQ) - return "airadio" - if(SEC_FREQ) - return "secradio" - if(ENG_FREQ) - return "engradio" - if(SCI_FREQ) - return "sciradio" - if(MED_FREQ) - return "medradio" - if(SUP_FREQ) - return "supradio" - if(SRV_FREQ) - return "srvradio" - - // If the above switch somehow failed. And it needs the SSradio. part otherwise it fails to compile - if(frequency in DEPT_FREQS) - return "deptradio" - - // If its none of the others - return "radio" - - -/datum/controller/subsystem/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null) - var/f_text = num2text(new_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] - - if(!frequency) - frequency = new - frequency.frequency = new_frequency - frequencies[f_text] = frequency - - frequency.add_listener(device, filter) - return frequency - -/datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency) - var/f_text = num2text(old_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] - - if(frequency) - frequency.remove_listener(device) - - if(frequency.devices.len == 0) - qdel(frequency) - frequencies -= f_text - - return 1 - -/datum/controller/subsystem/radio/proc/return_frequency(var/new_frequency as num) - var/f_text = num2text(new_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] - - if(!frequency) - frequency = new - frequency.frequency = new_frequency - frequencies[f_text] = frequency - - return frequency - - -// ALL THE SHIT BELOW THIS LINE ISNT PART OF THE SUBSYSTEM AND REALLY NEEDS ITS OWN FILE +SUBSYSTEM_DEF(radio) + name = "Radio" + flags = SS_NO_INIT | SS_NO_FIRE + + var/list/radiochannels = list( + "Common" = PUB_FREQ, + "Science" = SCI_FREQ, + "Command" = COMM_FREQ, + "Medical" = MED_FREQ, + "Engineering" = ENG_FREQ, + "Security" = SEC_FREQ, + "Response Team" = ERT_FREQ, + "Special Ops" = DTH_FREQ, + "Syndicate" = SYND_FREQ, + "SyndTeam" = SYNDTEAM_FREQ, + "Supply" = SUP_FREQ, + "Service" = SRV_FREQ, + "AI Private" = AI_FREQ, + "Medical(I)" = MED_I_FREQ, + "Security(I)" = SEC_I_FREQ + ) + var/list/CENT_FREQS = list(ERT_FREQ, DTH_FREQ) + var/list/ANTAG_FREQS = list(SYND_FREQ, SYNDTEAM_FREQ) + var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, MED_FREQ, SEC_FREQ, SCI_FREQ, SRV_FREQ, SUP_FREQ) + var/list/datum/radio_frequency/frequencies = list() + +// This is fucking disgusting and needs to die +/datum/controller/subsystem/radio/proc/frequency_span_class(var/frequency) + // Antags! + if(frequency in ANTAG_FREQS) + return "syndradio" + // centcomm channels (deathsquid and ert) + if(frequency in CENT_FREQS) + return "centradio" + // This switch used to be a shit tonne of if statements. I am gonna find who made this and give them a kind talking to + switch(frequency) + if(COMM_FREQ) + return "comradio" + if(AI_FREQ) + return "airadio" + if(SEC_FREQ) + return "secradio" + if(ENG_FREQ) + return "engradio" + if(SCI_FREQ) + return "sciradio" + if(MED_FREQ) + return "medradio" + if(SUP_FREQ) + return "supradio" + if(SRV_FREQ) + return "srvradio" + + // If the above switch somehow failed. And it needs the SSradio. part otherwise it fails to compile + if(frequency in DEPT_FREQS) + return "deptradio" + + // If its none of the others + return "radio" + + +/datum/controller/subsystem/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null) + var/f_text = num2text(new_frequency) + var/datum/radio_frequency/frequency = frequencies[f_text] + + if(!frequency) + frequency = new + frequency.frequency = new_frequency + frequencies[f_text] = frequency + + frequency.add_listener(device, filter) + return frequency + +/datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency) + var/f_text = num2text(old_frequency) + var/datum/radio_frequency/frequency = frequencies[f_text] + + if(frequency) + frequency.remove_listener(device) + + if(frequency.devices.len == 0) + qdel(frequency) + frequencies -= f_text + + return 1 + +/datum/controller/subsystem/radio/proc/return_frequency(var/new_frequency as num) + var/f_text = num2text(new_frequency) + var/datum/radio_frequency/frequency = frequencies[f_text] + + if(!frequency) + frequency = new + frequency.frequency = new_frequency + frequencies[f_text] = frequency + + return frequency + + +// ALL THE SHIT BELOW THIS LINE ISNT PART OF THE SUBSYSTEM AND REALLY NEEDS ITS OWN FILE diff --git a/code/controllers/subsystem/shuttles.dm b/code/controllers/subsystem/shuttles.dm index a1ca9b5fdad..247d71d05b8 100644 --- a/code/controllers/subsystem/shuttles.dm +++ b/code/controllers/subsystem/shuttles.dm @@ -6,6 +6,7 @@ SUBSYSTEM_DEF(shuttle) init_order = INIT_ORDER_SHUTTLE flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK runlevels = RUNLEVEL_SETUP | RUNLEVEL_GAME + offline_implications = "Shuttles will no longer function and cargo will not generate points. Immediate server restart recommended." var/list/mobile = list() var/list/stationary = list() var/list/transit = list() @@ -223,11 +224,12 @@ SUBSYSTEM_DEF(shuttle) return 0 //dock successful -/datum/controller/subsystem/shuttle/proc/moveShuttle(shuttleId, dockId, timed) +/datum/controller/subsystem/shuttle/proc/moveShuttle(shuttleId, dockId, timed, mob/user) var/obj/docking_port/mobile/M = getShuttle(shuttleId) var/obj/docking_port/stationary/D = getDock(dockId) if(!M) return 1 + M.last_caller = user // Save the caller of the shuttle for later logging if(timed) if(M.request(D)) return 2 @@ -311,4 +313,4 @@ SUBSYSTEM_DEF(shuttle) QDEL_LIST(remove_images) -#undef CALL_SHUTTLE_REASON_LENGTH \ No newline at end of file +#undef CALL_SHUTTLE_REASON_LENGTH diff --git a/code/controllers/subsystem/spacedrift.dm b/code/controllers/subsystem/spacedrift.dm index fcc62a2fa50..d9c46f9c9d7 100644 --- a/code/controllers/subsystem/spacedrift.dm +++ b/code/controllers/subsystem/spacedrift.dm @@ -4,6 +4,7 @@ SUBSYSTEM_DEF(spacedrift) wait = 5 flags = SS_NO_INIT|SS_KEEP_TIMING runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + offline_implications = "Mobs will no longer respect a lack of gravity. No immediate action is needed." var/list/currentrun = list() var/list/processing = list() diff --git a/code/controllers/subsystem/sun.dm b/code/controllers/subsystem/sun.dm index 8e5e47e7abf..3f8e15df05a 100644 --- a/code/controllers/subsystem/sun.dm +++ b/code/controllers/subsystem/sun.dm @@ -1,19 +1,29 @@ SUBSYSTEM_DEF(sun) name = "Sun" wait = 600 - flags = SS_NO_TICK_CHECK|SS_NO_INIT + flags = SS_NO_TICK_CHECK + init_order = INIT_ORDER_SUN + offline_implications = "Solar panels will no longer rotate. No immediate action is needed." var/angle var/dx var/dy var/rate var/list/solars = list() -/datum/controller/subsystem/sun/PreInit() +/datum/controller/subsystem/sun/Initialize(start_timeofday) + // Lets work out an angle for the "sun" to rotate around the station angle = rand (0,360) // the station position to the sun is randomised at round start rate = rand(50,200)/100 // 50% - 200% of standard rotation if(prob(50)) // same chance to rotate clockwise than counter-clockwise rate = -rate + // Solar consoles need to load after machines init, so this handles that + for(var/obj/machinery/power/solar_control/SC in solars) + SC.setup() + + return ..() + + /datum/controller/subsystem/sun/stat_entry(msg) ..("P:[solars.len]") @@ -39,4 +49,4 @@ SUBSYSTEM_DEF(sun) if(!SC.powernet) solars.Remove(SC) continue - SC.update() \ No newline at end of file + SC.update() diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm index 74dbf4c4d93..04ddf40f442 100644 --- a/code/controllers/subsystem/throwing.dm +++ b/code/controllers/subsystem/throwing.dm @@ -7,6 +7,7 @@ SUBSYSTEM_DEF(throwing) wait = 1 flags = SS_NO_INIT|SS_KEEP_TIMING|SS_TICKER runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + offline_implications = "Thrown objects may not react properly. Shuttle call recommended." var/list/currentrun var/list/processing = list() diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index f9528f2b2b9..825eab93a31 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -1,533 +1,534 @@ -SUBSYSTEM_DEF(ticker) - name = "Ticker" - init_order = INIT_ORDER_TICKER - - priority = FIRE_PRIORITY_TICKER - flags = SS_KEEP_TIMING - runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME - - var/round_start_time = 0 - var/const/restart_timeout = 600 - var/current_state = GAME_STATE_STARTUP - var/force_start = 0 // Do we want to force-start as soon as we can - var/force_ending = 0 - var/hide_mode = 0 // leave here at 0 ! setup() will take care of it when needed for Secret mode -walter0o - var/datum/game_mode/mode = null - var/event_time = null - var/event = 0 - var/login_music // music played in pregame lobby - var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking. - var/Bible_icon_state // icon_state the chaplain has chosen for his bible - var/Bible_item_state // item_state the chaplain has chosen for his bible - var/Bible_name // name of the bible - var/Bible_deity_name - var/datum/cult_info/cultdat = null //here instead of cult for adminbus purposes - var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders - var/list/syndicate_coalition = list() // list of traitor-compatible factions - var/list/factions = list() // list of all factions - var/list/availablefactions = list() // list of factions with openings - var/tipped = FALSE //Did we broadcast the tip of the day yet? - var/selected_tip // What will be the tip of the day? - var/pregame_timeleft // This is used for calculations - var/delay_end = 0 //if set to nonzero, the round will not restart on it's own - var/triai = 0//Global holder for Triumvirate - var/initialtpass = 0 //holder for inital autotransfer vote timer - var/obj/screen/cinematic = null //used for station explosion cinematic - var/round_end_announced = 0 // Spam Prevention. Announce round end only once.\ - -/datum/controller/subsystem/ticker/Initialize() - login_music = pick(\ - 'sound/music/thunderdome.ogg',\ - 'sound/music/space.ogg',\ - 'sound/music/title1.ogg',\ - 'sound/music/title2.ogg',\ - 'sound/music/title3.ogg',) - - // Map name - if(using_map && using_map.name) - GLOB.map_name = "[using_map.name]" - else - GLOB.map_name = "Unknown" - - // World name - if(config && config.server_name) - world.name = "[config.server_name]: [station_name()]" - else - world.name = station_name() - - return ..() - - -/datum/controller/subsystem/ticker/fire() - switch(current_state) - if(GAME_STATE_STARTUP) - // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails - round_start_time = world.time + (config.pregame_timestart * 10) - to_chat(world, "Welcome to the pre-game lobby!") - to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") - current_state = GAME_STATE_PREGAME - fire() // TG says this is a good idea - if(GAME_STATE_PREGAME) - if(!going) - return - - // This is so we dont have sleeps in controllers, because that is a bad, bad thing - if(!delay_end) - pregame_timeleft = max(0,round_start_time - world.time) // Normal lobby countdown when roundstart was not delayed - else - pregame_timeleft = max(0,pregame_timeleft - 20) // If roundstart was delayed, we should resume the countdown where it left off - - if(pregame_timeleft <= 600 && !tipped) // 60 seconds - send_tip_of_the_round() - tipped = TRUE - - if(pregame_timeleft <= 0 || force_start) - current_state = GAME_STATE_SETTING_UP - Master.SetRunLevel(RUNLEVEL_SETUP) - if(GAME_STATE_SETTING_UP) - if(!setup()) // Setup failed - current_state = GAME_STATE_STARTUP - Master.SetRunLevel(RUNLEVEL_LOBBY) - if(GAME_STATE_PLAYING) - delay_end = 0 // reset this in case round start was delayed - mode.process() - mode.process_job_tasks() - var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked - if(config.continuous_rounds) - mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result - else - game_finished |= mode.check_finished() - if(game_finished) - current_state = GAME_STATE_FINISHED - if(GAME_STATE_FINISHED) - current_state = GAME_STATE_FINISHED - Master.SetRunLevel(RUNLEVEL_POSTGAME) // This shouldnt process more than once, but you never know - auto_toggle_ooc(1) // Turn it on - - spawn(0) - declare_completion() - - spawn(50) - if(mode.station_was_nuked) - world.Reboot("Station destroyed by Nuclear Device.", "end_proper", "nuke") - else - world.Reboot("Round ended.", "end_proper", "proper completion") - - -/datum/controller/subsystem/ticker/proc/votetimer() - var/timerbuffer = 0 - if(initialtpass == 0) - timerbuffer = config.vote_autotransfer_initial - else - timerbuffer = config.vote_autotransfer_interval - spawn(timerbuffer) - SSvote.autotransfer() - initialtpass = 1 - votetimer() - - -/datum/controller/subsystem/ticker/proc/setup() - cultdat = setupcult() - //Create and announce mode - if(master_mode=="secret") - src.hide_mode = 1 - var/list/datum/game_mode/runnable_modes - if((master_mode=="random") || (master_mode=="secret")) - runnable_modes = config.get_runnable_modes() - if(runnable_modes.len==0) - current_state = GAME_STATE_PREGAME - Master.SetRunLevel(RUNLEVEL_LOBBY) - to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") - return 0 - if(secret_force_mode != "secret") - var/datum/game_mode/M = config.pick_mode(secret_force_mode) - if(M.can_start()) - src.mode = config.pick_mode(secret_force_mode) - SSjobs.ResetOccupations() - if(!src.mode) - src.mode = pickweight(runnable_modes) - if(src.mode) - var/mtype = src.mode.type - src.mode = new mtype - else - src.mode = config.pick_mode(master_mode) - if(!src.mode.can_start()) - to_chat(world, "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.") - mode = null - current_state = GAME_STATE_PREGAME - SSjobs.ResetOccupations() - Master.SetRunLevel(RUNLEVEL_LOBBY) - return 0 - - //Configure mode and assign player to special mode stuff - src.mode.pre_pre_setup() - var/can_continue - can_continue = src.mode.pre_setup()//Setup special modes - SSjobs.DivideOccupations() //Distribute jobs - if(!can_continue) - qdel(mode) - current_state = GAME_STATE_PREGAME - to_chat(world, "Error setting up [master_mode]. Reverting to pre-game lobby.") - SSjobs.ResetOccupations() - Master.SetRunLevel(RUNLEVEL_LOBBY) - return 0 - - if(hide_mode) - var/list/modes = new - for(var/datum/game_mode/M in runnable_modes) - modes+=M.name - modes = sortList(modes) - to_chat(world, "The current game mode is - Secret!") - to_chat(world, "Possibilities: [english_list(modes)]") - else - src.mode.announce() - - create_characters() //Create player characters and transfer them - populate_spawn_points() - collect_minds() - equip_characters() - data_core.manifest() - current_state = GAME_STATE_PLAYING - Master.SetRunLevel(RUNLEVEL_GAME) - - callHook("roundstart") - - //here to initialize the random events nicely at round start - setup_economy() - setupfactions() - - //shuttle_controller.setup_shuttle_docks() - - spawn(0)//Forking here so we dont have to wait for this to finish - if(!GLOB.syndicate_code_phrase) - GLOB.syndicate_code_phrase = generate_code_phrase() - if(!GLOB.syndicate_code_response) - GLOB.syndicate_code_response = generate_code_phrase() - mode.post_setup() - //Cleanup some stuff - for(var/obj/effect/landmark/start/S in GLOB.landmarks_list) - //Deleting Startpoints but we need the ai point to AI-ize people later - if(S.name != "AI") - qdel(S) - - // take care of random spesspod spawning - var/list/obj/effect/landmark/spacepod/random/L = list() - for(var/obj/effect/landmark/spacepod/random/SS in GLOB.landmarks_list) - if(istype(SS)) - L += SS - if(L.len) - var/obj/effect/landmark/spacepod/random/S = pick(L) - new /obj/spacepod/random(S.loc) - for(var/obj/effect/landmark/spacepod/random/R in L) - qdel(R) - - to_chat(world, "Enjoy the game!") - world << sound('sound/AI/welcome.ogg')// Skie - - if(SSholiday.holidays) - to_chat(world, "and...") - for(var/holidayname in SSholiday.holidays) - var/datum/holiday/holiday = SSholiday.holidays[holidayname] - to_chat(world, "

    [holiday.greet()]

    ") - - spawn(0) // Forking dynamic room selection - var/list/area/dynamic/source/available_source_candidates = subtypesof(/area/dynamic/source) - var/list/area/dynamic/destination/available_destination_candidates = subtypesof(/area/dynamic/destination) - - for(var/area/dynamic/destination/current_destination_candidate in available_destination_candidates) - var/area/dynamic/destination/current_destination = locate(current_destination_candidate) - - if(!current_destination) - continue - - if(current_destination.match_width == 0 || current_destination.match_height == 0) - message_admins("Dynamic area destination '[current_destination.name]' does not have its size requirements set.") - continue - - var/list/area/dynamic/source/candidate_source_areas = new /list(0) - for(var/area/dynamic/source/candidate_source_area in available_source_candidates) - var/area/dynamic/source/candidate_source = locate(candidate_source_area) - - if(!candidate_source) - continue - - if(candidate_source.match_tag != current_destination.match_tag) - continue - - if(candidate_source.match_width != current_destination.match_width || \ - candidate_source.match_height != current_destination.match_height) - continue - - candidate_source_areas += candidate_source - - if(candidate_source_areas.len == 0) - message_admins("Failed to find a matching source for dynamic area: [current_destination.name]") - continue - - var/area/dynamic/source/selected_source = pick(candidate_source_areas) - available_source_candidates -= selected_source - - selected_source.copy_contents_to(current_destination, 0) - - if(current_destination.enable_lights || selected_source.enable_lights) - current_destination.power_light = 1 - else - current_destination.power_light = 0 - current_destination.power_change() - - //start_events() //handles random events and space dust. - //new random event system is handled from the MC. - - var/list/admins_number = staff_countup(R_BAN) - if(admins_number[1] == 0 && admins_number[3] == 0) - send2irc(config.admin_notify_irc, "Round has started with no admins online.") - auto_toggle_ooc(0) // Turn it off - round_start_time = world.time - - if(config.sql_enabled) - spawn(3000) - statistic_cycle() // Polls population totals regularly and stores them in an SQL DB - - votetimer() - - for(var/mob/new_player/N in GLOB.mob_list) - if(N.client) - N.new_player_panel_proc() - - return 1 - -/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null) - if(cinematic) - return //already a cinematic in progress! - - auto_toggle_ooc(1) // Turn it on - //initialise our cinematic screen object - cinematic = new /obj/screen(src) - cinematic.icon = 'icons/effects/station_explosion.dmi' - cinematic.icon_state = "station_intact" - cinematic.layer = 21 - cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - cinematic.screen_loc = "1,0" - - var/obj/structure/bed/temp_buckle = new(src) - if(station_missed) - for(var/mob/M in GLOB.mob_list) - M.buckled = temp_buckle //buckles the mob so it can't do anything - if(M.client) - M.client.screen += cinematic //show every client the cinematic - else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" - for(var/mob/M in GLOB.mob_list) - M.buckled = temp_buckle - if(M.stat != DEAD) - var/turf/T = get_turf(M) - if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer)) - var/mob/ghost = M.ghostize() - M.dust() //no mercy - if(ghost && ghost.client) //Play the victims an uninterrupted cinematic. - ghost.client.screen += cinematic - CHECK_TICK - if(M && M.client) //Play the survivors a cinematic. - M.client.screen += cinematic - - //Now animate the cinematic - switch(station_missed) - if(1) //nuke was nearby but (mostly) missed - if(mode && !override) - override = mode.name - switch(override) - if("nuclear emergency") //Nuke wasn't on station when it blew up - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - flick("station_intact_fade_red", cinematic) - cinematic.icon_state = "summary_nukefail" - if("fake") //The round isn't over, we're just freaking people out for fun - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/items/bikehorn.ogg') - flick("summary_selfdes", cinematic) - else - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - - - if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation - sleep(50) - world << sound('sound/effects/explosionfar.ogg') - else //station was destroyed - if(mode && !override) - override = mode.name - switch(override) - if("nuclear emergency") //Nuke Ops successfully bombed the station - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_nukewin" - if("AI malfunction") //Malf (screen,explosion,summary) - flick("intro_malf", cinematic) - sleep(76) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_malf" - if("blob") //Station nuked (nuke,explosion,summary) - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - else //Station nuked (nuke,explosion,summary) - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - //If its actually the end of the round, wait for it to end. - //Otherwise if its a verb it will continue on afterwards. - spawn(300) - QDEL_NULL(cinematic) //end the cinematic - if(temp_buckle) - qdel(temp_buckle) //release everybody - - - -/datum/controller/subsystem/ticker/proc/create_characters() - for(var/mob/new_player/player in GLOB.player_list) - if(player.ready && player.mind) - if(player.mind.assigned_role == "AI") - player.close_spawn_windows() - var/mob/living/silicon/ai/ai_character = player.AIize() - ai_character.moveToAILandmark() - else if(!player.mind.assigned_role) - continue - else - player.create_character() - qdel(player) - - -/datum/controller/subsystem/ticker/proc/collect_minds() - for(var/mob/living/player in GLOB.player_list) - if(player.mind) - SSticker.minds += player.mind - - -/datum/controller/subsystem/ticker/proc/equip_characters() - var/captainless=1 - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player && player.mind && player.mind.assigned_role) - if(player.mind.assigned_role == "Captain") - captainless=0 - if(player.mind.assigned_role != player.mind.special_role) - SSjobs.AssignRank(player, player.mind.assigned_role, 0) - SSjobs.EquipRank(player, player.mind.assigned_role, 0) - EquipCustomItems(player) - if(captainless) - for(var/mob/M in GLOB.player_list) - if(!istype(M,/mob/new_player)) - to_chat(M, "Captainship not forced on anyone.") - -/datum/controller/subsystem/ticker/proc/send_tip_of_the_round() - var/m - if(selected_tip) - m = selected_tip - else - var/list/randomtips = file2list("strings/tips.txt") - var/list/memetips = file2list("strings/sillytips.txt") - if(randomtips.len && prob(95)) - m = pick(randomtips) - else if(memetips.len) - m = pick(memetips) - - if(m) - to_chat(world, "Tip of the round: [html_encode(m)]") - -/datum/controller/subsystem/ticker/proc/getfactionbyname(var/name) - for(var/datum/faction/F in factions) - if(F.name == name) - return F - - -/datum/controller/subsystem/ticker/proc/declare_completion() - nologevent = 1 //end of round murder and shenanigans are legal; there's no need to jam up attack logs past this point. - //Round statistics report - var/datum/station_state/end_state = new /datum/station_state() - end_state.count() - var/station_integrity = min(round( 100.0 * start_state.score(end_state), 0.1), 100.0) - - to_chat(world, "
    [TAB]Shift Duration: [round(ROUND_TIME / 36000)]:[add_zero("[ROUND_TIME / 600 % 60]", 2)]:[ROUND_TIME / 100 % 6][ROUND_TIME / 100 % 10]") - to_chat(world, "
    [TAB]Station Integrity: [mode.station_was_nuked ? "Destroyed" : "[station_integrity]%"]") - to_chat(world, "
    ") - - //Silicon laws report - for(var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list) - if(aiPlayer.stat != 2) - to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the game were:") - else - to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:") - aiPlayer.show_laws(1) - - if(aiPlayer.connected_robots.len) - var/robolist = "The AI's loyal minions were: " - for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) - robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" - to_chat(world, "[robolist]") - - var/dronecount = 0 - - for(var/mob/living/silicon/robot/robo in GLOB.mob_list) - - if(istype(robo,/mob/living/silicon/robot/drone)) - dronecount++ - continue - - if(!robo.connected_ai) - if(robo.stat != 2) - to_chat(world, "[robo.name] (Played by: [robo.key]) survived as an AI-less borg! Its laws were:") - else - to_chat(world, "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:") - - if(robo) //How the hell do we lose robo between here and the world messages directly above this? - robo.laws.show_laws(world) - - if(dronecount) - to_chat(world, "There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] this round.") - - if(mode.eventmiscs.len) - var/emobtext = "" - for(var/datum/mind/eventmind in mode.eventmiscs) - emobtext += printeventplayer(eventmind) - emobtext += "
    " - emobtext += printobjectives(eventmind) - emobtext += "
    " - emobtext += "
    " - to_chat(world, emobtext) - - mode.declare_completion()//To declare normal completion. - - //calls auto_declare_completion_* for all modes - for(var/handler in typesof(/datum/game_mode/proc)) - if(findtext("[handler]","auto_declare_completion_")) - call(mode, handler)() - - scoreboard() - - // Declare the completion of the station goals - mode.declare_station_goal_completion() - - //Ask the event manager to print round end information - SSevents.RoundEnd() - - // Add AntagHUD to everyone, see who was really evil the whole time! - for(var/datum/atom_hud/antag/H in huds) - for(var/m in GLOB.player_list) - var/mob/M = m - H.add_hud_to(M) - - return 1 - -/datum/controller/subsystem/ticker/proc/HasRoundStarted() - return current_state >= GAME_STATE_PLAYING - -/datum/controller/subsystem/ticker/proc/IsRoundInProgress() - return current_state == GAME_STATE_PLAYING \ No newline at end of file +SUBSYSTEM_DEF(ticker) + name = "Ticker" + init_order = INIT_ORDER_TICKER + + priority = FIRE_PRIORITY_TICKER + flags = SS_KEEP_TIMING + runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME + offline_implications = "The game is no longer aware of when the round ends. Immediate server restart recommended." + + var/round_start_time = 0 + var/const/restart_timeout = 600 + var/current_state = GAME_STATE_STARTUP + var/force_start = 0 // Do we want to force-start as soon as we can + var/force_ending = 0 + var/hide_mode = 0 // leave here at 0 ! setup() will take care of it when needed for Secret mode -walter0o + var/datum/game_mode/mode = null + var/event_time = null + var/event = 0 + var/login_music // music played in pregame lobby + var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking. + var/Bible_icon_state // icon_state the chaplain has chosen for his bible + var/Bible_item_state // item_state the chaplain has chosen for his bible + var/Bible_name // name of the bible + var/Bible_deity_name + var/datum/cult_info/cultdat = null //here instead of cult for adminbus purposes + var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders + var/list/syndicate_coalition = list() // list of traitor-compatible factions + var/list/factions = list() // list of all factions + var/list/availablefactions = list() // list of factions with openings + var/tipped = FALSE //Did we broadcast the tip of the day yet? + var/selected_tip // What will be the tip of the day? + var/pregame_timeleft // This is used for calculations + var/delay_end = 0 //if set to nonzero, the round will not restart on it's own + var/triai = 0//Global holder for Triumvirate + var/initialtpass = 0 //holder for inital autotransfer vote timer + var/obj/screen/cinematic = null //used for station explosion cinematic + var/round_end_announced = 0 // Spam Prevention. Announce round end only once. + var/ticker_going = TRUE // This used to be in the unused globals, but it turns out its actually used in a load of places. Its now a ticker var because its related to round stuff, -aa + +/datum/controller/subsystem/ticker/Initialize() + login_music = pick(\ + 'sound/music/thunderdome.ogg',\ + 'sound/music/space.ogg',\ + 'sound/music/title1.ogg',\ + 'sound/music/title2.ogg',\ + 'sound/music/title3.ogg',) + // Map name + if(GLOB.using_map && GLOB.using_map.name) + GLOB.map_name = "[GLOB.using_map.name]" + else + GLOB.map_name = "Unknown" + + // World name + if(config && config.server_name) + world.name = "[config.server_name]: [station_name()]" + else + world.name = station_name() + + return ..() + + +/datum/controller/subsystem/ticker/fire() + switch(current_state) + if(GAME_STATE_STARTUP) + // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails + round_start_time = world.time + (config.pregame_timestart * 10) + to_chat(world, "Welcome to the pre-game lobby!") + to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") + current_state = GAME_STATE_PREGAME + fire() // TG says this is a good idea + if(GAME_STATE_PREGAME) + if(!SSticker.ticker_going) // This has to be referenced like this, and I dont know why. If you dont put SSticker. it will break + return + + // This is so we dont have sleeps in controllers, because that is a bad, bad thing + if(!delay_end) + pregame_timeleft = max(0,round_start_time - world.time) // Normal lobby countdown when roundstart was not delayed + else + pregame_timeleft = max(0,pregame_timeleft - 20) // If roundstart was delayed, we should resume the countdown where it left off + + if(pregame_timeleft <= 600 && !tipped) // 60 seconds + send_tip_of_the_round() + tipped = TRUE + + if(pregame_timeleft <= 0 || force_start) + current_state = GAME_STATE_SETTING_UP + Master.SetRunLevel(RUNLEVEL_SETUP) + if(GAME_STATE_SETTING_UP) + if(!setup()) // Setup failed + current_state = GAME_STATE_STARTUP + Master.SetRunLevel(RUNLEVEL_LOBBY) + if(GAME_STATE_PLAYING) + delay_end = 0 // reset this in case round start was delayed + mode.process() + mode.process_job_tasks() + var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked + if(config.continuous_rounds) + mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result + else + game_finished |= mode.check_finished() + if(game_finished || force_ending) + current_state = GAME_STATE_FINISHED + if(GAME_STATE_FINISHED) + current_state = GAME_STATE_FINISHED + Master.SetRunLevel(RUNLEVEL_POSTGAME) // This shouldnt process more than once, but you never know + auto_toggle_ooc(1) // Turn it on + + spawn(0) + declare_completion() + + spawn(50) + if(mode.station_was_nuked) + world.Reboot("Station destroyed by Nuclear Device.", "end_proper", "nuke") + else + world.Reboot("Round ended.", "end_proper", "proper completion") + + +/datum/controller/subsystem/ticker/proc/votetimer() + var/timerbuffer = 0 + if(initialtpass == 0) + timerbuffer = config.vote_autotransfer_initial + else + timerbuffer = config.vote_autotransfer_interval + spawn(timerbuffer) + SSvote.autotransfer() + initialtpass = 1 + votetimer() + + +/datum/controller/subsystem/ticker/proc/setup() + cultdat = setupcult() + //Create and announce mode + if(GLOB.master_mode=="secret") + src.hide_mode = 1 + var/list/datum/game_mode/runnable_modes + if((GLOB.master_mode=="random") || (GLOB.master_mode=="secret")) + runnable_modes = config.get_runnable_modes() + if(runnable_modes.len==0) + current_state = GAME_STATE_PREGAME + Master.SetRunLevel(RUNLEVEL_LOBBY) + to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") + return 0 + if(GLOB.secret_force_mode != "secret") + var/datum/game_mode/M = config.pick_mode(GLOB.secret_force_mode) + if(M.can_start()) + src.mode = config.pick_mode(GLOB.secret_force_mode) + SSjobs.ResetOccupations() + if(!src.mode) + src.mode = pickweight(runnable_modes) + if(src.mode) + var/mtype = src.mode.type + src.mode = new mtype + else + src.mode = config.pick_mode(GLOB.master_mode) + if(!src.mode.can_start()) + to_chat(world, "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.") + mode = null + current_state = GAME_STATE_PREGAME + SSjobs.ResetOccupations() + Master.SetRunLevel(RUNLEVEL_LOBBY) + return 0 + + //Configure mode and assign player to special mode stuff + src.mode.pre_pre_setup() + var/can_continue + can_continue = src.mode.pre_setup()//Setup special modes + SSjobs.DivideOccupations() //Distribute jobs + if(!can_continue) + qdel(mode) + current_state = GAME_STATE_PREGAME + to_chat(world, "Error setting up [GLOB.master_mode]. Reverting to pre-game lobby.") + SSjobs.ResetOccupations() + Master.SetRunLevel(RUNLEVEL_LOBBY) + return 0 + + if(hide_mode) + var/list/modes = new + for(var/datum/game_mode/M in runnable_modes) + modes+=M.name + modes = sortList(modes) + to_chat(world, "The current game mode is - Secret!") + to_chat(world, "Possibilities: [english_list(modes)]") + else + src.mode.announce() + + create_characters() //Create player characters and transfer them + populate_spawn_points() + collect_minds() + equip_characters() + GLOB.data_core.manifest() + current_state = GAME_STATE_PLAYING + Master.SetRunLevel(RUNLEVEL_GAME) + + callHook("roundstart") + + //here to initialize the random events nicely at round start + setup_economy() + setupfactions() + + //shuttle_controller.setup_shuttle_docks() + + spawn(0)//Forking here so we dont have to wait for this to finish + if(!GLOB.syndicate_code_phrase) + GLOB.syndicate_code_phrase = generate_code_phrase() + if(!GLOB.syndicate_code_response) + GLOB.syndicate_code_response = generate_code_phrase() + mode.post_setup() + //Cleanup some stuff + for(var/obj/effect/landmark/start/S in GLOB.landmarks_list) + //Deleting Startpoints but we need the ai point to AI-ize people later + if(S.name != "AI") + qdel(S) + + // take care of random spesspod spawning + var/list/obj/effect/landmark/spacepod/random/L = list() + for(var/obj/effect/landmark/spacepod/random/SS in GLOB.landmarks_list) + if(istype(SS)) + L += SS + if(L.len) + var/obj/effect/landmark/spacepod/random/S = pick(L) + new /obj/spacepod/random(S.loc) + for(var/obj/effect/landmark/spacepod/random/R in L) + qdel(R) + + to_chat(world, "Enjoy the game!") + world << sound('sound/AI/welcome.ogg')// Skie + + if(SSholiday.holidays) + to_chat(world, "and...") + for(var/holidayname in SSholiday.holidays) + var/datum/holiday/holiday = SSholiday.holidays[holidayname] + to_chat(world, "

    [holiday.greet()]

    ") + + spawn(0) // Forking dynamic room selection + var/list/area/dynamic/source/available_source_candidates = subtypesof(/area/dynamic/source) + var/list/area/dynamic/destination/available_destination_candidates = subtypesof(/area/dynamic/destination) + + for(var/area/dynamic/destination/current_destination_candidate in available_destination_candidates) + var/area/dynamic/destination/current_destination = locate(current_destination_candidate) + + if(!current_destination) + continue + + if(current_destination.match_width == 0 || current_destination.match_height == 0) + message_admins("Dynamic area destination '[current_destination.name]' does not have its size requirements set.") + continue + + var/list/area/dynamic/source/candidate_source_areas = new /list(0) + for(var/area/dynamic/source/candidate_source_area in available_source_candidates) + var/area/dynamic/source/candidate_source = locate(candidate_source_area) + + if(!candidate_source) + continue + + if(candidate_source.match_tag != current_destination.match_tag) + continue + + if(candidate_source.match_width != current_destination.match_width || \ + candidate_source.match_height != current_destination.match_height) + continue + + candidate_source_areas += candidate_source + + if(candidate_source_areas.len == 0) + message_admins("Failed to find a matching source for dynamic area: [current_destination.name]") + continue + + var/area/dynamic/source/selected_source = pick(candidate_source_areas) + available_source_candidates -= selected_source + + selected_source.copy_contents_to(current_destination, 0) + + if(current_destination.enable_lights || selected_source.enable_lights) + current_destination.power_light = 1 + else + current_destination.power_light = 0 + current_destination.power_change() + + //start_events() //handles random events and space dust. + //new random event system is handled from the MC. + + var/list/admins_number = staff_countup(R_BAN) + if(admins_number[1] == 0 && admins_number[3] == 0) + send2irc(config.admin_notify_irc, "Round has started with no admins online.") + auto_toggle_ooc(0) // Turn it off + round_start_time = world.time + + if(config.sql_enabled) + spawn(3000) + statistic_cycle() // Polls population totals regularly and stores them in an SQL DB + + votetimer() + + for(var/mob/new_player/N in GLOB.mob_list) + if(N.client) + N.new_player_panel_proc() + + return 1 + +/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null) + if(cinematic) + return //already a cinematic in progress! + + auto_toggle_ooc(1) // Turn it on + //initialise our cinematic screen object + cinematic = new /obj/screen(src) + cinematic.icon = 'icons/effects/station_explosion.dmi' + cinematic.icon_state = "station_intact" + cinematic.layer = 21 + cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + cinematic.screen_loc = "1,0" + + if(station_missed) + for(var/mob/M in GLOB.mob_list) + if(M.client) + M.client.screen += cinematic //show every client the cinematic + else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" + for(var/mob/M in GLOB.mob_list) + if(M.stat != DEAD) + var/turf/T = get_turf(M) + if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer)) + var/mob/ghost = M.ghostize() + M.dust() //no mercy + if(ghost && ghost.client) //Play the victims an uninterrupted cinematic. + ghost.client.screen += cinematic + CHECK_TICK + if(M && M.client) //Play the survivors a cinematic. + M.client.screen += cinematic + + //Now animate the cinematic + switch(station_missed) + if(1) //nuke was nearby but (mostly) missed + if(mode && !override) + override = mode.name + switch(override) + if("nuclear emergency") //Nuke wasn't on station when it blew up + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + flick("station_intact_fade_red", cinematic) + cinematic.icon_state = "summary_nukefail" + if("fake") //The round isn't over, we're just freaking people out for fun + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/items/bikehorn.ogg') + flick("summary_selfdes", cinematic) + else + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + + + if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation + sleep(50) + world << sound('sound/effects/explosionfar.ogg') + else //station was destroyed + if(mode && !override) + override = mode.name + switch(override) + if("nuclear emergency") //Nuke Ops successfully bombed the station + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_nukewin" + if("AI malfunction") //Malf (screen,explosion,summary) + flick("intro_malf", cinematic) + sleep(76) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_malf" + if("blob") //Station nuked (nuke,explosion,summary) + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + else //Station nuked (nuke,explosion,summary) + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + //If its actually the end of the round, wait for it to end. + //Otherwise if its a verb it will continue on afterwards. + spawn(300) + QDEL_NULL(cinematic) //end the cinematic + + + +/datum/controller/subsystem/ticker/proc/create_characters() + for(var/mob/new_player/player in GLOB.player_list) + if(player.ready && player.mind) + if(player.mind.assigned_role == "AI") + player.close_spawn_windows() + var/mob/living/silicon/ai/ai_character = player.AIize() + ai_character.moveToAILandmark() + else if(!player.mind.assigned_role) + continue + else + player.create_character() + qdel(player) + + +/datum/controller/subsystem/ticker/proc/collect_minds() + for(var/mob/living/player in GLOB.player_list) + if(player.mind) + SSticker.minds += player.mind + + +/datum/controller/subsystem/ticker/proc/equip_characters() + var/captainless=1 + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(player && player.mind && player.mind.assigned_role) + if(player.mind.assigned_role == "Captain") + captainless=0 + if(player.mind.assigned_role != player.mind.special_role) + SSjobs.AssignRank(player, player.mind.assigned_role, 0) + SSjobs.EquipRank(player, player.mind.assigned_role, 0) + EquipCustomItems(player) + if(captainless) + for(var/mob/M in GLOB.player_list) + if(!istype(M,/mob/new_player)) + to_chat(M, "Captainship not forced on anyone.") + +/datum/controller/subsystem/ticker/proc/send_tip_of_the_round() + var/m + if(selected_tip) + m = selected_tip + else + var/list/randomtips = file2list("strings/tips.txt") + var/list/memetips = file2list("strings/sillytips.txt") + if(randomtips.len && prob(95)) + m = pick(randomtips) + else if(memetips.len) + m = pick(memetips) + + if(m) + to_chat(world, "Tip of the round: [html_encode(m)]") + +/datum/controller/subsystem/ticker/proc/getfactionbyname(var/name) + for(var/datum/faction/F in factions) + if(F.name == name) + return F + + +/datum/controller/subsystem/ticker/proc/declare_completion() + GLOB.nologevent = 1 //end of round murder and shenanigans are legal; there's no need to jam up attack logs past this point. + //Round statistics report + var/datum/station_state/end_state = new /datum/station_state() + end_state.count() + var/station_integrity = min(round( 100.0 * GLOB.start_state.score(end_state), 0.1), 100.0) + + to_chat(world, "
    [TAB]Shift Duration: [round(ROUND_TIME / 36000)]:[add_zero("[ROUND_TIME / 600 % 60]", 2)]:[ROUND_TIME / 100 % 6][ROUND_TIME / 100 % 10]") + to_chat(world, "
    [TAB]Station Integrity: [mode.station_was_nuked ? "Destroyed" : "[station_integrity]%"]") + to_chat(world, "
    ") + + //Silicon laws report + for(var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list) + if(aiPlayer.stat != 2) + to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the game were:") + else + to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:") + aiPlayer.show_laws(1) + + if(aiPlayer.connected_robots.len) + var/robolist = "The AI's loyal minions were: " + for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) + robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" + to_chat(world, "[robolist]") + + var/dronecount = 0 + + for(var/mob/living/silicon/robot/robo in GLOB.mob_list) + + if(istype(robo,/mob/living/silicon/robot/drone)) + dronecount++ + continue + + if(!robo.connected_ai) + if(robo.stat != 2) + to_chat(world, "[robo.name] (Played by: [robo.key]) survived as an AI-less borg! Its laws were:") + else + to_chat(world, "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:") + + if(robo) //How the hell do we lose robo between here and the world messages directly above this? + robo.laws.show_laws(world) + + if(dronecount) + to_chat(world, "There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] this round.") + + if(mode.eventmiscs.len) + var/emobtext = "" + for(var/datum/mind/eventmind in mode.eventmiscs) + emobtext += printeventplayer(eventmind) + emobtext += "
    " + emobtext += printobjectives(eventmind) + emobtext += "
    " + emobtext += "
    " + to_chat(world, emobtext) + + mode.declare_completion()//To declare normal completion. + + //calls auto_declare_completion_* for all modes + for(var/handler in typesof(/datum/game_mode/proc)) + if(findtext("[handler]","auto_declare_completion_")) + call(mode, handler)() + + scoreboard() + + // Declare the completion of the station goals + mode.declare_station_goal_completion() + + //Ask the event manager to print round end information + SSevents.RoundEnd() + + //make big obvious note in game logs that round ended + log_game("///////////////////////////////////////////////////////") + log_game("///////////////////// ROUND ENDED /////////////////////") + log_game("///////////////////////////////////////////////////////") + + // Add AntagHUD to everyone, see who was really evil the whole time! + for(var/datum/atom_hud/antag/H in GLOB.huds) + for(var/m in GLOB.player_list) + var/mob/M = m + H.add_hud_to(M) + + return 1 + +/datum/controller/subsystem/ticker/proc/HasRoundStarted() + return current_state >= GAME_STATE_PLAYING + +/datum/controller/subsystem/ticker/proc/IsRoundInProgress() + return current_state == GAME_STATE_PLAYING diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 1b2938fc70f..7bb6a496274 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -9,6 +9,7 @@ SUBSYSTEM_DEF(timer) init_order = INIT_ORDER_TIMER flags = SS_TICKER|SS_NO_INIT + offline_implications = "The game will no longer process timers. Immediate server restart recommended." var/list/second_queue = list() //awe, yes, you've had first queue, but what about second queue? var/list/hashes = list() @@ -518,4 +519,4 @@ SUBSYSTEM_DEF(timer) #undef BUCKET_LEN #undef BUCKET_POS #undef TIMER_MAX -#undef TIMER_ID_MAX \ No newline at end of file +#undef TIMER_ID_MAX diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index eca632b7b95..b0110258da7 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -1,401 +1,402 @@ -SUBSYSTEM_DEF(vote) - name = "Vote" - wait = 10 - flags = SS_KEEP_TIMING|SS_NO_INIT - runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - - var/initiator = null - var/started_time = null - var/time_remaining = 0 - var/mode = null - var/question = null - var/list/choices = list() - var/list/voted = list() - var/list/voting = list() - var/list/current_votes = list() - var/list/round_voters = list() - var/auto_muted = 0 - -/datum/controller/subsystem/vote/fire() - if(mode) - // No more change mode votes after the game has started. - if(mode == "gamemode" && SSticker.current_state >= GAME_STATE_SETTING_UP) - to_chat(world, "Voting aborted due to game start.") - reset() - return - - // Calculate how much time is remaining by comparing current time, to time of vote start, - // plus vote duration - time_remaining = round((started_time + config.vote_period - world.time)/10) - - if(time_remaining < 0) - result() - for(var/client/C in voting) - if(C) - C << browse(null,"window=vote") - reset() - else - for(var/client/C in voting) - update_panel(C) - CHECK_TICK - -/datum/controller/subsystem/vote/proc/autotransfer() - initiate_vote("crew_transfer","the server") - -/datum/controller/subsystem/vote/proc/reset() - initiator = null - time_remaining = 0 - mode = null - question = null - choices.Cut() - voted.Cut() - voting.Cut() - current_votes.Cut() - - if(auto_muted && !config.ooc_allowed && !(config.auto_toggle_ooc_during_round && SSticker.current_state == GAME_STATE_PLAYING)) - auto_muted = 0 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically enabled due to vote end.") - log_admin("OOC was toggled automatically due to vote end.") - message_admins("OOC has been toggled on automatically.") - - -/datum/controller/subsystem/vote/proc/get_result() - var/greatest_votes = 0 - var/total_votes = 0 - var/list/sorted_choices = list() - var/sorted_highest - var/sorted_votes = -1 - //get the highest number of votes, while also sorting the list - while(choices.len) - // This is a very inefficient sorting method, but that's okay - for(var/option in choices) - var/votes = choices[option] - if(sorted_votes < votes) - sorted_highest = option - sorted_votes = votes - if(votes > greatest_votes) - greatest_votes = votes - sorted_votes = -1 - total_votes += choices[sorted_highest] - sorted_choices[sorted_highest] = choices[sorted_highest] || 0 - choices -= sorted_highest - choices = sorted_choices - //default-vote for everyone who didn't vote - if(!config.vote_no_default && choices.len) - var/non_voters = (GLOB.clients.len - total_votes) - if(non_voters > 0) - if(mode == "restart") - choices["Continue Playing"] += non_voters - if(choices["Continue Playing"] >= greatest_votes) - greatest_votes = choices["Continue Playing"] - else if(mode == "gamemode") - if(master_mode in choices) - choices[master_mode] += non_voters - if(choices[master_mode] >= greatest_votes) - greatest_votes = choices[master_mode] - else if(mode == "crew_transfer") - var/factor = 0.5 - switch(world.time / (10 * 60)) // minutes - if(0 to 60) - factor = 0.5 - if(61 to 120) - factor = 0.8 - if(121 to 240) - factor = 1 - if(241 to 300) - factor = 1.2 - else - factor = 1.4 - choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) - to_chat(world, "Crew Transfer Factor: [factor]") - greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"]) - - - //get all options with that many votes and return them in a list - . = list() - if(greatest_votes) - for(var/option in choices) - if(choices[option] == greatest_votes) - . += option - return . - -/datum/controller/subsystem/vote/proc/announce_result() - var/list/winners = get_result() - var/text - if(winners.len > 0) - if(winners.len > 1) - if(mode != "gamemode" || SSticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes - text = "Vote Tied Between:\n" - for(var/option in winners) - text += "\t[option]\n" - . = pick(winners) - - for(var/key in current_votes) - if(choices[current_votes[key]] == .) - round_voters += key // Keep track of who voted for the winning round. - if(mode == "gamemode" && (. == "extended" || SSticker.hide_mode == 0)) // Announce Extended gamemode, but not other gamemodes - text += "Vote Result: [.] ([choices[.]] vote\s)" - else - if(mode == "custom") - // Completely replace text to show all results in custom votes - text = "[question]\n" - for(var/option in winners) - text += "\t[option]: [choices[option]] vote\s\n" - for(var/option in (choices-winners)) - text += "\t[option]: [choices[option]] vote\s\n" - else if(mode != "gamemode") - text += "Vote Result: [.] ([choices[.]] vote\s)" - else - text += "The vote has ended." // What will be shown if it is a gamemode vote that isn't extended - - else - text += "Vote Result: Inconclusive - No Votes!" - log_vote(text) - to_chat(world, "[text]") - return . - -/datum/controller/subsystem/vote/proc/result() - . = announce_result() - var/restart = 0 - if(.) - switch(mode) - if("restart") - if(. == "Restart Round") - restart = 1 - if("gamemode") - if(master_mode != .) - world.save_mode(.) - if(SSticker && SSticker.mode) - restart = 1 - else - master_mode = . - if(!going) - going = 1 - to_chat(world, "The round will start soon.") - if("crew_transfer") - if(. == "Initiate Crew Transfer") - init_shift_change(null, 1) - - - if(restart) - world.Reboot("Restart vote successful.", "end_error", "restart vote") - - return . - -/datum/controller/subsystem/vote/proc/submit_vote(var/ckey, var/vote) - if(mode) - if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder) - return 0 - if(current_votes[ckey]) - choices[choices[current_votes[ckey]]]-- - if(vote && 1<=vote && vote<=choices.len) - voted += usr.ckey - choices[choices[vote]]++ //check this - current_votes[ckey] = vote - return vote - return 0 - -/datum/controller/subsystem/vote/proc/initiate_vote(var/vote_type, var/initiator_key) - if(!mode) - if(started_time != null && !check_rights(R_ADMIN)) - var/next_allowed_time = (started_time + config.vote_delay) - if(next_allowed_time > world.time) - return 0 - - reset() - switch(vote_type) - if("restart") - choices.Add("Restart Round","Continue Playing") - if("gamemode") - if(SSticker.current_state >= 2) - return 0 - choices.Add(config.votable_modes) - if("crew_transfer") - if(check_rights(R_ADMIN|R_MOD)) - if(SSticker.current_state <= 2) - return 0 - question = "End the shift?" - choices.Add("Initiate Crew Transfer", "Continue The Round") - else - if(SSticker.current_state <= 2) - return 0 - question = "End the shift?" - choices.Add("Initiate Crew Transfer", "Continue The Round") - if("custom") - question = html_encode(input(usr,"What is the vote for?") as text|null) - if(!question) return 0 - for(var/i=1,i<=10,i++) - var/option = capitalize(html_encode(input(usr,"Please enter an option or hit cancel to finish") as text|null)) - if(!option || mode || !usr.client) break - choices.Add(option) - else - return 0 - mode = vote_type - initiator = initiator_key - started_time = world.time - var/text = "[capitalize(mode)] vote started by [initiator]." - if(mode == "custom") - text += "\n[question]" - if(usr) - log_admin("[capitalize(mode)] ([question]) vote started by [key_name(usr)].") - else if(usr) - log_admin("[capitalize(mode)] vote started by [key_name(usr)].") - - log_vote(text) - to_chat(world, {"[text] - Click here or type vote to place your vote. - You have [config.vote_period/10] seconds to vote."}) - switch(vote_type) - if("crew_transfer") - world << sound('sound/ambience/alarm4.ogg') - if("gamemode") - world << sound('sound/ambience/alarm4.ogg') - if("custom") - world << sound('sound/ambience/alarm4.ogg') - if(mode == "gamemode" && going) - going = 0 - to_chat(world, "Round start has been delayed.") - if(mode == "crew_transfer" && config.ooc_allowed) - auto_muted = 1 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically disabled due to a crew transfer vote.") - log_admin("OOC was toggled automatically due to crew_transfer vote.") - message_admins("OOC has been toggled off automatically.") - if(mode == "gamemode" && config.ooc_allowed) - auto_muted = 1 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically disabled due to the gamemode vote.") - log_admin("OOC was toggled automatically due to gamemode vote.") - message_admins("OOC has been toggled off automatically.") - if(mode == "custom" && config.ooc_allowed) - auto_muted = 1 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically disabled due to a custom vote.") - log_admin("OOC was toggled automatically due to custom vote.") - message_admins("OOC has been toggled off automatically.") - - time_remaining = round(config.vote_period/10) - return 1 - return 0 - -/datum/controller/subsystem/vote/proc/browse_to(var/client/C) - if(!C) - return - var/admin = check_rights(R_ADMIN, 0, user = C.mob) - voting |= C - - var/dat = {""} - if(mode) - dat += "
    [vote_html(C)]

    " - if(admin) - dat += "(Cancel Vote) " - else - dat += "

    Start a vote:



    " - var/datum/browser/popup = new(C.mob, "vote", "Voting Panel", nref=src) - popup.set_content(dat) - popup.open() - -/datum/controller/subsystem/vote/proc/update_panel(var/client/C) - C << output(url_encode(vote_html(C)), "vote.browser:update_vote_div") - -/datum/controller/subsystem/vote/proc/vote_html(var/client/C) - . = "" - if(question) - . += "

    Vote: '[question]'

    " - else - . += "

    Vote: [capitalize(mode)]

    " - . += "Time Left: [time_remaining] s
    " - - -/datum/controller/subsystem/vote/Topic(href,href_list[],hsrc) - if(!usr || !usr.client) - return //not necessary but meh...just in-case somebody does something stupid - var/admin = check_rights(R_ADMIN,0) - if(href_list["close"]) - voting -= usr.client - return - switch(href_list["vote"]) - if("open") - // vote proc will automatically get called after this switch ends - if("cancel") - if(admin && mode) - var/votedesc = capitalize(mode) - if(mode == "custom") - votedesc += " ([question])" - admin_log_and_message_admins("cancelled the running [votedesc] vote.") - reset() - if("toggle_restart") - if(admin) - config.allow_vote_restart = !config.allow_vote_restart - if("toggle_gamemode") - if(admin) - config.allow_vote_mode = !config.allow_vote_mode - if("restart") - if(config.allow_vote_restart || admin) - initiate_vote("restart",usr.key) - if("gamemode") - if(config.allow_vote_mode || admin) - initiate_vote("gamemode",usr.key) - if("crew_transfer") - if(config.allow_vote_restart || admin) - initiate_vote("crew_transfer",usr.key) - if("custom") - if(admin) - initiate_vote("custom",usr.key) - else - submit_vote(usr.ckey, round(text2num(href_list["vote"]))) - update_panel(usr.client) - return - usr.vote() - - -/mob/verb/vote() - set category = "OOC" - set name = "Vote" - - if(SSvote) - SSvote.browse_to(client) +SUBSYSTEM_DEF(vote) + name = "Vote" + wait = 10 + flags = SS_KEEP_TIMING|SS_NO_INIT + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + offline_implications = "Votes (Endround shuttle) will no longer function. Shuttle call recommended." + + var/initiator = null + var/started_time = null + var/time_remaining = 0 + var/mode = null + var/question = null + var/list/choices = list() + var/list/voted = list() + var/list/voting = list() + var/list/current_votes = list() + var/list/round_voters = list() + var/auto_muted = 0 + +/datum/controller/subsystem/vote/fire() + if(mode) + // No more change mode votes after the game has started. + if(mode == "gamemode" && SSticker.current_state >= GAME_STATE_SETTING_UP) + to_chat(world, "Voting aborted due to game start.") + reset() + return + + // Calculate how much time is remaining by comparing current time, to time of vote start, + // plus vote duration + time_remaining = round((started_time + config.vote_period - world.time)/10) + + if(time_remaining < 0) + result() + for(var/client/C in voting) + if(C) + C << browse(null,"window=vote") + reset() + else + for(var/client/C in voting) + update_panel(C) + CHECK_TICK + +/datum/controller/subsystem/vote/proc/autotransfer() + initiate_vote("crew_transfer","the server") + +/datum/controller/subsystem/vote/proc/reset() + initiator = null + time_remaining = 0 + mode = null + question = null + choices.Cut() + voted.Cut() + voting.Cut() + current_votes.Cut() + + if(auto_muted && !config.ooc_allowed && !(config.auto_toggle_ooc_during_round && SSticker.current_state == GAME_STATE_PLAYING)) + auto_muted = 0 + config.ooc_allowed = !( config.ooc_allowed ) + to_chat(world, "The OOC channel has been automatically enabled due to vote end.") + log_admin("OOC was toggled automatically due to vote end.") + message_admins("OOC has been toggled on automatically.") + + +/datum/controller/subsystem/vote/proc/get_result() + var/greatest_votes = 0 + var/total_votes = 0 + var/list/sorted_choices = list() + var/sorted_highest + var/sorted_votes = -1 + //get the highest number of votes, while also sorting the list + while(choices.len) + // This is a very inefficient sorting method, but that's okay + for(var/option in choices) + var/votes = choices[option] + if(sorted_votes < votes) + sorted_highest = option + sorted_votes = votes + if(votes > greatest_votes) + greatest_votes = votes + sorted_votes = -1 + total_votes += choices[sorted_highest] + sorted_choices[sorted_highest] = choices[sorted_highest] || 0 + choices -= sorted_highest + choices = sorted_choices + //default-vote for everyone who didn't vote + if(!config.vote_no_default && choices.len) + var/non_voters = (GLOB.clients.len - total_votes) + if(non_voters > 0) + if(mode == "restart") + choices["Continue Playing"] += non_voters + if(choices["Continue Playing"] >= greatest_votes) + greatest_votes = choices["Continue Playing"] + else if(mode == "gamemode") + if(GLOB.master_mode in choices) + choices[GLOB.master_mode] += non_voters + if(choices[GLOB.master_mode] >= greatest_votes) + greatest_votes = choices[GLOB.master_mode] + else if(mode == "crew_transfer") + var/factor = 0.5 + switch(world.time / (10 * 60)) // minutes + if(0 to 60) + factor = 0.5 + if(61 to 120) + factor = 0.8 + if(121 to 240) + factor = 1 + if(241 to 300) + factor = 1.2 + else + factor = 1.4 + choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) + to_chat(world, "Crew Transfer Factor: [factor]") + greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"]) + + + //get all options with that many votes and return them in a list + . = list() + if(greatest_votes) + for(var/option in choices) + if(choices[option] == greatest_votes) + . += option + return . + +/datum/controller/subsystem/vote/proc/announce_result() + var/list/winners = get_result() + var/text + if(winners.len > 0) + if(winners.len > 1) + if(mode != "gamemode" || SSticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes + text = "Vote Tied Between:\n" + for(var/option in winners) + text += "\t[option]\n" + . = pick(winners) + + for(var/key in current_votes) + if(choices[current_votes[key]] == .) + round_voters += key // Keep track of who voted for the winning round. + if(mode == "gamemode" && (. == "extended" || SSticker.hide_mode == 0)) // Announce Extended gamemode, but not other gamemodes + text += "Vote Result: [.] ([choices[.]] vote\s)" + else + if(mode == "custom") + // Completely replace text to show all results in custom votes + text = "[question]\n" + for(var/option in winners) + text += "\t[option]: [choices[option]] vote\s\n" + for(var/option in (choices-winners)) + text += "\t[option]: [choices[option]] vote\s\n" + else if(mode != "gamemode") + text += "Vote Result: [.] ([choices[.]] vote\s)" + else + text += "The vote has ended." // What will be shown if it is a gamemode vote that isn't extended + + else + text += "Vote Result: Inconclusive - No Votes!" + log_vote(text) + to_chat(world, "[text]") + return . + +/datum/controller/subsystem/vote/proc/result() + . = announce_result() + var/restart = 0 + if(.) + switch(mode) + if("restart") + if(. == "Restart Round") + restart = 1 + if("gamemode") + if(GLOB.master_mode != .) + world.save_mode(.) + if(SSticker && SSticker.mode) + restart = 1 + else + GLOB.master_mode = . + if(!SSticker.ticker_going) + SSticker.ticker_going = TRUE + to_chat(world, "The round will start soon.") + if("crew_transfer") + if(. == "Initiate Crew Transfer") + init_shift_change(null, 1) + + + if(restart) + world.Reboot("Restart vote successful.", "end_error", "restart vote") + + return . + +/datum/controller/subsystem/vote/proc/submit_vote(var/ckey, var/vote) + if(mode) + if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder) + return 0 + if(current_votes[ckey]) + choices[choices[current_votes[ckey]]]-- + if(vote && 1<=vote && vote<=choices.len) + voted += usr.ckey + choices[choices[vote]]++ //check this + current_votes[ckey] = vote + return vote + return 0 + +/datum/controller/subsystem/vote/proc/initiate_vote(var/vote_type, var/initiator_key) + if(!mode) + if(started_time != null && !check_rights(R_ADMIN)) + var/next_allowed_time = (started_time + config.vote_delay) + if(next_allowed_time > world.time) + return 0 + + reset() + switch(vote_type) + if("restart") + choices.Add("Restart Round","Continue Playing") + if("gamemode") + if(SSticker.current_state >= 2) + return 0 + choices.Add(config.votable_modes) + if("crew_transfer") + if(check_rights(R_ADMIN|R_MOD)) + if(SSticker.current_state <= 2) + return 0 + question = "End the shift?" + choices.Add("Initiate Crew Transfer", "Continue The Round") + else + if(SSticker.current_state <= 2) + return 0 + question = "End the shift?" + choices.Add("Initiate Crew Transfer", "Continue The Round") + if("custom") + question = html_encode(input(usr,"What is the vote for?") as text|null) + if(!question) return 0 + for(var/i=1,i<=10,i++) + var/option = capitalize(html_encode(input(usr,"Please enter an option or hit cancel to finish") as text|null)) + if(!option || mode || !usr.client) break + choices.Add(option) + else + return 0 + mode = vote_type + initiator = initiator_key + started_time = world.time + var/text = "[capitalize(mode)] vote started by [initiator]." + if(mode == "custom") + text += "\n[question]" + if(usr) + log_admin("[capitalize(mode)] ([question]) vote started by [key_name(usr)].") + else if(usr) + log_admin("[capitalize(mode)] vote started by [key_name(usr)].") + + log_vote(text) + to_chat(world, {"[text] + Click here or type vote to place your vote. + You have [config.vote_period/10] seconds to vote."}) + switch(vote_type) + if("crew_transfer") + world << sound('sound/ambience/alarm4.ogg') + if("gamemode") + world << sound('sound/ambience/alarm4.ogg') + if("custom") + world << sound('sound/ambience/alarm4.ogg') + if(mode == "gamemode" && SSticker.ticker_going) + SSticker.ticker_going = FALSE + to_chat(world, "Round start has been delayed.") + if(mode == "crew_transfer" && config.ooc_allowed) + auto_muted = 1 + config.ooc_allowed = !( config.ooc_allowed ) + to_chat(world, "The OOC channel has been automatically disabled due to a crew transfer vote.") + log_admin("OOC was toggled automatically due to crew_transfer vote.") + message_admins("OOC has been toggled off automatically.") + if(mode == "gamemode" && config.ooc_allowed) + auto_muted = 1 + config.ooc_allowed = !( config.ooc_allowed ) + to_chat(world, "The OOC channel has been automatically disabled due to the gamemode vote.") + log_admin("OOC was toggled automatically due to gamemode vote.") + message_admins("OOC has been toggled off automatically.") + if(mode == "custom" && config.ooc_allowed) + auto_muted = 1 + config.ooc_allowed = !( config.ooc_allowed ) + to_chat(world, "The OOC channel has been automatically disabled due to a custom vote.") + log_admin("OOC was toggled automatically due to custom vote.") + message_admins("OOC has been toggled off automatically.") + + time_remaining = round(config.vote_period/10) + return 1 + return 0 + +/datum/controller/subsystem/vote/proc/browse_to(var/client/C) + if(!C) + return + var/admin = check_rights(R_ADMIN, 0, user = C.mob) + voting |= C + + var/dat = {""} + if(mode) + dat += "
    [vote_html(C)]

    " + if(admin) + dat += "(Cancel Vote) " + else + dat += "

    Start a vote:



    " + var/datum/browser/popup = new(C.mob, "vote", "Voting Panel", nref=src) + popup.set_content(dat) + popup.open() + +/datum/controller/subsystem/vote/proc/update_panel(var/client/C) + C << output(url_encode(vote_html(C)), "vote.browser:update_vote_div") + +/datum/controller/subsystem/vote/proc/vote_html(var/client/C) + . = "" + if(question) + . += "

    Vote: '[question]'

    " + else + . += "

    Vote: [capitalize(mode)]

    " + . += "Time Left: [time_remaining] s
    " + + +/datum/controller/subsystem/vote/Topic(href,href_list[],hsrc) + if(!usr || !usr.client) + return //not necessary but meh...just in-case somebody does something stupid + var/admin = check_rights(R_ADMIN,0) + if(href_list["close"]) + voting -= usr.client + return + switch(href_list["vote"]) + if("open") + // vote proc will automatically get called after this switch ends + if("cancel") + if(admin && mode) + var/votedesc = capitalize(mode) + if(mode == "custom") + votedesc += " ([question])" + admin_log_and_message_admins("cancelled the running [votedesc] vote.") + reset() + if("toggle_restart") + if(admin) + config.allow_vote_restart = !config.allow_vote_restart + if("toggle_gamemode") + if(admin) + config.allow_vote_mode = !config.allow_vote_mode + if("restart") + if(config.allow_vote_restart || admin) + initiate_vote("restart",usr.key) + if("gamemode") + if(config.allow_vote_mode || admin) + initiate_vote("gamemode",usr.key) + if("crew_transfer") + if(config.allow_vote_restart || admin) + initiate_vote("crew_transfer",usr.key) + if("custom") + if(admin) + initiate_vote("custom",usr.key) + else + submit_vote(usr.ckey, round(text2num(href_list["vote"]))) + update_panel(usr.client) + return + usr.vote() + + +/mob/verb/vote() + set category = "OOC" + set name = "Vote" + + if(SSvote) + SSvote.browse_to(client) diff --git a/code/controllers/subsystem/weather.dm b/code/controllers/subsystem/weather.dm index 4b898df0f11..7b08bd3ca48 100644 --- a/code/controllers/subsystem/weather.dm +++ b/code/controllers/subsystem/weather.dm @@ -9,6 +9,7 @@ SUBSYSTEM_DEF(weather) flags = SS_BACKGROUND wait = 10 runlevels = RUNLEVEL_GAME + offline_implications = "Ash storms will no longer trigger. No immediate action is needed." var/list/processing = list() var/list/eligible_zlevels = list() var/list/next_hit_by_zlevel = list() //Used by barometers to know when the next storm is coming @@ -80,4 +81,4 @@ SUBSYSTEM_DEF(weather) if((z in W.impacted_z_levels) && W.area_type == active_area.type) A = W break - return A \ No newline at end of file + return A diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index f694b6d2e75..3eaed85c303 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -1,104 +1,104 @@ -//TODO: rewrite and standardise all controller datums to the datum/controller type -//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done - - -/client/proc/restart_controller(controller in list("Master", "Failsafe")) - set category = "Debug" - set name = "Restart Controller" - set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" - - if(!holder) - return - switch(controller) - if("Master") - Recreate_MC() - feedback_add_details("admin_verb","RMaster") - if("Failsafe") - new /datum/controller/failsafe() - feedback_add_details("admin_verb","RFailsafe") - - message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") - -/client/proc/debug_controller(controller in list("failsafe", "Master", "Ticker", "Air", "Jobs", "Sun", "Radio", "Configuration", "pAI", - "Cameras", "Garbage", "Event", "Alarm", "Nano", "Vote", "Fires", - "Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server","Input")) - set category = "Debug" - set name = "Debug Controller" - set desc = "Debug the various periodic loop controllers for the game (be careful!)" - - if(!holder) return - switch(controller) - if("failsafe") - debug_variables(Failsafe) - feedback_add_details("admin_verb", "dfailsafe") - if("Master") - debug_variables(Master) - feedback_add_details("admin_verb","Dsmc") - if("Ticker") - debug_variables(SSticker) - feedback_add_details("admin_verb","DTicker") - if("Air") - debug_variables(SSair) - feedback_add_details("admin_verb","DAir") - if("Jobs") - debug_variables(SSjobs) - feedback_add_details("admin_verb","DJobs") - if("Sun") - debug_variables(SSsun) - feedback_add_details("admin_verb","DSun") - if("Radio") - debug_variables(SSradio) - feedback_add_details("admin_verb","DRadio") - if("Configuration") - debug_variables(config) - feedback_add_details("admin_verb","DConf") - if("pAI") - debug_variables(paiController) - feedback_add_details("admin_verb","DpAI") - if("Cameras") - debug_variables(cameranet) - feedback_add_details("admin_verb","DCameras") - if("Garbage") - debug_variables(SSgarbage) - feedback_add_details("admin_verb","DGarbage") - if("Event") - debug_variables(SSevents) - feedback_add_details("admin_verb","DEvent") - if("Alarm") - debug_variables(SSalarms) - feedback_add_details("admin_verb", "DAlarm") - if("Nano") - debug_variables(SSnanoui) - feedback_add_details("admin_verb","DNano") - if("Vote") - debug_variables(SSvote) - feedback_add_details("admin_verb","DVote") - if("Fires") - debug_variables(SSfires) - feedback_add_details("admin_verb","DFires") - if("Mob") - debug_variables(SSmobs) - feedback_add_details("admin_verb","DMob") - if("NPC Pool") - debug_variables(SSnpcpool) - feedback_add_details("admin_verb","DNPCPool") - if("Shuttle") - debug_variables(SSshuttle) - feedback_add_details("admin_verb","DShuttle") - if("Timer") - debug_variables(SStimer) - feedback_add_details("admin_verb","DTimer") - if("Weather") - debug_variables(SSweather) - feedback_add_details("admin_verb","DWeather") - if("Space") - debug_variables(space_manager) - feedback_add_details("admin_verb","DSpace") - if("Mob Hunt Server") - debug_variables(SSmob_hunt) - feedback_add_details("admin_verb","DMobHuntServer") - if("Input") - debug_variables(SSinput) - feedback_add_details("admin_verb","DInput") - - message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") \ No newline at end of file +//TODO: rewrite and standardise all controller datums to the datum/controller type +//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done + + +/client/proc/restart_controller(controller in list("Master", "Failsafe")) + set category = "Debug" + set name = "Restart Controller" + set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" + + if(!holder) + return + switch(controller) + if("Master") + Recreate_MC() + feedback_add_details("admin_verb","RMaster") + if("Failsafe") + new /datum/controller/failsafe() + feedback_add_details("admin_verb","RFailsafe") + + message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") + +/client/proc/debug_controller(controller in list("failsafe", "Master", "Ticker", "Air", "Jobs", "Sun", "Radio", "Configuration", "pAI", + "Cameras", "Garbage", "Event", "Alarm", "Nano", "Vote", "Fires", + "Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server","Input")) + set category = "Debug" + set name = "Debug Controller" + set desc = "Debug the various periodic loop controllers for the game (be careful!)" + + if(!holder) return + switch(controller) + if("failsafe") + debug_variables(Failsafe) + feedback_add_details("admin_verb", "dfailsafe") + if("Master") + debug_variables(Master) + feedback_add_details("admin_verb","Dsmc") + if("Ticker") + debug_variables(SSticker) + feedback_add_details("admin_verb","DTicker") + if("Air") + debug_variables(SSair) + feedback_add_details("admin_verb","DAir") + if("Jobs") + debug_variables(SSjobs) + feedback_add_details("admin_verb","DJobs") + if("Sun") + debug_variables(SSsun) + feedback_add_details("admin_verb","DSun") + if("Radio") + debug_variables(SSradio) + feedback_add_details("admin_verb","DRadio") + if("Configuration") + debug_variables(config) + feedback_add_details("admin_verb","DConf") + if("pAI") + debug_variables(GLOB.paiController) + feedback_add_details("admin_verb","DpAI") + if("Cameras") + debug_variables(GLOB.cameranet) + feedback_add_details("admin_verb","DCameras") + if("Garbage") + debug_variables(SSgarbage) + feedback_add_details("admin_verb","DGarbage") + if("Event") + debug_variables(SSevents) + feedback_add_details("admin_verb","DEvent") + if("Alarm") + debug_variables(SSalarms) + feedback_add_details("admin_verb", "DAlarm") + if("Nano") + debug_variables(SSnanoui) + feedback_add_details("admin_verb","DNano") + if("Vote") + debug_variables(SSvote) + feedback_add_details("admin_verb","DVote") + if("Fires") + debug_variables(SSfires) + feedback_add_details("admin_verb","DFires") + if("Mob") + debug_variables(SSmobs) + feedback_add_details("admin_verb","DMob") + if("NPC Pool") + debug_variables(SSnpcpool) + feedback_add_details("admin_verb","DNPCPool") + if("Shuttle") + debug_variables(SSshuttle) + feedback_add_details("admin_verb","DShuttle") + if("Timer") + debug_variables(SStimer) + feedback_add_details("admin_verb","DTimer") + if("Weather") + debug_variables(SSweather) + feedback_add_details("admin_verb","DWeather") + if("Space") + debug_variables(GLOB.space_manager) + feedback_add_details("admin_verb","DSpace") + if("Mob Hunt Server") + debug_variables(SSmob_hunt) + feedback_add_details("admin_verb","DMobHuntServer") + if("Input") + debug_variables(SSinput) + feedback_add_details("admin_verb","DInput") + + message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index 3494d7b20d5..8e1a9d8bcce 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -1,320 +1,320 @@ -var/global/const/base_law_type = /datum/ai_laws/nanotrasen - -/datum/ai_law - var/law = "" - var/index = 0 - -/datum/ai_law/New(law, index) - src.law = law - src.index = index - -/datum/ai_law/proc/get_index() - return index - -/datum/ai_law/ion/get_index() - return ionnum() - -/datum/ai_law/zero/get_index() - return 0 - -/datum/ai_law/sixsixsix/get_index() - return 666 - - -/datum/ai_laws - var/name = "Unknown Laws" - var/law_header = "Prime Directives" - var/selectable = 0 - var/default = 0 - var/datum/ai_law/zero/zeroth_law = null - var/datum/ai_law/zero/zeroth_law_borg = null - var/list/datum/ai_law/inherent_laws = list() - var/list/datum/ai_law/supplied_laws = list() - var/list/datum/ai_law/ion/ion_laws = list() - var/list/datum/ai_law/sixsixsix/devil_laws = list() - var/list/datum/ai_law/sorted_laws = list() - - var/state_zeroth = 0 - var/list/state_devil = list() - var/list/state_ion = list() - var/list/state_inherent = list() - var/list/state_supplied = list() - -/datum/ai_laws/New() - ..() - sort_laws() - -/* General ai_law functions */ -/datum/ai_laws/proc/all_laws() - sort_laws() - return sorted_laws - -/datum/ai_laws/proc/laws_to_state() - sort_laws() - var/list/statements = new() - for(var/datum/ai_law/law in sorted_laws) - if(get_state_law(law)) - statements += law - - return statements - -/datum/ai_laws/proc/sort_laws() - if(sorted_laws.len) - return - - for(var/ion_law in ion_laws) - sorted_laws += ion_law - - for(var/evil_law in devil_laws) - sorted_laws += evil_law - - if(zeroth_law) - sorted_laws += zeroth_law - - var/index = 1 - for(var/datum/ai_law/inherent_law in inherent_laws) - inherent_law.index = index++ - if(supplied_laws.len < inherent_law.index || !istype(supplied_laws[inherent_law.index], /datum/ai_law)) - sorted_laws += inherent_law - - for(var/datum/ai_law/AL in supplied_laws) - if(istype(AL)) - sorted_laws += AL - -/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1) - // Add directly to laws to avoid log-spam - S.sync_zeroth(zeroth_law, zeroth_law_borg) - - if(full_sync || ion_laws.len) - S.laws.clear_ion_laws() - if(full_sync || inherent_laws.len) - S.laws.clear_inherent_laws() - if(full_sync || supplied_laws.len) - S.laws.clear_supplied_laws() - - for(var/datum/ai_law/law in ion_laws) - S.laws.add_ion_law(law.law) - for(var/datum/ai_law/law in inherent_laws) - S.laws.add_inherent_law(law.law) - for(var/datum/ai_law/law in supplied_laws) - if(law) - S.laws.add_supplied_law(law.index, law.law) - - -/mob/living/silicon/proc/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) - if(!is_special_character(src) || mind.original != src) - if(zeroth_law_borg) - laws.set_zeroth_law(zeroth_law_borg.law) - else if(zeroth_law) - laws.set_zeroth_law(zeroth_law.law) - -/mob/living/silicon/ai/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) - if(zeroth_law) - laws.set_zeroth_law(zeroth_law.law, zeroth_law_borg ? zeroth_law_borg.law : null) - -/**************** -* Add Laws * -****************/ -/datum/ai_laws/proc/set_zeroth_law(var/law, var/law_borg = null) - if(!law) - return - - zeroth_law = new(law) - if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO - zeroth_law_borg = new(law_borg) - else - zeroth_law_borg = null - sorted_laws.Cut() - -/datum/ai_laws/proc/set_sixsixsix_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in devil_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/sixsixsix(law) - devil_laws += new_law - if(state_devil.len < devil_laws.len) - state_devil += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_ion_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in ion_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/ion(law) - ion_laws += new_law - if(state_ion.len < ion_laws.len) - state_ion += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_inherent_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in inherent_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/inherent(law) - inherent_laws += new_law - if(state_inherent.len < inherent_laws.len) - state_inherent += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_supplied_law(var/number, var/law) - if(!law) - return - - if(supplied_laws.len >= number) - var/datum/ai_law/existing_law = supplied_laws[number] - if(existing_law && existing_law.law == law) - return - - if(supplied_laws.len >= number && supplied_laws[number]) - delete_law(supplied_laws[number]) - - while(src.supplied_laws.len < number) - src.supplied_laws += "" - if(state_supplied.len < supplied_laws.len) - state_supplied += 1 - - var/new_law = new/datum/ai_law/supplied(law, number) - supplied_laws[number] = new_law - if(state_supplied.len < supplied_laws.len) - state_supplied += 1 - - sorted_laws.Cut() - -/**************** -* Remove Laws * -*****************/ -/datum/ai_laws/proc/delete_law(var/datum/ai_law/law) - if(istype(law)) - law.delete_law(src) - -/datum/ai_law/proc/delete_law(var/datum/ai_laws/laws) - -/datum/ai_law/zero/delete_law(var/datum/ai_laws/laws) - laws.clear_zeroth_laws() - -/datum/ai_law/ion/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.ion_laws, laws.state_ion, src) - -/datum/ai_law/sixsixsix/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.devil_laws, laws.state_devil, src) - -/datum/ai_law/inherent/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.inherent_laws, laws.state_inherent, src) - -/datum/ai_law/supplied/delete_law(var/datum/ai_laws/laws) - var/index = laws.supplied_laws.Find(src) - if(index) - laws.supplied_laws[index] = "" - laws.state_supplied[index] = 1 - -/datum/ai_laws/proc/internal_delete_law(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) - var/index = laws.Find(law) - if(index) - laws -= law - for(index, index < state.len, index++) - state[index] = state[index+1] - sorted_laws.Cut() - -/**************** -* Clear Laws * -****************/ -/datum/ai_laws/proc/clear_zeroth_laws() - zeroth_law = null - zeroth_law_borg = null - -/datum/ai_laws/proc/clear_sixsixsix_laws() - devil_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_ion_laws() - ion_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_inherent_laws() - inherent_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_supplied_laws() - supplied_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/show_laws(var/who) - sort_laws() - for(var/datum/ai_law/law in sorted_laws) - if(law == zeroth_law_borg) - continue - if(law == zeroth_law) - to_chat(who, "[law.get_index()]. [law.law]") - else - to_chat(who, "[law.get_index()]. [law.law]") - -/******************** -* Stating Laws * -********************/ -/******** -* Get * -********/ -/datum/ai_laws/proc/get_state_law(var/datum/ai_law/law) - return law.get_state_law(src) - -/datum/ai_law/proc/get_state_law(var/datum/ai_laws/laws) - -/datum/ai_law/zero/get_state_law(var/datum/ai_laws/laws) - if(src == laws.zeroth_law) - return laws.state_zeroth - -/datum/ai_law/ion/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.ion_laws, laws.state_ion, src) - -/datum/ai_law/inherent/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.inherent_laws, laws.state_inherent, src) - -/datum/ai_law/supplied/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.supplied_laws, laws.state_supplied, src) - -/datum/ai_laws/proc/get_state_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) - var/index = laws.Find(law) - if(index) - return state[index] - return 0 - -/******** -* Set * -********/ -/datum/ai_laws/proc/set_state_law(var/datum/ai_law/law, var/state) - law.set_state_law(src, state) - -/datum/ai_law/proc/set_state_law(var/datum/ai_law/law, var/state) - -/datum/ai_law/zero/set_state_law(var/datum/ai_laws/laws, var/state) - if(src == laws.zeroth_law) - laws.state_zeroth = state - -/datum/ai_law/ion/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.ion_laws, laws.state_ion, src, state) - -/datum/ai_law/inherent/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.inherent_laws, laws.state_inherent, src, state) - -/datum/ai_law/supplied/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.supplied_laws, laws.state_supplied, src, state) - -/datum/ai_laws/proc/set_state_law_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law, var/do_state) - var/index = laws.Find(law) - if(index) - state[index] = do_state +#define BASE_LAW_TYPE /datum/ai_laws/nanotrasen + +/datum/ai_law + var/law = "" + var/index = 0 + +/datum/ai_law/New(law, index) + src.law = law + src.index = index + +/datum/ai_law/proc/get_index() + return index + +/datum/ai_law/ion/get_index() + return ionnum() + +/datum/ai_law/zero/get_index() + return 0 + +/datum/ai_law/sixsixsix/get_index() + return 666 + + +/datum/ai_laws + var/name = "Unknown Laws" + var/law_header = "Prime Directives" + var/selectable = 0 + var/default = 0 + var/datum/ai_law/zero/zeroth_law = null + var/datum/ai_law/zero/zeroth_law_borg = null + var/list/datum/ai_law/inherent_laws = list() + var/list/datum/ai_law/supplied_laws = list() + var/list/datum/ai_law/ion/ion_laws = list() + var/list/datum/ai_law/sixsixsix/devil_laws = list() + var/list/datum/ai_law/sorted_laws = list() + + var/state_zeroth = 0 + var/list/state_devil = list() + var/list/state_ion = list() + var/list/state_inherent = list() + var/list/state_supplied = list() + +/datum/ai_laws/New() + ..() + sort_laws() + +/* General ai_law functions */ +/datum/ai_laws/proc/all_laws() + sort_laws() + return sorted_laws + +/datum/ai_laws/proc/laws_to_state() + sort_laws() + var/list/statements = new() + for(var/datum/ai_law/law in sorted_laws) + if(get_state_law(law)) + statements += law + + return statements + +/datum/ai_laws/proc/sort_laws() + if(sorted_laws.len) + return + + if(zeroth_law) + sorted_laws += zeroth_law + + for(var/ion_law in ion_laws) + sorted_laws += ion_law + + for(var/evil_law in devil_laws) + sorted_laws += evil_law + + var/index = 1 + for(var/datum/ai_law/inherent_law in inherent_laws) + inherent_law.index = index++ + if(supplied_laws.len < inherent_law.index || !istype(supplied_laws[inherent_law.index], /datum/ai_law)) + sorted_laws += inherent_law + + for(var/datum/ai_law/AL in supplied_laws) + if(istype(AL)) + sorted_laws += AL + +/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1) + // Add directly to laws to avoid log-spam + S.sync_zeroth(zeroth_law, zeroth_law_borg) + + if(full_sync || ion_laws.len) + S.laws.clear_ion_laws() + if(full_sync || inherent_laws.len) + S.laws.clear_inherent_laws() + if(full_sync || supplied_laws.len) + S.laws.clear_supplied_laws() + + for(var/datum/ai_law/law in ion_laws) + S.laws.add_ion_law(law.law) + for(var/datum/ai_law/law in inherent_laws) + S.laws.add_inherent_law(law.law) + for(var/datum/ai_law/law in supplied_laws) + if(law) + S.laws.add_supplied_law(law.index, law.law) + + +/mob/living/silicon/proc/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) + if(!is_special_character(src) || mind.original != src) + if(zeroth_law_borg) + laws.set_zeroth_law(zeroth_law_borg.law) + else if(zeroth_law) + laws.set_zeroth_law(zeroth_law.law) + +/mob/living/silicon/ai/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) + if(zeroth_law) + laws.set_zeroth_law(zeroth_law.law, zeroth_law_borg ? zeroth_law_borg.law : null) + +/**************** +* Add Laws * +****************/ +/datum/ai_laws/proc/set_zeroth_law(var/law, var/law_borg = null) + if(!law) + return + + zeroth_law = new(law) + if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO + zeroth_law_borg = new(law_borg) + else + zeroth_law_borg = null + sorted_laws.Cut() + +/datum/ai_laws/proc/set_sixsixsix_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in devil_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/sixsixsix(law) + devil_laws += new_law + if(state_devil.len < devil_laws.len) + state_devil += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_ion_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in ion_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/ion(law) + ion_laws += new_law + if(state_ion.len < ion_laws.len) + state_ion += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_inherent_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in inherent_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/inherent(law) + inherent_laws += new_law + if(state_inherent.len < inherent_laws.len) + state_inherent += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_supplied_law(var/number, var/law) + if(!law) + return + + if(supplied_laws.len >= number) + var/datum/ai_law/existing_law = supplied_laws[number] + if(existing_law && existing_law.law == law) + return + + if(supplied_laws.len >= number && supplied_laws[number]) + delete_law(supplied_laws[number]) + + while(src.supplied_laws.len < number) + src.supplied_laws += "" + if(state_supplied.len < supplied_laws.len) + state_supplied += 1 + + var/new_law = new/datum/ai_law/supplied(law, number) + supplied_laws[number] = new_law + if(state_supplied.len < supplied_laws.len) + state_supplied += 1 + + sorted_laws.Cut() + +/**************** +* Remove Laws * +*****************/ +/datum/ai_laws/proc/delete_law(var/datum/ai_law/law) + if(istype(law)) + law.delete_law(src) + +/datum/ai_law/proc/delete_law(var/datum/ai_laws/laws) + +/datum/ai_law/zero/delete_law(var/datum/ai_laws/laws) + laws.clear_zeroth_laws() + +/datum/ai_law/ion/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.ion_laws, laws.state_ion, src) + +/datum/ai_law/sixsixsix/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.devil_laws, laws.state_devil, src) + +/datum/ai_law/inherent/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.inherent_laws, laws.state_inherent, src) + +/datum/ai_law/supplied/delete_law(var/datum/ai_laws/laws) + var/index = laws.supplied_laws.Find(src) + if(index) + laws.supplied_laws[index] = "" + laws.state_supplied[index] = 1 + +/datum/ai_laws/proc/internal_delete_law(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) + var/index = laws.Find(law) + if(index) + laws -= law + for(index, index < state.len, index++) + state[index] = state[index+1] + sorted_laws.Cut() + +/**************** +* Clear Laws * +****************/ +/datum/ai_laws/proc/clear_zeroth_laws() + zeroth_law = null + zeroth_law_borg = null + +/datum/ai_laws/proc/clear_sixsixsix_laws() + devil_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_ion_laws() + ion_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_inherent_laws() + inherent_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_supplied_laws() + supplied_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/show_laws(var/who) + sort_laws() + for(var/datum/ai_law/law in sorted_laws) + if(law == zeroth_law_borg) + continue + if(law == zeroth_law) + to_chat(who, "[law.get_index()]. [law.law]") + else + to_chat(who, "[law.get_index()]. [law.law]") + +/******************** +* Stating Laws * +********************/ +/******** +* Get * +********/ +/datum/ai_laws/proc/get_state_law(var/datum/ai_law/law) + return law.get_state_law(src) + +/datum/ai_law/proc/get_state_law(var/datum/ai_laws/laws) + +/datum/ai_law/zero/get_state_law(var/datum/ai_laws/laws) + if(src == laws.zeroth_law) + return laws.state_zeroth + +/datum/ai_law/ion/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.ion_laws, laws.state_ion, src) + +/datum/ai_law/inherent/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.inherent_laws, laws.state_inherent, src) + +/datum/ai_law/supplied/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.supplied_laws, laws.state_supplied, src) + +/datum/ai_laws/proc/get_state_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) + var/index = laws.Find(law) + if(index) + return state[index] + return 0 + +/******** +* Set * +********/ +/datum/ai_laws/proc/set_state_law(var/datum/ai_law/law, var/state) + law.set_state_law(src, state) + +/datum/ai_law/proc/set_state_law(var/datum/ai_law/law, var/state) + +/datum/ai_law/zero/set_state_law(var/datum/ai_laws/laws, var/state) + if(src == laws.zeroth_law) + laws.state_zeroth = state + +/datum/ai_law/ion/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.ion_laws, laws.state_ion, src, state) + +/datum/ai_law/inherent/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.inherent_laws, laws.state_inherent, src, state) + +/datum/ai_law/supplied/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.supplied_laws, laws.state_supplied, src, state) + +/datum/ai_laws/proc/set_state_law_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law, var/do_state) + var/index = laws.Find(law) + if(index) + state[index] = do_state diff --git a/code/datums/beam.dm b/code/datums/beam.dm index ac585d07197..86f816662c0 100644 --- a/code/datums/beam.dm +++ b/code/datums/beam.dm @@ -133,4 +133,4 @@ var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time) spawn(0) newbeam.Start() - return newbeam \ No newline at end of file + return newbeam diff --git a/code/datums/browser.dm b/code/datums/browser.dm index 85f8e52e89d..76be0b02a12 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -179,4 +179,4 @@ if(src && src.mob) // to_chat(world, "[src] was [src.mob.machine], setting to null") src.mob.unset_machine() - return \ No newline at end of file + return diff --git a/code/datums/cache/air_alarm.dm b/code/datums/cache/air_alarm.dm index 662b0958d5c..2edc0792a34 100644 --- a/code/datums/cache/air_alarm.dm +++ b/code/datums/cache/air_alarm.dm @@ -1,4 +1,4 @@ -var/global/datum/repository/air_alarm/air_alarm_repository = new() +GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new()) /datum/repository/air_alarm/proc/air_alarm_data(var/list/monitored_alarms, var/refresh = 0, var/obj/machinery/alarm/passed_alarm) var/alarms[0] @@ -11,13 +11,13 @@ var/global/datum/repository/air_alarm/air_alarm_repository = new() if(!refresh) return cache_entry.data - if(SSticker && SSticker.current_state < GAME_STATE_PLAYING && istype(passed_alarm)) // Generating the list for the first time as the game hasn't started - no need to run through the machines list everything every time + if(SSticker && SSticker.current_state < GAME_STATE_PLAYING && istype(passed_alarm)) // Generating the list for the first time as the game hasn't started - no need to run through the machines list everything every time alarms = cache_entry.data // Don't deleate the list if(is_station_contact(passed_alarm.z) && passed_alarm.remote_control) // Still need sanity checks - alarms[++alarms.len] = passed_alarm.get_nano_data_console() + alarms[++alarms.len] = passed_alarm.get_nano_data_console() else for(var/obj/machinery/alarm/alarm in (monitored_alarms ? monitored_alarms : GLOB.air_alarms)) // Generating the whole list again is a bad habit but I can't be bothered to fix it right now - if(!monitored_alarms && !is_station_contact(alarm.z)) + if(!monitored_alarms && !is_station_contact(alarm.z)) continue if(!alarm.remote_control) continue diff --git a/code/datums/cache/apc.dm b/code/datums/cache/apc.dm index 29a2574f6e0..318fd654fe3 100644 --- a/code/datums/cache/apc.dm +++ b/code/datums/cache/apc.dm @@ -1,4 +1,4 @@ -var/global/datum/repository/apc/apc_repository = new() +GLOBAL_DATUM_INIT(apc_repository, /datum/repository/apc, new()) /datum/repository/apc/proc/apc_data(datum/powernet/powernet) var/apcData[0] @@ -25,4 +25,4 @@ var/global/datum/repository/apc/apc_repository = new() cache_entry.timestamp = world.time + 5 SECONDS cache_entry.data = apcData - return apcData \ No newline at end of file + return apcData diff --git a/code/datums/cache/cache.dm b/code/datums/cache/cache.dm index f401e54517a..09d8670e29e 100644 --- a/code/datums/cache/cache.dm +++ b/code/datums/cache/cache.dm @@ -3,4 +3,4 @@ var/list/data = list() /datum/repository - var/cache_data \ No newline at end of file + var/cache_data diff --git a/code/datums/cache/crew.dm b/code/datums/cache/crew.dm index b30b5c57889..21ec5ebcc79 100644 --- a/code/datums/cache/crew.dm +++ b/code/datums/cache/crew.dm @@ -1,4 +1,4 @@ -var/global/datum/repository/crew/crew_repository = new() +GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new()) /datum/repository/crew/New() cache_data = list() diff --git a/code/datums/cache/powermonitor.dm b/code/datums/cache/powermonitor.dm index 5112d58ad29..606dd500eda 100644 --- a/code/datums/cache/powermonitor.dm +++ b/code/datums/cache/powermonitor.dm @@ -1,8 +1,8 @@ -var/global/datum/repository/powermonitor/powermonitor_repository = new() +GLOBAL_DATUM_INIT(powermonitor_repository, /datum/repository/powermonitor, new()) /datum/repository/powermonitor/proc/powermonitor_data(var/refresh = 0) var/pMonData[0] - + var/datum/cache_entry/cache_entry = cache_data if(!cache_entry) cache_entry = new/datum/cache_entry @@ -10,15 +10,15 @@ var/global/datum/repository/powermonitor/powermonitor_repository = new() if(!refresh) return cache_entry.data - + for(var/obj/machinery/computer/monitor/pMon in GLOB.power_monitors) if( !(pMon.stat & (NOPOWER|BROKEN)) && !pMon.is_secret_monitor ) pMonData[++pMonData.len] = list ("Name" = pMon.name, "ref" = "\ref[pMon]") cache_entry.timestamp = world.time //+ 30 SECONDS - cache_entry.data = pMonData + cache_entry.data = pMonData return pMonData - + /datum/repository/powermonitor/proc/update_cache() return powermonitor_data(refresh = 1) - \ No newline at end of file + diff --git a/code/datums/click_intercept.dm b/code/datums/click_intercept.dm index d3e9bccbda0..7eed1bb479b 100644 --- a/code/datums/click_intercept.dm +++ b/code/datums/click_intercept.dm @@ -28,4 +28,4 @@ return /datum/click_intercept/proc/InterceptClickOn(user,params,atom/object) - return \ No newline at end of file + return diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 7b3b2ec503a..e733ca78d2b 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -310,4 +310,4 @@ target.TakeComponent(comps) /datum/component/nano_host() - return parent \ No newline at end of file + return parent diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm index 63c6110aa5d..3bd740973a3 100644 --- a/code/datums/components/caltrop.dm +++ b/code/datums/components/caltrop.dm @@ -56,4 +56,4 @@ H.visible_message("[H] slides on [A]!", "You slide on [A]!") cooldown = world.time - H.Weaken(3) \ No newline at end of file + H.Weaken(3) diff --git a/code/datums/components/decal.dm b/code/datums/components/decal.dm index 26d4653b25a..6a0f686b13a 100644 --- a/code/datums/components/decal.dm +++ b/code/datums/components/decal.dm @@ -72,4 +72,4 @@ qdel(src) /datum/component/decal/proc/examine(datum/source, mob/user, var/list/examine_list) - examine_list += description \ No newline at end of file + examine_list += description diff --git a/code/datums/components/material_container.dm b/code/datums/components/material_container.dm index 8d44679fa4a..3071a07d5bb 100644 --- a/code/datums/components/material_container.dm +++ b/code/datums/components/material_container.dm @@ -398,4 +398,4 @@ /datum/material/plastic name = "Plastic" id = MAT_PLASTIC - sheet_type = /obj/item/stack/sheet/plastic \ No newline at end of file + sheet_type = /obj/item/stack/sheet/plastic diff --git a/code/datums/components/spawner.dm b/code/datums/components/spawner.dm index 68ec51959a2..bbc71ce101f 100644 --- a/code/datums/components/spawner.dm +++ b/code/datums/components/spawner.dm @@ -48,4 +48,4 @@ spawned_mobs += L L.nest = src L.faction = src.faction - P.visible_message("[L] [spawn_text] [P].") \ No newline at end of file + P.visible_message("[L] [spawn_text] [P].") diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm index aca505e6a03..818e78e8d2e 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -90,4 +90,4 @@ /datum/component/squeak/proc/holder_dir_change(datum/source, old_dir, new_dir) //If the dir changes it means we're going through a bend in the pipes, let's pretend we bumped the wall if(old_dir != new_dir) - play_squeak() \ No newline at end of file + play_squeak() diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 7845d836f49..7bfc79e4e71 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -1,652 +1,652 @@ -/hook/startup/proc/createDatacore() - data_core = new /datum/datacore() - return 1 - -/datum/datacore - var/list/medical = list() - var/list/general = list() - var/list/security = list() - //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). - var/list/locked = list() - -/datum/datacore/proc/get_manifest(monochrome, OOC) - var/list/heads = new() - var/list/sec = new() - var/list/eng = new() - var/list/med = new() - var/list/sci = new() - var/list/ser = new() - var/list/sup = new() - var/list/bot = new() - var/list/misc = new() - var/list/isactive = new() - var/dat = {" - - - - "} - var/even = 0 - // sort mobs - for(var/datum/data/record/t in data_core.general) - var/name = t.fields["name"] - var/rank = t.fields["rank"] - var/real_rank = t.fields["real_rank"] - if(OOC) - var/active = 0 - for(var/mob/M in GLOB.player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 * 60 * 10) - active = 1 - break - isactive[name] = active ? "Active" : "Inactive" - else - isactive[name] = t.fields["p_stat"] - var/department = 0 - if(real_rank in command_positions) - heads[name] = rank - department = 1 - if(real_rank in security_positions) - sec[name] = rank - department = 1 - if(real_rank in engineering_positions) - eng[name] = rank - department = 1 - if(real_rank in medical_positions) - med[name] = rank - department = 1 - if(real_rank in science_positions) - sci[name] = rank - department = 1 - if(real_rank in service_positions) - ser[name] = rank - department = 1 - if(real_rank in supply_positions) - sup[name] = rank - department = 1 - if(real_rank in nonhuman_positions) - bot[name] = rank - department = 1 - if(!department && !(name in heads)) - misc[name] = rank - if(heads.len > 0) - dat += "" - for(var/name in heads) - dat += "" - even = !even - if(sec.len > 0) - dat += "" - for(var/name in sec) - dat += "" - even = !even - if(eng.len > 0) - dat += "" - for(var/name in eng) - dat += "" - even = !even - if(med.len > 0) - dat += "" - for(var/name in med) - dat += "" - even = !even - if(sci.len > 0) - dat += "" - for(var/name in sci) - dat += "" - even = !even - if(ser.len > 0) - dat += "" - for(var/name in ser) - dat += "" - even = !even - if(sup.len > 0) - dat += "" - for(var/name in sup) - dat += "" - even = !even - if(bot.len > 0) - dat += "" - for(var/name in bot) - dat += "" - even = !even - if(misc.len > 0) - dat += "" - for(var/name in misc) - dat += "" - even = !even - - dat += "
    NameRankActivity
    Heads
    [name][heads[name]][isactive[name]]
    Security
    [name][sec[name]][isactive[name]]
    Engineering
    [name][eng[name]][isactive[name]]
    Medical
    [name][med[name]][isactive[name]]
    Science
    [name][sci[name]][isactive[name]]
    Service
    [name][ser[name]][isactive[name]]
    Supply
    [name][sup[name]][isactive[name]]
    Silicon
    [name][bot[name]][isactive[name]]
    Miscellaneous
    [name][misc[name]][isactive[name]]
    " - dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly - dat = replacetext(dat, "\t", "") - return dat - - -/* -We can't just insert in HTML into the nanoUI so we need the raw data to play with. -Instead of creating this list over and over when someone leaves their PDA open to the page -we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change -using /datum/datacore/proc/manifest_inject(), or manifest_insert() -*/ - -var/global/list/PDA_Manifest = list() - -/datum/datacore/proc/get_manifest_json() - if(PDA_Manifest.len) - return - var/heads[0] - var/sec[0] - var/eng[0] - var/med[0] - var/sci[0] - var/ser[0] - var/sup[0] - var/bot[0] - var/misc[0] - for(var/datum/data/record/t in data_core.general) - var/name = sanitize(t.fields["name"]) - var/rank = sanitize(t.fields["rank"]) - var/real_rank = t.fields["real_rank"] - - var/isactive = t.fields["p_stat"] - var/department = 0 - var/depthead = 0 // Department Heads will be placed at the top of their lists. - if(real_rank in command_positions) - heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - depthead = 1 - if(rank == "Captain" && heads.len != 1) - heads.Swap(1, heads.len) - - if(real_rank in security_positions) - sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sec.len != 1) - sec.Swap(1, sec.len) - - if(real_rank in engineering_positions) - eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && eng.len != 1) - eng.Swap(1, eng.len) - - if(real_rank in medical_positions) - med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && med.len != 1) - med.Swap(1, med.len) - - if(real_rank in science_positions) - sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sci.len != 1) - sci.Swap(1, sci.len) - - if(real_rank in service_positions) - ser[++ser.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && ser.len != 1) - ser.Swap(1, ser.len) - - if(real_rank in supply_positions) - sup[++sup.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sup.len != 1) - sup.Swap(1, sup.len) - - if(real_rank in nonhuman_positions) - bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - - if(!department && !(name in heads)) - misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) - - - PDA_Manifest = list(\ - "heads" = heads,\ - "sec" = sec,\ - "eng" = eng,\ - "med" = med,\ - "sci" = sci,\ - "ser" = ser,\ - "sup" = sup,\ - "bot" = bot,\ - "misc" = misc\ - ) - return - - - -/datum/datacore/proc/manifest() - for(var/mob/living/carbon/human/H in GLOB.player_list) - manifest_inject(H) - -/datum/datacore/proc/manifest_modify(name, assignment) - if(PDA_Manifest.len) - PDA_Manifest.Cut() - var/datum/data/record/foundrecord - var/real_title = assignment - - for(var/datum/data/record/t in data_core.general) - if(t) - if(t.fields["name"] == name) - foundrecord = t - break - - var/list/all_jobs = get_job_datums() - - for(var/datum/job/J in all_jobs) - var/list/alttitles = get_alternate_titles(J.title) - if(!J) continue - if(assignment in alttitles) - real_title = J.title - break - - if(foundrecord) - foundrecord.fields["rank"] = assignment - foundrecord.fields["real_rank"] = real_title - -var/record_id_num = 1001 -/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H) - if(PDA_Manifest.len) - PDA_Manifest.Cut() - - if(H.mind && (H.mind.assigned_role != H.mind.special_role)) - var/assignment - if(H.mind.role_alt_title) - assignment = H.mind.role_alt_title - else if(H.mind.assigned_role) - assignment = H.mind.assigned_role - else if(H.job) - assignment = H.job - else - assignment = "Unassigned" - - var/id = num2hex(record_id_num++, 6) - - - //General Record - var/datum/data/record/G = new() - G.fields["id"] = id - G.fields["name"] = H.real_name - G.fields["real_rank"] = H.mind.assigned_role - G.fields["rank"] = assignment - G.fields["age"] = H.age - G.fields["fingerprint"] = md5(H.dna.uni_identity) - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["sex"] = capitalize(H.gender) - G.fields["species"] = H.dna.species.name - G.fields["photo"] = get_id_photo(H) - G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = SOUTH))]'" - G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = WEST))]'" - if(H.gen_record && !jobban_isbanned(H, "Records")) - G.fields["notes"] = H.gen_record - else - G.fields["notes"] = "No notes found." - general += G - - //Medical Record - var/datum/data/record/M = new() - M.fields["id"] = id - M.fields["name"] = H.real_name - M.fields["blood_type"] = H.dna.blood_type - M.fields["b_dna"] = H.dna.unique_enzymes - M.fields["mi_dis"] = "None" - M.fields["mi_dis_d"] = "No minor disabilities have been declared." - M.fields["ma_dis"] = "None" - M.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - M.fields["alg"] = "None" - M.fields["alg_d"] = "No allergies have been detected in this patient." - M.fields["cdi"] = "None" - M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - if(H.med_record && !jobban_isbanned(H, "Records")) - M.fields["notes"] = H.med_record - else - M.fields["notes"] = "No notes found." - medical += M - - //Security Record - var/datum/data/record/S = new() - S.fields["id"] = id - S.fields["name"] = H.real_name - S.fields["criminal"] = "None" - S.fields["mi_crim"] = "None" - S.fields["mi_crim_d"] = "No minor crime convictions." - S.fields["ma_crim"] = "None" - S.fields["ma_crim_d"] = "No major crime convictions." - S.fields["notes"] = "No notes." - if(H.sec_record && !jobban_isbanned(H, "Records")) - S.fields["notes"] = H.sec_record - else - S.fields["notes"] = "No notes." - LAZYINITLIST(S.fields["comments"]) - security += S - - //Locked Record - var/datum/data/record/L = new() - L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") - L.fields["name"] = H.real_name - L.fields["rank"] = H.mind.assigned_role - L.fields["age"] = H.age - L.fields["sex"] = capitalize(H.gender) - L.fields["blood_type"] = H.dna.blood_type - L.fields["b_dna"] = H.dna.unique_enzymes - L.fields["enzymes"] = H.dna.SE // Used in respawning - L.fields["identity"] = H.dna.UI // " - L.fields["image"] = getFlatIcon(H) //This is god-awful - L.fields["reference"] = H - locked += L - return - -/proc/get_id_photo(mob/living/carbon/human/H, var/custom_job = null) - var/icon/preview_icon = null - var/obj/item/organ/external/head/head_organ = H.get_organ("head") - var/obj/item/organ/internal/eyes/eyes_organ = H.get_int_organ(/obj/item/organ/internal/eyes) - - var/g = "m" - if(H.gender == FEMALE) - g = "f" - - var/icon/icobase = head_organ.icobase //At this point all the organs would have the same icobase, so this is just recycling. - - preview_icon = new /icon(icobase, "torso_[g]") - var/icon/temp - temp = new /icon(icobase, "groin_[g]") - preview_icon.Blend(temp, ICON_OVERLAY) - var/head = "head" - if(head_organ.alt_head && head_organ.dna.species.bodyflags & HAS_ALT_HEADS) - var/datum/sprite_accessory/alt_heads/alternate_head = GLOB.alt_heads_list[head_organ.alt_head] - if(alternate_head.icon_state) - head = alternate_head.icon_state - temp = new /icon(icobase, "[head]_[g]") - preview_icon.Blend(temp, ICON_OVERLAY) - - //Tail - if(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/tail)) - temp = new/icon("icon" = H.body_accessory.icon, "icon_state" = H.body_accessory.icon_state) - preview_icon.Blend(temp, ICON_OVERLAY) - else if(H.tail && H.dna.species.bodyflags & HAS_TAIL) - temp = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[H.tail]_s") - preview_icon.Blend(temp, ICON_OVERLAY) - - for(var/obj/item/organ/external/E in H.bodyparts) - preview_icon.Blend(E.get_icon(), ICON_OVERLAY) - - // Skin tone - if(H.dna.species.bodyflags & HAS_SKIN_TONE) - if(H.s_tone >= 0) - preview_icon.Blend(rgb(H.s_tone, H.s_tone, H.s_tone), ICON_ADD) - else - preview_icon.Blend(rgb(-H.s_tone, -H.s_tone, -H.s_tone), ICON_SUBTRACT) - - // Proper Skin color - Fix, you can't have HAS_SKIN_TONE *and* HAS_SKIN_COLOR - if(H.dna.species.bodyflags & HAS_SKIN_COLOR) - preview_icon.Blend(H.skin_colour, ICON_ADD) - - //Tail Markings - var/icon/t_marking_s - if(H.dna.species.bodyflags & HAS_TAIL_MARKINGS) - var/tail_marking = H.m_styles["tail"] - var/datum/sprite_accessory/tail_marking_style = GLOB.marking_styles_list[tail_marking] - if(tail_marking_style && tail_marking_style.species_allowed) - t_marking_s = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") - t_marking_s.Blend(H.m_colours["tail"], ICON_ADD) - if(!(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/body))) - preview_icon.Blend(t_marking_s, ICON_OVERLAY) - - var/icon/face_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "bald_s") - if(!(H.dna.species.bodyflags & NO_EYES)) - var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = H.dna.species ? H.dna.species.eyes : "eyes_s") - if(!eyes_organ) - return - eyes_s.Blend(eyes_organ.eye_colour, ICON_ADD) - face_s.Blend(eyes_s, ICON_OVERLAY) - - var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] - if(hair_style) - var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - // I'll want to make a species-specific proc for this sooner or later - // But this'll do for now - if(istype(head_organ.dna.species, /datum/species/slime)) - hair_s.Blend("[H.skin_colour]A0", ICON_AND) //A0 = 160 alpha. - else - hair_s.Blend(head_organ.hair_colour, ICON_ADD) - - if(hair_style.secondary_theme) - var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") - if(!hair_style.no_sec_colour) - hair_secondary_s.Blend(head_organ.sec_hair_colour, ICON_ADD) - hair_s.Blend(hair_secondary_s, ICON_OVERLAY) - - face_s.Blend(hair_s, ICON_OVERLAY) - - //Head Accessory - if(head_organ.dna.species.bodyflags & HAS_HEAD_ACCESSORY) - var/datum/sprite_accessory/head_accessory_style = GLOB.head_accessory_styles_list[head_organ.ha_style] - if(head_accessory_style && head_accessory_style.species_allowed) - var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") - head_accessory_s.Blend(head_organ.headacc_colour, ICON_ADD) - face_s.Blend(head_accessory_s, ICON_OVERLAY) - - var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[head_organ.f_style] - if(facial_hair_style && facial_hair_style.species_allowed) - var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") - if(istype(head_organ.dna.species, /datum/species/slime)) - facial_s.Blend("[H.skin_colour]A0", ICON_ADD) //A0 = 160 alpha. - else - facial_s.Blend(head_organ.facial_colour, ICON_ADD) - - if(facial_hair_style.secondary_theme) - var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") - if(!facial_hair_style.no_sec_colour) - facial_secondary_s.Blend(head_organ.sec_facial_colour, ICON_ADD) - facial_s.Blend(facial_secondary_s, ICON_OVERLAY) - - face_s.Blend(facial_s, ICON_OVERLAY) - - //Markings - if((H.dna.species.bodyflags & HAS_HEAD_MARKINGS) || (H.dna.species.bodyflags & HAS_BODY_MARKINGS)) - if(H.dna.species.bodyflags & HAS_BODY_MARKINGS) //Body markings. - var/body_marking = H.m_styles["body"] - var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] - if(body_marking_style && body_marking_style.species_allowed) - var/icon/b_marking_s = new/icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") - b_marking_s.Blend(H.m_colours["body"], ICON_ADD) - face_s.Blend(b_marking_s, ICON_OVERLAY) - if(H.dna.species.bodyflags & HAS_HEAD_MARKINGS) //Head markings. - var/head_marking = H.m_styles["head"] - var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] - if(head_marking_style && head_marking_style.species_allowed) - var/icon/h_marking_s = new/icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") - h_marking_s.Blend(H.m_colours["head"], ICON_ADD) - face_s.Blend(h_marking_s, ICON_OVERLAY) - - preview_icon.Blend(face_s, ICON_OVERLAY) - - - var/icon/clothes_s = null - var/job_clothes = null - if(custom_job) - job_clothes = custom_job - else if(H.mind) - job_clothes = H.mind.assigned_role - switch(job_clothes) - if("Head of Personnel") - clothes_s = new /icon('icons/mob/uniform.dmi', "hop_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if("Nanotrasen Representative") - clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - if("Blueshield") - clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "blueshield"), ICON_OVERLAY) - if("Magistrate") - clothes_s = new /icon('icons/mob/uniform.dmi', "really_black_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "judge"), ICON_OVERLAY) - if("Bartender") - clothes_s = new /icon('icons/mob/uniform.dmi', "ba_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Botanist") - clothes_s = new /icon('icons/mob/uniform.dmi', "hydroponics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Chef") - clothes_s = new /icon('icons/mob/uniform.dmi', "chef_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Janitor") - clothes_s = new /icon('icons/mob/uniform.dmi', "janitor_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Librarian") - clothes_s = new /icon('icons/mob/uniform.dmi', "red_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Barber") - clothes_s = new /icon('icons/mob/uniform.dmi', "barber_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Clown") - clothes_s = new /icon('icons/mob/uniform.dmi', "clown_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "clown"), ICON_UNDERLAY) - if("Mime") - clothes_s = new /icon('icons/mob/uniform.dmi', "mime_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Quartermaster") - clothes_s = new /icon('icons/mob/uniform.dmi', "qm_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if("Cargo Technician") - clothes_s = new /icon('icons/mob/uniform.dmi', "cargotech_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Shaft Miner") - clothes_s = new /icon('icons/mob/uniform.dmi', "miner_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Lawyer") - clothes_s = new /icon('icons/mob/uniform.dmi', "internalaffairs_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if("Chaplain") - clothes_s = new /icon('icons/mob/uniform.dmi', "chapblack_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Research Director") - clothes_s = new /icon('icons/mob/uniform.dmi', "director_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if("Scientist") - clothes_s = new /icon('icons/mob/uniform.dmi', "toxinswhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) - if("Chemist") - clothes_s = new /icon('icons/mob/uniform.dmi', "chemistrywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_chem_open"), ICON_OVERLAY) - if("Chief Medical Officer") - clothes_s = new /icon('icons/mob/uniform.dmi', "cmo_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_cmo_open"), ICON_OVERLAY) - if("Medical Doctor") - clothes_s = new /icon('icons/mob/uniform.dmi', "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if("Coroner") - clothes_s = new /icon('icons/mob/uniform.dmi', "scrubsblack_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_mort_open"), ICON_OVERLAY) - if("Geneticist") - clothes_s = new /icon('icons/mob/uniform.dmi', "geneticswhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_gen_open"), ICON_OVERLAY) - if("Virologist") - clothes_s = new /icon('icons/mob/uniform.dmi', "virologywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_vir_open"), ICON_OVERLAY) - if("Psychiatrist") - clothes_s = new /icon('icons/mob/uniform.dmi', "psych_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_UNDERLAY) - if("Paramedic") - clothes_s = new /icon('icons/mob/uniform.dmi', "paramedic_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Captain") - clothes_s = new /icon('icons/mob/uniform.dmi', "captain_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if("Head of Security") - clothes_s = new /icon('icons/mob/uniform.dmi', "hosred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - if("Warden") - clothes_s = new /icon('icons/mob/uniform.dmi', "warden_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - if("Detective") - clothes_s = new /icon('icons/mob/uniform.dmi', "detective_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY) - if("Security Pod Pilot") - clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bomber"), ICON_OVERLAY) - if("Brig Physician") - clothes_s = new /icon('icons/mob/uniform.dmi', "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "fr_jacket_open"), ICON_OVERLAY) - if("Security Officer") - clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - if("Chief Engineer") - clothes_s = new /icon('icons/mob/uniform.dmi', "chief_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if("Station Engineer") - clothes_s = new /icon('icons/mob/uniform.dmi', "engine_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if("Life Support Specialist") - clothes_s = new /icon('icons/mob/uniform.dmi', "atmos_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if("Mechanic") - clothes_s = new /icon('icons/mob/uniform.dmi', "mechanic_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if("Roboticist") - clothes_s = new /icon('icons/mob/uniform.dmi', "robotics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if("Syndicate Agent") - clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Syndicate Officer") - clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) - if("Syndicate Nuclear Operative") - clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) - else if(H.mind && H.mind.assigned_role in get_all_centcom_jobs()) - clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - else - clothes_s = new /icon('icons/mob/uniform.dmi', "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - - preview_icon.Blend(face_s, ICON_OVERLAY) // Why do we do this twice - if(clothes_s) - preview_icon.Blend(clothes_s, ICON_OVERLAY) - //Bus body accessories that go over clothes. - if(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/body)) - temp = new/icon("icon" = H.body_accessory.icon, "icon_state" = H.body_accessory.icon_state) - if(H.body_accessory.pixel_x_offset) - temp.Shift(EAST, H.body_accessory.pixel_x_offset) - if(H.body_accessory.pixel_y_offset) - temp.Shift(NORTH, H.body_accessory.pixel_y_offset) - if(H.dna.species.bodyflags & HAS_SKIN_COLOR) - temp.Blend(H.skin_colour, H.body_accessory.blend_mode) - if(t_marking_s) - temp.Blend(t_marking_s, ICON_OVERLAY) - preview_icon.Blend(temp, ICON_OVERLAY) - qdel(face_s) - qdel(clothes_s) - - return preview_icon +/hook/startup/proc/createDatacore() + GLOB.data_core = new /datum/datacore() + return 1 + +/datum/datacore + var/list/medical = list() + var/list/general = list() + var/list/security = list() + //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). + var/list/locked = list() + +/datum/datacore/proc/get_manifest(monochrome, OOC) + var/list/heads = new() + var/list/sec = new() + var/list/eng = new() + var/list/med = new() + var/list/sci = new() + var/list/ser = new() + var/list/sup = new() + var/list/bot = new() + var/list/misc = new() + var/list/isactive = new() + var/dat = {" + + + + "} + var/even = 0 + // sort mobs + for(var/datum/data/record/t in GLOB.data_core.general) + var/name = t.fields["name"] + var/rank = t.fields["rank"] + var/real_rank = t.fields["real_rank"] + if(OOC) + var/active = 0 + for(var/mob/M in GLOB.player_list) + if(M.real_name == name && M.client && M.client.inactivity <= 10 * 60 * 10) + active = 1 + break + isactive[name] = active ? "Active" : "Inactive" + else + isactive[name] = t.fields["p_stat"] + var/department = 0 + if(real_rank in GLOB.command_positions) + heads[name] = rank + department = 1 + if(real_rank in GLOB.security_positions) + sec[name] = rank + department = 1 + if(real_rank in GLOB.engineering_positions) + eng[name] = rank + department = 1 + if(real_rank in GLOB.medical_positions) + med[name] = rank + department = 1 + if(real_rank in GLOB.science_positions) + sci[name] = rank + department = 1 + if(real_rank in GLOB.service_positions) + ser[name] = rank + department = 1 + if(real_rank in GLOB.supply_positions) + sup[name] = rank + department = 1 + if(real_rank in GLOB.nonhuman_positions) + bot[name] = rank + department = 1 + if(!department && !(name in heads)) + misc[name] = rank + if(heads.len > 0) + dat += "" + for(var/name in heads) + dat += "" + even = !even + if(sec.len > 0) + dat += "" + for(var/name in sec) + dat += "" + even = !even + if(eng.len > 0) + dat += "" + for(var/name in eng) + dat += "" + even = !even + if(med.len > 0) + dat += "" + for(var/name in med) + dat += "" + even = !even + if(sci.len > 0) + dat += "" + for(var/name in sci) + dat += "" + even = !even + if(ser.len > 0) + dat += "" + for(var/name in ser) + dat += "" + even = !even + if(sup.len > 0) + dat += "" + for(var/name in sup) + dat += "" + even = !even + if(bot.len > 0) + dat += "" + for(var/name in bot) + dat += "" + even = !even + if(misc.len > 0) + dat += "" + for(var/name in misc) + dat += "" + even = !even + + dat += "
    NameRankActivity
    Heads
    [name][heads[name]][isactive[name]]
    Security
    [name][sec[name]][isactive[name]]
    Engineering
    [name][eng[name]][isactive[name]]
    Medical
    [name][med[name]][isactive[name]]
    Science
    [name][sci[name]][isactive[name]]
    Service
    [name][ser[name]][isactive[name]]
    Supply
    [name][sup[name]][isactive[name]]
    Silicon
    [name][bot[name]][isactive[name]]
    Miscellaneous
    [name][misc[name]][isactive[name]]
    " + dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly + dat = replacetext(dat, "\t", "") + return dat + + +/* +We can't just insert in HTML into the nanoUI so we need the raw data to play with. +Instead of creating this list over and over when someone leaves their PDA open to the page +we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change +using /datum/datacore/proc/manifest_inject(), or manifest_insert() +*/ + +GLOBAL_LIST_EMPTY(PDA_Manifest) + +/datum/datacore/proc/get_manifest_json() + if(GLOB.PDA_Manifest.len) + return + var/heads[0] + var/sec[0] + var/eng[0] + var/med[0] + var/sci[0] + var/ser[0] + var/sup[0] + var/bot[0] + var/misc[0] + for(var/datum/data/record/t in GLOB.data_core.general) + var/name = sanitize(t.fields["name"]) + var/rank = sanitize(t.fields["rank"]) + var/real_rank = t.fields["real_rank"] + + var/isactive = t.fields["p_stat"] + var/department = 0 + var/depthead = 0 // Department Heads will be placed at the top of their lists. + if(real_rank in GLOB.command_positions) + heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + depthead = 1 + if(rank == "Captain" && heads.len != 1) + heads.Swap(1, heads.len) + + if(real_rank in GLOB.security_positions) + sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sec.len != 1) + sec.Swap(1, sec.len) + + if(real_rank in GLOB.engineering_positions) + eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && eng.len != 1) + eng.Swap(1, eng.len) + + if(real_rank in GLOB.medical_positions) + med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && med.len != 1) + med.Swap(1, med.len) + + if(real_rank in GLOB.science_positions) + sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sci.len != 1) + sci.Swap(1, sci.len) + + if(real_rank in GLOB.service_positions) + ser[++ser.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && ser.len != 1) + ser.Swap(1, ser.len) + + if(real_rank in GLOB.supply_positions) + sup[++sup.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sup.len != 1) + sup.Swap(1, sup.len) + + if(real_rank in GLOB.nonhuman_positions) + bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + + if(!department && !(name in heads)) + misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) + + + GLOB.PDA_Manifest = list(\ + "heads" = heads,\ + "sec" = sec,\ + "eng" = eng,\ + "med" = med,\ + "sci" = sci,\ + "ser" = ser,\ + "sup" = sup,\ + "bot" = bot,\ + "misc" = misc\ + ) + return + + + +/datum/datacore/proc/manifest() + for(var/mob/living/carbon/human/H in GLOB.player_list) + manifest_inject(H) + +/datum/datacore/proc/manifest_modify(name, assignment) + if(GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + var/datum/data/record/foundrecord + var/real_title = assignment + + for(var/datum/data/record/t in GLOB.data_core.general) + if(t) + if(t.fields["name"] == name) + foundrecord = t + break + + var/list/all_jobs = get_job_datums() + + for(var/datum/job/J in all_jobs) + var/list/alttitles = get_alternate_titles(J.title) + if(!J) continue + if(assignment in alttitles) + real_title = J.title + break + + if(foundrecord) + foundrecord.fields["rank"] = assignment + foundrecord.fields["real_rank"] = real_title + +GLOBAL_VAR_INIT(record_id_num, 1001) +/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H) + if(GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + + if(H.mind && (H.mind.assigned_role != H.mind.special_role)) + var/assignment + if(H.mind.role_alt_title) + assignment = H.mind.role_alt_title + else if(H.mind.assigned_role) + assignment = H.mind.assigned_role + else if(H.job) + assignment = H.job + else + assignment = "Unassigned" + + var/id = num2hex(GLOB.record_id_num++, 6) + + + //General Record + var/datum/data/record/G = new() + G.fields["id"] = id + G.fields["name"] = H.real_name + G.fields["real_rank"] = H.mind.assigned_role + G.fields["rank"] = assignment + G.fields["age"] = H.age + G.fields["fingerprint"] = md5(H.dna.uni_identity) + G.fields["p_stat"] = "Active" + G.fields["m_stat"] = "Stable" + G.fields["sex"] = capitalize(H.gender) + G.fields["species"] = H.dna.species.name + G.fields["photo"] = get_id_photo(H) + G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = SOUTH))]'" + G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = WEST))]'" + if(H.gen_record && !jobban_isbanned(H, "Records")) + G.fields["notes"] = H.gen_record + else + G.fields["notes"] = "No notes found." + general += G + + //Medical Record + var/datum/data/record/M = new() + M.fields["id"] = id + M.fields["name"] = H.real_name + M.fields["blood_type"] = H.dna.blood_type + M.fields["b_dna"] = H.dna.unique_enzymes + M.fields["mi_dis"] = "None" + M.fields["mi_dis_d"] = "No minor disabilities have been declared." + M.fields["ma_dis"] = "None" + M.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + M.fields["alg"] = "None" + M.fields["alg_d"] = "No allergies have been detected in this patient." + M.fields["cdi"] = "None" + M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + if(H.med_record && !jobban_isbanned(H, "Records")) + M.fields["notes"] = H.med_record + else + M.fields["notes"] = "No notes found." + medical += M + + //Security Record + var/datum/data/record/S = new() + S.fields["id"] = id + S.fields["name"] = H.real_name + S.fields["criminal"] = "None" + S.fields["mi_crim"] = "None" + S.fields["mi_crim_d"] = "No minor crime convictions." + S.fields["ma_crim"] = "None" + S.fields["ma_crim_d"] = "No major crime convictions." + S.fields["notes"] = "No notes." + if(H.sec_record && !jobban_isbanned(H, "Records")) + S.fields["notes"] = H.sec_record + else + S.fields["notes"] = "No notes." + LAZYINITLIST(S.fields["comments"]) + security += S + + //Locked Record + var/datum/data/record/L = new() + L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") + L.fields["name"] = H.real_name + L.fields["rank"] = H.mind.assigned_role + L.fields["age"] = H.age + L.fields["sex"] = capitalize(H.gender) + L.fields["blood_type"] = H.dna.blood_type + L.fields["b_dna"] = H.dna.unique_enzymes + L.fields["enzymes"] = H.dna.SE // Used in respawning + L.fields["identity"] = H.dna.UI // " + L.fields["image"] = getFlatIcon(H) //This is god-awful + L.fields["reference"] = H + locked += L + return + +/proc/get_id_photo(mob/living/carbon/human/H, var/custom_job = null) + var/icon/preview_icon = null + var/obj/item/organ/external/head/head_organ = H.get_organ("head") + var/obj/item/organ/internal/eyes/eyes_organ = H.get_int_organ(/obj/item/organ/internal/eyes) + + var/g = "m" + if(H.gender == FEMALE) + g = "f" + + var/icon/icobase = head_organ.icobase //At this point all the organs would have the same icobase, so this is just recycling. + + preview_icon = new /icon(icobase, "torso_[g]") + var/icon/temp + temp = new /icon(icobase, "groin_[g]") + preview_icon.Blend(temp, ICON_OVERLAY) + var/head = "head" + if(head_organ.alt_head && head_organ.dna.species.bodyflags & HAS_ALT_HEADS) + var/datum/sprite_accessory/alt_heads/alternate_head = GLOB.alt_heads_list[head_organ.alt_head] + if(alternate_head.icon_state) + head = alternate_head.icon_state + temp = new /icon(icobase, "[head]_[g]") + preview_icon.Blend(temp, ICON_OVERLAY) + + //Tail + if(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/tail)) + temp = new/icon("icon" = H.body_accessory.icon, "icon_state" = H.body_accessory.icon_state) + preview_icon.Blend(temp, ICON_OVERLAY) + else if(H.tail && H.dna.species.bodyflags & HAS_TAIL) + temp = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[H.tail]_s") + preview_icon.Blend(temp, ICON_OVERLAY) + + for(var/obj/item/organ/external/E in H.bodyparts) + preview_icon.Blend(E.get_icon(), ICON_OVERLAY) + + // Skin tone + if(H.dna.species.bodyflags & HAS_SKIN_TONE) + if(H.s_tone >= 0) + preview_icon.Blend(rgb(H.s_tone, H.s_tone, H.s_tone), ICON_ADD) + else + preview_icon.Blend(rgb(-H.s_tone, -H.s_tone, -H.s_tone), ICON_SUBTRACT) + + // Proper Skin color - Fix, you can't have HAS_SKIN_TONE *and* HAS_SKIN_COLOR + if(H.dna.species.bodyflags & HAS_SKIN_COLOR) + preview_icon.Blend(H.skin_colour, ICON_ADD) + + //Tail Markings + var/icon/t_marking_s + if(H.dna.species.bodyflags & HAS_TAIL_MARKINGS) + var/tail_marking = H.m_styles["tail"] + var/datum/sprite_accessory/tail_marking_style = GLOB.marking_styles_list[tail_marking] + if(tail_marking_style && tail_marking_style.species_allowed) + t_marking_s = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") + t_marking_s.Blend(H.m_colours["tail"], ICON_ADD) + if(!(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/body))) + preview_icon.Blend(t_marking_s, ICON_OVERLAY) + + var/icon/face_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "bald_s") + if(!(H.dna.species.bodyflags & NO_EYES)) + var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = H.dna.species ? H.dna.species.eyes : "eyes_s") + if(!eyes_organ) + return + eyes_s.Blend(eyes_organ.eye_colour, ICON_ADD) + face_s.Blend(eyes_s, ICON_OVERLAY) + + var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] + if(hair_style) + var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") + // I'll want to make a species-specific proc for this sooner or later + // But this'll do for now + if(istype(head_organ.dna.species, /datum/species/slime)) + hair_s.Blend("[H.skin_colour]A0", ICON_AND) //A0 = 160 alpha. + else + hair_s.Blend(head_organ.hair_colour, ICON_ADD) + + if(hair_style.secondary_theme) + var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") + if(!hair_style.no_sec_colour) + hair_secondary_s.Blend(head_organ.sec_hair_colour, ICON_ADD) + hair_s.Blend(hair_secondary_s, ICON_OVERLAY) + + face_s.Blend(hair_s, ICON_OVERLAY) + + //Head Accessory + if(head_organ.dna.species.bodyflags & HAS_HEAD_ACCESSORY) + var/datum/sprite_accessory/head_accessory_style = GLOB.head_accessory_styles_list[head_organ.ha_style] + if(head_accessory_style && head_accessory_style.species_allowed) + var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") + head_accessory_s.Blend(head_organ.headacc_colour, ICON_ADD) + face_s.Blend(head_accessory_s, ICON_OVERLAY) + + var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[head_organ.f_style] + if(facial_hair_style && facial_hair_style.species_allowed) + var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") + if(istype(head_organ.dna.species, /datum/species/slime)) + facial_s.Blend("[H.skin_colour]A0", ICON_ADD) //A0 = 160 alpha. + else + facial_s.Blend(head_organ.facial_colour, ICON_ADD) + + if(facial_hair_style.secondary_theme) + var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") + if(!facial_hair_style.no_sec_colour) + facial_secondary_s.Blend(head_organ.sec_facial_colour, ICON_ADD) + facial_s.Blend(facial_secondary_s, ICON_OVERLAY) + + face_s.Blend(facial_s, ICON_OVERLAY) + + //Markings + if((H.dna.species.bodyflags & HAS_HEAD_MARKINGS) || (H.dna.species.bodyflags & HAS_BODY_MARKINGS)) + if(H.dna.species.bodyflags & HAS_BODY_MARKINGS) //Body markings. + var/body_marking = H.m_styles["body"] + var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] + if(body_marking_style && body_marking_style.species_allowed) + var/icon/b_marking_s = new/icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") + b_marking_s.Blend(H.m_colours["body"], ICON_ADD) + face_s.Blend(b_marking_s, ICON_OVERLAY) + if(H.dna.species.bodyflags & HAS_HEAD_MARKINGS) //Head markings. + var/head_marking = H.m_styles["head"] + var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] + if(head_marking_style && head_marking_style.species_allowed) + var/icon/h_marking_s = new/icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") + h_marking_s.Blend(H.m_colours["head"], ICON_ADD) + face_s.Blend(h_marking_s, ICON_OVERLAY) + + preview_icon.Blend(face_s, ICON_OVERLAY) + + + var/icon/clothes_s = null + var/job_clothes = null + if(custom_job) + job_clothes = custom_job + else if(H.mind) + job_clothes = H.mind.assigned_role + switch(job_clothes) + if("Head of Personnel") + clothes_s = new /icon('icons/mob/uniform.dmi', "hop_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if("Nanotrasen Representative") + clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + if("Blueshield") + clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "blueshield"), ICON_OVERLAY) + if("Magistrate") + clothes_s = new /icon('icons/mob/uniform.dmi', "really_black_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "judge"), ICON_OVERLAY) + if("Bartender") + clothes_s = new /icon('icons/mob/uniform.dmi', "ba_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Botanist") + clothes_s = new /icon('icons/mob/uniform.dmi', "hydroponics_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Chef") + clothes_s = new /icon('icons/mob/uniform.dmi', "chef_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Janitor") + clothes_s = new /icon('icons/mob/uniform.dmi', "janitor_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Librarian") + clothes_s = new /icon('icons/mob/uniform.dmi', "red_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Barber") + clothes_s = new /icon('icons/mob/uniform.dmi', "barber_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Clown") + clothes_s = new /icon('icons/mob/uniform.dmi', "clown_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "clown"), ICON_UNDERLAY) + if("Mime") + clothes_s = new /icon('icons/mob/uniform.dmi', "mime_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Quartermaster") + clothes_s = new /icon('icons/mob/uniform.dmi', "qm_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if("Cargo Technician") + clothes_s = new /icon('icons/mob/uniform.dmi', "cargotech_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Shaft Miner") + clothes_s = new /icon('icons/mob/uniform.dmi', "miner_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Lawyer") + clothes_s = new /icon('icons/mob/uniform.dmi', "internalaffairs_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if("Chaplain") + clothes_s = new /icon('icons/mob/uniform.dmi', "chapblack_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Research Director") + clothes_s = new /icon('icons/mob/uniform.dmi', "director_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if("Scientist") + clothes_s = new /icon('icons/mob/uniform.dmi', "toxinswhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) + if("Chemist") + clothes_s = new /icon('icons/mob/uniform.dmi', "chemistrywhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_chem_open"), ICON_OVERLAY) + if("Chief Medical Officer") + clothes_s = new /icon('icons/mob/uniform.dmi', "cmo_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_cmo_open"), ICON_OVERLAY) + if("Medical Doctor") + clothes_s = new /icon('icons/mob/uniform.dmi', "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if("Coroner") + clothes_s = new /icon('icons/mob/uniform.dmi', "scrubsblack_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_mort_open"), ICON_OVERLAY) + if("Geneticist") + clothes_s = new /icon('icons/mob/uniform.dmi', "geneticswhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_gen_open"), ICON_OVERLAY) + if("Virologist") + clothes_s = new /icon('icons/mob/uniform.dmi', "virologywhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_vir_open"), ICON_OVERLAY) + if("Psychiatrist") + clothes_s = new /icon('icons/mob/uniform.dmi', "psych_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_UNDERLAY) + if("Paramedic") + clothes_s = new /icon('icons/mob/uniform.dmi', "paramedic_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Captain") + clothes_s = new /icon('icons/mob/uniform.dmi', "captain_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if("Head of Security") + clothes_s = new /icon('icons/mob/uniform.dmi', "hosred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + if("Warden") + clothes_s = new /icon('icons/mob/uniform.dmi', "warden_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + if("Detective") + clothes_s = new /icon('icons/mob/uniform.dmi', "detective_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY) + if("Security Pod Pilot") + clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bomber"), ICON_OVERLAY) + if("Brig Physician") + clothes_s = new /icon('icons/mob/uniform.dmi', "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "fr_jacket_open"), ICON_OVERLAY) + if("Security Officer") + clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + if("Chief Engineer") + clothes_s = new /icon('icons/mob/uniform.dmi', "chief_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if("Station Engineer") + clothes_s = new /icon('icons/mob/uniform.dmi', "engine_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if("Life Support Specialist") + clothes_s = new /icon('icons/mob/uniform.dmi', "atmos_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if("Mechanic") + clothes_s = new /icon('icons/mob/uniform.dmi', "mechanic_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if("Roboticist") + clothes_s = new /icon('icons/mob/uniform.dmi', "robotics_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if("Syndicate Agent") + clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Syndicate Officer") + clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) + if("Syndicate Nuclear Operative") + clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) + else if(H.mind && (H.mind.assigned_role in get_all_centcom_jobs())) + clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + else + clothes_s = new /icon('icons/mob/uniform.dmi', "grey_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + + preview_icon.Blend(face_s, ICON_OVERLAY) // Why do we do this twice + if(clothes_s) + preview_icon.Blend(clothes_s, ICON_OVERLAY) + //Bus body accessories that go over clothes. + if(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/body)) + temp = new/icon("icon" = H.body_accessory.icon, "icon_state" = H.body_accessory.icon_state) + if(H.body_accessory.pixel_x_offset) + temp.Shift(EAST, H.body_accessory.pixel_x_offset) + if(H.body_accessory.pixel_y_offset) + temp.Shift(NORTH, H.body_accessory.pixel_y_offset) + if(H.dna.species.bodyflags & HAS_SKIN_COLOR) + temp.Blend(H.skin_colour, H.body_accessory.blend_mode) + if(t_marking_s) + temp.Blend(t_marking_s, ICON_OVERLAY) + preview_icon.Blend(temp, ICON_OVERLAY) + qdel(face_s) + qdel(clothes_s) + + return preview_icon diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 6e716f057c4..6a183987a30 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -5,7 +5,7 @@ /// Status traits attached to this datum var/list/status_traits var/list/comp_lookup - var/list/signal_procs + var/list/list/datum/callback/signal_procs var/signal_enabled = FALSE var/datum_flags = NONE var/var_edited = FALSE //Warranty void if seal is broken @@ -66,4 +66,4 @@ /datum/nothing - // Placeholder object, used for ispath checks. Has to be defined to prevent errors, but shouldn't ever be created. \ No newline at end of file + // Placeholder object, used for ispath checks. Has to be defined to prevent errors, but shouldn't ever be created. diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index daa50119135..b2b09003934 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -1,1387 +1,1387 @@ -// reference: /client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0) - -/datum/proc/can_vv_get(var_name) - return TRUE - -// /client/proc/can_vv_get(var_name) -// return TRUE - -/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited - switch(var_name) - if("vars") - return FALSE - if("var_edited") - return FALSE - var_edited = TRUE - vars[var_name] = var_value - - . = TRUE - - -/client/vv_edit_var(var_name, var_value) //called whenever a var is edited - switch(var_name) - if("vars") - return FALSE - if("var_edited") - return FALSE - var_edited = TRUE - vars[var_name] = var_value - - . = TRUE - -/datum/proc/vv_get_var(var_name) - switch(var_name) - if("attack_log", "debug_log") - return debug_variable(var_name, vars[var_name], 0, src, sanitize = FALSE) - if("vars") - return debug_variable(var_name, list(), 0, src) - return debug_variable(var_name, vars[var_name], 0, src) - -/client/vv_get_var(var_name) - switch(var_name) - if("vars") - return debug_variable(var_name, list(), 0, src) - return debug_variable(var_name, vars[var_name], 0, src) - -/datum/proc/can_vv_delete() - return TRUE - -//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down -//add seperaters by doing . += "---" -/datum/proc/vv_get_dropdown() - . = list() - . += "---" - .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" - .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" - .["Jump to Object"] = "?_src_=vars;jump_to=[UID()]" - .["Delete"] = "?_src_=vars;delete=[UID()]" - .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]" - . += "---" - -/client/vv_get_dropdown() - . = list() - . += "---" - .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" - .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" - .["Delete"] = "?_src_=vars;delete=[UID()]" - .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]" - . += "---" - -/client/proc/debug_variables(datum/D in world) - set category = "Debug" - set name = "View Variables" - - var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. - - if(!is_admin(usr)) - to_chat(usr, "You need to be an administrator to access this.") - return - - if(!D) - return - - - var/islist = islist(D) - var/isclient = isclient(D) - if(!islist && !isclient && !istype(D)) - return - - var/title = "" - var/icon/sprite - var/hash - var/refid - - if(!islist) - refid = "[D.UID()]" - else - refid = "\ref[D]" - - var/type = /list - if(!islist) - type = D.type - - if(isatom(D)) - var/atom/A = D - if(A.icon && A.icon_state) - sprite = new /icon(A.icon, A.icon_state) - hash = md5(A.icon) - hash = md5(hash + A.icon_state) - usr << browse_rsc(sprite, "vv[hash].png") - - - var/sprite_text - if(sprite) - sprite_text = "" - - - var/list/atomsnowflake = list() - if(isatom(D)) - var/atom/A = D - if(isliving(A)) - var/mob/living/L = A - atomsnowflake += "[L]" - if(L.dir) - atomsnowflake += "
    << [dir2text(L.dir)] >>" - atomsnowflake += {" -
    [L.ckey ? L.ckey : "No ckey"] / [L.real_name ? L.real_name : "No real name"] -
    - BRUTE:[L.getBruteLoss()] - FIRE:[L.getFireLoss()] - TOXIN:[L.getToxLoss()] - OXY:[L.getOxyLoss()] - CLONE:[L.getCloneLoss()] - BRAIN:[L.getBrainLoss()] - STAMINA:[L.getStaminaLoss()] - - "} - else - atomsnowflake += "[A]" - if(A.dir) - atomsnowflake += "
    << [dir2text(A.dir)] >>" - else - atomsnowflake += "[D]" - - - var/formatted_type = "[type]" - if(length(formatted_type) > 25) - var/middle_point = length(formatted_type) / 2 - var/splitpoint = findtext(formatted_type, "/", middle_point) - if(splitpoint) - formatted_type = "[copytext(formatted_type, 1, splitpoint)]
    [copytext(formatted_type, splitpoint)]" - else - formatted_type = "Type too long" //No suitable splitpoint (/) found. - - - var/marked - if(holder.marked_datum && holder.marked_datum == D) - marked = "
    Marked Object" - - - var/varedited_line = "" - if(isatom(D)) - var/atom/A = D - if(A.admin_spawned) - varedited_line += "
    Admin Spawned" - - - if(!islist && D.var_edited) - varedited_line += "
    Var Edited" - - - var/dropdownoptions = list() - if(islist) - dropdownoptions = list( - "---", - "Add Item" = "?_src_=vars;listadd=[refid]", - "Remove Nulls" = "?_src_=vars;listnulls=[refid]", - "Remove Dupes" = "?_src_=vars;listdupes=[refid]", - "Set len" = "?_src_=vars;listlen=[refid]", - "Shuffle" = "?_src_=vars;listshuffle=[refid]" - ) - else - dropdownoptions = D.vv_get_dropdown() - - - var/list/dropdownoptions_html = list() - for(var/name in dropdownoptions) - var/link = dropdownoptions[name] - if(link) - dropdownoptions_html += "" - else - dropdownoptions_html += "" - - - var/list/names = list() - if(!islist) - for(var/V in D.vars) - names += V - - - sleep(1) // Without a sleep here, VV sometimes disconnects clients - - - var/list/variable_html = list() - if(islist) - var/list/L = D - for(var/i in 1 to L.len) - var/key = L[i] - var/value - if(IS_NORMAL_LIST(L) && !isnum(key)) - value = L[key] - variable_html += debug_variable(i, value, 0, D) - else - names = sortList(names) - for(var/V in names) - if(D.can_vv_get(V)) - variable_html += D.vv_get_var(V) - - var/html = {" - - - [title] - - - - -
    - - - - - -
    - - - - -
    - [sprite_text] -
    - [atomsnowflake.Join()] -
    -
    -
    - [formatted_type] - [marked] - [varedited_line] -
    -
    -
    - Refresh -
    - -
    -
    -
    -
    -
    - - E - Edit, tries to determine the variable type by itself.
    - C - Change, asks you for the var type first.
    - M - Mass modify: changes this variable for all objects of this type.
    -
    -
    - - - - - -
    -
    - Search: -
    -
    - -
    -
    -
      - [variable_html.Join()] -
    - - - - "} - - usr << browse(html, "window=variables[refid];size=475x650") - -#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) -/proc/debug_variable(name, value, level, var/datum/DA = null, sanitize = TRUE) - var/header - if(DA) - if(islist(DA)) - var/index = name - if(value) - name = DA[name] // name is really the index until this line - else - value = DA[name] - header = "
  • (E) (C) (-) " - else - header = "
  • (E) (C) (M) " - else - header = "
  • " - - var/item - if(isnull(value)) - item = "[VV_HTML_ENCODE(name)] = null" - - else if(istext(value)) - item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" - - else if(isicon(value)) - #ifdef VARSICON - item = "[name] = /icon ([value]) [bicon(value, use_class=0)]" - #else - item = "[name] = /icon ([value])" - #endif - - else if(istype(value, /image)) - var/image/I = value - #ifdef VARSICON - item = "[name] \ref[value] = /image ([value]) [bicon(value, use_class=0)]" - #else - item = "[name] \ref[value] = /image ([value])" - #endif - - else if(isfile(value)) - item = "[VV_HTML_ENCODE(name)] = '[value]'" - - else if(istype(value, /datum)) - var/datum/D = value - item = "[VV_HTML_ENCODE(name)] \ref[value] = [D.type]" - - else if(istype(value, /client)) - var/client/C = value - item = "[VV_HTML_ENCODE(name)] \ref[value] = [C] [C.type]" -// - else if(islist(value)) - var/list/L = value - var/list/items = list() - - if(L.len > 0 && !(name == "underlays" || name == "overlays" || name == "vars" || L.len > (IS_NORMAL_LIST(L) ? 250 : 300))) - for(var/i in 1 to L.len) - var/key = L[i] - var/val - if(IS_NORMAL_LIST(L) && !isnum(key)) - val = L[key] - if(!val) - val = key - key = i - - items += debug_variable(key, val, level + 1, sanitize = sanitize) - - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])
      [items.Join()]
    " - - else - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" - - else - item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" - - return "[header][item]
  • " - -#undef VV_HTML_ENCODE - -/client/proc/view_var_Topic(href, href_list, hsrc) - //This should all be moved over to datum/admins/Topic() or something ~Carn - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(view_var_Topic_list(href, href_list, hsrc)) // done because you can't use UIDs with lists and I don't want to snowflake into the below check to supress warnings - return - - // Correct and warn about any VV topic links that aren't using UIDs - for(var/paramname in href_list) - if(findtext(href_list[paramname], "]_")) - continue // Contains UID-specific formatting, skip it - var/datum/D = locate(href_list[paramname]) - if(!D) - continue - var/datuminfo = "[D]" - if(istype(D)) - datuminfo = datum_info_line(D) - href_list[paramname] = D.UID() - else if(isclient(D)) - var/client/C = D - href_list[paramname] = C.UID() - log_runtime(EXCEPTION("Found \\ref-based '[paramname]' param in VV topic for [datuminfo], should be UID: [href]")) - - if(href_list["Vars"]) - debug_variables(locateUID(href_list["Vars"])) - - //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records). - else if(href_list["rename"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["rename"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - var/new_name = reject_bad_name(sanitize(copytext(input(usr, "What would you like to name this mob?", "Input a name", M.real_name) as text|null, 1, MAX_NAME_LEN)), allow_numbers = TRUE) - if( !new_name || !M ) return - - message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].") - M.rename_character(M.real_name, new_name) - href_list["datumrefresh"] = href_list["rename"] - - else if(href_list["varnameedit"] && href_list["datumedit"]) - if(!check_rights(R_VAREDIT)) return - - var/D = locateUID(href_list["datumedit"]) - if(!istype(D,/datum) && !istype(D,/client)) - to_chat(usr, "This can only be used on instances of types /client or /datum") - return - - modify_variables(D, href_list["varnameedit"], 1) - - else if(href_list["togbit"]) - if(!check_rights(R_VAREDIT)) return - - var/atom/D = locateUID(href_list["subject"]) - if(!istype(D,/datum) && !istype(D,/client)) - to_chat(usr, "This can only be used on instances of types /client or /datum") - return - if(!(href_list["var"] in D.vars)) - to_chat(usr, "Unable to find variable specified.") - return - var/value = D.vars[href_list["var"]] - value ^= 1 << text2num(href_list["togbit"]) - - D.vars[href_list["var"]] = value - - else if(href_list["varnamechange"] && href_list["datumchange"]) - if(!check_rights(R_VAREDIT)) return - - var/D = locateUID(href_list["datumchange"]) - if(!istype(D,/datum) && !istype(D,/client)) - to_chat(usr, "This can only be used on instances of types /client or /datum") - return - - modify_variables(D, href_list["varnamechange"], 0) - - else if(href_list["varnamemass"] && href_list["datummass"]) - if(!check_rights(R_VAREDIT)) return - - var/atom/A = locateUID(href_list["datummass"]) - if(!istype(A)) - to_chat(usr, "This can only be used on instances of type /atom") - return - - cmd_mass_modify_object_variables(A, href_list["varnamemass"]) - - - else if(href_list["mob_player_panel"]) - if(!check_rights(R_ADMIN|R_MOD)) return - - var/mob/M = locateUID(href_list["mob_player_panel"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.holder.show_player_panel(M) - href_list["datumrefresh"] = href_list["mob_player_panel"] - - else if(href_list["give_spell"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/M = locateUID(href_list["give_spell"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.give_spell(M) - href_list["datumrefresh"] = href_list["give_spell"] - - else if(href_list["givemartialart"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/living/carbon/C = locateUID(href_list["givemartialart"]) - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/list/artpaths = subtypesof(/datum/martial_art) - var/list/artnames = list() - for(var/i in artpaths) - var/datum/martial_art/M = i - artnames[initial(M.name)] = M - - var/result = input(usr, "Choose the martial art to teach", "JUDO CHOP") as null|anything in artnames - if(!usr) - return - if(QDELETED(C)) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(result) - var/chosenart = artnames[result] - var/datum/martial_art/MA = new chosenart - MA.teach(C) - - href_list["datumrefresh"] = href_list["givemartialart"] - - else if(href_list["give_disease"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/M = locateUID(href_list["give_disease"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.give_disease(M) - href_list["datumrefresh"] = href_list["give_spell"] - - else if(href_list["godmode"]) - if(!check_rights(R_REJUVINATE)) return - - var/mob/M = locateUID(href_list["godmode"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.cmd_admin_godmode(M) - href_list["datumrefresh"] = href_list["godmode"] - - else if(href_list["gib"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - var/mob/M = locateUID(href_list["gib"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.cmd_admin_gib(M) - - else if(href_list["build_mode"]) - if(!check_rights(R_BUILDMODE)) return - - var/mob/M = locateUID(href_list["build_mode"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - togglebuildmode(M) - href_list["datumrefresh"] = href_list["build_mode"] - - else if(href_list["drop_everything"]) - if(!check_rights(R_DEBUG|R_ADMIN)) return - - var/mob/M = locateUID(href_list["drop_everything"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(usr.client) - usr.client.cmd_admin_drop_everything(M) - - else if(href_list["direct_control"]) - if(!check_rights(R_DEBUG|R_ADMIN)) return - - var/mob/M = locateUID(href_list["direct_control"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(usr.client) - usr.client.cmd_assume_direct_control(M) - - else if(href_list["make_skeleton"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/living/carbon/human/H = locateUID(href_list["make_skeleton"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - var/confirm = alert("Are you sure you want to turn this mob into a skeleton?","Confirm Skeleton Transformation","Yes","No") - if(confirm != "Yes") - return - - H.makeSkeleton() - message_admins("[key_name(usr)] has turned [key_name(H)] into a skeleton") - log_admin("[key_name_admin(usr)] has turned [key_name_admin(H)] into a skeleton") - href_list["datumrefresh"] = href_list["make_skeleton"] - - else if(href_list["offer_control"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["offer_control"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - offer_control(M) - - else if(href_list["delete"]) - if(!check_rights(R_DEBUG, 0)) - return - - var/datum/D = locateUID(href_list["delete"]) - if(!D) - to_chat(usr, "Unable to locate item!") - admin_delete(D) - href_list["datumrefresh"] = href_list["delete"] - - else if(href_list["delall"]) - if(!check_rights(R_DEBUG|R_SERVER)) return - - var/obj/O = locateUID(href_list["delall"]) - if(!isobj(O)) - to_chat(usr, "This can only be used on instances of type /obj") - return - - var/action_type = alert("Strict type ([O.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") - if(action_type == "Cancel" || !action_type) - return - - if(alert("Are you really sure you want to delete all objects of type [O.type]?",,"Yes","No") != "Yes") - return - - if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes") - return - - var/O_type = O.type - switch(action_type) - if("Strict type") - var/i = 0 - for(var/obj/Obj in world) - if(Obj.type == O_type) - i++ - qdel(Obj) - if(!i) - to_chat(usr, "No objects of this type exist") - return - log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted)") - message_admins("[key_name_admin(usr)] deleted all objects of type [O_type] ([i] objects deleted)") - if("Type and subtypes") - var/i = 0 - for(var/obj/Obj in world) - if(istype(Obj,O_type)) - i++ - qdel(Obj) - if(!i) - to_chat(usr, "No objects of this type exist") - return - log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") - message_admins("[key_name_admin(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") - - else if(href_list["makespeedy"]) - if(!check_rights(R_DEBUG|R_ADMIN)) - return - var/obj/A = locateUID(href_list["makespeedy"]) - if(!istype(A)) - return - A.var_edited = TRUE - A.makeSpeedProcess() - log_admin("[key_name(usr)] has made [A] speed process") - message_admins("[key_name(usr)] has made [A] speed process") - return TRUE - - else if(href_list["makenormalspeed"]) - if(!check_rights(R_DEBUG|R_ADMIN)) - return - var/obj/A = locateUID(href_list["makenormalspeed"]) - if(!istype(A)) - return - A.var_edited = TRUE - A.makeNormalProcess() - log_admin("[key_name(usr)] has made [A] process normally") - message_admins("[key_name(usr)] has made [A] process normally") - return TRUE - - else if(href_list["addreagent"]) /* Made on /TG/, credit to them. */ - if(!check_rights(R_DEBUG|R_ADMIN)) return - - var/atom/A = locateUID(href_list["addreagent"]) - - if(!A.reagents) - var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num - if(amount) - A.create_reagents(amount) - - if(A.reagents) - var/chosen_id - var/list/reagent_options = sortAssoc(GLOB.chemical_reagents_list) - switch(alert(usr, "Choose a method.", "Add Reagents", "Enter ID", "Choose ID")) - if("Enter ID") - var/valid_id - while(!valid_id) - chosen_id = stripped_input(usr, "Enter the ID of the reagent you want to add.") - if(!chosen_id) //Get me out of here! - break - for(var/ID in reagent_options) - if(ID == chosen_id) - valid_id = 1 - if(!valid_id) - to_chat(usr, "A reagent with that ID doesn't exist!") - if("Choose ID") - chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in reagent_options - if(chosen_id) - var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num - if(amount) - A.reagents.add_reagent(chosen_id, amount) - log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") - message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") - - else if(href_list["explode"]) - if(!check_rights(R_DEBUG|R_EVENT)) return - - var/atom/A = locateUID(href_list["explode"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) - to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") - return - - src.cmd_admin_explosion(A) - href_list["datumrefresh"] = href_list["explode"] - - else if(href_list["emp"]) - if(!check_rights(R_DEBUG|R_EVENT)) return - - var/atom/A = locateUID(href_list["emp"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) - to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") - return - - src.cmd_admin_emp(A) - href_list["datumrefresh"] = href_list["emp"] - - else if(href_list["mark_object"]) - if(!check_rights(0)) return - - var/datum/D = locateUID(href_list["mark_object"]) - if(!istype(D)) - to_chat(usr, "This can only be done to instances of type /datum") - return - - src.holder.marked_datum = D - href_list["datumrefresh"] = href_list["mark_object"] - - else if(href_list["proc_call"]) - if(!check_rights(R_PROCCALL)) - return - - var/T = locateUID(href_list["proc_call"]) - - if(T) - callproc_datum(T) - - else if(href_list["jump_to"]) - if(!check_rights(R_ADMIN)) - return - - var/atom/A = locateUID(href_list["jump_to"]) - var/turf/T = get_turf(A) - if(T) - usr.client.jumptoturf(T) - href_list["datumrefresh"] = href_list["jump_to"] - - - else if(href_list["rotatedatum"]) - if(!check_rights(R_DEBUG|R_ADMIN)) return - - var/atom/A = locateUID(href_list["rotatedatum"]) - if(!istype(A)) - to_chat(usr, "This can only be done to instances of type /atom") - return - - switch(href_list["rotatedir"]) - if("right") A.dir = turn(A.dir, -45) - if("left") A.dir = turn(A.dir, 45) - - message_admins("[key_name_admin(usr)] has rotated \the [A]") - log_admin("[key_name(usr)] has rotated \the [A]") - href_list["datumrefresh"] = href_list["rotatedatum"] - - else if(href_list["makemonkey"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makemonkey"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("monkeyone"=href_list["makemonkey"])) - - else if(href_list["makerobot"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makerobot"=href_list["makerobot"])) - - else if(href_list["makealien"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makealien"=href_list["makealien"])) - - else if(href_list["makeslime"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makeslime"=href_list["makeslime"])) - - else if(href_list["makesuper"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makesuper"=href_list["makesuper"])) - - else if(href_list["makeai"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makeai"=href_list["makeai"])) - - else if(href_list["setspecies"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["setspecies"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - var/new_species = input("Please choose a new species.","Species",null) as null|anything in GLOB.all_species - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - var/datum/species/S = GLOB.all_species[new_species] - if(H.set_species(S.type)) - to_chat(usr, "Set species of [H] to [H.dna.species].") - H.regenerate_icons() - message_admins("[key_name_admin(usr)] has changed the species of [key_name_admin(H)] to [new_species]") - log_admin("[key_name(usr)] has changed the species of [key_name(H)] to [new_species]") - else - to_chat(usr, "Failed! Something went wrong.") - - else if(href_list["addlanguage"]) - if(!check_rights(R_SPAWN)) return - - var/mob/H = locateUID(href_list["addlanguage"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob") - return - - var/new_language = input("Please choose a language to add.","Language",null) as null|anything in GLOB.all_languages - - if(!new_language) - return - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(H.add_language(new_language)) - to_chat(usr, "Added [new_language] to [H].") - message_admins("[key_name_admin(usr)] has given [key_name_admin(H)] the language [new_language]") - log_admin("[key_name(usr)] has given [key_name(H)] the language [new_language]") - else - to_chat(usr, "Mob already knows that language.") - - else if(href_list["remlanguage"]) - if(!check_rights(R_SPAWN)) return - - var/mob/H = locateUID(href_list["remlanguage"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob") - return - - if(!H.languages.len) - to_chat(usr, "This mob knows no languages.") - return - - var/datum/language/rem_language = input("Please choose a language to remove.","Language",null) as null|anything in H.languages - - if(!rem_language) - return - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(H.remove_language(rem_language.name)) - to_chat(usr, "Removed [rem_language] from [H].") - message_admins("[key_name_admin(usr)] has removed language [rem_language] from [key_name_admin(H)]") - log_admin("[key_name(usr)] has removed language [rem_language] from [key_name(H)]") - else - to_chat(usr, "Mob doesn't know that language.") - - else if(href_list["addverb"]) - if(!check_rights(R_DEBUG)) return - - var/mob/living/H = locateUID(href_list["addverb"]) - - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living") - return - var/list/possibleverbs = list() - possibleverbs += "Cancel" // One for the top... - possibleverbs += typesof(/mob/proc,/mob/verb,/mob/living/proc,/mob/living/verb) - switch(H.type) - if(/mob/living/carbon/human) - possibleverbs += typesof(/mob/living/carbon/proc,/mob/living/carbon/verb,/mob/living/carbon/human/verb,/mob/living/carbon/human/proc) - if(/mob/living/silicon/robot) - possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/robot/proc,/mob/living/silicon/robot/verb) - if(/mob/living/silicon/ai) - possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/ai/proc,/mob/living/silicon/ai/verb) - possibleverbs -= H.verbs - possibleverbs += "Cancel" // ...And one for the bottom - - var/verb = input("Select a verb!", "Verbs",null) as anything in possibleverbs - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - if(!verb || verb == "Cancel") - return - else - H.verbs += verb - message_admins("[key_name_admin(usr)] has given [key_name_admin(H)] the verb [verb]") - log_admin("[key_name(usr)] has given [key_name(H)] the verb [verb]") - - else if(href_list["remverb"]) - if(!check_rights(R_DEBUG)) return - - var/mob/H = locateUID(href_list["remverb"]) - - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob") - return - var/verb = input("Please choose a verb to remove.","Verbs",null) as null|anything in H.verbs - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - if(!verb) - return - else - H.verbs -= verb - message_admins("[key_name_admin(usr)] has removed verb [verb] from [key_name_admin(H)]") - log_admin("[key_name(usr)] has removed verb [verb] from [key_name(H)]") - - else if(href_list["addorgan"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/M = locateUID(href_list["addorgan"]) - if(!istype(M)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/new_organ = input("Please choose an organ to add.","Organ",null) as null|anything in subtypesof(/obj/item/organ)-/obj/item/organ - if(!new_organ) return - - if(!M) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(locateUID(new_organ) in M.internal_organs) - to_chat(usr, "Mob already has that organ.") - return - var/obj/item/organ/internal/organ = new new_organ - organ.insert(M) - message_admins("[key_name_admin(usr)] has given [key_name_admin(M)] the organ [new_organ]") - log_admin("[key_name(usr)] has given [key_name(M)] the organ [new_organ]") - - else if(href_list["remorgan"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/M = locateUID(href_list["remorgan"]) - if(!istype(M)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/obj/item/organ/internal/rem_organ = input("Please choose an organ to remove.","Organ",null) as null|anything in M.internal_organs - - if(!M) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(!(rem_organ in M.internal_organs)) - to_chat(usr, "Mob does not have that organ.") - return - - to_chat(usr, "Removed [rem_organ] from [M].") - rem_organ.remove(M) - message_admins("[key_name_admin(usr)] has removed the organ [rem_organ] from [key_name_admin(M)]") - log_admin("[key_name(usr)] has removed the organ [rem_organ] from [key_name(M)]") - qdel(rem_organ) - - else if(href_list["fix_nano"]) - if(!check_rights(R_DEBUG)) return - - var/mob/H = locateUID(href_list["fix_nano"]) - - if(!istype(H) || !H.client) - to_chat(usr, "This can only be done on mobs with clients") - return - - H.client.reload_nanoui_resources() - - to_chat(usr, "Resource files sent") - to_chat(H, "Your NanoUI Resource files have been refreshed") - - log_admin("[key_name(usr)] resent the NanoUI resource files to [key_name(H)]") - - else if(href_list["regenerateicons"]) - if(!check_rights(0)) return - - var/mob/M = locateUID(href_list["regenerateicons"]) - if(!ismob(M)) - to_chat(usr, "This can only be done to instances of type /mob") - return - M.regenerate_icons() - - else if(href_list["adjustDamage"] && href_list["mobToDamage"]) - if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return - - var/mob/living/L = locateUID(href_list["mobToDamage"]) - if(!istype(L)) return - - var/Text = href_list["adjustDamage"] - - var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num - - if(!L) - to_chat(usr, "Mob doesn't exist anymore") - return - - switch(Text) - if("brute") - if(ishuman(L)) - var/mob/living/carbon/human/H = L - H.adjustBruteLoss(amount, robotic = TRUE) - else - L.adjustBruteLoss(amount) - if("fire") - if(ishuman(L)) - var/mob/living/carbon/human/H = L - H.adjustFireLoss(amount, robotic = TRUE) - else - L.adjustFireLoss(amount) - if("toxin") - L.adjustToxLoss(amount) - if("oxygen") - L.adjustOxyLoss(amount) - if("brain") - L.adjustBrainLoss(amount) - if("clone") - L.adjustCloneLoss(amount) - if("stamina") - L.adjustStaminaLoss(amount) - else - to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") - return - - if(amount != 0) - log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L]") - message_admins("[key_name_admin(usr)] dealt [amount] amount of [Text] damage to [L]") - href_list["datumrefresh"] = href_list["mobToDamage"] - - else if(href_list["traitmod"]) - if(!check_rights(NONE)) - return - var/datum/A = locateUID(href_list["traitmod"]) - if(!istype(A)) - return - holder.modify_traits(A) - - if(href_list["datumrefresh"]) - var/datum/DAT = locateUID(href_list["datumrefresh"]) - if(!istype(DAT, /datum) && !isclient(DAT)) - return - src.debug_variables(DAT) - - if(href_list["copyoutfit"]) - if(!check_rights(R_EVENT)) - return - var/mob/living/carbon/human/H = locateUID(href_list["copyoutfit"]) - if(istype(H)) - H.copy_outfit() - -/client/proc/view_var_Topic_list(href, href_list, hsrc) - if(href_list["VarsList"]) - debug_variables(locate(href_list["VarsList"])) - return TRUE - - if(href_list["listedit"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if(!index) - return TRUE - - var/list/L = locate(href_list["listedit"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list(L, null, "list", "contents", index, autodetect_class = TRUE) - return TRUE - - if(href_list["listchange"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if(!index) - return TRUE - - var/list/L = locate(href_list["listchange"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list(L, null, "list", "contents", index, autodetect_class = FALSE) - return TRUE - - if(href_list["listremove"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if(!index) - return TRUE - - var/list/L = locate(href_list["listremove"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - var/variable = L[index] - var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No") - if(prompt != "Yes") - return - L.Cut(index, index+1) - log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") - log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") - message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") - return TRUE - - if(href_list["listadd"]) - var/list/L = locate(href_list["listadd"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - - mod_list_add(L, null, "list", "contents") - return TRUE - - if(href_list["listdupes"]) - var/list/L = locate(href_list["listdupes"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - - uniqueList_inplace(L) - log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES") - log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") - message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") - return TRUE - - if(href_list["listnulls"]) - var/list/L = locate(href_list["listnulls"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - - listclearnulls(L) - log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS") - log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") - message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") - return TRUE - - if(href_list["listlen"]) - var/list/L = locate(href_list["listlen"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - var/value = vv_get_value(VV_NUM) - if(value["class"] != VV_NUM) - return TRUE - - L.len = value["value"] - log_world("### ListVarEdit by [src]: /list len: [L.len]") - log_admin("[key_name(src)] modified list's len: [L.len]") - message_admins("[key_name_admin(src)] modified list's len: [L.len]") - return TRUE - - if(href_list["listshuffle"]) - var/list/L = locate(href_list["listshuffle"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - - shuffle_inplace(L) - log_world("### ListVarEdit by [src]: /list contents: SHUFFLE") - log_admin("[key_name(src)] modified list's contents: SHUFFLE") - message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") - return TRUE - - if(href_list["listrefresh"]) - debug_variables(locate(href_list["listrefresh"])) - return TRUE +// reference: /client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0) + +/datum/proc/can_vv_get(var_name) + return TRUE + +// /client/proc/can_vv_get(var_name) +// return TRUE + +/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited + switch(var_name) + if("vars") + return FALSE + if("var_edited") + return FALSE + var_edited = TRUE + vars[var_name] = var_value + + . = TRUE + + +/client/vv_edit_var(var_name, var_value) //called whenever a var is edited + switch(var_name) + if("vars") + return FALSE + if("var_edited") + return FALSE + var_edited = TRUE + vars[var_name] = var_value + + . = TRUE + +/datum/proc/vv_get_var(var_name) + switch(var_name) + if("attack_log_old", "debug_log") + return debug_variable(var_name, vars[var_name], 0, src, sanitize = FALSE) + if("vars") + return debug_variable(var_name, list(), 0, src) + return debug_variable(var_name, vars[var_name], 0, src) + +/client/vv_get_var(var_name) + switch(var_name) + if("vars") + return debug_variable(var_name, list(), 0, src) + return debug_variable(var_name, vars[var_name], 0, src) + +/datum/proc/can_vv_delete() + return TRUE + +//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down +//add seperaters by doing . += "---" +/datum/proc/vv_get_dropdown() + . = list() + . += "---" + .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" + .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" + .["Jump to Object"] = "?_src_=vars;jump_to=[UID()]" + .["Delete"] = "?_src_=vars;delete=[UID()]" + .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]" + . += "---" + +/client/vv_get_dropdown() + . = list() + . += "---" + .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" + .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" + .["Delete"] = "?_src_=vars;delete=[UID()]" + .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]" + . += "---" + +/client/proc/debug_variables(datum/D in world) + set category = "Debug" + set name = "View Variables" + + var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. + + if(!is_admin(usr)) + to_chat(usr, "You need to be an administrator to access this.") + return + + if(!D) + return + + + var/islist = islist(D) + var/isclient = isclient(D) + if(!islist && !isclient && !istype(D)) + return + + var/title = "" + var/icon/sprite + var/hash + var/refid + + if(!islist) + refid = "[D.UID()]" + else + refid = "\ref[D]" + + var/type = /list + if(!islist) + type = D.type + + if(isatom(D)) + var/atom/A = D + if(A.icon && A.icon_state) + sprite = new /icon(A.icon, A.icon_state) + hash = md5(A.icon) + hash = md5(hash + A.icon_state) + usr << browse_rsc(sprite, "vv[hash].png") + + + var/sprite_text + if(sprite) + sprite_text = "" + + + var/list/atomsnowflake = list() + if(isatom(D)) + var/atom/A = D + if(isliving(A)) + var/mob/living/L = A + atomsnowflake += "[L]" + if(L.dir) + atomsnowflake += "
    << [dir2text(L.dir)] >>" + atomsnowflake += {" +
    [L.ckey ? L.ckey : "No ckey"] / [L.real_name ? L.real_name : "No real name"] +
    + BRUTE:[L.getBruteLoss()] + FIRE:[L.getFireLoss()] + TOXIN:[L.getToxLoss()] + OXY:[L.getOxyLoss()] + CLONE:[L.getCloneLoss()] + BRAIN:[L.getBrainLoss()] + STAMINA:[L.getStaminaLoss()] + + "} + else + atomsnowflake += "[A]" + if(A.dir) + atomsnowflake += "
    << [dir2text(A.dir)] >>" + else + atomsnowflake += "[D]" + + + var/formatted_type = "[type]" + if(length(formatted_type) > 25) + var/middle_point = length(formatted_type) / 2 + var/splitpoint = findtext(formatted_type, "/", middle_point) + if(splitpoint) + formatted_type = "[copytext(formatted_type, 1, splitpoint)]
    [copytext(formatted_type, splitpoint)]" + else + formatted_type = "Type too long" //No suitable splitpoint (/) found. + + + var/marked + if(holder.marked_datum && holder.marked_datum == D) + marked = "
    Marked Object" + + + var/varedited_line = "" + if(isatom(D)) + var/atom/A = D + if(A.admin_spawned) + varedited_line += "
    Admin Spawned" + + + if(!islist && D.var_edited) + varedited_line += "
    Var Edited" + + + var/dropdownoptions = list() + if(islist) + dropdownoptions = list( + "---", + "Add Item" = "?_src_=vars;listadd=[refid]", + "Remove Nulls" = "?_src_=vars;listnulls=[refid]", + "Remove Dupes" = "?_src_=vars;listdupes=[refid]", + "Set len" = "?_src_=vars;listlen=[refid]", + "Shuffle" = "?_src_=vars;listshuffle=[refid]" + ) + else + dropdownoptions = D.vv_get_dropdown() + + + var/list/dropdownoptions_html = list() + for(var/name in dropdownoptions) + var/link = dropdownoptions[name] + if(link) + dropdownoptions_html += "" + else + dropdownoptions_html += "" + + + var/list/names = list() + if(!islist) + for(var/V in D.vars) + names += V + + + sleep(1) // Without a sleep here, VV sometimes disconnects clients + + + var/list/variable_html = list() + if(islist) + var/list/L = D + for(var/i in 1 to L.len) + var/key = L[i] + var/value + if(IS_NORMAL_LIST(L) && !isnum(key)) + value = L[key] + variable_html += debug_variable(i, value, 0, D) + else + names = sortList(names) + for(var/V in names) + if(D.can_vv_get(V)) + variable_html += D.vv_get_var(V) + + var/html = {" + + + [title] + + + + +
    + + + + + +
    + + + + +
    + [sprite_text] +
    + [atomsnowflake.Join()] +
    +
    +
    + [formatted_type] + [marked] + [varedited_line] +
    +
    +
    + Refresh +
    + +
    +
    +
    +
    +
    + + E - Edit, tries to determine the variable type by itself.
    + C - Change, asks you for the var type first.
    + M - Mass modify: changes this variable for all objects of this type.
    +
    +
    + + + + + +
    +
    + Search: +
    +
    + +
    +
    +
      + [variable_html.Join()] +
    + + + + "} + + usr << browse(html, "window=variables[refid];size=475x650") + +#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) +/proc/debug_variable(name, value, level, var/datum/DA = null, sanitize = TRUE) + var/header + if(DA) + if(islist(DA)) + var/index = name + if(value) + name = DA[name] // name is really the index until this line + else + value = DA[name] + header = "
  • (E) (C) (-) " + else + header = "
  • (E) (C) (M) " + else + header = "
  • " + + var/item + if(isnull(value)) + item = "[VV_HTML_ENCODE(name)] = null" + + else if(istext(value)) + item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" + + else if(isicon(value)) + #ifdef VARSICON + item = "[name] = /icon ([value]) [bicon(value, use_class=0)]" + #else + item = "[name] = /icon ([value])" + #endif + + else if(istype(value, /image)) + var/image/I = value + #ifdef VARSICON + item = "[name] \ref[value] = /image ([value]) [bicon(value, use_class=0)]" + #else + item = "[name] \ref[value] = /image ([value])" + #endif + + else if(isfile(value)) + item = "[VV_HTML_ENCODE(name)] = '[value]'" + + else if(istype(value, /datum)) + var/datum/D = value + item = "[VV_HTML_ENCODE(name)] \ref[value] = [D.type]" + + else if(istype(value, /client)) + var/client/C = value + item = "[VV_HTML_ENCODE(name)] \ref[value] = [C] [C.type]" +// + else if(islist(value)) + var/list/L = value + var/list/items = list() + + if(L.len > 0 && !(name == "underlays" || name == "overlays" || name == "vars" || L.len > (IS_NORMAL_LIST(L) ? 250 : 300))) + for(var/i in 1 to L.len) + var/key = L[i] + var/val + if(IS_NORMAL_LIST(L) && !isnum(key)) + val = L[key] + if(!val) + val = key + key = i + + items += debug_variable(key, val, level + 1, sanitize = sanitize) + + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])
      [items.Join()]
    " + + else + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" + + else + item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" + + return "[header][item]
  • " + +#undef VV_HTML_ENCODE + +/client/proc/view_var_Topic(href, href_list, hsrc) + //This should all be moved over to datum/admins/Topic() or something ~Carn + if(!check_rights(R_ADMIN|R_MOD)) + return + + if(view_var_Topic_list(href, href_list, hsrc)) // done because you can't use UIDs with lists and I don't want to snowflake into the below check to supress warnings + return + + // Correct and warn about any VV topic links that aren't using UIDs + for(var/paramname in href_list) + if(findtext(href_list[paramname], "]_")) + continue // Contains UID-specific formatting, skip it + var/datum/D = locate(href_list[paramname]) + if(!D) + continue + var/datuminfo = "[D]" + if(istype(D)) + datuminfo = datum_info_line(D) + href_list[paramname] = D.UID() + else if(isclient(D)) + var/client/C = D + href_list[paramname] = C.UID() + log_runtime(EXCEPTION("Found \\ref-based '[paramname]' param in VV topic for [datuminfo], should be UID: [href]")) + + if(href_list["Vars"]) + debug_variables(locateUID(href_list["Vars"])) + + //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records). + else if(href_list["rename"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["rename"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + var/new_name = reject_bad_name(sanitize(copytext(input(usr, "What would you like to name this mob?", "Input a name", M.real_name) as text|null, 1, MAX_NAME_LEN)), allow_numbers = TRUE) + if( !new_name || !M ) return + + message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].") + M.rename_character(M.real_name, new_name) + href_list["datumrefresh"] = href_list["rename"] + + else if(href_list["varnameedit"] && href_list["datumedit"]) + if(!check_rights(R_VAREDIT)) return + + var/D = locateUID(href_list["datumedit"]) + if(!istype(D,/datum) && !istype(D,/client)) + to_chat(usr, "This can only be used on instances of types /client or /datum") + return + + modify_variables(D, href_list["varnameedit"], 1) + + else if(href_list["togbit"]) + if(!check_rights(R_VAREDIT)) return + + var/atom/D = locateUID(href_list["subject"]) + if(!istype(D,/datum) && !istype(D,/client)) + to_chat(usr, "This can only be used on instances of types /client or /datum") + return + if(!(href_list["var"] in D.vars)) + to_chat(usr, "Unable to find variable specified.") + return + var/value = D.vars[href_list["var"]] + value ^= 1 << text2num(href_list["togbit"]) + + D.vars[href_list["var"]] = value + + else if(href_list["varnamechange"] && href_list["datumchange"]) + if(!check_rights(R_VAREDIT)) return + + var/D = locateUID(href_list["datumchange"]) + if(!istype(D,/datum) && !istype(D,/client)) + to_chat(usr, "This can only be used on instances of types /client or /datum") + return + + modify_variables(D, href_list["varnamechange"], 0) + + else if(href_list["varnamemass"] && href_list["datummass"]) + if(!check_rights(R_VAREDIT)) return + + var/atom/A = locateUID(href_list["datummass"]) + if(!istype(A)) + to_chat(usr, "This can only be used on instances of type /atom") + return + + cmd_mass_modify_object_variables(A, href_list["varnamemass"]) + + + else if(href_list["mob_player_panel"]) + if(!check_rights(R_ADMIN|R_MOD)) return + + var/mob/M = locateUID(href_list["mob_player_panel"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.holder.show_player_panel(M) + href_list["datumrefresh"] = href_list["mob_player_panel"] + + else if(href_list["give_spell"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/M = locateUID(href_list["give_spell"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.give_spell(M) + href_list["datumrefresh"] = href_list["give_spell"] + + else if(href_list["givemartialart"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/living/carbon/C = locateUID(href_list["givemartialart"]) + if(!istype(C)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") + return + + var/list/artpaths = subtypesof(/datum/martial_art) + var/list/artnames = list() + for(var/i in artpaths) + var/datum/martial_art/M = i + artnames[initial(M.name)] = M + + var/result = input(usr, "Choose the martial art to teach", "JUDO CHOP") as null|anything in artnames + if(!usr) + return + if(QDELETED(C)) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(result) + var/chosenart = artnames[result] + var/datum/martial_art/MA = new chosenart + MA.teach(C) + + href_list["datumrefresh"] = href_list["givemartialart"] + + else if(href_list["give_disease"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/M = locateUID(href_list["give_disease"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.give_disease(M) + href_list["datumrefresh"] = href_list["give_spell"] + + else if(href_list["godmode"]) + if(!check_rights(R_REJUVINATE)) return + + var/mob/M = locateUID(href_list["godmode"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.cmd_admin_godmode(M) + href_list["datumrefresh"] = href_list["godmode"] + + else if(href_list["gib"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + var/mob/M = locateUID(href_list["gib"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.cmd_admin_gib(M) + + else if(href_list["build_mode"]) + if(!check_rights(R_BUILDMODE)) return + + var/mob/M = locateUID(href_list["build_mode"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + togglebuildmode(M) + href_list["datumrefresh"] = href_list["build_mode"] + + else if(href_list["drop_everything"]) + if(!check_rights(R_DEBUG|R_ADMIN)) return + + var/mob/M = locateUID(href_list["drop_everything"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(usr.client) + usr.client.cmd_admin_drop_everything(M) + + else if(href_list["direct_control"]) + if(!check_rights(R_DEBUG|R_ADMIN)) return + + var/mob/M = locateUID(href_list["direct_control"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(usr.client) + usr.client.cmd_assume_direct_control(M) + + else if(href_list["make_skeleton"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/living/carbon/human/H = locateUID(href_list["make_skeleton"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + var/confirm = alert("Are you sure you want to turn this mob into a skeleton?","Confirm Skeleton Transformation","Yes","No") + if(confirm != "Yes") + return + + H.makeSkeleton() + message_admins("[key_name(usr)] has turned [key_name(H)] into a skeleton") + log_admin("[key_name_admin(usr)] has turned [key_name_admin(H)] into a skeleton") + href_list["datumrefresh"] = href_list["make_skeleton"] + + else if(href_list["offer_control"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["offer_control"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + offer_control(M) + + else if(href_list["delete"]) + if(!check_rights(R_DEBUG, 0)) + return + + var/datum/D = locateUID(href_list["delete"]) + if(!D) + to_chat(usr, "Unable to locate item!") + admin_delete(D) + href_list["datumrefresh"] = href_list["delete"] + + else if(href_list["delall"]) + if(!check_rights(R_DEBUG|R_SERVER)) return + + var/obj/O = locateUID(href_list["delall"]) + if(!isobj(O)) + to_chat(usr, "This can only be used on instances of type /obj") + return + + var/action_type = alert("Strict type ([O.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") + if(action_type == "Cancel" || !action_type) + return + + if(alert("Are you really sure you want to delete all objects of type [O.type]?",,"Yes","No") != "Yes") + return + + if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes") + return + + var/O_type = O.type + switch(action_type) + if("Strict type") + var/i = 0 + for(var/obj/Obj in world) + if(Obj.type == O_type) + i++ + qdel(Obj) + if(!i) + to_chat(usr, "No objects of this type exist") + return + log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted)") + message_admins("[key_name_admin(usr)] deleted all objects of type [O_type] ([i] objects deleted)") + if("Type and subtypes") + var/i = 0 + for(var/obj/Obj in world) + if(istype(Obj,O_type)) + i++ + qdel(Obj) + if(!i) + to_chat(usr, "No objects of this type exist") + return + log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") + message_admins("[key_name_admin(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") + + else if(href_list["makespeedy"]) + if(!check_rights(R_DEBUG|R_ADMIN)) + return + var/obj/A = locateUID(href_list["makespeedy"]) + if(!istype(A)) + return + A.var_edited = TRUE + A.makeSpeedProcess() + log_admin("[key_name(usr)] has made [A] speed process") + message_admins("[key_name(usr)] has made [A] speed process") + return TRUE + + else if(href_list["makenormalspeed"]) + if(!check_rights(R_DEBUG|R_ADMIN)) + return + var/obj/A = locateUID(href_list["makenormalspeed"]) + if(!istype(A)) + return + A.var_edited = TRUE + A.makeNormalProcess() + log_admin("[key_name(usr)] has made [A] process normally") + message_admins("[key_name(usr)] has made [A] process normally") + return TRUE + + else if(href_list["addreagent"]) /* Made on /TG/, credit to them. */ + if(!check_rights(R_DEBUG|R_ADMIN)) return + + var/atom/A = locateUID(href_list["addreagent"]) + + if(!A.reagents) + var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num + if(amount) + A.create_reagents(amount) + + if(A.reagents) + var/chosen_id + var/list/reagent_options = sortAssoc(GLOB.chemical_reagents_list) + switch(alert(usr, "Choose a method.", "Add Reagents", "Enter ID", "Choose ID")) + if("Enter ID") + var/valid_id + while(!valid_id) + chosen_id = stripped_input(usr, "Enter the ID of the reagent you want to add.") + if(!chosen_id) //Get me out of here! + break + for(var/ID in reagent_options) + if(ID == chosen_id) + valid_id = 1 + if(!valid_id) + to_chat(usr, "A reagent with that ID doesn't exist!") + if("Choose ID") + chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in reagent_options + if(chosen_id) + var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num + if(amount) + A.reagents.add_reagent(chosen_id, amount) + log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") + message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") + + else if(href_list["explode"]) + if(!check_rights(R_DEBUG|R_EVENT)) return + + var/atom/A = locateUID(href_list["explode"]) + if(!isobj(A) && !ismob(A) && !isturf(A)) + to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") + return + + src.cmd_admin_explosion(A) + href_list["datumrefresh"] = href_list["explode"] + + else if(href_list["emp"]) + if(!check_rights(R_DEBUG|R_EVENT)) return + + var/atom/A = locateUID(href_list["emp"]) + if(!isobj(A) && !ismob(A) && !isturf(A)) + to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") + return + + src.cmd_admin_emp(A) + href_list["datumrefresh"] = href_list["emp"] + + else if(href_list["mark_object"]) + if(!check_rights(0)) return + + var/datum/D = locateUID(href_list["mark_object"]) + if(!istype(D)) + to_chat(usr, "This can only be done to instances of type /datum") + return + + src.holder.marked_datum = D + href_list["datumrefresh"] = href_list["mark_object"] + + else if(href_list["proc_call"]) + if(!check_rights(R_PROCCALL)) + return + + var/T = locateUID(href_list["proc_call"]) + + if(T) + callproc_datum(T) + + else if(href_list["jump_to"]) + if(!check_rights(R_ADMIN)) + return + + var/atom/A = locateUID(href_list["jump_to"]) + var/turf/T = get_turf(A) + if(T) + usr.client.jumptoturf(T) + href_list["datumrefresh"] = href_list["jump_to"] + + + else if(href_list["rotatedatum"]) + if(!check_rights(R_DEBUG|R_ADMIN)) return + + var/atom/A = locateUID(href_list["rotatedatum"]) + if(!istype(A)) + to_chat(usr, "This can only be done to instances of type /atom") + return + + switch(href_list["rotatedir"]) + if("right") A.dir = turn(A.dir, -45) + if("left") A.dir = turn(A.dir, 45) + + message_admins("[key_name_admin(usr)] has rotated \the [A]") + log_admin("[key_name(usr)] has rotated \the [A]") + href_list["datumrefresh"] = href_list["rotatedatum"] + + else if(href_list["makemonkey"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makemonkey"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("monkeyone"=href_list["makemonkey"])) + + else if(href_list["makerobot"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makerobot"=href_list["makerobot"])) + + else if(href_list["makealien"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makealien"=href_list["makealien"])) + + else if(href_list["makeslime"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makeslime"=href_list["makeslime"])) + + else if(href_list["makesuper"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makesuper"=href_list["makesuper"])) + + else if(href_list["makeai"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makeai"=href_list["makeai"])) + + else if(href_list["setspecies"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["setspecies"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + var/new_species = input("Please choose a new species.","Species",null) as null|anything in GLOB.all_species + + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + + var/datum/species/S = GLOB.all_species[new_species] + if(H.set_species(S.type)) + to_chat(usr, "Set species of [H] to [H.dna.species].") + H.regenerate_icons() + message_admins("[key_name_admin(usr)] has changed the species of [key_name_admin(H)] to [new_species]") + log_admin("[key_name(usr)] has changed the species of [key_name(H)] to [new_species]") + else + to_chat(usr, "Failed! Something went wrong.") + + else if(href_list["addlanguage"]) + if(!check_rights(R_SPAWN)) return + + var/mob/H = locateUID(href_list["addlanguage"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob") + return + + var/new_language = input("Please choose a language to add.","Language",null) as null|anything in GLOB.all_languages + + if(!new_language) + return + + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(H.add_language(new_language)) + to_chat(usr, "Added [new_language] to [H].") + message_admins("[key_name_admin(usr)] has given [key_name_admin(H)] the language [new_language]") + log_admin("[key_name(usr)] has given [key_name(H)] the language [new_language]") + else + to_chat(usr, "Mob already knows that language.") + + else if(href_list["remlanguage"]) + if(!check_rights(R_SPAWN)) return + + var/mob/H = locateUID(href_list["remlanguage"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob") + return + + if(!H.languages.len) + to_chat(usr, "This mob knows no languages.") + return + + var/datum/language/rem_language = input("Please choose a language to remove.","Language",null) as null|anything in H.languages + + if(!rem_language) + return + + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(H.remove_language(rem_language.name)) + to_chat(usr, "Removed [rem_language] from [H].") + message_admins("[key_name_admin(usr)] has removed language [rem_language] from [key_name_admin(H)]") + log_admin("[key_name(usr)] has removed language [rem_language] from [key_name(H)]") + else + to_chat(usr, "Mob doesn't know that language.") + + else if(href_list["addverb"]) + if(!check_rights(R_DEBUG)) return + + var/mob/living/H = locateUID(href_list["addverb"]) + + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living") + return + var/list/possibleverbs = list() + possibleverbs += "Cancel" // One for the top... + possibleverbs += typesof(/mob/proc,/mob/verb,/mob/living/proc,/mob/living/verb) + switch(H.type) + if(/mob/living/carbon/human) + possibleverbs += typesof(/mob/living/carbon/proc,/mob/living/carbon/verb,/mob/living/carbon/human/verb,/mob/living/carbon/human/proc) + if(/mob/living/silicon/robot) + possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/robot/proc,/mob/living/silicon/robot/verb) + if(/mob/living/silicon/ai) + possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/ai/proc,/mob/living/silicon/ai/verb) + possibleverbs -= H.verbs + possibleverbs += "Cancel" // ...And one for the bottom + + var/verb = input("Select a verb!", "Verbs",null) as anything in possibleverbs + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + if(!verb || verb == "Cancel") + return + else + H.verbs += verb + message_admins("[key_name_admin(usr)] has given [key_name_admin(H)] the verb [verb]") + log_admin("[key_name(usr)] has given [key_name(H)] the verb [verb]") + + else if(href_list["remverb"]) + if(!check_rights(R_DEBUG)) return + + var/mob/H = locateUID(href_list["remverb"]) + + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob") + return + var/verb = input("Please choose a verb to remove.","Verbs",null) as null|anything in H.verbs + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + if(!verb) + return + else + H.verbs -= verb + message_admins("[key_name_admin(usr)] has removed verb [verb] from [key_name_admin(H)]") + log_admin("[key_name(usr)] has removed verb [verb] from [key_name(H)]") + + else if(href_list["addorgan"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/M = locateUID(href_list["addorgan"]) + if(!istype(M)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") + return + + var/new_organ = input("Please choose an organ to add.","Organ",null) as null|anything in subtypesof(/obj/item/organ)-/obj/item/organ + if(!new_organ) return + + if(!M) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(locateUID(new_organ) in M.internal_organs) + to_chat(usr, "Mob already has that organ.") + return + var/obj/item/organ/internal/organ = new new_organ + organ.insert(M) + message_admins("[key_name_admin(usr)] has given [key_name_admin(M)] the organ [new_organ]") + log_admin("[key_name(usr)] has given [key_name(M)] the organ [new_organ]") + + else if(href_list["remorgan"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/M = locateUID(href_list["remorgan"]) + if(!istype(M)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") + return + + var/obj/item/organ/internal/rem_organ = input("Please choose an organ to remove.","Organ",null) as null|anything in M.internal_organs + + if(!M) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(!(rem_organ in M.internal_organs)) + to_chat(usr, "Mob does not have that organ.") + return + + to_chat(usr, "Removed [rem_organ] from [M].") + rem_organ.remove(M) + message_admins("[key_name_admin(usr)] has removed the organ [rem_organ] from [key_name_admin(M)]") + log_admin("[key_name(usr)] has removed the organ [rem_organ] from [key_name(M)]") + qdel(rem_organ) + + else if(href_list["fix_nano"]) + if(!check_rights(R_DEBUG)) return + + var/mob/H = locateUID(href_list["fix_nano"]) + + if(!istype(H) || !H.client) + to_chat(usr, "This can only be done on mobs with clients") + return + + H.client.reload_nanoui_resources() + + to_chat(usr, "Resource files sent") + to_chat(H, "Your NanoUI Resource files have been refreshed") + + log_admin("[key_name(usr)] resent the NanoUI resource files to [key_name(H)]") + + else if(href_list["regenerateicons"]) + if(!check_rights(0)) return + + var/mob/M = locateUID(href_list["regenerateicons"]) + if(!ismob(M)) + to_chat(usr, "This can only be done to instances of type /mob") + return + M.regenerate_icons() + + else if(href_list["adjustDamage"] && href_list["mobToDamage"]) + if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return + + var/mob/living/L = locateUID(href_list["mobToDamage"]) + if(!istype(L)) return + + var/Text = href_list["adjustDamage"] + + var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num + + if(!L) + to_chat(usr, "Mob doesn't exist anymore") + return + + switch(Text) + if("brute") + if(ishuman(L)) + var/mob/living/carbon/human/H = L + H.adjustBruteLoss(amount, robotic = TRUE) + else + L.adjustBruteLoss(amount) + if("fire") + if(ishuman(L)) + var/mob/living/carbon/human/H = L + H.adjustFireLoss(amount, robotic = TRUE) + else + L.adjustFireLoss(amount) + if("toxin") + L.adjustToxLoss(amount) + if("oxygen") + L.adjustOxyLoss(amount) + if("brain") + L.adjustBrainLoss(amount) + if("clone") + L.adjustCloneLoss(amount) + if("stamina") + L.adjustStaminaLoss(amount) + else + to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") + return + + if(amount != 0) + log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L]") + message_admins("[key_name_admin(usr)] dealt [amount] amount of [Text] damage to [L]") + href_list["datumrefresh"] = href_list["mobToDamage"] + + else if(href_list["traitmod"]) + if(!check_rights(NONE)) + return + var/datum/A = locateUID(href_list["traitmod"]) + if(!istype(A)) + return + holder.modify_traits(A) + + if(href_list["datumrefresh"]) + var/datum/DAT = locateUID(href_list["datumrefresh"]) + if(!istype(DAT, /datum) && !isclient(DAT)) + return + src.debug_variables(DAT) + + if(href_list["copyoutfit"]) + if(!check_rights(R_EVENT)) + return + var/mob/living/carbon/human/H = locateUID(href_list["copyoutfit"]) + if(istype(H)) + H.copy_outfit() + +/client/proc/view_var_Topic_list(href, href_list, hsrc) + if(href_list["VarsList"]) + debug_variables(locate(href_list["VarsList"])) + return TRUE + + if(href_list["listedit"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if(!index) + return TRUE + + var/list/L = locate(href_list["listedit"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + mod_list(L, null, "list", "contents", index, autodetect_class = TRUE) + return TRUE + + if(href_list["listchange"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if(!index) + return TRUE + + var/list/L = locate(href_list["listchange"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + mod_list(L, null, "list", "contents", index, autodetect_class = FALSE) + return TRUE + + if(href_list["listremove"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if(!index) + return TRUE + + var/list/L = locate(href_list["listremove"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + var/variable = L[index] + var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No") + if(prompt != "Yes") + return + L.Cut(index, index+1) + log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") + log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") + message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") + return TRUE + + if(href_list["listadd"]) + var/list/L = locate(href_list["listadd"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + mod_list_add(L, null, "list", "contents") + return TRUE + + if(href_list["listdupes"]) + var/list/L = locate(href_list["listdupes"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + uniqueList_inplace(L) + log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES") + log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") + return TRUE + + if(href_list["listnulls"]) + var/list/L = locate(href_list["listnulls"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + listclearnulls(L) + log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS") + log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") + return TRUE + + if(href_list["listlen"]) + var/list/L = locate(href_list["listlen"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + var/value = vv_get_value(VV_NUM) + if(value["class"] != VV_NUM) + return TRUE + + L.len = value["value"] + log_world("### ListVarEdit by [src]: /list len: [L.len]") + log_admin("[key_name(src)] modified list's len: [L.len]") + message_admins("[key_name_admin(src)] modified list's len: [L.len]") + return TRUE + + if(href_list["listshuffle"]) + var/list/L = locate(href_list["listshuffle"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + shuffle_inplace(L) + log_world("### ListVarEdit by [src]: /list contents: SHUFFLE") + log_admin("[key_name(src)] modified list's contents: SHUFFLE") + message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") + return TRUE + + if(href_list["listrefresh"]) + debug_variables(locate(href_list["listrefresh"])) + return TRUE diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index 84dde0f22e0..6b7c2d89606 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -29,7 +29,7 @@ #define BIOHAZARD "BIOHAZARD THREAT!" -var/list/diseases = subtypesof(/datum/disease) +GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) /datum/disease diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index e0711d88e5f..5464bd9f012 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -6,16 +6,15 @@ If you need help with creating new symptoms or expanding the advance disease, ask for Giacom on #coderbus. */ - -var/list/archive_diseases = list() +GLOBAL_LIST_EMPTY(archive_diseases) // The order goes from easy to cure to hard to cure. -var/list/advance_cures = list( +GLOBAL_LIST_INIT(advance_cures, list( "sodiumchloride", "sugar", "orangejuice", "spaceacillin", "salglu_solution", "ethanol", "teporone", "diphenhydramine", "lipolicide", "silver", "gold" - ) +)) /* @@ -132,7 +131,7 @@ var/list/advance_cures = list( // Generate symptoms. By default, we only choose non-deadly symptoms. var/list/possible_symptoms = list() - for(var/symp in list_symptoms) + for(var/symp in GLOB.list_symptoms) var/datum/symptom/S = new symp if(S.level >= level_min && S.level <= level_max) if(!HasSymptom(S)) @@ -158,13 +157,13 @@ var/list/advance_cures = list( AssignProperties(properties) id = null - if(!archive_diseases[GetDiseaseID()]) + if(!GLOB.archive_diseases[GetDiseaseID()]) if(new_name) AssignName() - archive_diseases[GetDiseaseID()] = src // So we don't infinite loop - archive_diseases[GetDiseaseID()] = new /datum/disease/advance(0, src, 1) + GLOB.archive_diseases[GetDiseaseID()] = src // So we don't infinite loop + GLOB.archive_diseases[GetDiseaseID()] = new /datum/disease/advance(0, src, 1) - var/datum/disease/advance/A = archive_diseases[GetDiseaseID()] + var/datum/disease/advance/A = GLOB.archive_diseases[GetDiseaseID()] AssignName(A.name) //Generate disease properties based on the effects. Returns an associated list. @@ -246,9 +245,9 @@ var/list/advance_cures = list( // Will generate a random cure, the less resistance the symptoms have, the harder the cure. /datum/disease/advance/proc/GenerateCure(list/properties = list()) if(properties && properties.len) - var/res = Clamp(properties["resistance"] - (symptoms.len / 2), 1, advance_cures.len) + var/res = Clamp(properties["resistance"] - (symptoms.len / 2), 1, GLOB.advance_cures.len) // to_chat(world, "Res = [res]") - cures = list(advance_cures[res]) + cures = list(GLOB.advance_cures[res]) // Get the cure name from the cure_id var/datum/reagent/D = GLOB.chemical_reagents_list[cures[1]] @@ -371,7 +370,7 @@ var/list/advance_cures = list( var/list/symptoms = list() symptoms += "Done" - symptoms += list_symptoms.Copy() + symptoms += GLOB.list_symptoms.Copy() do if(user) var/symptom = input(user, "Choose a symptom to add ([i] remaining)", "Choose a Symptom") in symptoms diff --git a/code/datums/diseases/advance/presets.dm b/code/datums/diseases/advance/presets.dm index a641db2383f..c1df095f9c2 100644 --- a/code/datums/diseases/advance/presets.dm +++ b/code/datums/diseases/advance/presets.dm @@ -48,4 +48,4 @@ if(!D) name = "Reality Enhancer" symptoms = list(new/datum/symptom/sensory_restoration) - ..(process, D, copy) \ No newline at end of file + ..(process, D, copy) diff --git a/code/datums/diseases/advance/symptoms/beard.dm b/code/datums/diseases/advance/symptoms/beard.dm index 2ce411367f8..75bf2267ff9 100644 --- a/code/datums/diseases/advance/symptoms/beard.dm +++ b/code/datums/diseases/advance/symptoms/beard.dm @@ -47,4 +47,4 @@ BONUS if(!(head_organ.f_style == "Dwarf Beard") && !(head_organ.f_style == "Very Long Beard")) head_organ.f_style = pick("Dwarf Beard", "Very Long Beard") H.update_fhair() - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm index 62e31105b92..f0a3eaa9ce0 100644 --- a/code/datums/diseases/advance/symptoms/choking.dm +++ b/code/datums/diseases/advance/symptoms/choking.dm @@ -50,4 +50,4 @@ Bonus /datum/symptom/choking/proc/Choke(mob/living/M, datum/disease/advance/A) var/get_damage = sqrtor0(21+A.totalStageSpeed()*0.5)+sqrtor0(16+A.totalStealth()*5) M.adjustOxyLoss(get_damage) - return 1 \ No newline at end of file + return 1 diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm index 44f57ce0fac..5f495718fff 100644 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ b/code/datums/diseases/advance/symptoms/cough.dm @@ -37,4 +37,4 @@ BONUS var/obj/item/I = M.get_active_hand() if(I && I.w_class == 1) M.drop_item() - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/dizzy.dm b/code/datums/diseases/advance/symptoms/dizzy.dm index c594298b11c..920d6d15b49 100644 --- a/code/datums/diseases/advance/symptoms/dizzy.dm +++ b/code/datums/diseases/advance/symptoms/dizzy.dm @@ -35,4 +35,4 @@ Bonus else to_chat(M, "A wave of dizziness washes over you!") M.Dizzy(5) - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/fever.dm b/code/datums/diseases/advance/symptoms/fever.dm index b19d821b75f..32c051d5a26 100644 --- a/code/datums/diseases/advance/symptoms/fever.dm +++ b/code/datums/diseases/advance/symptoms/fever.dm @@ -38,4 +38,4 @@ Bonus /datum/symptom/fever/proc/Heat(mob/living/M, datum/disease/advance/A) var/get_heat = (sqrtor0(21+A.totalTransmittable()*2))+(sqrtor0(20+A.totalStageSpeed()*3)) M.bodytemperature = min(M.bodytemperature + (get_heat * A.stage), BODYTEMP_HEAT_DAMAGE_LIMIT - 1) - return 1 \ No newline at end of file + return 1 diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm index 9cfdf8a9d34..e7d68bebb92 100644 --- a/code/datums/diseases/advance/symptoms/flesh_eating.dm +++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm @@ -40,4 +40,4 @@ Bonus /datum/symptom/flesh_eating/proc/Flesheat(mob/living/M, datum/disease/advance/A) var/get_damage = ((sqrtor0(16-A.totalStealth()))*5) M.adjustBruteLoss(get_damage) - return 1 \ No newline at end of file + return 1 diff --git a/code/datums/diseases/advance/symptoms/headache.dm b/code/datums/diseases/advance/symptoms/headache.dm index 526ab93836f..d1efc35fc98 100644 --- a/code/datums/diseases/advance/symptoms/headache.dm +++ b/code/datums/diseases/advance/symptoms/headache.dm @@ -31,4 +31,4 @@ BONUS if(prob(SYMPTOM_ACTIVATION_PROB)) var/mob/living/M = A.affected_mob to_chat(M, "[pick("Your head hurts.", "Your head starts pounding.")]") - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm index 8563520b09a..f6689c3c464 100644 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ b/code/datums/diseases/advance/symptoms/itching.dm @@ -31,4 +31,4 @@ BONUS if(prob(SYMPTOM_ACTIVATION_PROB)) var/mob/living/M = A.affected_mob to_chat(M, "Your [pick("back", "arm", "leg", "elbow", "head")] itches.") - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index b1571aec62f..5759ab32de6 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -62,4 +62,4 @@ Bonus M.reagents.add_reagent("oculine", 20) else if(prob(SYMPTOM_ACTIVATION_PROB * 5)) - to_chat(M, "[pick("Your eyes feel great.","You feel like your eyes can focus more clearly.", "You don't feel the need to blink.","Your ears feel great.","Your healing feels more acute.")]") \ No newline at end of file + to_chat(M, "[pick("Your eyes feel great.","You feel like your eyes can focus more clearly.", "You don't feel the need to blink.","Your ears feel great.","Your hearing feels more acute.")]") diff --git a/code/datums/diseases/advance/symptoms/shedding.dm b/code/datums/diseases/advance/symptoms/shedding.dm index e79b87d1d4b..c3a64501759 100644 --- a/code/datums/diseases/advance/symptoms/shedding.dm +++ b/code/datums/diseases/advance/symptoms/shedding.dm @@ -47,4 +47,4 @@ BONUS head_organ.h_style = "Bald" H.update_hair() H.update_fhair() - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/skin.dm b/code/datums/diseases/advance/symptoms/skin.dm index 6916a46994a..aa5a3494584 100644 --- a/code/datums/diseases/advance/symptoms/skin.dm +++ b/code/datums/diseases/advance/symptoms/skin.dm @@ -83,4 +83,4 @@ BONUS else H.visible_message("[H] looks a bit dark...", "Your skin suddenly appears darker...") - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index 0a31a4acae6..b8af8f92a24 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -36,4 +36,4 @@ Bonus else M.emote("sneeze") A.spread(5) - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index 74da571836b..8f72de7f599 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -1,8 +1,6 @@ // Symptoms are the effects that engineered advanced diseases do. -var/list/list_symptoms = subtypesof(/datum/symptom) - -var/global/const/SYMPTOM_ACTIVATION_PROB = 3 +GLOBAL_LIST_INIT(list_symptoms, subtypesof(/datum/symptom)) /datum/symptom // Buffs/Debuffs the symptom has to the overall engineered disease. @@ -19,7 +17,7 @@ var/global/const/SYMPTOM_ACTIVATION_PROB = 3 var/id = "" /datum/symptom/New() - var/list/S = list_symptoms + var/list/S = GLOB.list_symptoms for(var/i = 1; i <= S.len; i++) if(type == S[i]) id = "[i]" diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm index aa47b1c7a2a..252eaf16ecc 100644 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ b/code/datums/diseases/advance/symptoms/vision.dm @@ -47,4 +47,4 @@ Bonus M.BecomeNearsighted() if(prob(eyes.damage - 10 + 1)) if(M.BecomeBlind()) - to_chat(M, "You go blind!") \ No newline at end of file + to_chat(M, "You go blind!") diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm index 98b940f2b83..732e56b69ed 100644 --- a/code/datums/diseases/advance/symptoms/weight.dm +++ b/code/datums/diseases/advance/symptoms/weight.dm @@ -36,4 +36,4 @@ Bonus else to_chat(M, "[pick("So hungry...", "You'd kill someone for a bite of food...", "Hunger cramps seize you...")]") M.overeatduration = max(M.overeatduration - 100, 0) - M.adjust_nutrition(-100) \ No newline at end of file + M.adjust_nutrition(-100) diff --git a/code/datums/diseases/advance/symptoms/youth.dm b/code/datums/diseases/advance/symptoms/youth.dm index 56e3b66fab3..5165f7916f4 100644 --- a/code/datums/diseases/advance/symptoms/youth.dm +++ b/code/datums/diseases/advance/symptoms/youth.dm @@ -52,4 +52,4 @@ BONUS H.age = 21 to_chat(H, "You feel like you can take on the world!") - return \ No newline at end of file + return diff --git a/code/datums/diseases/berserker.dm b/code/datums/diseases/berserker.dm index 3efb4d9e06a..81b3ca8cb72 100644 --- a/code/datums/diseases/berserker.dm +++ b/code/datums/diseases/berserker.dm @@ -52,4 +52,4 @@ M.adjustBruteLoss(damage) else playsound(affected_mob.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - affected_mob.visible_message("[affected_mob] fails to hit [M] with [affected_mob.p_their()] thrashing!") \ No newline at end of file + affected_mob.visible_message("[affected_mob] fails to hit [M] with [affected_mob.p_their()] thrashing!") diff --git a/code/datums/diseases/brainrot.dm b/code/datums/diseases/brainrot.dm index cacd229c429..02f36b532d7 100644 --- a/code/datums/diseases/brainrot.dm +++ b/code/datums/diseases/brainrot.dm @@ -30,7 +30,7 @@ affected_mob.emote("stare") if(prob(2)) affected_mob.emote("drool") - if(prob(10) && affected_mob.getBrainLoss()<=98)//shouldn't retard you to death now + if(prob(10) && affected_mob.getBrainLoss()<=98)//shouldn't brainpain you to death now affected_mob.adjustBrainLoss(2) if(prob(2)) to_chat(affected_mob, "Your try to remember something important...but can't.") @@ -40,7 +40,7 @@ affected_mob.emote("stare") if(prob(2)) affected_mob.emote("drool") - if(prob(15) && affected_mob.getBrainLoss()<=98) //shouldn't retard you to death now + if(prob(15) && affected_mob.getBrainLoss()<=98) //shouldn't brainpain you to death now affected_mob.adjustBrainLoss(3) if(prob(2)) to_chat(affected_mob, "Strange buzzing fills your head, removing all thoughts.") diff --git a/code/datums/diseases/cold.dm b/code/datums/diseases/cold.dm index 975c92b2aef..955ec568c77 100644 --- a/code/datums/diseases/cold.dm +++ b/code/datums/diseases/cold.dm @@ -63,4 +63,4 @@ if(!affected_mob.resistances.Find(/datum/disease/flu)) var/datum/disease/Flu = new /datum/disease/flu(0) affected_mob.ContractDisease(Flu) - cure() \ No newline at end of file + cure() diff --git a/code/datums/diseases/critical.dm b/code/datums/diseases/critical.dm index 37d77687d8c..3afb48ce534 100644 --- a/code/datums/diseases/critical.dm +++ b/code/datums/diseases/critical.dm @@ -6,12 +6,17 @@ if(prob(stage_prob)) stage = min(stage + 1, max_stages) + if(has_cure()) + cure() + return FALSE + return TRUE + +/datum/disease/critical/has_cure() for(var/C_id in cures) if(affected_mob.reagents.has_reagent(C_id)) if(prob(cure_chance)) - cure() - return FALSE - return TRUE + return TRUE + return FALSE /datum/disease/critical/shock name = "Shock" @@ -31,7 +36,7 @@ /datum/disease/critical/shock/stage_act() if(..()) - if(affected_mob.health >= 25) + if(affected_mob.health >= 25 && affected_mob.nutrition >= NUTRITION_LEVEL_HYPOGLYCEMIA) to_chat(affected_mob, "You feel better.") cure() return @@ -80,8 +85,8 @@ spread_text = "The patient is having a cardiac emergency" max_stages = 3 spread_flags = SPECIAL - cure_text = "Atropine or Epinephrine" - cures = list("atropine", "epinephrine") + cure_text = "Atropine, Epinephrine, or Heparin" + cures = list("atropine", "epinephrine", "heparin") cure_chance = 10 needs_all_cures = FALSE viable_mobtypes = list(/mob/living/carbon/human) @@ -133,4 +138,65 @@ affected_mob.emote(pick("twitch", "gasp")) if(prob(5) && ishuman(affected_mob)) var/mob/living/carbon/human/H = affected_mob - H.set_heartattack(TRUE) \ No newline at end of file + H.set_heartattack(TRUE) + +/datum/disease/critical/hypoglycemia + name = "Hypoglycemia" + form = "Medical Emergency" + max_stages = 3 + spread_flags = SPECIAL + spread_text = "The patient has low blood sugar." + cure_text = "Eating or administration of vitamins or nutrients" + viable_mobtypes = list(/mob/living/carbon/human) + stage_prob = 1 + severity = DANGEROUS + disease_flags = CURABLE + bypasses_immunity = TRUE + virus_heal_resistant = TRUE + +/datum/disease/critical/hypoglycemia/has_cure() + if(ishuman(affected_mob)) + var/mob/living/carbon/human/H = affected_mob + if(NO_HUNGER in H.dna.species.species_traits) + return TRUE + if(ismachine(H)) + return TRUE + return ..() + +/datum/disease/critical/hypoglycemia/stage_act() + if(..()) + if(affected_mob.nutrition > NUTRITION_LEVEL_HYPOGLYCEMIA) + to_chat(affected_mob, "You feel a lot better!") + cure() + return + switch(stage) + if(1) + if(prob(4)) + to_chat(affected_mob, "You feel hungry!") + if(prob(2)) + to_chat(affected_mob, "You have a headache!") + if(prob(2)) + to_chat(affected_mob, "You feel [pick("anxious", "depressed")]!") + if(2) + if(prob(4)) + to_chat(affected_mob, "You feel like everything is wrong with your life!") + if(prob(5)) + affected_mob.Slowed(rand(4, 16)) + to_chat(affected_mob, "You feel [pick("tired", "exhausted", "sluggish")].") + if(prob(5)) + affected_mob.Weaken(6) + affected_mob.Stuttering(10) + to_chat(affected_mob, "You feel [pick("numb", "confused", "dizzy", "lightheaded")].") + affected_mob.emote("collapse") + if(3) + if(prob(8)) + var/datum/disease/D = new /datum/disease/critical/shock + affected_mob.ForceContractDisease(D) + if(prob(12)) + affected_mob.Weaken(6) + affected_mob.Stuttering(10) + to_chat(affected_mob, "You feel [pick("numb", "confused", "dizzy", "lightheaded")].") + affected_mob.emote("collapse") + if(prob(12)) + to_chat(affected_mob, "You feel [pick("tired", "exhausted", "sluggish")].") + affected_mob.Slowed(rand(4, 16)) diff --git a/code/datums/diseases/food_poisoning.dm b/code/datums/diseases/food_poisoning.dm index 4df31ec57d9..a0e3792de0c 100644 --- a/code/datums/diseases/food_poisoning.dm +++ b/code/datums/diseases/food_poisoning.dm @@ -68,4 +68,4 @@ to_chat(affected_mob, "Your stomach lurches painfully!") affected_mob.visible_message("[affected_mob] gags and retches!") affected_mob.Stun(rand(2,4)) - affected_mob.Weaken(rand(2,4)) \ No newline at end of file + affected_mob.Weaken(rand(2,4)) diff --git a/code/datums/diseases/kingstons.dm b/code/datums/diseases/kingstons.dm index c7db27968ef..733ee3cf0e7 100644 --- a/code/datums/diseases/kingstons.dm +++ b/code/datums/diseases/kingstons.dm @@ -93,4 +93,4 @@ twisted.visible_message("[twisted] scratches at thier skin!", \ "You scratch your skin to try not to itch!") twisted.adjustBruteLoss(-5) - twisted.adjustStaminaLoss(5) \ No newline at end of file + twisted.adjustStaminaLoss(5) diff --git a/code/datums/diseases/kuru.dm b/code/datums/diseases/kuru.dm index 5cb04ef5f85..f147bbd1612 100644 --- a/code/datums/diseases/kuru.dm +++ b/code/datums/diseases/kuru.dm @@ -52,4 +52,4 @@ affected_mob.drop_r_hand() affected_mob.Stun(10) affected_mob.Weaken(10) - affected_mob.visible_message("[affected_mob] laughs uncontrollably!") \ No newline at end of file + affected_mob.visible_message("[affected_mob] laughs uncontrollably!") diff --git a/code/datums/diseases/magnitis.dm b/code/datums/diseases/magnitis.dm index 924ee01dd69..dd1d062cbf7 100644 --- a/code/datums/diseases/magnitis.dm +++ b/code/datums/diseases/magnitis.dm @@ -60,4 +60,4 @@ var/iter = rand(1,3) for(i=0,i (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen))) return 0 if((abs(carbon_dioxide-sample.carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && \ - ((carbon_dioxide < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide) || (oxygen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide))) + ((carbon_dioxide < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide) || (carbon_dioxide > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide))) return 0 if((abs(toxins-sample.toxins) > MINIMUM_AIR_TO_SUSPEND) && \ ((toxins < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins) || (toxins > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins))) diff --git a/code/datums/helper_datums/construction_datum.dm b/code/datums/helper_datums/construction_datum.dm index f73607a2a4e..c10ca5a488b 100644 --- a/code/datums/helper_datums/construction_datum.dm +++ b/code/datums/helper_datums/construction_datum.dm @@ -1,244 +1,244 @@ -#define FORWARD -1 -#define BACKWARD 1 -#define CONSTRUCTION_TOOL_BEHAVIOURS list(TOOL_CROWBAR, TOOL_SCREWDRIVER, TOOL_WELDER, TOOL_WRENCH) - -/datum/construction - var/list/steps - var/atom/holder - var/result - var/list/steps_desc - var/taskpath = null // Path of job objective completed. - -/datum/construction/New(atom) - ..() - holder = atom - if(!holder) //don't want this without a holder - spawn - qdel(src) - set_desc(steps.len) - return - -/datum/construction/proc/next_step(mob/user as mob) - steps.len-- - if(!steps.len) - spawn_result(user) - else - set_desc(steps.len) - return - -/datum/construction/proc/action(atom/used_atom,mob/user as mob) - return - -/datum/construction/proc/check_step(atom/used_atom,mob/user as mob) //check last step only - var/valid_step = is_right_key(used_atom) - if(valid_step) - if(custom_action(valid_step, used_atom, user)) - next_step(user) - return 1 - return 0 - -/datum/construction/proc/is_right_key(atom/used_atom) // returns current step num if used_atom is of the right type. - var/list/L = steps[steps.len] - if(do_tool_or_atom_check(used_atom, L["key"])) - return steps.len - return 0 - - -/datum/construction/proc/custom_action(step, used_atom, user) - if(istype(used_atom, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = used_atom - if(C.amount<4) - to_chat(user, ("There's not enough cable to finish the task.")) - return 0 - else - C.use(4) - playsound(holder, C.usesound, 50, 1) - else if(istype(used_atom, /obj/item/stack)) - var/obj/item/stack/S = used_atom - if(S.amount < 5) - to_chat(user, ("There's not enough material in this stack.")) - return 0 - else - S.use(5) - else if(isitem(used_atom)) - var/obj/item/I = used_atom - if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) - if(!I.use_tool(holder, user, 0, volume = I.tool_volume)) - return 0 - return 1 - -/datum/construction/proc/check_all_steps(atom/used_atom,mob/user as mob) //check all steps, remove matching one. - for(var/i=1;i<=steps.len;i++) - var/list/L = steps[i]; - if(do_tool_or_atom_check(used_atom, L["key"]) && custom_action(i, used_atom, user)) - steps[i]=null;//stupid byond list from list removal... - listclearnulls(steps); - if(!steps.len) - spawn_result(user) - return 1 - return 0 - - -/datum/construction/proc/spawn_result(mob/user as mob) - if(result) - if(taskpath) - var/datum/job_objective/task = user.mind.findJobTask(taskpath) - if(istype(task)) - task.unit_completed() - - new result(get_turf(holder)) - spawn() - qdel(holder) - return - -/datum/construction/proc/set_desc(index as num) - var/list/step = steps[index] - holder.desc = step["desc"] - return - -/datum/construction/proc/try_consume(mob/user as mob, atom/used_atom, amount) - if(amount > 0) - // CABLES - if(istype(used_atom,/obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil=used_atom - if(!coil.use(amount)) - to_chat(user, "You don't have enough cable! You need at least [amount] coils.") - return 0 - // TOOLS - if(isitem(used_atom)) - var/obj/item/I = used_atom - if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) - if(!I.use(amount)) - return 0 - // STACKS - if(istype(used_atom,/obj/item/stack)) - var/obj/item/stack/stack=used_atom - if(stack.amount < amount) - to_chat(user, "You don't have enough [stack]! You need at least [amount].") - return 0 - stack.use(amount) - return 1 - -/datum/construction/proc/do_tool_or_atom_check(used_atom, thing_to_check) //Checks if an atom is either a required thing; or if it's a required tool - if(istype(used_atom, thing_to_check)) - return TRUE - else if(isitem(used_atom)) - var/obj/item/I = used_atom - if(I.tool_behaviour == thing_to_check) - return TRUE - -/datum/construction/reversible - var/index - -/datum/construction/reversible/New(atom) - ..() - index = steps.len - return - -/datum/construction/reversible/proc/update_index(diff as num, mob/user as mob) - index+=diff - if(index==0) - spawn_result(user) - else - set_desc(index) - return - -/datum/construction/reversible/is_right_key(atom/used_atom) // returns index step - var/list/L = steps[index] - if(do_tool_or_atom_check(used_atom, L["key"])) - return FORWARD //to the first step -> forward - else if(L["backkey"] && do_tool_or_atom_check(used_atom, L["backkey"])) - return BACKWARD //to the last step -> backwards - return 0 - -/datum/construction/reversible/check_step(atom/used_atom,mob/user as mob) - var/diff = is_right_key(used_atom) - if(diff) - if(custom_action(index, diff, used_atom, user)) - update_index(diff, user) - return 1 - return 0 - -/datum/construction/reversible/custom_action(index, diff, used_atom, user) - if(!..(index,used_atom,user)) - return 0 - return 1 - -#define state_next "next" -#define state_prev "prev" - -/datum/construction/reversible2 - var/index - var/base_icon = "durand" - -/datum/construction/reversible2/New(atom) - ..() - index = 1 - return - -/datum/construction/reversible2/proc/update_index(diff as num, mob/user as mob) - index-=diff - if(index==steps.len+1) - spawn_result(user) - else - set_desc(index) - return - -/datum/construction/reversible2/proc/update_icon() - holder.icon_state="[base_icon]_[index]" - -/datum/construction/reversible2/is_right_key(mob/user as mob,atom/used_atom) // returns index step - var/list/state = steps[index] - if(state_next in state) - var/list/step = state[state_next] - if(do_tool_or_atom_check(used_atom, step["key"])) - //if(L["consume"] && !try_consume(used_atom,L["consume"])) - // return 0 - return FORWARD //to the first step -> forward - else if(state_prev in state) - var/list/step = state[state_prev] - if(do_tool_or_atom_check(used_atom, step["key"])) - //if(L["consume"] && !try_consume(used_atom,L["consume"])) - // return 0 - return BACKWARD //to the first step -> forward - return 0 - -/datum/construction/reversible2/check_step(atom/used_atom,mob/user as mob) - var/diff = is_right_key(user,used_atom) - if(diff) - if(custom_action(index, diff, used_atom, user)) - update_index(diff,user) - update_icon() - return 1 - return 0 - -/datum/construction/reversible2/proc/fixText(text,user) - text = replacetext(text,"{USER}","[user]") - text = replacetext(text,"{HOLDER}","[holder]") - return text - -/datum/construction/reversible2/custom_action(index, diff, used_atom, var/mob/user) - if(!..(index,used_atom,user)) - return 0 - - var/list/step = steps[index] - var/list/state = step[diff==FORWARD ? state_next : state_prev] - user.visible_message(fixText(state["vis_msg"],user),fixText(state["self_msg"],user)) - - if("delete" in state) - qdel(used_atom) - else if("spawn" in state) - var/spawntype=state["spawn"] - var/atom/A = new spawntype(holder.loc) - if("amount" in state) - if(istype(A,/obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C=A - C.amount=state["amount"] - if(istype(A,/obj/item/stack)) - var/obj/item/stack/S=A - S.amount=state["amount"] - - return 1 - -/datum/construction/reversible2/action(used_atom,user) - return check_step(used_atom,user) +#define FORWARD -1 +#define BACKWARD 1 +#define CONSTRUCTION_TOOL_BEHAVIOURS list(TOOL_CROWBAR, TOOL_SCREWDRIVER, TOOL_WELDER, TOOL_WRENCH) + +/datum/construction + var/list/steps + var/atom/holder + var/result + var/list/steps_desc + var/taskpath = null // Path of job objective completed. + +/datum/construction/New(atom) + ..() + holder = atom + if(!holder) //don't want this without a holder + spawn + qdel(src) + set_desc(steps.len) + return + +/datum/construction/proc/next_step(mob/user as mob) + steps.len-- + if(!steps.len) + spawn_result(user) + else + set_desc(steps.len) + return + +/datum/construction/proc/action(atom/used_atom,mob/user as mob) + return + +/datum/construction/proc/check_step(atom/used_atom,mob/user as mob) //check last step only + var/valid_step = is_right_key(used_atom) + if(valid_step) + if(custom_action(valid_step, used_atom, user)) + next_step(user) + return 1 + return 0 + +/datum/construction/proc/is_right_key(atom/used_atom) // returns current step num if used_atom is of the right type. + var/list/L = steps[steps.len] + if(do_tool_or_atom_check(used_atom, L["key"])) + return steps.len + return 0 + + +/datum/construction/proc/custom_action(step, used_atom, user) + if(istype(used_atom, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = used_atom + if(C.amount<4) + to_chat(user, ("There's not enough cable to finish the task.")) + return 0 + else + C.use(4) + playsound(holder, C.usesound, 50, 1) + else if(istype(used_atom, /obj/item/stack)) + var/obj/item/stack/S = used_atom + if(S.amount < 5) + to_chat(user, ("There's not enough material in this stack.")) + return 0 + else + S.use(5) + else if(isitem(used_atom)) + var/obj/item/I = used_atom + if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) + if(!I.use_tool(holder, user, 0, volume = I.tool_volume)) + return 0 + return 1 + +/datum/construction/proc/check_all_steps(atom/used_atom,mob/user as mob) //check all steps, remove matching one. + for(var/i=1;i<=steps.len;i++) + var/list/L = steps[i]; + if(do_tool_or_atom_check(used_atom, L["key"]) && custom_action(i, used_atom, user)) + steps[i]=null;//stupid byond list from list removal... + listclearnulls(steps); + if(!steps.len) + spawn_result(user) + return 1 + return 0 + + +/datum/construction/proc/spawn_result(mob/user as mob) + if(result) + if(taskpath) + var/datum/job_objective/task = user.mind.findJobTask(taskpath) + if(istype(task)) + task.unit_completed() + + new result(get_turf(holder)) + spawn() + qdel(holder) + return + +/datum/construction/proc/set_desc(index as num) + var/list/step = steps[index] + holder.desc = step["desc"] + return + +/datum/construction/proc/try_consume(mob/user as mob, atom/used_atom, amount) + if(amount > 0) + // CABLES + if(istype(used_atom,/obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/coil=used_atom + if(!coil.use(amount)) + to_chat(user, "You don't have enough cable! You need at least [amount] coils.") + return 0 + // TOOLS + if(isitem(used_atom)) + var/obj/item/I = used_atom + if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) + if(!I.use(amount)) + return 0 + // STACKS + if(istype(used_atom,/obj/item/stack)) + var/obj/item/stack/stack=used_atom + if(stack.amount < amount) + to_chat(user, "You don't have enough [stack]! You need at least [amount].") + return 0 + stack.use(amount) + return 1 + +/datum/construction/proc/do_tool_or_atom_check(used_atom, thing_to_check) //Checks if an atom is either a required thing; or if it's a required tool + if(istype(used_atom, thing_to_check)) + return TRUE + else if(isitem(used_atom)) + var/obj/item/I = used_atom + if(I.tool_behaviour == thing_to_check) + return TRUE + +/datum/construction/reversible + var/index + +/datum/construction/reversible/New(atom) + ..() + index = steps.len + return + +/datum/construction/reversible/proc/update_index(diff as num, mob/user as mob) + index+=diff + if(index==0) + spawn_result(user) + else + set_desc(index) + return + +/datum/construction/reversible/is_right_key(atom/used_atom) // returns index step + var/list/L = steps[index] + if(do_tool_or_atom_check(used_atom, L["key"])) + return FORWARD //to the first step -> forward + else if(L["backkey"] && do_tool_or_atom_check(used_atom, L["backkey"])) + return BACKWARD //to the last step -> backwards + return 0 + +/datum/construction/reversible/check_step(atom/used_atom,mob/user as mob) + var/diff = is_right_key(used_atom) + if(diff) + if(custom_action(index, diff, used_atom, user)) + update_index(diff, user) + return 1 + return 0 + +/datum/construction/reversible/custom_action(index, diff, used_atom, user) + if(!..(index,used_atom,user)) + return 0 + return 1 + +#define state_next "next" +#define state_prev "prev" + +/datum/construction/reversible2 + var/index + var/base_icon = "durand" + +/datum/construction/reversible2/New(atom) + ..() + index = 1 + return + +/datum/construction/reversible2/proc/update_index(diff as num, mob/user as mob) + index-=diff + if(index==steps.len+1) + spawn_result(user) + else + set_desc(index) + return + +/datum/construction/reversible2/proc/update_icon() + holder.icon_state="[base_icon]_[index]" + +/datum/construction/reversible2/is_right_key(mob/user as mob,atom/used_atom) // returns index step + var/list/state = steps[index] + if(state_next in state) + var/list/step = state[state_next] + if(do_tool_or_atom_check(used_atom, step["key"])) + //if(L["consume"] && !try_consume(used_atom,L["consume"])) + // return 0 + return FORWARD //to the first step -> forward + else if(state_prev in state) + var/list/step = state[state_prev] + if(do_tool_or_atom_check(used_atom, step["key"])) + //if(L["consume"] && !try_consume(used_atom,L["consume"])) + // return 0 + return BACKWARD //to the first step -> forward + return 0 + +/datum/construction/reversible2/check_step(atom/used_atom,mob/user as mob) + var/diff = is_right_key(user,used_atom) + if(diff) + if(custom_action(index, diff, used_atom, user)) + update_index(diff,user) + update_icon() + return 1 + return 0 + +/datum/construction/reversible2/proc/fixText(text,user) + text = replacetext(text,"{USER}","[user]") + text = replacetext(text,"{HOLDER}","[holder]") + return text + +/datum/construction/reversible2/custom_action(index, diff, used_atom, var/mob/user) + if(!..(index,used_atom,user)) + return 0 + + var/list/step = steps[index] + var/list/state = step[diff==FORWARD ? state_next : state_prev] + user.visible_message(fixText(state["vis_msg"],user),fixText(state["self_msg"],user)) + + if("delete" in state) + qdel(used_atom) + else if("spawn" in state) + var/spawntype=state["spawn"] + var/atom/A = new spawntype(holder.loc) + if("amount" in state) + if(istype(A,/obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C=A + C.amount=state["amount"] + if(istype(A,/obj/item/stack)) + var/obj/item/stack/S=A + S.amount=state["amount"] + + return 1 + +/datum/construction/reversible2/action(used_atom,user) + return check_step(used_atom,user) diff --git a/code/datums/helper_datums/events.dm b/code/datums/helper_datums/events.dm index fa0dccd2a15..2fba9d996ae 100644 --- a/code/datums/helper_datums/events.dm +++ b/code/datums/helper_datums/events.dm @@ -1,68 +1,68 @@ -/* - * WARRANTY VOID IF CODE USED - */ - - -/datum/events - var/list/events - -/datum/events/New() - ..() - events = new - -/datum/events/proc/addEventType(event_type as text) - if(!(event_type in events) || !islist(events[event_type])) - events[event_type] = list() - return 1 - return - - -// Arguments: event_type as text, proc_holder as datum, proc_name as text -// Returns: New event, null on error. -/datum/events/proc/addEvent(event_type as text, proc_holder, proc_name as text) - if(!event_type || !proc_holder || !proc_name) - return - addEventType(event_type) - var/list/event = events[event_type] - var/datum/event/E = new /datum/event(proc_holder,proc_name) - event += E - return E - -// Arguments: event_type as text, any number of additional arguments to pass to event handler -// Returns: null -/datum/events/proc/fireEvent() - //world << "Events in [args[1]] called" - var/list/event = listgetindex(events,args[1]) - if(istype(event)) - spawn(0) - for(var/datum/event/E in event) - if(!E.Fire(arglist(args.Copy(2)))) - clearEvent(args[1],E) - return - -// Arguments: event_type as text, E as /datum/event -// Returns: 1 if event cleared, null on error - -/datum/events/proc/clearEvent(event_type as text, datum/event/E) - if(!event_type || !E) - return - var/list/event = listgetindex(events,event_type) - event -= E - return 1 - - -/datum/event - var/listener - var/proc_name - -/datum/event/New(tlistener,tprocname) - listener = tlistener - proc_name = tprocname - return ..() - -/datum/event/proc/Fire() - //world << "Event fired" - if(listener) - call(listener,proc_name)(arglist(args)) - return 1 - return +/* + * WARRANTY VOID IF CODE USED + */ + + +/datum/events + var/list/events + +/datum/events/New() + ..() + events = new + +/datum/events/proc/addEventType(event_type as text) + if(!(event_type in events) || !islist(events[event_type])) + events[event_type] = list() + return 1 + return + + +// Arguments: event_type as text, proc_holder as datum, proc_name as text +// Returns: New event, null on error. +/datum/events/proc/addEvent(event_type as text, proc_holder, proc_name as text) + if(!event_type || !proc_holder || !proc_name) + return + addEventType(event_type) + var/list/event = events[event_type] + var/datum/event/E = new /datum/event(proc_holder,proc_name) + event += E + return E + +// Arguments: event_type as text, any number of additional arguments to pass to event handler +// Returns: null +/datum/events/proc/fireEvent() + //world << "Events in [args[1]] called" + var/list/event = listgetindex(events,args[1]) + if(istype(event)) + spawn(0) + for(var/datum/event/E in event) + if(!E.Fire(arglist(args.Copy(2)))) + clearEvent(args[1],E) + return + +// Arguments: event_type as text, E as /datum/event +// Returns: 1 if event cleared, null on error + +/datum/events/proc/clearEvent(event_type as text, datum/event/E) + if(!event_type || !E) + return + var/list/event = listgetindex(events,event_type) + event -= E + return 1 + + +/datum/event + var/listener + var/proc_name + +/datum/event/New(tlistener,tprocname) + listener = tlistener + proc_name = tprocname + return ..() + +/datum/event/proc/Fire() + //world << "Event fired" + if(listener) + call(listener,proc_name)(arglist(args)) + return 1 + return diff --git a/code/datums/helper_datums/global_iterator.dm b/code/datums/helper_datums/global_iterator.dm index e3d14743fe1..214045941b5 100644 --- a/code/datums/helper_datums/global_iterator.dm +++ b/code/datums/helper_datums/global_iterator.dm @@ -1,152 +1,152 @@ -/* -README: - -The global_iterator datum is supposed to provide a simple and robust way to -create some constantly "looping" processes with ability to stop and restart them at will. -Generally, the only thing you want to play with (meaning, redefine) is the process() proc. -It must contain all the things you want done. - -Control functions: - new - used to create datum. First argument (optional) - var list(to use in process() proc) as list, - second (optional) - autostart control. - If autostart == TRUE, the loop will be started immediately after datum creation. - - start(list/arguments) - starts the loop. Takes arguments(optional) as a list, which is then used - by process() proc. Returns null if datum already active, 1 if loop started succesfully and 0 if there's - an error in supplied arguments (not list or empty list). - - stop() - stops the loop. Returns null if datum is already inactive and 1 on success. - - set_delay(new_delay) - sets the delay between iterations. Pretty selfexplanatory. - Returns 0 on error(new_delay is not numerical), 1 otherwise. - - set_process_args(list/arguments) - passes the supplied arguments to the process() proc. - - active() - Returns 1 if datum is active, 0 otherwise. - - toggle() - toggles datum state. Returns new datum state (see active()). - -Misc functions: - - get_last_exec_time() - Returns the time of last iteration. - - get_last_exec_time_as_text() - Returns the time of last iteration as text - - -Control vars: - - delay - delay between iterations - - check_for_null - if equals TRUE, on each iteration the supplied arguments will be checked for nulls. - If some varible equals null (and null only), the loop is stopped. - Usefull, if some var unexpectedly becomes null - due to object deletion, for example. - Of course, you can also check the variables inside process() proc to prevent runtime errors. - -Data storage vars: - - result - stores the value returned by process() proc -*/ - -/datum/global_iterator - var/control_switch = 0 - var/delay = 10 - var/list/arg_list = new - var/last_exec = null - var/check_for_null = 1 - var/forbid_garbage = 0 - var/result - var/state = 0 - -/datum/global_iterator/New(list/arguments=null,autostart=1) - delay = delay>0?(delay):1 - if(forbid_garbage) //prevents garbage collection with tag != null - tag = "\ref[src]" - set_process_args(arguments) - if(autostart) - start() - return - -/datum/global_iterator/proc/main() - state = 1 - while(src && control_switch) - last_exec = world.timeofday - if(check_for_null && has_null_args()) - stop() - return 0 - result = process(arglist(arg_list)) - for(var/sleep_time=delay;sleep_time>0;sleep_time--) //uhh, this is ugly. But I see no other way to terminate sleeping proc. Such disgrace. - if(!control_switch) - return 0 - sleep(1) - return 0 - -/datum/global_iterator/proc/start(list/arguments=null) - if(active()) - return - if(arguments) - if(!set_process_args(arguments)) - return 0 - if(!state_check()) //the main loop is sleeping, wait for it to terminate. - return - control_switch = 1 - spawn() - state = main() - return 1 - -/datum/global_iterator/proc/stop() - if(!active()) - return - control_switch = 0 - spawn(-1) //report termination error but don't wait for state_check(). - state_check() - return 1 - -/datum/global_iterator/proc/state_check() - var/lag = 0 - while(state) - sleep(1) - if(++lag>10) - CRASH("The global_iterator loop \ref[src] failed to terminate in designated timeframe. This may be caused by server lagging.") - return 1 - -/datum/global_iterator/process() - return - -/datum/global_iterator/proc/active() - return control_switch - -/datum/global_iterator/proc/has_null_args() - if(null in arg_list) - return 1 - return 0 - - -/datum/global_iterator/proc/set_delay(new_delay) - if(isnum(new_delay)) - delay = max(1, round(new_delay)) - return 1 - else - return 0 - -/datum/global_iterator/proc/get_last_exec_time() - return (last_exec||0) - -/datum/global_iterator/proc/get_last_exec_time_as_text() - return (time2text(last_exec)||"Wasn't executed yet") - -/datum/global_iterator/proc/set_process_args(list/arguments) - if(arguments && istype(arguments, /list) && arguments.len) - arg_list = arguments - return 1 - else -// to_chat(world, "Invalid arguments supplied for [src.type], ref = \ref[src]") - return 0 - -/datum/global_iterator/proc/toggle_null_checks() - check_for_null = !check_for_null - return check_for_null - -/datum/global_iterator/proc/toggle() - if(!stop()) - start() - return active() +/* +README: + +The global_iterator datum is supposed to provide a simple and robust way to +create some constantly "looping" processes with ability to stop and restart them at will. +Generally, the only thing you want to play with (meaning, redefine) is the process() proc. +It must contain all the things you want done. + +Control functions: + new - used to create datum. First argument (optional) - var list(to use in process() proc) as list, + second (optional) - autostart control. + If autostart == TRUE, the loop will be started immediately after datum creation. + + start(list/arguments) - starts the loop. Takes arguments(optional) as a list, which is then used + by process() proc. Returns null if datum already active, 1 if loop started succesfully and 0 if there's + an error in supplied arguments (not list or empty list). + + stop() - stops the loop. Returns null if datum is already inactive and 1 on success. + + set_delay(new_delay) - sets the delay between iterations. Pretty selfexplanatory. + Returns 0 on error(new_delay is not numerical), 1 otherwise. + + set_process_args(list/arguments) - passes the supplied arguments to the process() proc. + + active() - Returns 1 if datum is active, 0 otherwise. + + toggle() - toggles datum state. Returns new datum state (see active()). + +Misc functions: + + get_last_exec_time() - Returns the time of last iteration. + + get_last_exec_time_as_text() - Returns the time of last iteration as text + + +Control vars: + + delay - delay between iterations + + check_for_null - if equals TRUE, on each iteration the supplied arguments will be checked for nulls. + If some varible equals null (and null only), the loop is stopped. + Usefull, if some var unexpectedly becomes null - due to object deletion, for example. + Of course, you can also check the variables inside process() proc to prevent runtime errors. + +Data storage vars: + + result - stores the value returned by process() proc +*/ + +/datum/global_iterator + var/control_switch = 0 + var/delay = 10 + var/list/arg_list = new + var/last_exec = null + var/check_for_null = 1 + var/forbid_garbage = 0 + var/result + var/state = 0 + +/datum/global_iterator/New(list/arguments=null,autostart=1) + delay = delay>0?(delay):1 + if(forbid_garbage) //prevents garbage collection with tag != null + tag = "\ref[src]" + set_process_args(arguments) + if(autostart) + start() + return + +/datum/global_iterator/proc/main() + state = 1 + while(src && control_switch) + last_exec = world.timeofday + if(check_for_null && has_null_args()) + stop() + return 0 + result = process(arglist(arg_list)) + for(var/sleep_time=delay;sleep_time>0;sleep_time--) //uhh, this is ugly. But I see no other way to terminate sleeping proc. Such disgrace. + if(!control_switch) + return 0 + sleep(1) + return 0 + +/datum/global_iterator/proc/start(list/arguments=null) + if(active()) + return + if(arguments) + if(!set_process_args(arguments)) + return 0 + if(!state_check()) //the main loop is sleeping, wait for it to terminate. + return + control_switch = 1 + spawn() + state = main() + return 1 + +/datum/global_iterator/proc/stop() + if(!active()) + return + control_switch = 0 + spawn(-1) //report termination error but don't wait for state_check(). + state_check() + return 1 + +/datum/global_iterator/proc/state_check() + var/lag = 0 + while(state) + sleep(1) + if(++lag>10) + CRASH("The global_iterator loop \ref[src] failed to terminate in designated timeframe. This may be caused by server lagging.") + return 1 + +/datum/global_iterator/process() + return + +/datum/global_iterator/proc/active() + return control_switch + +/datum/global_iterator/proc/has_null_args() + if(null in arg_list) + return 1 + return 0 + + +/datum/global_iterator/proc/set_delay(new_delay) + if(isnum(new_delay)) + delay = max(1, round(new_delay)) + return 1 + else + return 0 + +/datum/global_iterator/proc/get_last_exec_time() + return (last_exec||0) + +/datum/global_iterator/proc/get_last_exec_time_as_text() + return (time2text(last_exec)||"Wasn't executed yet") + +/datum/global_iterator/proc/set_process_args(list/arguments) + if(arguments && istype(arguments, /list) && arguments.len) + arg_list = arguments + return 1 + else +// to_chat(world, "Invalid arguments supplied for [src.type], ref = \ref[src]") + return 0 + +/datum/global_iterator/proc/toggle_null_checks() + check_for_null = !check_for_null + return check_for_null + +/datum/global_iterator/proc/toggle() + if(!stop()) + start() + return active() diff --git a/code/datums/helper_datums/map_template.dm b/code/datums/helper_datums/map_template.dm index 2030897e3ce..a54707842db 100644 --- a/code/datums/helper_datums/map_template.dm +++ b/code/datums/helper_datums/map_template.dm @@ -17,7 +17,7 @@ name = rename /datum/map_template/proc/preload_size(path) - var/bounds = maploader.load_map(file(path), 1, 1, 1, cropMap = 0, measureOnly = 1) + var/bounds = GLOB.maploader.load_map(file(path), 1, 1, 1, cropMap = 0, measureOnly = 1) if(bounds) width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1 height = bounds[MAP_MAXY] @@ -48,8 +48,8 @@ // This system will metaphorically snap in half (not postpone init everywhere) // if given a multi-z template // it might need to be adapted for that when that time comes - space_manager.add_dirt(placement.z) - var/list/bounds = maploader.load_map(get_file(), min_x, min_y, placement.z, cropMap = 1) + GLOB.space_manager.add_dirt(placement.z) + var/list/bounds = GLOB.maploader.load_map(get_file(), min_x, min_y, placement.z, cropMap = 1) if(!bounds) return 0 if(bot_left == null || top_right == null) @@ -58,7 +58,7 @@ if(ST_bot_left == null || ST_top_right == null) log_runtime(EXCEPTION("One of the smoothing corners is bust"), src) - space_manager.remove_dirt(placement.z) + GLOB.space_manager.remove_dirt(placement.z) late_setup_level( block(bot_left, top_right), block(ST_bot_left, ST_top_right)) @@ -108,7 +108,7 @@ for(var/map in flist(path)) if(cmptext(copytext(map, length(map) - 3), ".dmm")) var/datum/map_template/T = new(path = "[path][map]", rename = "[map]") - map_templates[T.name] = T + GLOB.map_templates[T.name] = T if(!config.disable_space_ruins) // so we don't unnecessarily clutter start-up preloadRuinTemplates() @@ -134,13 +134,13 @@ if(banned.Find(R.mappath)) continue - map_templates[R.name] = R - ruins_templates[R.name] = R + GLOB.map_templates[R.name] = R + GLOB.ruins_templates[R.name] = R if(istype(R, /datum/map_template/ruin/lavaland)) - lava_ruins_templates[R.name] = R + GLOB.lava_ruins_templates[R.name] = R if(istype(R, /datum/map_template/ruin/space)) - space_ruins_templates[R.name] = R + GLOB.space_ruins_templates[R.name] = R /proc/preloadShelterTemplates() for(var/item in subtypesof(/datum/map_template/shelter)) @@ -149,8 +149,8 @@ continue var/datum/map_template/shelter/S = new shelter_type() - shelter_templates[S.shelter_id] = S - map_templates[S.shelter_id] = S + GLOB.shelter_templates[S.shelter_id] = S + GLOB.map_templates[S.shelter_id] = S /proc/preloadShuttleTemplates() for(var/item in subtypesof(/datum/map_template/shuttle)) @@ -160,5 +160,5 @@ var/datum/map_template/shuttle/S = new shuttle_type() - shuttle_templates[S.shuttle_id] = S - map_templates[S.shuttle_id] = S \ No newline at end of file + GLOB.shuttle_templates[S.shuttle_id] = S + GLOB.map_templates[S.shuttle_id] = S diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 2170ad4196e..9bba49f2678 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -1,237 +1,237 @@ -//wrapper -/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, bypass_area_flag=FALSE) - var/datum/teleport/instant/science/D = new - if(D.start(arglist(args))) - return 1 - return 0 - -/datum/teleport - var/atom/movable/teleatom //atom to teleport - var/atom/destination //destination to teleport to - var/precision = 0 //teleport precision - var/datum/effect_system/effectin //effect to show right before teleportation - var/datum/effect_system/effectout //effect to show right after teleportation - var/soundin //soundfile to play before teleportation - var/soundout //soundfile to play after teleportation - var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation) - var/ignore_area_flag = FALSE - - -/datum/teleport/proc/start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, bypass_area_flag=FALSE) - if(!initTeleport(arglist(args))) - return 0 - return 1 - -/datum/teleport/proc/initTeleport(ateleatom, adestination, aprecision, afteleport, aeffectin, aeffectout, asoundin, asoundout, bypass_area_flag=FALSE) - if(!setTeleatom(ateleatom)) - return 0 - if(!setDestination(adestination)) - return 0 - if(!setPrecision(aprecision)) - return 0 - setEffects(aeffectin,aeffectout) - setForceTeleport(afteleport) - setSounds(asoundin,asoundout) - ignore_area_flag = bypass_area_flag - return 1 - -//must succeed -/datum/teleport/proc/setPrecision(aprecision) - if(isnum(aprecision)) - precision = aprecision - return 1 - return 0 - -//must succeed -/datum/teleport/proc/setDestination(atom/adestination) - if(istype(adestination)) - destination = adestination - return 1 - return 0 - -//must succeed in most cases -/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) - if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) - qdel(ateleatom) - return 0 - if(istype(ateleatom)) - teleatom = ateleatom - return 1 - return 0 - -//custom effects must be properly set up first for instant-type teleports -//optional -/datum/teleport/proc/setEffects(datum/effect_system/aeffectin=null,datum/effect_system/aeffectout=null) - effectin = istype(aeffectin) ? aeffectin : null - effectout = istype(aeffectout) ? aeffectout : null - return 1 - -//optional -/datum/teleport/proc/setForceTeleport(afteleport) - force_teleport = afteleport - return 1 - -//optional -/datum/teleport/proc/setSounds(asoundin=null,asoundout=null) - soundin = isfile(asoundin) ? asoundin : null - soundout = isfile(asoundout) ? asoundout : null - return 1 - -//placeholder -/datum/teleport/proc/teleportChecks() - return 1 - -/datum/teleport/proc/playSpecials(atom/location,datum/effect_system/effect,sound) - if(location) - if(effect) - spawn(-1) - src = null - effect.attach(location) - effect.start() - if(sound) - spawn(-1) - src = null - playsound(location,sound,60,1) - return - -//do the monkey dance -/datum/teleport/proc/doTeleport() - - var/turf/destturf - var/turf/curturf = get_turf(teleatom) - var/area/curarea = get_area(curturf) - - if(precision) - var/list/posturfs = list() - var/center = get_turf(destination) - if(!center) - center = destination - for(var/turf/T in range(precision,center)) - posturfs.Add(T) - destturf = safepick(posturfs) - else - destturf = get_turf(destination) - - if(!is_teleport_allowed(destturf.z) && !ignore_area_flag) - return 0 - // Only check the destination zlevel for is_teleport_allowed. Checking origin as well breaks ERT teleporters. - - var/area/destarea = get_area(destturf) - - if(!ignore_area_flag) - if(curarea.tele_proof) - return 0 - if(destarea.tele_proof) - return 0 - - if(!destturf || !curturf) - return 0 - - playSpecials(curturf,effectin,soundin) - - if(force_teleport) - teleatom.forceMove(destturf) - playSpecials(destturf,effectout,soundout) - else - if(teleatom.Move(destturf)) - playSpecials(destturf,effectout,soundout) - - if(isliving(teleatom)) - var/mob/living/L = teleatom - if(L.buckled) - L.buckled.unbuckle_mob(L, force = TRUE) - if(L.has_buckled_mobs()) - L.unbuckle_all_mobs(force = TRUE) - - destarea.Entered(teleatom) - - return 1 - -/datum/teleport/proc/teleport() - if(teleportChecks()) - return doTeleport() - return 0 - -/datum/teleport/instant //teleports when datum is created - - start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) - if(..()) - if(teleport()) - return 1 - return 0 - - -/datum/teleport/instant/science - -/datum/teleport/instant/science/setEffects(datum/effect_system/aeffectin,datum/effect_system/aeffectout) - if(aeffectin==null || aeffectout==null) - var/datum/effect_system/spark_spread/aeffect = new - aeffect.set_up(5, 1, teleatom) - effectin = effectin || aeffect - effectout = effectout || aeffect - return 1 - else - return ..() - -/datum/teleport/instant/science/setPrecision(aprecision) - ..() - if(!is_admin_level(destination.z)) - if(istype(teleatom, /obj/item/storage/backpack/holding)) - precision = rand(1, 100) - - var/list/bagholding = teleatom.search_contents_for(/obj/item/storage/backpack/holding) - if(bagholding.len) - precision = max(rand(1, 100)*bagholding.len, 100) - if(istype(teleatom, /mob/living)) - var/mob/living/MM = teleatom - to_chat(MM, "The bluespace interface on your bag of holding interferes with the teleport!") - return 1 - -// Safe location finder -/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE) - if(!zlevels) - if(zlevel) - zlevels = list(zlevel) - else - zlevels = levels_by_trait(STATION_LEVEL) - var/cycles = 1000 - for(var/cycle in 1 to cycles) - // DRUNK DIALLING WOOOOOOOOO - var/x = rand(1, world.maxx) - var/y = rand(1, world.maxy) - var/z = pick(zlevels) - var/random_location = locate(x,y,z) - - if(!isfloorturf(random_location)) - continue - var/turf/simulated/floor/F = random_location - if(!F.air) - continue - - var/datum/gas_mixture/A = F.air - - // Can most things breathe? - if(A.trace_gases.len) - continue - if(A.oxygen < 16) - continue - if(A.toxins) - continue - if(A.carbon_dioxide >= 10) - continue - - // Aim for goldilocks temperatures and pressure - if((A.temperature <= 270) || (A.temperature >= 360)) - continue - var/pressure = A.return_pressure() - if((pressure <= 20) || (pressure >= 550)) - continue - - if(extended_safety_checks) - if(islava(F)) //chasms aren't /floor, and so are pre-filtered - var/turf/simulated/floor/plating/lava/L = F - if(!L.is_safe()) - continue - - // DING! You have passed the gauntlet, and are "probably" safe. - return F \ No newline at end of file +//wrapper +/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, bypass_area_flag=FALSE) + var/datum/teleport/instant/science/D = new + if(D.start(arglist(args))) + return 1 + return 0 + +/datum/teleport + var/atom/movable/teleatom //atom to teleport + var/atom/destination //destination to teleport to + var/precision = 0 //teleport precision + var/datum/effect_system/effectin //effect to show right before teleportation + var/datum/effect_system/effectout //effect to show right after teleportation + var/soundin //soundfile to play before teleportation + var/soundout //soundfile to play after teleportation + var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation) + var/ignore_area_flag = FALSE + + +/datum/teleport/proc/start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, bypass_area_flag=FALSE) + if(!initTeleport(arglist(args))) + return 0 + return 1 + +/datum/teleport/proc/initTeleport(ateleatom, adestination, aprecision, afteleport, aeffectin, aeffectout, asoundin, asoundout, bypass_area_flag=FALSE) + if(!setTeleatom(ateleatom)) + return 0 + if(!setDestination(adestination)) + return 0 + if(!setPrecision(aprecision)) + return 0 + setEffects(aeffectin,aeffectout) + setForceTeleport(afteleport) + setSounds(asoundin,asoundout) + ignore_area_flag = bypass_area_flag + return 1 + +//must succeed +/datum/teleport/proc/setPrecision(aprecision) + if(isnum(aprecision)) + precision = aprecision + return 1 + return 0 + +//must succeed +/datum/teleport/proc/setDestination(atom/adestination) + if(istype(adestination)) + destination = adestination + return 1 + return 0 + +//must succeed in most cases +/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) + if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) + qdel(ateleatom) + return 0 + if(istype(ateleatom)) + teleatom = ateleatom + return 1 + return 0 + +//custom effects must be properly set up first for instant-type teleports +//optional +/datum/teleport/proc/setEffects(datum/effect_system/aeffectin=null,datum/effect_system/aeffectout=null) + effectin = istype(aeffectin) ? aeffectin : null + effectout = istype(aeffectout) ? aeffectout : null + return 1 + +//optional +/datum/teleport/proc/setForceTeleport(afteleport) + force_teleport = afteleport + return 1 + +//optional +/datum/teleport/proc/setSounds(asoundin=null,asoundout=null) + soundin = isfile(asoundin) ? asoundin : null + soundout = isfile(asoundout) ? asoundout : null + return 1 + +//placeholder +/datum/teleport/proc/teleportChecks() + return 1 + +/datum/teleport/proc/playSpecials(atom/location,datum/effect_system/effect,sound) + if(location) + if(effect) + spawn(-1) + src = null + effect.attach(location) + effect.start() + if(sound) + spawn(-1) + src = null + playsound(location,sound,60,1) + return + +//do the monkey dance +/datum/teleport/proc/doTeleport() + + var/turf/destturf + var/turf/curturf = get_turf(teleatom) + var/area/curarea = get_area(curturf) + + if(precision) + var/list/posturfs = list() + var/center = get_turf(destination) + if(!center) + center = destination + for(var/turf/T in range(precision,center)) + posturfs.Add(T) + destturf = safepick(posturfs) + else + destturf = get_turf(destination) + + if(!is_teleport_allowed(destturf.z) && !ignore_area_flag) + return 0 + // Only check the destination zlevel for is_teleport_allowed. Checking origin as well breaks ERT teleporters. + + var/area/destarea = get_area(destturf) + + if(!ignore_area_flag) + if(curarea.tele_proof) + return 0 + if(destarea.tele_proof) + return 0 + + if(!destturf || !curturf) + return 0 + + playSpecials(curturf,effectin,soundin) + + if(force_teleport) + teleatom.forceMove(destturf) + playSpecials(destturf,effectout,soundout) + else + if(teleatom.Move(destturf)) + playSpecials(destturf,effectout,soundout) + + if(isliving(teleatom)) + var/mob/living/L = teleatom + if(L.buckled) + L.buckled.unbuckle_mob(L, force = TRUE) + if(L.has_buckled_mobs()) + L.unbuckle_all_mobs(force = TRUE) + + destarea.Entered(teleatom) + + return 1 + +/datum/teleport/proc/teleport() + if(teleportChecks()) + return doTeleport() + return 0 + +/datum/teleport/instant //teleports when datum is created + + start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) + if(..()) + if(teleport()) + return 1 + return 0 + + +/datum/teleport/instant/science + +/datum/teleport/instant/science/setEffects(datum/effect_system/aeffectin,datum/effect_system/aeffectout) + if(aeffectin==null || aeffectout==null) + var/datum/effect_system/spark_spread/aeffect = new + aeffect.set_up(5, 1, teleatom) + effectin = effectin || aeffect + effectout = effectout || aeffect + return 1 + else + return ..() + +/datum/teleport/instant/science/setPrecision(aprecision) + ..() + if(!is_admin_level(destination.z)) + if(istype(teleatom, /obj/item/storage/backpack/holding)) + precision = rand(1, 100) + + var/list/bagholding = teleatom.search_contents_for(/obj/item/storage/backpack/holding) + if(bagholding.len) + precision = max(rand(1, 100)*bagholding.len, 100) + if(istype(teleatom, /mob/living)) + var/mob/living/MM = teleatom + to_chat(MM, "The bluespace interface on your bag of holding interferes with the teleport!") + return 1 + +// Safe location finder +/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE) + if(!zlevels) + if(zlevel) + zlevels = list(zlevel) + else + zlevels = levels_by_trait(STATION_LEVEL) + var/cycles = 1000 + for(var/cycle in 1 to cycles) + // DRUNK DIALLING WOOOOOOOOO + var/x = rand(1, world.maxx) + var/y = rand(1, world.maxy) + var/z = pick(zlevels) + var/random_location = locate(x,y,z) + + if(!isfloorturf(random_location)) + continue + var/turf/simulated/floor/F = random_location + if(!F.air) + continue + + var/datum/gas_mixture/A = F.air + + // Can most things breathe? + if(A.trace_gases.len) + continue + if(A.oxygen < 16) + continue + if(A.toxins) + continue + if(A.carbon_dioxide >= 10) + continue + + // Aim for goldilocks temperatures and pressure + if((A.temperature <= 270) || (A.temperature >= 360)) + continue + var/pressure = A.return_pressure() + if((pressure <= 20) || (pressure >= 550)) + continue + + if(extended_safety_checks) + if(islava(F)) //chasms aren't /floor, and so are pre-filtered + var/turf/simulated/floor/plating/lava/L = F + if(!L.is_safe()) + continue + + // DING! You have passed the gauntlet, and are "probably" safe. + return F diff --git a/code/datums/helper_datums/topic_input.dm b/code/datums/helper_datums/topic_input.dm index 8090a5221eb..0f6a9f7d689 100644 --- a/code/datums/helper_datums/topic_input.dm +++ b/code/datums/helper_datums/topic_input.dm @@ -1,60 +1,60 @@ -/datum/topic_input - var/href - var/list/href_list - -/datum/topic_input/New(thref,list/thref_list) - href = thref - href_list = thref_list.Copy() - return - -/datum/topic_input/proc/get(i) - return listgetindex(href_list,i) - -/datum/topic_input/proc/getAndLocate(i) - var/t = get(i) - if(t) - t = locate(t) - return t || null - -/datum/topic_input/proc/getNum(i) - var/t = get(i) - if(t) - t = text2num(t) - return isnum(t) ? t : null - -/datum/topic_input/proc/getObj(i) - var/t = getAndLocate(i) - return isobj(t) ? t : null - -/datum/topic_input/proc/getMob(i) - var/t = getAndLocate(i) - return ismob(t) ? t : null - -/datum/topic_input/proc/getTurf(i) - var/t = getAndLocate(i) - return isturf(t) ? t : null - -/datum/topic_input/proc/getAtom(i) - return getType(i,/atom) - -/datum/topic_input/proc/getArea(i) - var/t = getAndLocate(i) - return isarea(t) ? t : null - -/datum/topic_input/proc/getStr(i)//params should always be text, but... - var/t = get(i) - return istext(t) ? t : null - -/datum/topic_input/proc/getType(i,type) - var/t = getAndLocate(i) - return istype(t,type) ? t : null - -/datum/topic_input/proc/getPath(i) - var/t = get(i) - if(t) - t = text2path(t) - return ispath(t) ? t : null - -/datum/topic_input/proc/getList(i) - var/t = getAndLocate(i) - return islist(t) ? t : null +/datum/topic_input + var/href + var/list/href_list + +/datum/topic_input/New(thref,list/thref_list) + href = thref + href_list = thref_list.Copy() + return + +/datum/topic_input/proc/get(i) + return listgetindex(href_list,i) + +/datum/topic_input/proc/getAndLocate(i) + var/t = get(i) + if(t) + t = locate(t) + return t || null + +/datum/topic_input/proc/getNum(i) + var/t = get(i) + if(t) + t = text2num(t) + return isnum(t) ? t : null + +/datum/topic_input/proc/getObj(i) + var/t = getAndLocate(i) + return isobj(t) ? t : null + +/datum/topic_input/proc/getMob(i) + var/t = getAndLocate(i) + return ismob(t) ? t : null + +/datum/topic_input/proc/getTurf(i) + var/t = getAndLocate(i) + return isturf(t) ? t : null + +/datum/topic_input/proc/getAtom(i) + return getType(i,/atom) + +/datum/topic_input/proc/getArea(i) + var/t = getAndLocate(i) + return isarea(t) ? t : null + +/datum/topic_input/proc/getStr(i)//params should always be text, but... + var/t = get(i) + return istext(t) ? t : null + +/datum/topic_input/proc/getType(i,type) + var/t = getAndLocate(i) + return istype(t,type) ? t : null + +/datum/topic_input/proc/getPath(i) + var/t = get(i) + if(t) + t = text2path(t) + return ispath(t) ? t : null + +/datum/topic_input/proc/getList(i) + var/t = getAndLocate(i) + return islist(t) ? t : null diff --git a/code/datums/hud.dm b/code/datums/hud.dm index 365a4309413..50152c4873d 100644 --- a/code/datums/hud.dm +++ b/code/datums/hud.dm @@ -1,8 +1,8 @@ /* HUD DATUMS */ -var/global/list/all_huds = list() +GLOBAL_LIST_EMPTY(all_huds) ///GLOBAL HUD LIST -var/datum/atom_hud/huds = list( \ +GLOBAL_LIST_INIT(huds, list( \ DATA_HUD_SECURITY_BASIC = new/datum/atom_hud/data/human/security/basic(), \ DATA_HUD_SECURITY_ADVANCED = new/datum/atom_hud/data/human/security/advanced(), \ DATA_HUD_MEDICAL_BASIC = new/datum/atom_hud/data/human/medical/basic(), \ @@ -24,7 +24,7 @@ var/datum/atom_hud/huds = list( \ ANTAG_HUD_DEVIL = new/datum/atom_hud/antag/hidden(),\ ANTAG_HUD_EVENTMISC = new/datum/atom_hud/antag/hidden(),\ ANTAG_HUD_BLOB = new/datum/atom_hud/antag/hidden()\ - ) +)) /datum/atom_hud var/list/atom/hudatoms = list() //list of all atoms which display this hud @@ -33,14 +33,14 @@ var/datum/atom_hud/huds = list( \ /datum/atom_hud/New() - all_huds += src + GLOB.all_huds += src /datum/atom_hud/Destroy() for(var/v in hudusers) remove_hud_from(v) for(var/v in hudatoms) remove_from_hud(v) - all_huds -= src + GLOB.all_huds -= src return ..() /datum/atom_hud/proc/remove_hud_from(mob/M) @@ -99,7 +99,7 @@ var/datum/atom_hud/huds = list( \ serv_huds += serv.thrallhud - for(var/datum/atom_hud/hud in (all_huds|serv_huds))//|gang_huds)) + for(var/datum/atom_hud/hud in (GLOB.all_huds|serv_huds))//|gang_huds)) if(src in hud.hudusers) hud.add_hud_to(src) @@ -110,4 +110,4 @@ var/datum/atom_hud/huds = list( \ client.screen += client.void /mob/new_player/add_click_catcher() - return \ No newline at end of file + return diff --git a/code/datums/log_record.dm b/code/datums/log_record.dm new file mode 100644 index 00000000000..9b7d52b5390 --- /dev/null +++ b/code/datums/log_record.dm @@ -0,0 +1,37 @@ +/datum/log_record + var/log_type // Type of log + var/raw_time // When did this happen? + var/what // What happened + var/who // Who did it + var/target // Who/what was targeted (can be a string) + var/turf/where // Where did it happen + +/datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time) + log_type = _log_type + + who = get_subject_text(_who) + what = _what + target = get_subject_text(_target) + if(!_where) + _where = get_turf(_who) + where = _where + if(!_raw_time) + _raw_time = world.time + raw_time = _raw_time + +/datum/log_record/proc/get_subject_text(subject) + if(ismob(subject) || isclient(subject) || istype(subject, /datum/mind)) + return key_name_admin(subject) + if(isatom(subject)) + var/atom/A = subject + return A.name + if(istype(subject, /datum)) + var/datum/D = subject + return D.type + return subject + +/proc/compare_log_record(datum/log_record/A, datum/log_record/B) + var/time_diff = A.raw_time - B.raw_time + if(!time_diff) // Same time + return cmp_text_asc(A.log_type, B.log_type) + return time_diff diff --git a/code/datums/log_viewer.dm b/code/datums/log_viewer.dm new file mode 100644 index 00000000000..67eb3d21d86 --- /dev/null +++ b/code/datums/log_viewer.dm @@ -0,0 +1,235 @@ +#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG) + +/datum/log_viewer + var/time_from = 0 + var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up + var/list/selected_mobs = list() // The mobs in question + var/list/selected_log_types = list() // The log types being searched for + + var/list/log_records = list() // Found and sorted records + +/datum/log_viewer/proc/clear_all() + selected_mobs.Cut() + selected_log_types.Cut() + time_from = initial(time_from) + time_to = initial(time_to) + log_records.Cut() + return + +/datum/log_viewer/proc/search() + log_records.Cut() // Empty the old results + var/list/invalid_mobs = list() + for(var/i in selected_mobs) + var/mob/M = i + if(!M || QDELETED(M)) + invalid_mobs |= M + continue + for(var/log_type in selected_log_types) + var/list/logs = M.logs[log_type] + var/len_logs = length(logs) + if(len_logs) + var/start_index = get_earliest_log_index(logs) + if(!start_index) // No log found that matches the starting time criteria + continue + var/end_index = get_latest_log_index(logs) + if(!end_index) // No log found that matches the end time criteria + continue + log_records.Add(logs.Copy(start_index, end_index + 1)) + + if(invalid_mobs.len) + to_chat(usr, "The search criteria contained invalid mobs. They have been removed from the criteria.") + for(var/i in invalid_mobs) + selected_mobs -= i // Cleanup + + log_records = sortTim(log_records, /proc/compare_log_record) + +/** Binary search like implementation to find the earliest log + * Returns the index of the earliest log using the time_from value for the given list of logs. + * It will return 0 if no log after time_from is found +*/ +/datum/log_viewer/proc/get_earliest_log_index(list/logs) + if(!time_from) + return 1 + var/start = 1 + var/end = length(logs) + var/mid + do + mid = round_down((end + start) / 2) + var/datum/log_record/L = logs[mid] + if(L.raw_time >= time_from) + end = mid + else + start = mid + while(end - start > 1) + var/datum/log_record/L = logs[end] + if(L.raw_time >= time_from) // Check if there is atleast one valid log + return end + return 0 + +/** Binary search like implementation to find the latest log + * Returns the index of the latest log using the time_to value (1 second is added to prevent rounding weirdness) for the given list of logs. + * It will return 0 if no log before time_to + 10 is found +*/ +/datum/log_viewer/proc/get_latest_log_index(list/logs) + if(world.time < time_to) + return length(logs) + + var/end = length(logs) + var/start = 1 + var/mid + var/max_time = time_to + 10 + do + mid = round((end + start) / 2 + 0.5) + var/datum/log_record/L = logs[mid] + if(L.raw_time >= max_time) + end = mid + else + start = mid + while(end - start > 1) + var/datum/log_record/L = logs[start] + if(L.raw_time < max_time) // Check if there is atleast one valid log + return start + return 0 + +/datum/log_viewer/proc/add_mob(mob/user, mob/M) + if(!M || !user) + return + selected_mobs |= M + + show_ui(user) + +/datum/log_viewer/proc/show_ui(mob/user) + var/all_log_types = ALL_LOGS + var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;" + var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;" + var/dat + dat += "" + dat += "
    " + dat += "Time Search Range: [gameTimestamp(wtime = time_from)]" + dat += " To: [gameTimestamp(wtime = time_to)]" + dat += "
    " + + dat += "Mobs being used:" + for(var/i in selected_mobs) + var/mob/M = i + if(QDELETED(M)) + selected_mobs -= i + continue + dat += "[M.name]" + dat += "Add Mob" + dat += "Clear All Mobs" + dat += "
    " + + dat += "Log Types:" + for(var/i in all_log_types) + var/log_type = i + var/enabled = (log_type in selected_log_types) + var/text + var/style + if(enabled) + text = "[log_type]" + style = "background: [get_logtype_color(i)]" + else + text = log_type + + dat += "[text]" + + dat += "
    " + dat += "Clear All Settings" + dat += "Search" + dat += "
    " + + // Search results + var/tdStyleTime = "width:80px; text-align:center;" + var/tdStyleType = "width:80px; text-align:center;" + var/tdStyleWho = "width:300px; text-align:center;" + var/tdStyleWhere = "width:150px; text-align:center;" + dat += "
    " + dat += "" + dat += "" + for(var/i in log_records) + var/datum/log_record/L = i + var/time = gameTimestamp(wtime = L.raw_time - 9.99) // The time rounds up for some reason. Will result in weird filtering results + + dat +="\ + \ + " + + dat += "
    WhenTypeWhoWhatTargetWhere
    [time][L.log_type][L.who][L.what][L.target][ADMIN_COORDJMP(L.where)]
    " + dat += "
    " + + var/datum/browser/popup = new(user, "Log viewer", "Log viewer", 1400, 600) + popup.set_content(dat) + popup.open() + +/datum/log_viewer/Topic(href, href_list) + if(href_list["start_time"]) + var/input = input(usr, "hh:mm:ss", "Start time", "00:00:00") as text|null + if(!input) + return + var/res = timeStampToNum(input) + if(res < 0) + to_chat(usr, "'[input]' is an invalid input value.") + return + time_from = res + show_ui(usr) + return + if(href_list["end_time"]) + var/input = input(usr, "hh:mm:ss", "End time", "04:00:00") as text|null + if(!input) + return + var/res = timeStampToNum(input) + if(res < 0) + to_chat(usr, "'[input]' is an invalid input value.") + return + time_to = res + + show_ui(usr) + return + if(href_list["search"]) + search(usr) + show_ui(usr) + return + if(href_list["clear_all"]) + clear_all(usr) + show_ui(usr) + return + if(href_list["clear_mobs"]) + selected_mobs.Cut() + show_ui(usr) + return + if(href_list["add_mob"]) + var/list/mobs = getpois(TRUE, TRUE) + var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs) + A.on_close(CALLBACK(src, .proc/add_mob, usr)) + return + if(href_list["remove_mob"]) + var/mob/M = locate(href_list["remove_mob"]) + if(M) + selected_mobs -= M + show_ui(usr) + return + if(href_list["toggle_log_type"]) + var/log_type = href_list["toggle_log_type"] + if(log_type in selected_log_types) + selected_log_types -= log_type + else + selected_log_types += log_type + show_ui(usr) + return + +/datum/log_viewer/proc/get_logtype_color(log_type) + switch(log_type) + if(ATTACK_LOG) + return "darkred" + if(DEFENSE_LOG) + return "chocolate" + if(CONVERSION_LOG) + return "indigo" + if(SAY_LOG) + return "teal" + if(EMOTE_LOG) + return "deepskyblue" + if(MISC_LOG) + return "gray" + return "slategray" diff --git a/code/datums/looping_sounds/looping_sound.dm b/code/datums/looping_sounds/looping_sound.dm index 2cf79ad318b..f44a87bdd7a 100644 --- a/code/datums/looping_sounds/looping_sound.dm +++ b/code/datums/looping_sounds/looping_sound.dm @@ -97,4 +97,4 @@ /datum/looping_sound/proc/on_stop(looped) if(end_sound) - play(end_sound) \ No newline at end of file + play(end_sound) diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm index b1f5bdc6908..8fe37324237 100644 --- a/code/datums/looping_sounds/machinery_sounds.dm +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -4,4 +4,4 @@ mid_sounds = list('sound/machines/shower/shower_mid1.ogg' = 1,'sound/machines/shower/shower_mid2.ogg' = 1,'sound/machines/shower/shower_mid3.ogg' = 1) mid_length = 10 end_sound = 'sound/machines/shower/shower_end.ogg' - volume = 20 \ No newline at end of file + volume = 20 diff --git a/code/datums/looping_sounds/thermal_drill.dm b/code/datums/looping_sounds/thermal_drill.dm index d4a20647f2b..3346d7e0e06 100644 --- a/code/datums/looping_sounds/thermal_drill.dm +++ b/code/datums/looping_sounds/thermal_drill.dm @@ -1,4 +1,4 @@ /datum/looping_sound/thermal_drill mid_sounds = list('sound/items/thermal_drill.ogg' = 1) mid_length = 19 - volume = 30 \ No newline at end of file + volume = 30 diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 036d70fa17a..2a3a32d8119 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1,1822 +1,1827 @@ -/* Note from Carnie: - The way datum/mind stuff works has been changed a lot. - Minds now represent IC characters rather than following a client around constantly. - Guidelines for using minds properly: - - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! - ghost.mind is however used as a reference to the ghost's corpse - - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) - the existing mind of the old mob should be transfered to the new mob like so: - mind.transfer_to(new_mob) - - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. - By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing - the player. - - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. - - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting - a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. - new_mob.key = key - The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! - However if you want that mind to have any special properties like being a traitor etc you will have to do that - yourself. -*/ - -/datum/mind - var/key - var/name //replaces mob/var/original_name - var/mob/living/current - var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. - var/active = 0 - - var/memory - - var/assigned_role //assigned role is what job you're assigned to when you join the station. - var/playtime_role //if set, overrides your assigned_role for the purpose of playtime awards. Set by IDcomputer when your ID is changed. - var/special_role //special roles are typically reserved for antags or roles like ERT. If you want to avoid a character being automatically announced by the AI, on arrival (becuase they're an off station character or something); ensure that special_role and assigned_role are equal. - var/offstation_role = FALSE //set to true for ERT, deathsquad, abductors, etc, that can go from and to z2 at will and shouldn't be antag targets - var/list/restricted_roles = list() - - var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. - - var/role_alt_title - - var/datum/job/assigned_job - var/list/kills = list() - var/list/datum/objective/objectives = list() - var/list/datum/objective/special_verbs = list() - var/list/targets = list() - - var/has_been_rev = 0//Tracks if this mind has been a rev or not - - var/miming = 0 // Mime's vow of silence - var/list/antag_datums - var/speech_span // What span any body this mind has talks in. - var/datum/faction/faction //associated faction - var/datum/changeling/changeling //changeling holder - var/linglink - var/datum/vampire/vampire //vampire holder - var/datum/abductor/abductor //abductor holder - var/datum/devilinfo/devilinfo //devil holder - - var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state - var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD - var/datum/mindslaves/som //stands for slave or master...hush.. - var/datum/devilinfo/devilinfo //Information about the devil, if any. - var/damnation_type = 0 - var/datum/mind/soulOwner //who owns the soul. Under normal circumstances, this will point to src - var/hasSoul = TRUE - - var/rev_cooldown = 0 - - var/isholy = FALSE // is this person a chaplain or admin role allowed to use bibles - var/isblessed = FALSE // is this person blessed by a chaplain? - var/num_blessed = 0 // for prayers - - // the world.time since the mob has been brigged, or -1 if not at all - var/brigged_since = -1 - var/suicided = FALSE - - //put this here for easier tracking ingame - var/datum/money_account/initial_account - - //zealot_master is a reference to the mob that converted them into a zealot (for ease of investigation and such) - var/mob/living/carbon/human/zealot_master = null - - var/list/learned_recipes //List of learned recipe TYPES. - -/datum/mind/New(new_key) - key = new_key - soulOwner = src - -/datum/mind/Destroy() - SSticker.minds -= src - if(islist(antag_datums)) - for(var/i in antag_datums) - var/datum/antagonist/antag_datum = i - if(antag_datum.delete_on_mind_deletion) - qdel(i) - antag_datums = null - return ..() - -/datum/mind/proc/transfer_to(mob/living/new_character) - var/datum/atom_hud/antag/hud_to_transfer = antag_hud //we need this because leave_hud() will clear this list - var/mob/living/old_current = current - if(!istype(new_character)) - log_runtime(EXCEPTION("transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob."), src) - if(current) //remove ourself from our old body's mind variable - current.mind = null - leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it - - SSnanoui.user_transferred(current, new_character) - - if(new_character.mind) //remove any mind currently in our new body's mind variable - new_character.mind.current = null - current = new_character //link ourself to our new body - new_character.mind = src //and link our new body to ourself - for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body - var/datum/antagonist/A = a - A.on_body_transfer(old_current, current) - transfer_antag_huds(hud_to_transfer) //inherit the antag HUD - transfer_actions(new_character) - - if(active) - new_character.key = key //now transfer the key to link the client to our new body - -/datum/mind/proc/store_memory(new_text) - memory += "[new_text]
    " - -/datum/mind/proc/wipe_memory() - memory = null - -/datum/mind/proc/show_memory(mob/recipient, window = 1) - if(!recipient) - recipient = current - var/output = "[current.real_name]'s Memories:
    " - output += memory - - var/antag_datum_objectives = FALSE - for(var/datum/antagonist/A in antag_datums) - output += A.antag_memory - if(!antag_datum_objectives && LAZYLEN(A.objectives)) - antag_datum_objectives = TRUE - - if(LAZYLEN(objectives) || antag_datum_objectives) - output += "
    Objectives:
    " - output += gen_objective_text() - - if(LAZYLEN(job_objectives)) - output += "
    Job Objectives:
      " - - var/obj_count = 1 - for(var/datum/job_objective/objective in job_objectives) - output += "
    • Task #[obj_count]: [objective.get_description()]
    • " - obj_count++ - output += "
    " - if(window) - recipient << browse(output, "window=memory") - else - to_chat(recipient, "[output]") - -/datum/mind/proc/gen_objective_text(admin = FALSE) - . = "" - var/obj_count = 1 - var/list/all_objectives = list() - for(var/datum/antagonist/A in antag_datums) - all_objectives |= A.objectives - - if(LAZYLEN(all_objectives)) - for(var/datum/objective/objective in all_objectives) - . += "
    Objective #[obj_count++]: [objective.explanation_text]" - - for(var/datum/objective/objective in objectives) - . += "Objective #[obj_count++]: [objective.explanation_text]" - if(admin) - . += " Edit " // Edit - . += "Delete " // Delete - - . += "" // Mark Completed - . += "Toggle Completion" - . += "" - . += "
    " - -/datum/mind/proc/_memory_edit_header(gamemode, list/alt) - . = gamemode - if(SSticker.mode.config_tag == gamemode || (LAZYLEN(alt) && (SSticker.mode.config_tag in alt))) - . = uppertext(.) - . = "[.]: " - -/datum/mind/proc/_memory_edit_role_enabled(role) - . = "|Disabled in Prefs" - if(current && current.client && (role in current.client.prefs.be_special)) - . = "|Enabled in Prefs" - -/datum/mind/proc/memory_edit_implant(mob/living/carbon/human/H) - if(ismindshielded(H)) - . = "Mindshield Implant:Remove|Implanted
    " - else - . = "Mindshield Implant:No Implant|Implant [H.p_them()]!
    " - - -/datum/mind/proc/memory_edit_revolution(mob/living/carbon/human/H) - . = _memory_edit_header("revolution") - if(ismindshielded(H)) - . += "NO|headrev|rev" - else if(src in SSticker.mode.head_revolutionaries) - . += "no|HEADREV|rev" - . += "
    Flash: give" - - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - if(flash) - if(!flash.broken) - . += "|take." - else - . += "|take|repair." - else - . += "." - - . += " Reequip (gives traitor uplink)." - if(objectives.len==0) - . += "
    Objectives are empty! Set to kill all heads." - else if(src in SSticker.mode.revolutionaries) - . += "no|headrev|REV" - else - . += "NO|headrev|rev" - - . += _memory_edit_role_enabled(ROLE_REV) - -/datum/mind/proc/memory_edit_cult(mob/living/carbon/human/H) - . = _memory_edit_header("cult") - if(src in SSticker.mode.cult) - . += "no|CULTIST" - . += "
    Give tome|equip." - else - . += "NO|cultist" - - . += _memory_edit_role_enabled(ROLE_CULTIST) - -/datum/mind/proc/memory_edit_wizard(mob/living/carbon/human/H) - . = _memory_edit_header("wizard") - if(src in SSticker.mode.wizards) - . += "WIZARD|no" - . += "
    To lair, undress, dress up, let choose name." - if(objectives.len==0) - . += "
    Objectives are empty! Randomize!" - else - . += "wizard|NO" - - . += _memory_edit_role_enabled(ROLE_WIZARD) - -/datum/mind/proc/memory_edit_changeling(mob/living/carbon/human/H) - . = _memory_edit_header("changeling", list("traitorchan")) - if(src in SSticker.mode.changelings) - . += "CHANGELING|no" - if(objectives.len==0) - . += "
    Objectives are empty! Randomize!" - if(changeling && changeling.absorbed_dna.len && (current.real_name != changeling.absorbed_dna[1])) - . += "
    Transform to initial appearance." - else - . += "changeling|NO" - - . += _memory_edit_role_enabled(ROLE_CHANGELING) - -/datum/mind/proc/memory_edit_vampire(mob/living/carbon/human/H) - . = _memory_edit_header("vampire", list("traitorvamp")) - if(src in SSticker.mode.vampires) - . += "VAMPIRE|no" - if(objectives.len==0) - . += "
    Objectives are empty! Randomize!" - else - . += "vampire|NO" - - . += _memory_edit_role_enabled(ROLE_VAMPIRE) - /** Enthralled ***/ - . += "
    enthralled: " - if(src in SSticker.mode.vampire_enthralled) - . += "THRALL|no" - else - . += "thrall|NO" - -/datum/mind/proc/memory_edit_nuclear(mob/living/carbon/human/H) - . = _memory_edit_header("nuclear") - if(src in SSticker.mode.syndicates) - . += "OPERATIVE|no" - . += "
    To shuttle, undress, dress up." - var/code - for(var/obj/machinery/nuclearbomb/bombue in GLOB.machines) - if(length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") - code = bombue.r_code - break - if(code) - . += " Code is [code]. tell the code." - else - . += "operative|NO" - - . += _memory_edit_role_enabled(ROLE_OPERATIVE) - -/datum/mind/proc/memory_edit_shadowling(mob/living/carbon/human/H) - . = _memory_edit_header("shadowling") - if(src in SSticker.mode.shadows) - . += "SHADOWLING|thrall|no" - else if(src in SSticker.mode.shadowling_thralls) - . += "Shadowling|THRALL|no" - else - . += "shadowling|thrall|NO" - - . += _memory_edit_role_enabled(ROLE_SHADOWLING) - -/datum/mind/proc/memory_edit_abductor(mob/living/carbon/human/H) - . = _memory_edit_header("abductor") - if(src in SSticker.mode.abductors) - . += "ABDUCTOR|no" - . += "|undress|equip" - else - . += "abductor|NO" - - . += _memory_edit_role_enabled(ROLE_ABDUCTOR) - -/datum/mind/proc/memory_edit_devil(mob/living/H) - . = _memory_edit_header("devil", list("devilagents")) - if(src in SSticker.mode.devils) - if(!devilinfo) - . += "No devilinfo found! Yell at a coder!" - else if(!devilinfo.ascendable) - . += "DEVIL|Ascendable Devil|sintouched|no" - else - . += "DEVIL|ASCENDABLE DEVIL|sintouched|no" - else if(src in SSticker.mode.sintouched) - . += "devil|Ascendable Devil|SINTOUCHED|no" - else - . += "devil|Ascendable Devil|sintouched|NO" - - . += _memory_edit_role_enabled(ROLE_DEVIL) - -/datum/mind/proc/memory_edit_eventmisc(mob/living/H) - . = _memory_edit_header("event", list()) - if(src in SSticker.mode.eventmiscs) - . += "YES|no" - else - . += "Event Role|NO" - -/datum/mind/proc/memory_edit_traitor() - . = _memory_edit_header("traitor", list("traitorchan", "traitorvamp")) - if(has_antag_datum(/datum/antagonist/traitor)) - . += "TRAITOR|no" - if(objectives.len==0) - . += "
    Objectives are empty! Randomize!" - else - . += "traitor|NO" - - . += _memory_edit_role_enabled(ROLE_TRAITOR) - // Mindslave - . += "
    mindslaved: " - if(has_antag_datum(/datum/antagonist/mindslave)) - . += "MINDSLAVE|no" - else - . += "mindslave|NO" - -/datum/mind/proc/memory_edit_silicon() - . = "Silicon: " - var/mob/living/silicon/robot/robot = current - if(istype(robot) && robot.emagged) - . += "
    Cyborg: Is emagged! Unemag!
    0th law: [robot.laws.zeroth_law]" - var/mob/living/silicon/ai/ai = current - if(istype(ai) && ai.connected_robots.len) - var/n_e_robots = 0 - for(var/mob/living/silicon/robot/R in ai.connected_robots) - if(R.emagged) - n_e_robots++ - . += "
    [n_e_robots] of [ai.connected_robots.len] slaved cyborgs are emagged. Unemag" - -/datum/mind/proc/memory_edit_uplink() - . = "" - if(ishuman(current) && ((src in SSticker.mode.head_revolutionaries) || \ - (has_antag_datum(/datum/antagonist/traitor)) || \ - (src in SSticker.mode.syndicates))) - . = "Uplink: give" - var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() - var/crystals - if(suplink) - crystals = suplink.uses - if(suplink) - . += "|take" - if(usr.client.holder.rights & (R_SERVER|R_EVENT)) - . += ", [crystals] crystals" - else - . += ", [crystals] crystals" - . += "." //hiel grammar - // ^ whoever left this comment is literally a grammar nazi. stalin better. in russia grammar correct you. - -/datum/mind/proc/edit_memory() - if(!SSticker || !SSticker.mode) - alert("Not before round-start!", "Alert") - return - - var/out = "[name][(current && (current.real_name != name))?" (as [current.real_name])" : ""]
    " - out += "Mind currently owned by key: [key] [active ? "(synced)" : "(not synced)"]
    " - out += "Assigned role: [assigned_role]. Edit
    " - out += "Factions and special roles:
    " - - var/list/sections = list( - "implant", - "revolution", - "cult", - "wizard", - "changeling", - "vampire", // "traitorvamp", - "nuclear", - "traitor", // "traitorchan", - ) - var/mob/living/carbon/human/H = current - if(ishuman(current)) - /** Impanted**/ - sections["implant"] = memory_edit_implant(H) - /** REVOLUTION ***/ - sections["revolution"] = memory_edit_revolution(H) - /** CULT ***/ - sections["cult"] = memory_edit_cult(H) - /** WIZARD ***/ - sections["wizard"] = memory_edit_wizard(H) - /** CHANGELING ***/ - sections["changeling"] = memory_edit_changeling(H) - /** VAMPIRE ***/ - sections["vampire"] = memory_edit_vampire(H) - /** NUCLEAR ***/ - sections["nuclear"] = memory_edit_nuclear(H) - /** SHADOWLING **/ - sections["shadowling"] = memory_edit_shadowling(H) - /** Abductors **/ - sections["abductor"] = memory_edit_abductor(H) - /** DEVIL ***/ - var/static/list/devils_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/carbon/true_devil, /mob/living/silicon/robot)) - if(is_type_in_typecache(current, devils_typecache)) - sections["devil"] = memory_edit_devil(H) - sections["eventmisc"] = memory_edit_eventmisc(H) - /** TRAITOR ***/ - sections["traitor"] = memory_edit_traitor() - /** SILICON ***/ - if(issilicon(current)) - sections["silicon"] = memory_edit_silicon() - /* - This prioritizes antags relevant to the current round to make them appear at the top of the panel. - Traitorchan and traitorvamp are snowflaked in because they have multiple sections. - */ - if(SSticker.mode.config_tag == "traitorchan") - if(sections["traitor"]) - out += sections["traitor"] + "
    " - if(sections["changeling"]) - out += sections["changeling"] + "
    " - sections -= "traitor" - sections -= "changeling" - // Elif technically unnecessary but it makes the following else look better - else if(SSticker.mode.config_tag == "traitorvamp") - if(sections["traitor"]) - out += sections["traitor"] + "
    " - if(sections["vampire"]) - out += sections["vampire"] + "
    " - sections -= "traitor" - sections -= "vampire" - else - if(sections[SSticker.mode.config_tag]) - out += sections[SSticker.mode.config_tag] + "
    " - sections -= SSticker.mode.config_tag - - for(var/i in sections) - if(sections[i]) - out += sections[i] + "
    " - - out += memory_edit_uplink() - out += "
    " - - out += "Memory:
    " - out += memory - out += "
    Edit memory
    " - out += "Objectives:
    " - if(objectives.len == 0) - out += "EMPTY
    " - else - out += gen_objective_text(admin = TRUE) - out += "Add objective

    " - out += "Announce objectives

    " - usr << browse(out, "window=edit_memory[src];size=500x500") - -/datum/mind/Topic(href, href_list) - if(!check_rights(R_ADMIN)) - return - - if(href_list["role_edit"]) - var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in GLOB.joblist - if(!new_role) - return - assigned_role = new_role - log_admin("[key_name(usr)] has changed [key_name(current)]'s assigned role to [assigned_role]") - message_admins("[key_name_admin(usr)] has changed [key_name_admin(current)]'s assigned role to [assigned_role]") - - else if(href_list["memory_edit"]) - var/messageinput = input("Write new memory", "Memory", memory) as null|message - if(isnull(messageinput)) - return - var/new_memo = copytext(messageinput, 1,MAX_MESSAGE_LEN) - var/confirmed = alert(usr, "Are you sure you want to edit their memory? It will wipe out their original memory!", "Edit Memory", "Yes", "No") - if(confirmed == "Yes") // Because it is too easy to accidentally wipe someone's memory - memory = new_memo - log_admin("[key_name(usr)] has edited [key_name(current)]'s memory") - message_admins("[key_name_admin(usr)] has edited [key_name_admin(current)]'s memory") - - else if(href_list["obj_edit"] || href_list["obj_add"]) - var/datum/objective/objective - var/objective_pos - var/def_value - - if(href_list["obj_edit"]) - objective = locate(href_list["obj_edit"]) - if(!objective) - return - objective_pos = objectives.Find(objective) - - //Text strings are easy to manipulate. Revised for simplicity. - var/temp_obj_type = "[objective.type]"//Convert path into a text string. - def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. - if(!def_value)//If it's a custom objective, it will be an empty string. - def_value = "custom" - - var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "blood", "debrain", "protect", "prevent", "brig", "hijack", "escape", "survive", "steal", "download", "nuclear", "capture", "absorb", "destroy", "maroon", "identity theft", "custom") - if(!new_obj_type) - return - - var/datum/objective/new_objective = null - - switch(new_obj_type) - if("assassinate","protect","debrain", "brig", "maroon") - //To determine what to name the objective in explanation text. - var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. - var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. - var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. - - var/list/possible_targets = list() - for(var/datum/mind/possible_target in SSticker.minds) - if((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) - possible_targets += possible_target.current - - var/mob/def_target = null - var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) - if(objective&&(objective.type in objective_list) && objective:target) - def_target = objective:target.current - possible_targets = sortAtom(possible_targets) - possible_targets += "Free objective" - - var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets - if(!new_target) - return - - var/objective_path = text2path("/datum/objective/[new_obj_type]") - if(new_target == "Free objective") - new_objective = new objective_path - new_objective.owner = src - new_objective:target = null - new_objective.explanation_text = "Free objective" - else - new_objective = new objective_path - new_objective.owner = src - new_objective:target = new_target:mind - //Will display as special role if assigned mode is equal to special role.. Ninjas/commandos/nuke ops. - new_objective.explanation_text = "[objective_type] [new_target:real_name], the [new_target:mind:assigned_role == new_target:mind:special_role ? (new_target:mind:special_role) : (new_target:mind:assigned_role)]." - - if("destroy") - var/list/possible_targets = active_ais(1) - if(possible_targets.len) - var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets - new_objective = new /datum/objective/destroy - new_objective.target = new_target.mind - new_objective.owner = src - new_objective.explanation_text = "Destroy [new_target.name], the experimental AI." - else - to_chat(usr, "No active AIs with minds") - - if("prevent") - new_objective = new /datum/objective/block - new_objective.owner = src - - if("hijack") - new_objective = new /datum/objective/hijack - new_objective.owner = src - - if("escape") - new_objective = new /datum/objective/escape - new_objective.owner = src - - if("survive") - new_objective = new /datum/objective/survive - new_objective.owner = src - - if("die") - new_objective = new /datum/objective/die - new_objective.owner = src - - if("nuclear") - new_objective = new /datum/objective/nuclear - new_objective.owner = src - - if("steal") - if(!istype(objective, /datum/objective/steal)) - new_objective = new /datum/objective/steal - new_objective.owner = src - else - new_objective = objective - var/datum/objective/steal/steal = new_objective - if(!steal.select_target()) - return - - if("download","capture","absorb", "blood") - var/def_num - if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) - def_num = objective.target_amount - - var/target_number = input("Input target number:", "Objective", def_num) as num|null - if(isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. - return - - switch(new_obj_type) - if("download") - new_objective = new /datum/objective/download - new_objective.explanation_text = "Download [target_number] research levels." - if("capture") - new_objective = new /datum/objective/capture - new_objective.explanation_text = "Accumulate [target_number] capture points." - if("absorb") - new_objective = new /datum/objective/absorb - new_objective.explanation_text = "Absorb [target_number] compatible genomes." - if("blood") - new_objective = new /datum/objective/blood - new_objective.explanation_text = "Accumulate at least [target_number] total units of blood." - new_objective.owner = src - new_objective.target_amount = target_number - - if("identity theft") - var/list/possible_targets = list() - for(var/datum/mind/possible_target in SSticker.minds) - if((possible_target != src) && ishuman(possible_target.current)) - possible_targets += possible_target - possible_targets = sortAtom(possible_targets) - possible_targets += "Free objective" - var/new_target = input("Select target:", "Objective target") as null|anything in possible_targets - if(!new_target) - return - var/datum/mind/targ = new_target - if(!istype(targ)) - log_runtime(EXCEPTION("Invalid target for identity theft objective, cancelling"), src) - return - new_objective = new /datum/objective/escape/escape_with_identity - new_objective.owner = src - new_objective.target = new_target - new_objective.explanation_text = "Escape on the shuttle or an escape pod with the identity of [targ.current.real_name], the [targ.assigned_role] while wearing [targ.current.p_their()] identification card." - if("custom") - var/expl = sanitize(copytext(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null,1,MAX_MESSAGE_LEN)) - if(!expl) - return - new_objective = new /datum/objective - new_objective.owner = src - new_objective.explanation_text = expl - - if(!new_objective) - return - - if(objective) - objectives -= objective - qdel(objective) - objectives.Insert(objective_pos, new_objective) - else - objectives += new_objective - - log_admin("[key_name(usr)] has updated [key_name(current)]'s objectives: [new_objective]") - message_admins("[key_name_admin(usr)] has updated [key_name_admin(current)]'s objectives: [new_objective]") - - else if(href_list["obj_delete"]) - var/datum/objective/objective = locate(href_list["obj_delete"]) - if(!istype(objective)) - return - objectives -= objective - - log_admin("[key_name(usr)] has removed one of [key_name(current)]'s objectives: [objective]") - message_admins("[key_name_admin(usr)] has removed one of [key_name_admin(current)]'s objectives: [objective]") - qdel(objective) - - else if(href_list["obj_completed"]) - var/datum/objective/objective = locate(href_list["obj_completed"]) - if(!istype(objective)) - return - objective.completed = !objective.completed - - log_admin("[key_name(usr)] has toggled the completion of one of [key_name(current)]'s objectives") - message_admins("[key_name_admin(usr)] has toggled the completion of one of [key_name_admin(current)]'s objectives") - - else if(href_list["implant"]) - var/mob/living/carbon/human/H = current - - switch(href_list["implant"]) - if("remove") - for(var/obj/item/implant/mindshield/I in H.contents) - if(I && I.implanted) - qdel(I) - to_chat(H, "Your mindshield implant has been deactivated.") - log_admin("[key_name(usr)] has deactivated [key_name(current)]'s mindshield implant") - message_admins("[key_name_admin(usr)] has deactivated [key_name_admin(current)]'s mindshield implant") - if("add") - var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(H) - L.implant(H) - - log_admin("[key_name(usr)] has given [key_name(current)] a mindshield implant") - message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a mindshield implant") - - to_chat(H, "You somehow have become the recepient of a mindshield transplant, and it just activated!") - if(src in SSticker.mode.revolutionaries) - special_role = null - SSticker.mode.revolutionaries -= src - to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") - if(src in SSticker.mode.head_revolutionaries) - special_role = null - SSticker.mode.head_revolutionaries -=src - to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") - if(src in SSticker.mode.cult) - SSticker.mode.cult -= src - SSticker.mode.update_cult_icons_removed(src) - special_role = null - var/datum/game_mode/cult/cult = SSticker.mode - if(istype(cult)) - cult.memorize_cult_objectives(src) - to_chat(current, "The nanobots in the mindshield implant remove all thoughts about being in a cult. Have a productive day!") - memory = "" - - else if(href_list["revolution"]) - - switch(href_list["revolution"]) - if("clear") - if(src in SSticker.mode.revolutionaries) - SSticker.mode.revolutionaries -= src - to_chat(current, "You have been brainwashed! You are no longer a revolutionary!") - SSticker.mode.update_rev_icons_removed(src) - special_role = null - if(src in SSticker.mode.head_revolutionaries) - SSticker.mode.head_revolutionaries -= src - to_chat(current, "You have been brainwashed! You are no longer a head revolutionary!") - SSticker.mode.update_rev_icons_removed(src) - special_role = null - log_admin("[key_name(usr)] has de-rev'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-rev'd [key_name_admin(current)]") - - if("rev") - if(src in SSticker.mode.head_revolutionaries) - SSticker.mode.head_revolutionaries -= src - SSticker.mode.update_rev_icons_removed(src) - to_chat(current, "Revolution has been disappointed of your leadership traits! You are a regular revolutionary now!") - else if(!(src in SSticker.mode.revolutionaries)) - to_chat(current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") - else - return - SSticker.mode.revolutionaries += src - SSticker.mode.update_rev_icons_added(src) - special_role = SPECIAL_ROLE_REV - log_admin("[key_name(usr)] has rev'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has rev'd [key_name_admin(current)]") - - if("headrev") - if(src in SSticker.mode.revolutionaries) - SSticker.mode.revolutionaries -= src - SSticker.mode.update_rev_icons_removed(src) - to_chat(current, "You have proven your devotion to revolution! You are a head revolutionary now!") - else if(!(src in SSticker.mode.head_revolutionaries)) - to_chat(current, "You are a member of the revolutionaries' leadership now!") - else - return - if(SSticker.mode.head_revolutionaries.len>0) - // copy targets - var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries - if(valid_head) - for(var/datum/objective/mutiny/O in valid_head.objectives) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = src - rev_obj.target = O.target - rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]." - objectives += rev_obj - SSticker.mode.greet_revolutionary(src,0) - SSticker.mode.head_revolutionaries += src - SSticker.mode.update_rev_icons_added(src) - special_role = SPECIAL_ROLE_HEAD_REV - log_admin("[key_name(usr)] has head-rev'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has head-rev'd [key_name_admin(current)]") - - if("autoobjectives") - SSticker.mode.forge_revolutionary_objectives(src) - SSticker.mode.greet_revolutionary(src,0) - log_admin("[key_name(usr)] has automatically forged revolutionary objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged revolutionary objectives for [key_name_admin(current)]") - - if("flash") - if(!SSticker.mode.equip_revolutionary(current)) - to_chat(usr, "Spawning flash failed!") - log_admin("[key_name(usr)] has given [key_name(current)] a flash") - message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a flash") - - if("takeflash") - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - if(!flash) - to_chat(usr, "Deleting flash failed!") - qdel(flash) - log_admin("[key_name(usr)] has taken [key_name(current)]'s flash") - message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s flash") - - if("repairflash") - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - if(!flash) - to_chat(usr, "Repairing flash failed!") - else - flash.broken = 0 - log_admin("[key_name(usr)] has repaired [key_name(current)]'s flash") - message_admins("[key_name_admin(usr)] has repaired [key_name_admin(current)]'s flash") - - if("reequip") - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - qdel(flash) - take_uplink() - var/fail = 0 - var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) - fail |= !T.equip_traitor(src) - fail |= !SSticker.mode.equip_revolutionary(current) - if(fail) - to_chat(usr, "Reequipping revolutionary goes wrong!") - return - log_admin("[key_name(usr)] has equipped [key_name(current)] as a revolutionary") - message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a revolutionary") - - else if(href_list["cult"]) - switch(href_list["cult"]) - if("clear") - if(src in SSticker.mode.cult) - SSticker.mode.remove_cultist(src) - special_role = null - log_admin("[key_name(usr)] has de-culted [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-culted [key_name_admin(current)]") - if("cultist") - if(!(src in SSticker.mode.cult)) - SSticker.mode.add_cultist(src) - special_role = SPECIAL_ROLE_CULTIST - to_chat(current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") - to_chat(current, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve [SSticker.cultdat.entity_title2] above all else. Bring It back.") - log_admin("[key_name(usr)] has culted [key_name(current)]") - message_admins("[key_name_admin(usr)] has culted [key_name_admin(current)]") - if(!summon_spots.len) - while(summon_spots.len < SUMMON_POSSIBILITIES) - var/area/summon = pick(return_sorted_areas() - summon_spots) - if(summon && is_station_level(summon.z) && summon.valid_territory) - summon_spots += summon - if("tome") - var/mob/living/carbon/human/H = current - if(istype(H)) - var/obj/item/tome/T = new(H) - - var/list/slots = list ( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/where = H.equip_in_one_of_slots(T, slots) - if(!where) - to_chat(usr, "Spawning tome failed!") - qdel(T) - else - to_chat(H, "A tome, a message from your new master, appears in your [where].") - log_admin("[key_name(usr)] has spawned a tome for [key_name(current)]") - message_admins("[key_name_admin(usr)] has spawned a tome for [key_name_admin(current)]") - - if("equip") - if(!SSticker.mode.equip_cultist(current)) - to_chat(usr, "Spawning equipment failed!") - log_admin("[key_name(usr)] has equipped [key_name(current)] as a cultist") - message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a cultist") - - else if(href_list["wizard"]) - - switch(href_list["wizard"]) - if("clear") - if(src in SSticker.mode.wizards) - SSticker.mode.wizards -= src - special_role = null - current.spellremove(current) - current.faction = list("Station") - SSticker.mode.update_wiz_icons_removed(src) - to_chat(current, "You have been brainwashed! You are no longer a wizard!") - log_admin("[key_name(usr)] has de-wizarded [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-wizarded [key_name_admin(current)]") - if("wizard") - if(!(src in SSticker.mode.wizards)) - SSticker.mode.wizards += src - special_role = SPECIAL_ROLE_WIZARD - //ticker.mode.learn_basic_spells(current) - SSticker.mode.update_wiz_icons_added(src) - SEND_SOUND(current, 'sound/ambience/antag/ragesmages.ogg') - to_chat(current, "You are a Space Wizard!") - current.faction = list("wizard") - log_admin("[key_name(usr)] has wizarded [key_name(current)]") - message_admins("[key_name_admin(usr)] has wizarded [key_name_admin(current)]") - if("lair") - current.forceMove(pick(wizardstart)) - log_admin("[key_name(usr)] has moved [key_name(current)] to the wizard's lair") - message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the wizard's lair") - if("dressup") - SSticker.mode.equip_wizard(current) - log_admin("[key_name(usr)] has equipped [key_name(current)] as a wizard") - message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a wizard") - if("name") - SSticker.mode.name_wizard(current) - log_admin("[key_name(usr)] has allowed wizard [key_name(current)] to name themselves") - message_admins("[key_name_admin(usr)] has allowed wizard [key_name_admin(current)] to name themselves") - if("autoobjectives") - SSticker.mode.forge_wizard_objectives(src) - to_chat(usr, "The objectives for wizard [key] have been generated. You can edit them and announce manually.") - log_admin("[key_name(usr)] has automatically forged wizard objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged wizard objectives for [key_name_admin(current)]") - - - else if(href_list["changeling"]) - switch(href_list["changeling"]) - if("clear") - if(src in SSticker.mode.changelings) - SSticker.mode.changelings -= src - special_role = null - if(changeling) - current.remove_changeling_powers() - qdel(changeling) - changeling = null - SSticker.mode.update_change_icons_removed(src) - to_chat(current, "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!") - log_admin("[key_name(usr)] has de-changelinged [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-changelinged [key_name_admin(current)]") - if("changeling") - if(!(src in SSticker.mode.changelings)) - SSticker.mode.changelings += src - SSticker.mode.grant_changeling_powers(current) - SSticker.mode.update_change_icons_added(src) - special_role = SPECIAL_ROLE_CHANGELING - SEND_SOUND(current, 'sound/ambience/antag/ling_aler.ogg') - to_chat(current, "Your powers have awoken. A flash of memory returns to us... we are a changeling!") - log_admin("[key_name(usr)] has changelinged [key_name(current)]") - message_admins("[key_name_admin(usr)] has changelinged [key_name_admin(current)]") - - if("autoobjectives") - SSticker.mode.forge_changeling_objectives(src) - to_chat(usr, "The objectives for changeling [key] have been generated. You can edit them and announce manually.") - log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") - - if("initialdna") - if(!changeling || !changeling.absorbed_dna.len) - to_chat(usr, "Resetting DNA failed!") - else - current.dna = changeling.absorbed_dna[1] - current.real_name = current.dna.real_name - current.UpdateAppearance() - domutcheck(current, null) - log_admin("[key_name(usr)] has reset [key_name(current)]'s DNA") - message_admins("[key_name_admin(usr)] has reset [key_name_admin(current)]'s DNA") - - else if(href_list["vampire"]) - switch(href_list["vampire"]) - if("clear") - if(src in SSticker.mode.vampires) - SSticker.mode.vampires -= src - special_role = null - if(vampire) - vampire.remove_vampire_powers() - qdel(vampire) - vampire = null - SSticker.mode.update_vampire_icons_removed(src) - to_chat(current, "You grow weak and lose your powers! You are no longer a vampire and are stuck in your current form!") - log_admin("[key_name(usr)] has de-vampired [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-vampired [key_name_admin(current)]") - if("vampire") - if(!(src in SSticker.mode.vampires)) - SSticker.mode.vampires += src - SSticker.mode.grant_vampire_powers(current) - SSticker.mode.update_vampire_icons_added(src) - var/datum/mindslaves/slaved = new() - slaved.masters += src - som = slaved //we MIGT want to mindslave someone - special_role = SPECIAL_ROLE_VAMPIRE - SEND_SOUND(current, 'sound/ambience/antag/vampalert.ogg') - to_chat(current, "Your powers have awoken. Your lust for blood grows... You are a Vampire!") - log_admin("[key_name(usr)] has vampired [key_name(current)]") - message_admins("[key_name_admin(usr)] has vampired [key_name_admin(current)]") - - if("autoobjectives") - SSticker.mode.forge_vampire_objectives(src) - to_chat(usr, "The objectives for vampire [key] have been generated. You can edit them and announce manually.") - log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") - - else if(href_list["vampthrall"]) - switch(href_list["vampthrall"]) - if("clear") - if(src in SSticker.mode.vampire_enthralled) - SSticker.mode.remove_vampire_mind(src) - log_admin("[key_name(usr)] has de-vampthralled [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-vampthralled [key_name_admin(current)]") - - else if(href_list["nuclear"]) - var/mob/living/carbon/human/H = current - - switch(href_list["nuclear"]) - if("clear") - if(src in SSticker.mode.syndicates) - SSticker.mode.syndicates -= src - SSticker.mode.update_synd_icons_removed(src) - special_role = null - for(var/datum/objective/nuclear/O in objectives) - objectives-=O - qdel(O) - to_chat(current, "You have been brainwashed! You are no longer a syndicate operative!") - log_admin("[key_name(usr)] has de-nuke op'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-nuke op'd [key_name_admin(current)]") - if("nuclear") - if(!(src in SSticker.mode.syndicates)) - SSticker.mode.syndicates += src - SSticker.mode.update_synd_icons_added(src) - if(SSticker.mode.syndicates.len==1) - SSticker.mode.prepare_syndicate_leader(src) - else - current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" - special_role = SPECIAL_ROLE_NUKEOPS - to_chat(current, "You are a [syndicate_name()] agent!") - SSticker.mode.forge_syndicate_objectives(src) - SSticker.mode.greet_syndicate(src) - log_admin("[key_name(usr)] has nuke op'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has nuke op'd [key_name_admin(current)]") - if("lair") - current.forceMove(get_turf(locate("landmark*Syndicate-Spawn"))) - log_admin("[key_name(usr)] has moved [key_name(current)] to the nuclear operative spawn") - message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the nuclear operative spawn") - if("dressup") - qdel(H.belt) - qdel(H.back) - qdel(H.l_ear) - qdel(H.r_ear) - qdel(H.gloves) - qdel(H.head) - qdel(H.shoes) - qdel(H.wear_id) - qdel(H.wear_pda) - qdel(H.wear_suit) - qdel(H.w_uniform) - - if(!SSticker.mode.equip_syndicate(current)) - to_chat(usr, "Equipping a syndicate failed!") - return - SSticker.mode.update_syndicate_id(current.mind, SSticker.mode.syndicates.len == 1) - log_admin("[key_name(usr)] has equipped [key_name(current)] as a nuclear operative") - message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a nuclear operative") - - if("tellcode") - var/code - for(var/obj/machinery/nuclearbomb/bombue in GLOB.machines) - if(length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") - code = bombue.r_code - break - if(code) - store_memory("Syndicate Nuclear Bomb Code: [code]", 0, 0) - to_chat(current, "The nuclear authorization code is: [code]") - log_admin("[key_name(usr)] has given [key_name(current)] the nuclear authorization code") - message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] the nuclear authorization code") - else - to_chat(usr, "No valid nuke found!") - - else if(href_list["eventmisc"]) - switch(href_list["eventmisc"]) - if("clear") - if(src in SSticker.mode.eventmiscs) - SSticker.mode.eventmiscs -= src - SSticker.mode.update_eventmisc_icons_removed(src) - special_role = null - message_admins("[key_name_admin(usr)] has de-eventantag'ed [current].") - log_admin("[key_name(usr)] has de-eventantag'ed [current].") - if("eventmisc") - SSticker.mode.eventmiscs += src - special_role = SPECIAL_ROLE_EVENTMISC - SSticker.mode.update_eventmisc_icons_added(src) - message_admins("[key_name_admin(usr)] has eventantag'ed [current].") - log_admin("[key_name(usr)] has eventantag'ed [current].") - else if(href_list["devil"]) - switch(href_list["devil"]) - if("clear") - if(src in SSticker.mode.devils) - if(istype(current,/mob/living/carbon/true_devil/)) - to_chat(usr,"This cannot be used on true or arch-devils.") - else - SSticker.mode.devils -= src - SSticker.mode.update_devil_icons_removed(src) - special_role = null - to_chat(current,"Your infernal link has been severed! You are no longer a devil!") - RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt) - RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/ascended) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/violin) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_dancefloor) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch/ascended) - message_admins("[key_name_admin(usr)] has de-devil'ed [current].") - if(issilicon(current)) - var/mob/living/silicon/S = current - S.laws.clear_sixsixsix_laws() - devilinfo = null - log_admin("[key_name(usr)] has de-devil'ed [current].") - else if(src in SSticker.mode.sintouched) - SSticker.mode.sintouched -= src - message_admins("[key_name_admin(usr)] has de-sintouch'ed [current].") - log_admin("[key_name(usr)] has de-sintouch'ed [current].") - if("devil") - if(devilinfo) - devilinfo.ascendable = FALSE - message_admins("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") - log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") - return - if(!ishuman(current) && !isrobot(current)) - to_chat(usr, "This only works on humans and cyborgs!") - return - SSticker.mode.devils += src - special_role = "devil" - SSticker.mode.update_devil_icons_added(src) - SSticker.mode.finalize_devil(src, FALSE) - SSticker.mode.forge_devil_objectives(src, 2) - SSticker.mode.greet_devil(src) - message_admins("[key_name_admin(usr)] has devil'ed [current].") - log_admin("[key_name(usr)] has devil'ed [current].") - if("ascendable_devil") - if(devilinfo) - devilinfo.ascendable = TRUE - message_admins("[key_name_admin(usr)] has made [current] able to ascend as a devil.") - log_admin("[key_name_admin(usr)] has made [current] able to ascend as a devil.") - return - if(!ishuman(current) && !isrobot(current)) - to_chat(usr, "This only works on humans and cyborgs!") - return - SSticker.mode.devils += src - special_role = "devil" - SSticker.mode.update_devil_icons_added(src) - SSticker.mode.finalize_devil(src, TRUE) - SSticker.mode.forge_devil_objectives(src, 2) - SSticker.mode.greet_devil(src) - message_admins("[key_name_admin(usr)] has devil'ed [current]. The devil has been marked as ascendable.") - log_admin("[key_name(usr)] has devil'ed [current]. The devil has been marked as ascendable.") - if("sintouched") - var/mob/living/carbon/human/H = current - H.influenceSin() - message_admins("[key_name_admin(usr)] has sintouch'ed [current].") - log_admin("[key_name(usr)] has sintouch'ed [current].") - - else if(href_list["traitor"]) - switch(href_list["traitor"]) - if("clear") - if(has_antag_datum(/datum/antagonist/traitor)) - to_chat(current, "You have been brainwashed! You are no longer a traitor!") - remove_antag_datum(/datum/antagonist/traitor) - log_admin("[key_name(usr)] has de-traitored [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-traitored [key_name_admin(current)]") - - if("traitor") - if(!(has_antag_datum(/datum/antagonist/traitor))) - var/datum/antagonist/traitor/T = new() - T.give_objectives = FALSE - T.should_equip = FALSE - add_antag_datum(T) - log_admin("[key_name(usr)] has traitored [key_name(current)]") - message_admins("[key_name_admin(usr)] has traitored [key_name_admin(current)]") - - if("autoobjectives") - var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) - T.forge_traitor_objectives(src) - to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and announce manually.") - log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") - - else if(href_list["mindslave"]) - switch(href_list["mindslave"]) - if("clear") - if(has_antag_datum(/datum/antagonist/mindslave)) - var/mob/living/carbon/human/H = current - for(var/i in H.contents) - if(istype(i, /obj/item/implant/traitor)) - qdel(i) - break - remove_antag_datum(/datum/antagonist/mindslave) - log_admin("[key_name(usr)] has de-mindslaved [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-mindslaved [key_name_admin(current)]") - - else if(href_list["shadowling"]) - switch(href_list["shadowling"]) - if("clear") - SSticker.mode.update_shadow_icons_removed(src) - if(src in SSticker.mode.shadows) - SSticker.mode.shadows -= src - special_role = null - to_chat(current, "Your powers have been quenched! You are no longer a shadowling!") - message_admins("[key_name_admin(usr)] has de-shadowlinged [current].") - log_admin("[key_name(usr)] has de-shadowlinged [current].") - current.spellremove(current) - current.remove_language("Shadowling Hivemind") - else if(src in SSticker.mode.shadowling_thralls) - SSticker.mode.remove_thrall(src,0) - message_admins("[key_name_admin(usr)] has de-thrall'ed [current].") - log_admin("[key_name(usr)] has de-thralled [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-thralled [key_name_admin(current)]") - if("shadowling") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - SSticker.mode.shadows += src - special_role = SPECIAL_ROLE_SHADOWLING - to_chat(current, "Something stirs deep in your mind. A red light floods your vision, and slowly you remember. Though your human disguise has served you well, the \ - time is nigh to cast it off and enter your true form. You have disguised yourself amongst the humans, but you are not one of them. You are a shadowling, and you are to ascend at all costs.\ - ") - SSticker.mode.finalize_shadowling(src) - SSticker.mode.update_shadow_icons_added(src) - log_admin("[key_name(usr)] has shadowlinged [key_name(current)]") - message_admins("[key_name_admin(usr)] has shadowlinged [key_name_admin(current)]") - if("thrall") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - SSticker.mode.add_thrall(src) - message_admins("[key_name_admin(usr)] has thralled [current].") - log_admin("[key_name(usr)] has thralled [current].") - - else if(href_list["abductor"]) - switch(href_list["abductor"]) - if("clear") - to_chat(usr, "Not implemented yet. Sorry!") - //ticker.mode.update_abductor_icons_removed(src) - if("abductor") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - make_Abductor() - log_admin("[key_name(usr)] turned [current] into abductor.") - SSticker.mode.update_abductor_icons_added(src) - if("equip") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - - var/mob/living/carbon/human/H = current - var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") - if(gear) - if(gear=="Agent") - H.equipOutfit(/datum/outfit/abductor/agent) - else - H.equipOutfit(/datum/outfit/abductor/scientist) - - else if(href_list["silicon"]) - switch(href_list["silicon"]) - if("unemag") - var/mob/living/silicon/robot/R = current - if(istype(R)) - R.emagged = 0 - if(R.module) - if(R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - R.clear_supplied_laws() - R.laws = new /datum/ai_laws/crewsimov - log_admin("[key_name(usr)] has un-emagged [key_name(current)]") - message_admins("[key_name_admin(usr)] has un-emagged [key_name_admin(current)]") - - if("unemagcyborgs") - if(isAI(current)) - var/mob/living/silicon/ai/ai = current - for(var/mob/living/silicon/robot/R in ai.connected_robots) - R.emagged = 0 - if(R.module) - if(R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - R.clear_supplied_laws() - R.laws = new /datum/ai_laws/crewsimov - log_admin("[key_name(usr)] has unemagged [key_name(ai)]'s cyborgs") - message_admins("[key_name_admin(usr)] has unemagged [key_name_admin(ai)]'s cyborgs") - - else if(href_list["common"]) - switch(href_list["common"]) - if("undress") - if(ishuman(current)) - var/mob/living/carbon/human/H = current - // Don't "undress" organs right out of the body - for(var/obj/item/W in H.contents - (H.bodyparts | H.internal_organs)) - current.unEquip(W, 1) - else - for(var/obj/item/W in current) - current.unEquip(W, 1) - log_admin("[key_name(usr)] has unequipped [key_name(current)]") - message_admins("[key_name_admin(usr)] has unequipped [key_name_admin(current)]") - if("takeuplink") - take_uplink() - var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) - T.antag_memory = "" //Remove any antag memory they may have had (uplink codes, code phrases) - log_admin("[key_name(usr)] has taken [key_name(current)]'s uplink") - message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s uplink") - if("crystals") - if(usr.client.holder.rights & (R_SERVER|R_EVENT)) - var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() - var/crystals - if(suplink) - crystals = suplink.uses - crystals = input("Amount of telecrystals for [key]","Syndicate uplink", crystals) as null|num - if(!isnull(crystals)) - if(suplink) - suplink.uses = crystals - log_admin("[key_name(usr)] has set [key_name(current)]'s telecrystals to [crystals]") - message_admins("[key_name_admin(usr)] has set [key_name_admin(current)]'s telecrystals to [crystals]") - if("uplink") - if(has_antag_datum(/datum/antagonist/traitor)) - var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) - T.give_codewords() - if(!T.equip_traitor(src)) - to_chat(usr, "Equipping a syndicate failed!") - return - log_admin("[key_name(usr)] has given [key_name(current)] an uplink") - message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] an uplink") - - else if(href_list["obj_announce"]) - announce_objectives() - SEND_SOUND(current, sound('sound/ambience/alarm4.ogg')) - log_admin("[key_name(usr)] has announced [key_name(current)]'s objectives") - message_admins("[key_name_admin(usr)] has announced [key_name_admin(current)]'s objectives") - - edit_memory() - - -// Datum antag mind procs -/datum/mind/proc/add_antag_datum(datum_type_or_instance, team) - if(!datum_type_or_instance) - return - var/datum/antagonist/A - if(!ispath(datum_type_or_instance)) - A = datum_type_or_instance - if(!istype(A)) - return - else - A = new datum_type_or_instance() - //Choose snowflake variation if antagonist handles it - var/datum/antagonist/S = A.specialization(src) - if(S && S != A) - qdel(A) - A = S - if(!A.can_be_owned(src)) - qdel(A) - return - A.owner = src - LAZYADD(antag_datums, A) - A.create_team(team) - var/datum/team/antag_team = A.get_team() - if(antag_team) - antag_team.add_member(src) - A.on_gain() - return A - -/datum/mind/proc/remove_antag_datum(datum_type) - if(!datum_type) - return - var/datum/antagonist/A = has_antag_datum(datum_type) - if(A) - A.on_removal() - return TRUE - - -/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us. - for(var/a in antag_datums) - var/datum/antagonist/A = a - A.on_removal() - -/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE) - if(!datum_type) - return - . = FALSE - for(var/a in antag_datums) - var/datum/antagonist/A = a - if(check_subtypes && istype(A, datum_type)) - return A - else if(A.type == datum_type) - return A - -/datum/mind/proc/announce_objectives() - to_chat(current, "Your current objectives:") - for(var/line in splittext(gen_objective_text(), "
    ")) - to_chat(current, line) - -/datum/mind/proc/find_syndicate_uplink() - var/list/L = current.get_contents() - for(var/obj/item/I in L) - if(I.hidden_uplink) - return I.hidden_uplink - return null - -/datum/mind/proc/take_uplink() - var/obj/item/uplink/hidden/H = find_syndicate_uplink() - if(H) - qdel(H) - -/datum/mind/proc/make_Traitor() - if(!has_antag_datum(/datum/antagonist/traitor)) - add_antag_datum(/datum/antagonist/traitor) - -/datum/mind/proc/make_Nuke() - if(!(src in SSticker.mode.syndicates)) - SSticker.mode.syndicates += src - SSticker.mode.update_synd_icons_added(src) - if(SSticker.mode.syndicates.len==1) - SSticker.mode.prepare_syndicate_leader(src) - else - current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" - special_role = SPECIAL_ROLE_NUKEOPS - assigned_role = SPECIAL_ROLE_NUKEOPS - to_chat(current, "You are a [syndicate_name()] agent!") - SSticker.mode.forge_syndicate_objectives(src) - SSticker.mode.greet_syndicate(src) - - current.loc = get_turf(locate("landmark*Syndicate-Spawn")) - - var/mob/living/carbon/human/H = current - qdel(H.belt) - qdel(H.back) - qdel(H.l_ear) - qdel(H.r_ear) - qdel(H.gloves) - qdel(H.head) - qdel(H.shoes) - qdel(H.wear_id) - qdel(H.wear_pda) - qdel(H.wear_suit) - qdel(H.w_uniform) - - SSticker.mode.equip_syndicate(current) - -/datum/mind/proc/make_Vampire() - if(!(src in SSticker.mode.vampires)) - SSticker.mode.vampires += src - SSticker.mode.grant_vampire_powers(current) - special_role = SPECIAL_ROLE_VAMPIRE - SSticker.mode.forge_vampire_objectives(src) - SSticker.mode.greet_vampire(src) - SSticker.mode.update_vampire_icons_added(src) - -/datum/mind/proc/make_Changeling() - if(!(src in SSticker.mode.changelings)) - SSticker.mode.changelings += src - SSticker.mode.grant_changeling_powers(current) - special_role = SPECIAL_ROLE_CHANGELING - SSticker.mode.forge_changeling_objectives(src) - SSticker.mode.greet_changeling(src) - SSticker.mode.update_change_icons_added(src) - -/datum/mind/proc/make_Overmind() - if(!(src in SSticker.mode.blob_overminds)) - SSticker.mode.blob_overminds += src - special_role = SPECIAL_ROLE_BLOB_OVERMIND - -/datum/mind/proc/make_Wizard() - if(!(src in SSticker.mode.wizards)) - SSticker.mode.wizards += src - special_role = SPECIAL_ROLE_WIZARD - assigned_role = SPECIAL_ROLE_WIZARD - //ticker.mode.learn_basic_spells(current) - if(!wizardstart.len) - current.loc = pick(latejoin) - to_chat(current, "HOT INSERTION, GO GO GO") - else - current.loc = pick(wizardstart) - - SSticker.mode.equip_wizard(current) - for(var/obj/item/spellbook/S in current.contents) - S.op = 0 - SSticker.mode.name_wizard(current) - SSticker.mode.forge_wizard_objectives(src) - SSticker.mode.greet_wizard(src) - SSticker.mode.update_wiz_icons_added(src) - -/datum/mind/proc/make_Rev() - if(SSticker.mode.head_revolutionaries.len>0) - // copy targets - var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries - if(valid_head) - for(var/datum/objective/mutiny/O in valid_head.objectives) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = src - rev_obj.target = O.target - rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]." - objectives += rev_obj - SSticker.mode.greet_revolutionary(src,0) - SSticker.mode.head_revolutionaries += src - SSticker.mode.update_rev_icons_added(src) - special_role = SPECIAL_ROLE_HEAD_REV - - SSticker.mode.forge_revolutionary_objectives(src) - SSticker.mode.greet_revolutionary(src,0) - - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - qdel(flash) - take_uplink() - var/fail = 0 -// fail |= !ticker.mode.equip_traitor(current, 1) - fail |= !SSticker.mode.equip_revolutionary(current) - -/datum/mind/proc/make_Abductor() - var/role = alert("Abductor Role ?","Role","Agent","Scientist") - var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4) - var/teleport = alert("Teleport to ship ?","Teleport","Yes","No") - - if(!role || !team || !teleport) - return - - if(!ishuman(current)) - return - - SSticker.mode.abductors |= src - - var/datum/objective/stay_hidden/hidden_obj = new - hidden_obj.owner = src - objectives += hidden_obj - - var/datum/objective/experiment/O = new - O.owner = src - objectives += O - - var/mob/living/carbon/human/H = current - - H.set_species(/datum/species/abductor) - var/datum/species/abductor/S = H.dna.species - - if(role == "Scientist") - S.scientist = TRUE - - S.team = team - - var/list/obj/effect/landmark/abductor/agent_landmarks = new - var/list/obj/effect/landmark/abductor/scientist_landmarks = new - agent_landmarks.len = 4 - scientist_landmarks.len = 4 - for(var/obj/effect/landmark/abductor/A in GLOB.landmarks_list) - if(istype(A, /obj/effect/landmark/abductor/agent)) - agent_landmarks[text2num(A.team)] = A - else if(istype(A, /obj/effect/landmark/abductor/scientist)) - scientist_landmarks[text2num(A.team)] = A - - var/obj/effect/landmark/L - if(teleport == "Yes") - switch(role) - if("Agent") - L = agent_landmarks[team] - if("Scientist") - L = agent_landmarks[team] - H.forceMove(L.loc) - - -// check whether this mind's mob has been brigged for the given duration -// have to call this periodically for the duration to work properly -/datum/mind/proc/is_brigged(duration) - var/turf/T = current.loc - if(!istype(T)) - brigged_since = -1 - return 0 - - var/is_currently_brigged = current.is_in_brig() - if(!is_currently_brigged) - brigged_since = -1 - return 0 - - if(brigged_since == -1) - brigged_since = world.time - - return (duration <= world.time - brigged_since) - -/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S) - spell_list += S - S.action.Grant(current) - -/datum/mind/proc/RemoveSpell(obj/effect/proc_holder/spell/spell) //To remove a specific spell from a mind - if(!spell) - return - for(var/obj/effect/proc_holder/spell/S in spell_list) - if(istype(S, spell)) - qdel(S) - spell_list -= S - -/datum/mind/proc/transfer_actions(mob/living/new_character) - if(current && current.actions) - for(var/datum/action/A in current.actions) - A.Grant(new_character) - transfer_mindbound_actions(new_character) - -/datum/mind/proc/transfer_mindbound_actions(mob/living/new_character) - for(var/X in spell_list) - var/obj/effect/proc_holder/spell/S = X - S.action.Grant(new_character) - -/datum/mind/proc/disrupt_spells(delay, list/exceptions = New()) - for(var/X in spell_list) - var/obj/effect/proc_holder/spell/S = X - for(var/type in exceptions) - if(istype(S, type)) - continue - S.charge_counter = delay - spawn(0) - S.start_recharge() - S.updateButtonIcon() - -/datum/mind/proc/get_ghost(even_if_they_cant_reenter) - for(var/mob/dead/observer/G in GLOB.dead_mob_list) - if(G.mind == src) - if(G.can_reenter_corpse || even_if_they_cant_reenter) - return G - break - -/datum/mind/proc/grab_ghost(force) - var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force) - . = G - if(G) - G.reenter_corpse() - - -/datum/mind/proc/make_zealot(mob/living/carbon/human/missionary, convert_duration = 6000, team_color = "red") - - zealot_master = missionary - - var/list/implanters - if(!(missionary.mind in SSticker.mode.implanter)) - SSticker.mode.implanter[missionary.mind] = list() - implanters = SSticker.mode.implanter[missionary.mind] - implanters.Add(src) - SSticker.mode.implanted.Add(src) - SSticker.mode.implanted[src] = missionary.mind - SSticker.mode.implanter[missionary.mind] = implanters - SSticker.mode.traitors += src - - - var/datum/objective/protect/zealot_objective = new - zealot_objective.target = missionary.mind - zealot_objective.owner = src - zealot_objective.explanation_text = "Obey every order from and protect [missionary.real_name], the [missionary.mind.assigned_role == missionary.mind.special_role ? (missionary.mind.special_role) : (missionary.mind.assigned_role)]." - objectives += zealot_objective - add_antag_datum(/datum/antagonist/mindslave) - - var/datum/antagonist/traitor/T = missionary.mind.has_antag_datum(/datum/antagonist) - T.update_traitor_icons_added(missionary.mind) - - to_chat(current, "You're now a loyal zealot of [missionary.name]! You now must lay down your life to protect [missionary.p_them()] and assist in [missionary.p_their()] goals at any cost.") - - var/datum/mindslaves/slaved = missionary.mind.som - som = slaved - slaved.serv += current - slaved.add_serv_hud(missionary.mind, "master") //handles master servent icons - slaved.add_serv_hud(src, "mindslave") - - var/obj/item/clothing/under/jumpsuit = null - if(ishuman(current)) //only bother with the jumpsuit stuff if we are a human type, since we won't have the slot otherwise - var/mob/living/carbon/human/H = current - if(H.w_uniform) - jumpsuit = H.w_uniform - jumpsuit.color = team_color - H.update_inv_w_uniform(0,0) - - add_attack_logs(missionary, current, "Converted to a zealot for [convert_duration/600] minutes") - addtimer(CALLBACK(src, .proc/remove_zealot, jumpsuit), convert_duration) //deconverts after the timer expires - return 1 - -/datum/mind/proc/remove_zealot(obj/item/clothing/under/jumpsuit = null) - if(!zealot_master) //if they aren't a zealot, we can't remove their zealot status, obviously. don't bother with the rest so we don't confuse them with the messages - return - remove_antag_datum(/datum/antagonist/mindslave) - add_attack_logs(zealot_master, current, "Lost control of zealot") - zealot_master = null - - if(jumpsuit) - jumpsuit.color = initial(jumpsuit.color) //reset the jumpsuit no matter where our mind is - if(ishuman(current)) //but only try updating us if we are still a human type since it is a human proc - var/mob/living/carbon/human/H = current - H.update_inv_w_uniform(0,0) - - to_chat(current, "You seem to have forgotten the events of the past 10 minutes or so, and your head aches a bit as if someone beat it savagely with a stick.") - to_chat(current, "This means you don't remember who you were working for or what you were doing.") - -/datum/mind/proc/is_revivable() //Note, this ONLY checks the mind. - if(damnation_type) - return FALSE - return TRUE - -// returns a mob to message to produce something visible for the target mind -/datum/mind/proc/messageable_mob() - if(!QDELETED(current) && current.client) - return current - else - return get_ghost(even_if_they_cant_reenter = TRUE) - -//Initialisation procs -/mob/proc/mind_initialize() - if(mind) - mind.key = key - else - mind = new /datum/mind(key) - if(SSticker) - SSticker.minds += mind - else - error("mind_initialize(): No ticker ready yet! Please inform Carn") - if(!mind.name) - mind.name = real_name - mind.current = src - -//HUMAN -/mob/living/carbon/human/mind_initialize() - ..() - if(!mind.assigned_role) - mind.assigned_role = "Civilian" //defualt - -/mob/proc/sync_mind() - mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) - mind.active = 1 //indicates that the mind is currently synced with a client - -//slime -/mob/living/simple_animal/slime/mind_initialize() - ..() - mind.assigned_role = "slime" - -//XENO -/mob/living/carbon/alien/mind_initialize() - ..() - mind.assigned_role = "Alien" - //XENO HUMANOID -/mob/living/carbon/alien/humanoid/queen/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_QUEEN - -/mob/living/carbon/alien/humanoid/hunter/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_HUNTER - -/mob/living/carbon/alien/humanoid/drone/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_DRONE - -/mob/living/carbon/alien/humanoid/sentinel/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_SENTINEL - //XENO LARVA -/mob/living/carbon/alien/larva/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_LARVA - -//AI -/mob/living/silicon/ai/mind_initialize() - ..() - mind.assigned_role = "AI" - -//BORG -/mob/living/silicon/robot/mind_initialize() - ..() - mind.assigned_role = "Cyborg" - -//PAI -/mob/living/silicon/pai/mind_initialize() - ..() - mind.assigned_role = "pAI" - mind.special_role = null - -//BLOB -/mob/camera/overmind/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_BLOB - -//Animals -/mob/living/simple_animal/mind_initialize() - ..() - mind.assigned_role = "Animal" - -/mob/living/simple_animal/pet/dog/corgi/mind_initialize() - ..() - mind.assigned_role = "Corgi" - -/mob/living/simple_animal/shade/mind_initialize() - ..() - mind.assigned_role = "Shade" - -/mob/living/simple_animal/construct/builder/mind_initialize() - ..() - mind.assigned_role = "Artificer" - mind.special_role = SPECIAL_ROLE_CULTIST - -/mob/living/simple_animal/construct/wraith/mind_initialize() - ..() - mind.assigned_role = "Wraith" - mind.special_role = SPECIAL_ROLE_CULTIST - -/mob/living/simple_animal/construct/armoured/mind_initialize() - ..() - mind.assigned_role = "Juggernaut" - mind.special_role = SPECIAL_ROLE_CULTIST +/* Note from Carnie: + The way datum/mind stuff works has been changed a lot. + Minds now represent IC characters rather than following a client around constantly. + Guidelines for using minds properly: + - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! + ghost.mind is however used as a reference to the ghost's corpse + - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) + the existing mind of the old mob should be transfered to the new mob like so: + mind.transfer_to(new_mob) + - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. + By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing + the player. + - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. + - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting + a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. + new_mob.key = key + The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! + However if you want that mind to have any special properties like being a traitor etc you will have to do that + yourself. +*/ + +/datum/mind + var/key + var/name //replaces mob/var/original_name + var/mob/living/current + var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. + var/active = 0 + + var/memory + + var/assigned_role //assigned role is what job you're assigned to when you join the station. + var/playtime_role //if set, overrides your assigned_role for the purpose of playtime awards. Set by IDcomputer when your ID is changed. + var/special_role //special roles are typically reserved for antags or roles like ERT. If you want to avoid a character being automatically announced by the AI, on arrival (becuase they're an off station character or something); ensure that special_role and assigned_role are equal. + var/offstation_role = FALSE //set to true for ERT, deathsquad, abductors, etc, that can go from and to z2 at will and shouldn't be antag targets + var/list/restricted_roles = list() + + var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. + + var/role_alt_title + + var/datum/job/assigned_job + var/list/kills = list() + var/list/datum/objective/objectives = list() + var/list/datum/objective/special_verbs = list() + var/list/targets = list() + + var/has_been_rev = 0//Tracks if this mind has been a rev or not + + var/miming = 0 // Mime's vow of silence + var/list/antag_datums + var/speech_span // What span any body this mind has talks in. + var/datum/faction/faction //associated faction + var/datum/changeling/changeling //changeling holder + var/linglink + var/datum/vampire/vampire //vampire holder + var/datum/abductor/abductor //abductor holder + + var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state + var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD + var/datum/mindslaves/som //stands for slave or master...hush.. + var/datum/devilinfo/devilinfo //Information about the devil, if any. + var/damnation_type = 0 + var/datum/mind/soulOwner //who owns the soul. Under normal circumstances, this will point to src + var/hasSoul = TRUE + + var/isholy = FALSE // is this person a chaplain or admin role allowed to use bibles + var/isblessed = FALSE // is this person blessed by a chaplain? + var/num_blessed = 0 // for prayers + + // the world.time since the mob has been brigged, or -1 if not at all + var/brigged_since = -1 + var/suicided = FALSE + + //put this here for easier tracking ingame + var/datum/money_account/initial_account + + //zealot_master is a reference to the mob that converted them into a zealot (for ease of investigation and such) + var/mob/living/carbon/human/zealot_master = null + + var/list/learned_recipes //List of learned recipe TYPES. + +/datum/mind/New(new_key) + key = new_key + soulOwner = src + +/datum/mind/Destroy() + SSticker.minds -= src + if(islist(antag_datums)) + for(var/i in antag_datums) + var/datum/antagonist/antag_datum = i + if(antag_datum.delete_on_mind_deletion) + qdel(i) + antag_datums = null + return ..() + +/datum/mind/proc/transfer_to(mob/living/new_character) + var/datum/atom_hud/antag/hud_to_transfer = antag_hud //we need this because leave_hud() will clear this list + var/mob/living/old_current = current + if(!istype(new_character)) + log_runtime(EXCEPTION("transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob."), src) + if(current) //remove ourself from our old body's mind variable + current.mind = null + leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it + + for(var/log_type in current.logs) // Copy the old logs + var/list/logs = current.logs[log_type] + if(new_character.logs[log_type]) + new_character.logs[log_type] += logs.Copy() // Append the old ones + new_character.logs[log_type] = sortTim(new_character.logs[log_type], /proc/compare_log_record) // Sort them on time + else + new_character.logs[log_type] = logs.Copy() // Just copy them + + SSnanoui.user_transferred(current, new_character) + + if(new_character.mind) //remove any mind currently in our new body's mind variable + new_character.mind.current = null + current = new_character //link ourself to our new body + new_character.mind = src //and link our new body to ourself + for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body + var/datum/antagonist/A = a + A.on_body_transfer(old_current, current) + transfer_antag_huds(hud_to_transfer) //inherit the antag HUD + transfer_actions(new_character) + + if(active) + new_character.key = key //now transfer the key to link the client to our new body + +/datum/mind/proc/store_memory(new_text) + memory += "[new_text]
    " + +/datum/mind/proc/wipe_memory() + memory = null + +/datum/mind/proc/show_memory(mob/recipient, window = 1) + if(!recipient) + recipient = current + var/output = "[current.real_name]'s Memories:
    " + output += memory + + var/antag_datum_objectives = FALSE + for(var/datum/antagonist/A in antag_datums) + output += A.antag_memory + if(!antag_datum_objectives && LAZYLEN(A.objectives)) + antag_datum_objectives = TRUE + + if(LAZYLEN(objectives) || antag_datum_objectives) + output += "
    Objectives:
    " + output += gen_objective_text() + + if(LAZYLEN(job_objectives)) + output += "
    Job Objectives:
      " + + var/obj_count = 1 + for(var/datum/job_objective/objective in job_objectives) + output += "
    • Task #[obj_count]: [objective.get_description()]
    • " + obj_count++ + output += "
    " + if(window) + recipient << browse(output, "window=memory") + else + to_chat(recipient, "[output]") + +/datum/mind/proc/gen_objective_text(admin = FALSE) + . = "" + var/obj_count = 1 + var/list/all_objectives = list() + for(var/datum/antagonist/A in antag_datums) + all_objectives |= A.objectives + + if(LAZYLEN(all_objectives)) + for(var/datum/objective/objective in all_objectives) + . += "
    Objective #[obj_count++]: [objective.explanation_text]" + + for(var/datum/objective/objective in objectives) + . += "Objective #[obj_count++]: [objective.explanation_text]" + if(admin) + . += " Edit " // Edit + . += "Delete " // Delete + + . += "" // Mark Completed + . += "Toggle Completion" + . += "" + . += "
    " + +/datum/mind/proc/_memory_edit_header(gamemode, list/alt) + . = gamemode + if(SSticker.mode.config_tag == gamemode || (LAZYLEN(alt) && (SSticker.mode.config_tag in alt))) + . = uppertext(.) + . = "[.]: " + +/datum/mind/proc/_memory_edit_role_enabled(role) + . = "|Disabled in Prefs" + if(current && current.client && (role in current.client.prefs.be_special)) + . = "|Enabled in Prefs" + +/datum/mind/proc/memory_edit_implant(mob/living/carbon/human/H) + if(ismindshielded(H)) + . = "Mindshield Implant:Remove|Implanted
    " + else + . = "Mindshield Implant:No Implant|Implant [H.p_them()]!
    " + + +/datum/mind/proc/memory_edit_revolution(mob/living/carbon/human/H) + . = _memory_edit_header("revolution") + if(ismindshielded(H)) + . += "NO|headrev|rev" + else if(src in SSticker.mode.head_revolutionaries) + . += "no|HEADREV|rev" + . += "
    Flash: give" + + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + if(flash) + if(!flash.broken) + . += "|take." + else + . += "|take|repair." + else + . += "." + + . += " Reequip (gives traitor uplink)." + if(objectives.len==0) + . += "
    Objectives are empty! Set to kill all heads." + else if(src in SSticker.mode.revolutionaries) + . += "no|headrev|REV" + else + . += "NO|headrev|rev" + + . += _memory_edit_role_enabled(ROLE_REV) + +/datum/mind/proc/memory_edit_cult(mob/living/carbon/human/H) + . = _memory_edit_header("cult") + if(src in SSticker.mode.cult) + . += "no|CULTIST" + . += "
    Give tome|equip." + else + . += "NO|cultist" + + . += _memory_edit_role_enabled(ROLE_CULTIST) + +/datum/mind/proc/memory_edit_wizard(mob/living/carbon/human/H) + . = _memory_edit_header("wizard") + if(src in SSticker.mode.wizards) + . += "WIZARD|no" + . += "
    To lair, undress, dress up, let choose name." + if(objectives.len==0) + . += "
    Objectives are empty! Randomize!" + else + . += "wizard|NO" + + . += _memory_edit_role_enabled(ROLE_WIZARD) + +/datum/mind/proc/memory_edit_changeling(mob/living/carbon/human/H) + . = _memory_edit_header("changeling", list("traitorchan")) + if(src in SSticker.mode.changelings) + . += "CHANGELING|no" + if(objectives.len==0) + . += "
    Objectives are empty! Randomize!" + if(changeling && changeling.absorbed_dna.len && (current.real_name != changeling.absorbed_dna[1])) + . += "
    Transform to initial appearance." + else + . += "changeling|NO" + + . += _memory_edit_role_enabled(ROLE_CHANGELING) + +/datum/mind/proc/memory_edit_vampire(mob/living/carbon/human/H) + . = _memory_edit_header("vampire", list("traitorvamp")) + if(src in SSticker.mode.vampires) + . += "VAMPIRE|no" + if(objectives.len==0) + . += "
    Objectives are empty! Randomize!" + else + . += "vampire|NO" + + . += _memory_edit_role_enabled(ROLE_VAMPIRE) + /** Enthralled ***/ + . += "
    enthralled: " + if(src in SSticker.mode.vampire_enthralled) + . += "THRALL|no" + else + . += "thrall|NO" + +/datum/mind/proc/memory_edit_nuclear(mob/living/carbon/human/H) + . = _memory_edit_header("nuclear") + if(src in SSticker.mode.syndicates) + . += "OPERATIVE|no" + . += "
    To shuttle, undress, dress up." + var/code + for(var/obj/machinery/nuclearbomb/bombue in GLOB.machines) + if(length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if(code) + . += " Code is [code]. tell the code." + else + . += "operative|NO" + + . += _memory_edit_role_enabled(ROLE_OPERATIVE) + +/datum/mind/proc/memory_edit_shadowling(mob/living/carbon/human/H) + . = _memory_edit_header("shadowling") + if(src in SSticker.mode.shadows) + . += "SHADOWLING|thrall|no" + else if(src in SSticker.mode.shadowling_thralls) + . += "Shadowling|THRALL|no" + else + . += "shadowling|thrall|NO" + + . += _memory_edit_role_enabled(ROLE_SHADOWLING) + +/datum/mind/proc/memory_edit_abductor(mob/living/carbon/human/H) + . = _memory_edit_header("abductor") + if(src in SSticker.mode.abductors) + . += "ABDUCTOR|no" + . += "|undress|equip" + else + . += "abductor|NO" + + . += _memory_edit_role_enabled(ROLE_ABDUCTOR) + +/datum/mind/proc/memory_edit_devil(mob/living/H) + . = _memory_edit_header("devil", list("devilagents")) + if(src in SSticker.mode.devils) + if(!devilinfo) + . += "No devilinfo found! Yell at a coder!" + else if(!devilinfo.ascendable) + . += "DEVIL|Ascendable Devil|sintouched|no" + else + . += "DEVIL|ASCENDABLE DEVIL|sintouched|no" + else if(src in SSticker.mode.sintouched) + . += "devil|Ascendable Devil|SINTOUCHED|no" + else + . += "devil|Ascendable Devil|sintouched|NO" + + . += _memory_edit_role_enabled(ROLE_DEVIL) + +/datum/mind/proc/memory_edit_eventmisc(mob/living/H) + . = _memory_edit_header("event", list()) + if(src in SSticker.mode.eventmiscs) + . += "YES|no" + else + . += "Event Role|NO" + +/datum/mind/proc/memory_edit_traitor() + . = _memory_edit_header("traitor", list("traitorchan", "traitorvamp")) + if(has_antag_datum(/datum/antagonist/traitor)) + . += "TRAITOR|no" + if(objectives.len==0) + . += "
    Objectives are empty! Randomize!" + else + . += "traitor|NO" + + . += _memory_edit_role_enabled(ROLE_TRAITOR) + // Mindslave + . += "
    mindslaved: " + if(has_antag_datum(/datum/antagonist/mindslave)) + . += "MINDSLAVE|no" + else + . += "mindslave|NO" + +/datum/mind/proc/memory_edit_silicon() + . = "Silicon: " + var/mob/living/silicon/robot/robot = current + if(istype(robot) && robot.emagged) + . += "
    Cyborg: Is emagged! Unemag!
    0th law: [robot.laws.zeroth_law]" + var/mob/living/silicon/ai/ai = current + if(istype(ai) && ai.connected_robots.len) + var/n_e_robots = 0 + for(var/mob/living/silicon/robot/R in ai.connected_robots) + if(R.emagged) + n_e_robots++ + . += "
    [n_e_robots] of [ai.connected_robots.len] slaved cyborgs are emagged. Unemag" + +/datum/mind/proc/memory_edit_uplink() + . = "" + if(ishuman(current) && ((src in SSticker.mode.head_revolutionaries) || \ + (has_antag_datum(/datum/antagonist/traitor)) || \ + (src in SSticker.mode.syndicates))) + . = "Uplink: give" + var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() + var/crystals + if(suplink) + crystals = suplink.uses + if(suplink) + . += "|take" + if(usr.client.holder.rights & (R_SERVER|R_EVENT)) + . += ", [crystals] crystals" + else + . += ", [crystals] crystals" + . += "." //hiel grammar + // ^ whoever left this comment is literally a grammar nazi. stalin better. in russia grammar correct you. + +/datum/mind/proc/edit_memory() + if(!SSticker || !SSticker.mode) + alert("Not before round-start!", "Alert") + return + + var/out = "[name][(current && (current.real_name != name))?" (as [current.real_name])" : ""]
    " + out += "Mind currently owned by key: [key] [active ? "(synced)" : "(not synced)"]
    " + out += "Assigned role: [assigned_role]. Edit
    " + out += "Factions and special roles:
    " + + var/list/sections = list( + "implant", + "revolution", + "cult", + "wizard", + "changeling", + "vampire", // "traitorvamp", + "nuclear", + "traitor", // "traitorchan", + ) + var/mob/living/carbon/human/H = current + if(ishuman(current)) + /** Impanted**/ + sections["implant"] = memory_edit_implant(H) + /** REVOLUTION ***/ + sections["revolution"] = memory_edit_revolution(H) + /** CULT ***/ + sections["cult"] = memory_edit_cult(H) + /** WIZARD ***/ + sections["wizard"] = memory_edit_wizard(H) + /** CHANGELING ***/ + sections["changeling"] = memory_edit_changeling(H) + /** VAMPIRE ***/ + sections["vampire"] = memory_edit_vampire(H) + /** NUCLEAR ***/ + sections["nuclear"] = memory_edit_nuclear(H) + /** SHADOWLING **/ + sections["shadowling"] = memory_edit_shadowling(H) + /** Abductors **/ + sections["abductor"] = memory_edit_abductor(H) + /** DEVIL ***/ + var/static/list/devils_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/carbon/true_devil, /mob/living/silicon/robot)) + if(is_type_in_typecache(current, devils_typecache)) + sections["devil"] = memory_edit_devil(H) + sections["eventmisc"] = memory_edit_eventmisc(H) + /** TRAITOR ***/ + sections["traitor"] = memory_edit_traitor() + /** SILICON ***/ + if(issilicon(current)) + sections["silicon"] = memory_edit_silicon() + /* + This prioritizes antags relevant to the current round to make them appear at the top of the panel. + Traitorchan and traitorvamp are snowflaked in because they have multiple sections. + */ + if(SSticker.mode.config_tag == "traitorchan") + if(sections["traitor"]) + out += sections["traitor"] + "
    " + if(sections["changeling"]) + out += sections["changeling"] + "
    " + sections -= "traitor" + sections -= "changeling" + // Elif technically unnecessary but it makes the following else look better + else if(SSticker.mode.config_tag == "traitorvamp") + if(sections["traitor"]) + out += sections["traitor"] + "
    " + if(sections["vampire"]) + out += sections["vampire"] + "
    " + sections -= "traitor" + sections -= "vampire" + else + if(sections[SSticker.mode.config_tag]) + out += sections[SSticker.mode.config_tag] + "
    " + sections -= SSticker.mode.config_tag + + for(var/i in sections) + if(sections[i]) + out += sections[i] + "
    " + + out += memory_edit_uplink() + out += "
    " + + out += "Memory:
    " + out += memory + out += "
    Edit memory
    " + out += "Objectives:
    " + if(objectives.len == 0) + out += "EMPTY
    " + else + out += gen_objective_text(admin = TRUE) + out += "Add objective

    " + out += "Announce objectives

    " + usr << browse(out, "window=edit_memory[src];size=500x500") + +/datum/mind/Topic(href, href_list) + if(!check_rights(R_ADMIN)) + return + + if(href_list["role_edit"]) + var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in GLOB.joblist + if(!new_role) + return + assigned_role = new_role + log_admin("[key_name(usr)] has changed [key_name(current)]'s assigned role to [assigned_role]") + message_admins("[key_name_admin(usr)] has changed [key_name_admin(current)]'s assigned role to [assigned_role]") + + else if(href_list["memory_edit"]) + var/messageinput = input("Write new memory", "Memory", memory) as null|message + if(isnull(messageinput)) + return + var/new_memo = copytext(messageinput, 1,MAX_MESSAGE_LEN) + var/confirmed = alert(usr, "Are you sure you want to edit their memory? It will wipe out their original memory!", "Edit Memory", "Yes", "No") + if(confirmed == "Yes") // Because it is too easy to accidentally wipe someone's memory + memory = new_memo + log_admin("[key_name(usr)] has edited [key_name(current)]'s memory") + message_admins("[key_name_admin(usr)] has edited [key_name_admin(current)]'s memory") + + else if(href_list["obj_edit"] || href_list["obj_add"]) + var/datum/objective/objective + var/objective_pos + var/def_value + + if(href_list["obj_edit"]) + objective = locate(href_list["obj_edit"]) + if(!objective) + return + objective_pos = objectives.Find(objective) + + //Text strings are easy to manipulate. Revised for simplicity. + var/temp_obj_type = "[objective.type]"//Convert path into a text string. + def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. + if(!def_value)//If it's a custom objective, it will be an empty string. + def_value = "custom" + + var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "blood", "debrain", "protect", "prevent", "brig", "hijack", "escape", "survive", "steal", "download", "nuclear", "capture", "absorb", "destroy", "maroon", "identity theft", "custom") + if(!new_obj_type) + return + + var/datum/objective/new_objective = null + + switch(new_obj_type) + if("assassinate","protect","debrain", "brig", "maroon") + //To determine what to name the objective in explanation text. + var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. + var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. + var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. + + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.minds) + if((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) + possible_targets += possible_target.current + + var/mob/def_target = null + var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) + if(objective&&(objective.type in objective_list) && objective:target) + def_target = objective.target.current + possible_targets = sortAtom(possible_targets) + possible_targets += "Free objective" + + var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets + if(!new_target) + return + + var/objective_path = text2path("/datum/objective/[new_obj_type]") + if(new_target == "Free objective") + new_objective = new objective_path + new_objective.owner = src + new_objective:target = null + new_objective.explanation_text = "Free objective" + else + new_objective = new objective_path + new_objective.owner = src + new_objective:target = new_target:mind + //Will display as special role if assigned mode is equal to special role.. Ninjas/commandos/nuke ops. + new_objective.explanation_text = "[objective_type] [new_target:real_name], the [new_target:mind:assigned_role == new_target:mind:special_role ? (new_target:mind:special_role) : (new_target:mind:assigned_role)]." + + if("destroy") + var/list/possible_targets = active_ais(1) + if(possible_targets.len) + var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets + new_objective = new /datum/objective/destroy + new_objective.target = new_target.mind + new_objective.owner = src + new_objective.explanation_text = "Destroy [new_target.name], the experimental AI." + else + to_chat(usr, "No active AIs with minds") + + if("prevent") + new_objective = new /datum/objective/block + new_objective.owner = src + + if("hijack") + new_objective = new /datum/objective/hijack + new_objective.owner = src + + if("escape") + new_objective = new /datum/objective/escape + new_objective.owner = src + + if("survive") + new_objective = new /datum/objective/survive + new_objective.owner = src + + if("die") + new_objective = new /datum/objective/die + new_objective.owner = src + + if("nuclear") + new_objective = new /datum/objective/nuclear + new_objective.owner = src + + if("steal") + if(!istype(objective, /datum/objective/steal)) + new_objective = new /datum/objective/steal + new_objective.owner = src + else + new_objective = objective + var/datum/objective/steal/steal = new_objective + if(!steal.select_target()) + return + + if("download","capture","absorb", "blood") + var/def_num + if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) + def_num = objective.target_amount + + var/target_number = input("Input target number:", "Objective", def_num) as num|null + if(isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. + return + + switch(new_obj_type) + if("download") + new_objective = new /datum/objective/download + new_objective.explanation_text = "Download [target_number] research levels." + if("capture") + new_objective = new /datum/objective/capture + new_objective.explanation_text = "Accumulate [target_number] capture points." + if("absorb") + new_objective = new /datum/objective/absorb + new_objective.explanation_text = "Absorb [target_number] compatible genomes." + if("blood") + new_objective = new /datum/objective/blood + new_objective.explanation_text = "Accumulate at least [target_number] total units of blood." + new_objective.owner = src + new_objective.target_amount = target_number + + if("identity theft") + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.minds) + if((possible_target != src) && ishuman(possible_target.current)) + possible_targets += possible_target + possible_targets = sortAtom(possible_targets) + possible_targets += "Free objective" + var/new_target = input("Select target:", "Objective target") as null|anything in possible_targets + if(!new_target) + return + var/datum/mind/targ = new_target + if(!istype(targ)) + log_runtime(EXCEPTION("Invalid target for identity theft objective, cancelling"), src) + return + new_objective = new /datum/objective/escape/escape_with_identity + new_objective.owner = src + new_objective.target = new_target + new_objective.explanation_text = "Escape on the shuttle or an escape pod with the identity of [targ.current.real_name], the [targ.assigned_role] while wearing [targ.current.p_their()] identification card." + if("custom") + var/expl = sanitize(copytext(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null,1,MAX_MESSAGE_LEN)) + if(!expl) + return + new_objective = new /datum/objective + new_objective.owner = src + new_objective.explanation_text = expl + + if(!new_objective) + return + + if(objective) + objectives -= objective + qdel(objective) + objectives.Insert(objective_pos, new_objective) + else + objectives += new_objective + + log_admin("[key_name(usr)] has updated [key_name(current)]'s objectives: [new_objective]") + message_admins("[key_name_admin(usr)] has updated [key_name_admin(current)]'s objectives: [new_objective]") + + else if(href_list["obj_delete"]) + var/datum/objective/objective = locate(href_list["obj_delete"]) + if(!istype(objective)) + return + objectives -= objective + + log_admin("[key_name(usr)] has removed one of [key_name(current)]'s objectives: [objective]") + message_admins("[key_name_admin(usr)] has removed one of [key_name_admin(current)]'s objectives: [objective]") + qdel(objective) + + else if(href_list["obj_completed"]) + var/datum/objective/objective = locate(href_list["obj_completed"]) + if(!istype(objective)) + return + objective.completed = !objective.completed + + log_admin("[key_name(usr)] has toggled the completion of one of [key_name(current)]'s objectives") + message_admins("[key_name_admin(usr)] has toggled the completion of one of [key_name_admin(current)]'s objectives") + + else if(href_list["implant"]) + var/mob/living/carbon/human/H = current + + switch(href_list["implant"]) + if("remove") + for(var/obj/item/implant/mindshield/I in H.contents) + if(I && I.implanted) + qdel(I) + to_chat(H, "Your mindshield implant has been deactivated.") + log_admin("[key_name(usr)] has deactivated [key_name(current)]'s mindshield implant") + message_admins("[key_name_admin(usr)] has deactivated [key_name_admin(current)]'s mindshield implant") + if("add") + var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(H) + L.implant(H) + + log_admin("[key_name(usr)] has given [key_name(current)] a mindshield implant") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a mindshield implant") + + to_chat(H, "You somehow have become the recepient of a mindshield transplant, and it just activated!") + if(src in SSticker.mode.revolutionaries) + special_role = null + SSticker.mode.revolutionaries -= src + to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") + if(src in SSticker.mode.head_revolutionaries) + special_role = null + SSticker.mode.head_revolutionaries -=src + to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") + if(src in SSticker.mode.cult) + SSticker.mode.cult -= src + SSticker.mode.update_cult_icons_removed(src) + special_role = null + var/datum/game_mode/cult/cult = SSticker.mode + if(istype(cult)) + cult.memorize_cult_objectives(src) + to_chat(current, "The nanobots in the mindshield implant remove all thoughts about being in a cult. Have a productive day!") + memory = "" + + else if(href_list["revolution"]) + + switch(href_list["revolution"]) + if("clear") + if(src in SSticker.mode.revolutionaries) + SSticker.mode.revolutionaries -= src + to_chat(current, "You have been brainwashed! You are no longer a revolutionary!") + SSticker.mode.update_rev_icons_removed(src) + special_role = null + if(src in SSticker.mode.head_revolutionaries) + SSticker.mode.head_revolutionaries -= src + to_chat(current, "You have been brainwashed! You are no longer a head revolutionary!") + SSticker.mode.update_rev_icons_removed(src) + special_role = null + log_admin("[key_name(usr)] has de-rev'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-rev'd [key_name_admin(current)]") + + if("rev") + if(src in SSticker.mode.head_revolutionaries) + SSticker.mode.head_revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + to_chat(current, "Revolution has been disappointed of your leadership traits! You are a regular revolutionary now!") + else if(!(src in SSticker.mode.revolutionaries)) + to_chat(current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") + else + return + SSticker.mode.revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = SPECIAL_ROLE_REV + log_admin("[key_name(usr)] has rev'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has rev'd [key_name_admin(current)]") + + if("headrev") + if(src in SSticker.mode.revolutionaries) + SSticker.mode.revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + to_chat(current, "You have proven your devotion to revolution! You are a head revolutionary now!") + else if(!(src in SSticker.mode.head_revolutionaries)) + to_chat(current, "You are a member of the revolutionaries' leadership now!") + else + return + if(SSticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries + if(valid_head) + for(var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]." + objectives += rev_obj + SSticker.mode.greet_revolutionary(src,0) + SSticker.mode.head_revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = SPECIAL_ROLE_HEAD_REV + log_admin("[key_name(usr)] has head-rev'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has head-rev'd [key_name_admin(current)]") + + if("autoobjectives") + SSticker.mode.forge_revolutionary_objectives(src) + SSticker.mode.greet_revolutionary(src,0) + log_admin("[key_name(usr)] has automatically forged revolutionary objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged revolutionary objectives for [key_name_admin(current)]") + + if("flash") + if(!SSticker.mode.equip_revolutionary(current)) + to_chat(usr, "Spawning flash failed!") + log_admin("[key_name(usr)] has given [key_name(current)] a flash") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a flash") + + if("takeflash") + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + if(!flash) + to_chat(usr, "Deleting flash failed!") + qdel(flash) + log_admin("[key_name(usr)] has taken [key_name(current)]'s flash") + message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s flash") + + if("repairflash") + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + if(!flash) + to_chat(usr, "Repairing flash failed!") + else + flash.broken = 0 + log_admin("[key_name(usr)] has repaired [key_name(current)]'s flash") + message_admins("[key_name_admin(usr)] has repaired [key_name_admin(current)]'s flash") + + if("reequip") + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + qdel(flash) + take_uplink() + var/fail = 0 + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + fail |= !T.equip_traitor(src) + fail |= !SSticker.mode.equip_revolutionary(current) + if(fail) + to_chat(usr, "Reequipping revolutionary goes wrong!") + return + log_admin("[key_name(usr)] has equipped [key_name(current)] as a revolutionary") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a revolutionary") + + else if(href_list["cult"]) + switch(href_list["cult"]) + if("clear") + if(src in SSticker.mode.cult) + SSticker.mode.remove_cultist(src) + special_role = null + log_admin("[key_name(usr)] has de-culted [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-culted [key_name_admin(current)]") + if("cultist") + if(!(src in SSticker.mode.cult)) + SSticker.mode.add_cultist(src) + special_role = SPECIAL_ROLE_CULTIST + to_chat(current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") + to_chat(current, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve [SSticker.cultdat.entity_title2] above all else. Bring It back.") + log_admin("[key_name(usr)] has culted [key_name(current)]") + message_admins("[key_name_admin(usr)] has culted [key_name_admin(current)]") + if(!GLOB.summon_spots.len) + while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES) + var/area/summon = pick(return_sorted_areas() - GLOB.summon_spots) + if(summon && is_station_level(summon.z) && summon.valid_territory) + GLOB.summon_spots += summon + if("tome") + var/mob/living/carbon/human/H = current + if(istype(H)) + var/obj/item/tome/T = new(H) + + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = H.equip_in_one_of_slots(T, slots) + if(!where) + to_chat(usr, "Spawning tome failed!") + qdel(T) + else + to_chat(H, "A tome, a message from your new master, appears in your [where].") + log_admin("[key_name(usr)] has spawned a tome for [key_name(current)]") + message_admins("[key_name_admin(usr)] has spawned a tome for [key_name_admin(current)]") + + if("equip") + if(!SSticker.mode.equip_cultist(current)) + to_chat(usr, "Spawning equipment failed!") + log_admin("[key_name(usr)] has equipped [key_name(current)] as a cultist") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a cultist") + + else if(href_list["wizard"]) + + switch(href_list["wizard"]) + if("clear") + if(src in SSticker.mode.wizards) + SSticker.mode.wizards -= src + special_role = null + current.spellremove(current) + current.faction = list("Station") + SSticker.mode.update_wiz_icons_removed(src) + to_chat(current, "You have been brainwashed! You are no longer a wizard!") + log_admin("[key_name(usr)] has de-wizarded [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-wizarded [key_name_admin(current)]") + if("wizard") + if(!(src in SSticker.mode.wizards)) + SSticker.mode.wizards += src + special_role = SPECIAL_ROLE_WIZARD + //ticker.mode.learn_basic_spells(current) + SSticker.mode.update_wiz_icons_added(src) + SEND_SOUND(current, 'sound/ambience/antag/ragesmages.ogg') + to_chat(current, "You are a Space Wizard!") + current.faction = list("wizard") + log_admin("[key_name(usr)] has wizarded [key_name(current)]") + message_admins("[key_name_admin(usr)] has wizarded [key_name_admin(current)]") + if("lair") + current.forceMove(pick(GLOB.wizardstart)) + log_admin("[key_name(usr)] has moved [key_name(current)] to the wizard's lair") + message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the wizard's lair") + if("dressup") + SSticker.mode.equip_wizard(current) + log_admin("[key_name(usr)] has equipped [key_name(current)] as a wizard") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a wizard") + if("name") + SSticker.mode.name_wizard(current) + log_admin("[key_name(usr)] has allowed wizard [key_name(current)] to name themselves") + message_admins("[key_name_admin(usr)] has allowed wizard [key_name_admin(current)] to name themselves") + if("autoobjectives") + SSticker.mode.forge_wizard_objectives(src) + to_chat(usr, "The objectives for wizard [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged wizard objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged wizard objectives for [key_name_admin(current)]") + + + else if(href_list["changeling"]) + switch(href_list["changeling"]) + if("clear") + if(src in SSticker.mode.changelings) + SSticker.mode.changelings -= src + special_role = null + if(changeling) + current.remove_changeling_powers() + qdel(changeling) + changeling = null + SSticker.mode.update_change_icons_removed(src) + to_chat(current, "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!") + log_admin("[key_name(usr)] has de-changelinged [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-changelinged [key_name_admin(current)]") + if("changeling") + if(!(src in SSticker.mode.changelings)) + SSticker.mode.changelings += src + SSticker.mode.grant_changeling_powers(current) + SSticker.mode.update_change_icons_added(src) + special_role = SPECIAL_ROLE_CHANGELING + SEND_SOUND(current, 'sound/ambience/antag/ling_aler.ogg') + to_chat(current, "Your powers have awoken. A flash of memory returns to us... we are a changeling!") + log_admin("[key_name(usr)] has changelinged [key_name(current)]") + message_admins("[key_name_admin(usr)] has changelinged [key_name_admin(current)]") + + if("autoobjectives") + SSticker.mode.forge_changeling_objectives(src) + to_chat(usr, "The objectives for changeling [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") + + if("initialdna") + if(!changeling || !changeling.absorbed_dna.len) + to_chat(usr, "Resetting DNA failed!") + else + current.dna = changeling.absorbed_dna[1] + current.real_name = current.dna.real_name + current.UpdateAppearance() + domutcheck(current, null) + log_admin("[key_name(usr)] has reset [key_name(current)]'s DNA") + message_admins("[key_name_admin(usr)] has reset [key_name_admin(current)]'s DNA") + + else if(href_list["vampire"]) + switch(href_list["vampire"]) + if("clear") + if(src in SSticker.mode.vampires) + SSticker.mode.vampires -= src + special_role = null + if(vampire) + vampire.remove_vampire_powers() + qdel(vampire) + vampire = null + SSticker.mode.update_vampire_icons_removed(src) + to_chat(current, "You grow weak and lose your powers! You are no longer a vampire and are stuck in your current form!") + log_admin("[key_name(usr)] has de-vampired [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-vampired [key_name_admin(current)]") + if("vampire") + if(!(src in SSticker.mode.vampires)) + SSticker.mode.vampires += src + SSticker.mode.grant_vampire_powers(current) + SSticker.mode.update_vampire_icons_added(src) + var/datum/mindslaves/slaved = new() + slaved.masters += src + som = slaved //we MIGT want to mindslave someone + special_role = SPECIAL_ROLE_VAMPIRE + SEND_SOUND(current, 'sound/ambience/antag/vampalert.ogg') + to_chat(current, "Your powers have awoken. Your lust for blood grows... You are a Vampire!") + log_admin("[key_name(usr)] has vampired [key_name(current)]") + message_admins("[key_name_admin(usr)] has vampired [key_name_admin(current)]") + + if("autoobjectives") + SSticker.mode.forge_vampire_objectives(src) + to_chat(usr, "The objectives for vampire [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") + + else if(href_list["vampthrall"]) + switch(href_list["vampthrall"]) + if("clear") + if(src in SSticker.mode.vampire_enthralled) + SSticker.mode.remove_vampire_mind(src) + log_admin("[key_name(usr)] has de-vampthralled [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-vampthralled [key_name_admin(current)]") + + else if(href_list["nuclear"]) + var/mob/living/carbon/human/H = current + + switch(href_list["nuclear"]) + if("clear") + if(src in SSticker.mode.syndicates) + SSticker.mode.syndicates -= src + SSticker.mode.update_synd_icons_removed(src) + special_role = null + for(var/datum/objective/nuclear/O in objectives) + objectives-=O + qdel(O) + to_chat(current, "You have been brainwashed! You are no longer a syndicate operative!") + log_admin("[key_name(usr)] has de-nuke op'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-nuke op'd [key_name_admin(current)]") + if("nuclear") + if(!(src in SSticker.mode.syndicates)) + SSticker.mode.syndicates += src + SSticker.mode.update_synd_icons_added(src) + if(SSticker.mode.syndicates.len==1) + SSticker.mode.prepare_syndicate_leader(src) + else + current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" + special_role = SPECIAL_ROLE_NUKEOPS + to_chat(current, "You are a [syndicate_name()] agent!") + SSticker.mode.forge_syndicate_objectives(src) + SSticker.mode.greet_syndicate(src) + log_admin("[key_name(usr)] has nuke op'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has nuke op'd [key_name_admin(current)]") + if("lair") + current.forceMove(get_turf(locate("landmark*Syndicate-Spawn"))) + log_admin("[key_name(usr)] has moved [key_name(current)] to the nuclear operative spawn") + message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the nuclear operative spawn") + if("dressup") + qdel(H.belt) + qdel(H.back) + qdel(H.l_ear) + qdel(H.r_ear) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_pda) + qdel(H.wear_suit) + qdel(H.w_uniform) + + if(!SSticker.mode.equip_syndicate(current)) + to_chat(usr, "Equipping a syndicate failed!") + return + SSticker.mode.update_syndicate_id(current.mind, SSticker.mode.syndicates.len == 1) + log_admin("[key_name(usr)] has equipped [key_name(current)] as a nuclear operative") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a nuclear operative") + + if("tellcode") + var/code + for(var/obj/machinery/nuclearbomb/bombue in GLOB.machines) + if(length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if(code) + store_memory("Syndicate Nuclear Bomb Code: [code]", 0, 0) + to_chat(current, "The nuclear authorization code is: [code]") + log_admin("[key_name(usr)] has given [key_name(current)] the nuclear authorization code") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] the nuclear authorization code") + else + to_chat(usr, "No valid nuke found!") + + else if(href_list["eventmisc"]) + switch(href_list["eventmisc"]) + if("clear") + if(src in SSticker.mode.eventmiscs) + SSticker.mode.eventmiscs -= src + SSticker.mode.update_eventmisc_icons_removed(src) + special_role = null + message_admins("[key_name_admin(usr)] has de-eventantag'ed [current].") + log_admin("[key_name(usr)] has de-eventantag'ed [current].") + if("eventmisc") + SSticker.mode.eventmiscs += src + special_role = SPECIAL_ROLE_EVENTMISC + SSticker.mode.update_eventmisc_icons_added(src) + message_admins("[key_name_admin(usr)] has eventantag'ed [current].") + log_admin("[key_name(usr)] has eventantag'ed [current].") + else if(href_list["devil"]) + switch(href_list["devil"]) + if("clear") + if(src in SSticker.mode.devils) + if(istype(current,/mob/living/carbon/true_devil/)) + to_chat(usr,"This cannot be used on true or arch-devils.") + else + SSticker.mode.devils -= src + SSticker.mode.update_devil_icons_removed(src) + special_role = null + to_chat(current,"Your infernal link has been severed! You are no longer a devil!") + RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt) + RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/ascended) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/violin) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_dancefloor) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch/ascended) + message_admins("[key_name_admin(usr)] has de-devil'ed [current].") + if(issilicon(current)) + var/mob/living/silicon/S = current + S.laws.clear_sixsixsix_laws() + devilinfo = null + log_admin("[key_name(usr)] has de-devil'ed [current].") + else if(src in SSticker.mode.sintouched) + SSticker.mode.sintouched -= src + message_admins("[key_name_admin(usr)] has de-sintouch'ed [current].") + log_admin("[key_name(usr)] has de-sintouch'ed [current].") + if("devil") + if(devilinfo) + devilinfo.ascendable = FALSE + message_admins("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") + log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") + return + if(!ishuman(current) && !isrobot(current)) + to_chat(usr, "This only works on humans and cyborgs!") + return + SSticker.mode.devils += src + special_role = "devil" + SSticker.mode.update_devil_icons_added(src) + SSticker.mode.finalize_devil(src, FALSE) + SSticker.mode.forge_devil_objectives(src, 2) + SSticker.mode.greet_devil(src) + message_admins("[key_name_admin(usr)] has devil'ed [current].") + log_admin("[key_name(usr)] has devil'ed [current].") + if("ascendable_devil") + if(devilinfo) + devilinfo.ascendable = TRUE + message_admins("[key_name_admin(usr)] has made [current] able to ascend as a devil.") + log_admin("[key_name_admin(usr)] has made [current] able to ascend as a devil.") + return + if(!ishuman(current) && !isrobot(current)) + to_chat(usr, "This only works on humans and cyborgs!") + return + SSticker.mode.devils += src + special_role = "devil" + SSticker.mode.update_devil_icons_added(src) + SSticker.mode.finalize_devil(src, TRUE) + SSticker.mode.forge_devil_objectives(src, 2) + SSticker.mode.greet_devil(src) + message_admins("[key_name_admin(usr)] has devil'ed [current]. The devil has been marked as ascendable.") + log_admin("[key_name(usr)] has devil'ed [current]. The devil has been marked as ascendable.") + if("sintouched") + var/mob/living/carbon/human/H = current + H.influenceSin() + message_admins("[key_name_admin(usr)] has sintouch'ed [current].") + log_admin("[key_name(usr)] has sintouch'ed [current].") + + else if(href_list["traitor"]) + switch(href_list["traitor"]) + if("clear") + if(has_antag_datum(/datum/antagonist/traitor)) + to_chat(current, "You have been brainwashed! You are no longer a traitor!") + remove_antag_datum(/datum/antagonist/traitor) + log_admin("[key_name(usr)] has de-traitored [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-traitored [key_name_admin(current)]") + + if("traitor") + if(!(has_antag_datum(/datum/antagonist/traitor))) + var/datum/antagonist/traitor/T = new() + T.give_objectives = FALSE + T.should_equip = FALSE + add_antag_datum(T) + log_admin("[key_name(usr)] has traitored [key_name(current)]") + message_admins("[key_name_admin(usr)] has traitored [key_name_admin(current)]") + + if("autoobjectives") + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + T.forge_traitor_objectives(src) + to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") + + else if(href_list["mindslave"]) + switch(href_list["mindslave"]) + if("clear") + if(has_antag_datum(/datum/antagonist/mindslave)) + var/mob/living/carbon/human/H = current + for(var/i in H.contents) + if(istype(i, /obj/item/implant/traitor)) + qdel(i) + break + remove_antag_datum(/datum/antagonist/mindslave) + log_admin("[key_name(usr)] has de-mindslaved [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-mindslaved [key_name_admin(current)]") + + else if(href_list["shadowling"]) + switch(href_list["shadowling"]) + if("clear") + SSticker.mode.update_shadow_icons_removed(src) + if(src in SSticker.mode.shadows) + SSticker.mode.shadows -= src + special_role = null + to_chat(current, "Your powers have been quenched! You are no longer a shadowling!") + message_admins("[key_name_admin(usr)] has de-shadowlinged [current].") + log_admin("[key_name(usr)] has de-shadowlinged [current].") + current.spellremove(current) + current.remove_language("Shadowling Hivemind") + else if(src in SSticker.mode.shadowling_thralls) + SSticker.mode.remove_thrall(src,0) + message_admins("[key_name_admin(usr)] has de-thrall'ed [current].") + log_admin("[key_name(usr)] has de-thralled [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-thralled [key_name_admin(current)]") + if("shadowling") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + SSticker.mode.shadows += src + special_role = SPECIAL_ROLE_SHADOWLING + to_chat(current, "Something stirs deep in your mind. A red light floods your vision, and slowly you remember. Though your human disguise has served you well, the \ + time is nigh to cast it off and enter your true form. You have disguised yourself amongst the humans, but you are not one of them. You are a shadowling, and you are to ascend at all costs.\ + ") + SSticker.mode.finalize_shadowling(src) + SSticker.mode.update_shadow_icons_added(src) + log_admin("[key_name(usr)] has shadowlinged [key_name(current)]") + message_admins("[key_name_admin(usr)] has shadowlinged [key_name_admin(current)]") + if("thrall") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + SSticker.mode.add_thrall(src) + message_admins("[key_name_admin(usr)] has thralled [current].") + log_admin("[key_name(usr)] has thralled [current].") + + else if(href_list["abductor"]) + switch(href_list["abductor"]) + if("clear") + to_chat(usr, "Not implemented yet. Sorry!") + //ticker.mode.update_abductor_icons_removed(src) + if("abductor") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + make_Abductor() + log_admin("[key_name(usr)] turned [current] into abductor.") + SSticker.mode.update_abductor_icons_added(src) + if("equip") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + + var/mob/living/carbon/human/H = current + var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") + if(gear) + if(gear=="Agent") + H.equipOutfit(/datum/outfit/abductor/agent) + else + H.equipOutfit(/datum/outfit/abductor/scientist) + + else if(href_list["silicon"]) + switch(href_list["silicon"]) + if("unemag") + var/mob/living/silicon/robot/R = current + if(istype(R)) + R.emagged = 0 + if(R.module) + if(R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + R.clear_supplied_laws() + R.laws = new /datum/ai_laws/crewsimov + log_admin("[key_name(usr)] has un-emagged [key_name(current)]") + message_admins("[key_name_admin(usr)] has un-emagged [key_name_admin(current)]") + + if("unemagcyborgs") + if(isAI(current)) + var/mob/living/silicon/ai/ai = current + for(var/mob/living/silicon/robot/R in ai.connected_robots) + R.emagged = 0 + if(R.module) + if(R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + R.clear_supplied_laws() + R.laws = new /datum/ai_laws/crewsimov + log_admin("[key_name(usr)] has unemagged [key_name(ai)]'s cyborgs") + message_admins("[key_name_admin(usr)] has unemagged [key_name_admin(ai)]'s cyborgs") + + else if(href_list["common"]) + switch(href_list["common"]) + if("undress") + if(ishuman(current)) + var/mob/living/carbon/human/H = current + // Don't "undress" organs right out of the body + for(var/obj/item/W in H.contents - (H.bodyparts | H.internal_organs)) + current.unEquip(W, 1) + else + for(var/obj/item/W in current) + current.unEquip(W, 1) + log_admin("[key_name(usr)] has unequipped [key_name(current)]") + message_admins("[key_name_admin(usr)] has unequipped [key_name_admin(current)]") + if("takeuplink") + take_uplink() + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + T.antag_memory = "" //Remove any antag memory they may have had (uplink codes, code phrases) + log_admin("[key_name(usr)] has taken [key_name(current)]'s uplink") + message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s uplink") + if("crystals") + if(usr.client.holder.rights & (R_SERVER|R_EVENT)) + var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() + var/crystals + if(suplink) + crystals = suplink.uses + crystals = input("Amount of telecrystals for [key]","Syndicate uplink", crystals) as null|num + if(!isnull(crystals)) + if(suplink) + suplink.uses = crystals + log_admin("[key_name(usr)] has set [key_name(current)]'s telecrystals to [crystals]") + message_admins("[key_name_admin(usr)] has set [key_name_admin(current)]'s telecrystals to [crystals]") + if("uplink") + if(has_antag_datum(/datum/antagonist/traitor)) + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + T.give_codewords() + if(!T.equip_traitor(src)) + to_chat(usr, "Equipping a syndicate failed!") + return + log_admin("[key_name(usr)] has given [key_name(current)] an uplink") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] an uplink") + + else if(href_list["obj_announce"]) + announce_objectives() + SEND_SOUND(current, sound('sound/ambience/alarm4.ogg')) + log_admin("[key_name(usr)] has announced [key_name(current)]'s objectives") + message_admins("[key_name_admin(usr)] has announced [key_name_admin(current)]'s objectives") + + edit_memory() + + +// Datum antag mind procs +/datum/mind/proc/add_antag_datum(datum_type_or_instance, team) + if(!datum_type_or_instance) + return + var/datum/antagonist/A + if(!ispath(datum_type_or_instance)) + A = datum_type_or_instance + if(!istype(A)) + return + else + A = new datum_type_or_instance() + //Choose snowflake variation if antagonist handles it + var/datum/antagonist/S = A.specialization(src) + if(S && S != A) + qdel(A) + A = S + if(!A.can_be_owned(src)) + qdel(A) + return + A.owner = src + LAZYADD(antag_datums, A) + A.create_team(team) + var/datum/team/antag_team = A.get_team() + if(antag_team) + antag_team.add_member(src) + A.on_gain() + return A + +/datum/mind/proc/remove_antag_datum(datum_type) + if(!datum_type) + return + var/datum/antagonist/A = has_antag_datum(datum_type) + if(A) + A.on_removal() + return TRUE + + +/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us. + for(var/a in antag_datums) + var/datum/antagonist/A = a + A.on_removal() + +/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE) + if(!datum_type) + return + . = FALSE + for(var/a in antag_datums) + var/datum/antagonist/A = a + if(check_subtypes && istype(A, datum_type)) + return A + else if(A.type == datum_type) + return A + +/datum/mind/proc/announce_objectives() + to_chat(current, "Your current objectives:") + for(var/line in splittext(gen_objective_text(), "
    ")) + to_chat(current, line) + +/datum/mind/proc/find_syndicate_uplink() + var/list/L = current.get_contents() + for(var/obj/item/I in L) + if(I.hidden_uplink) + return I.hidden_uplink + return null + +/datum/mind/proc/take_uplink() + var/obj/item/uplink/hidden/H = find_syndicate_uplink() + if(H) + qdel(H) + +/datum/mind/proc/make_Traitor() + if(!has_antag_datum(/datum/antagonist/traitor)) + add_antag_datum(/datum/antagonist/traitor) + +/datum/mind/proc/make_Nuke() + if(!(src in SSticker.mode.syndicates)) + SSticker.mode.syndicates += src + SSticker.mode.update_synd_icons_added(src) + if(SSticker.mode.syndicates.len==1) + SSticker.mode.prepare_syndicate_leader(src) + else + current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" + special_role = SPECIAL_ROLE_NUKEOPS + assigned_role = SPECIAL_ROLE_NUKEOPS + to_chat(current, "You are a [syndicate_name()] agent!") + SSticker.mode.forge_syndicate_objectives(src) + SSticker.mode.greet_syndicate(src) + + current.loc = get_turf(locate("landmark*Syndicate-Spawn")) + + var/mob/living/carbon/human/H = current + qdel(H.belt) + qdel(H.back) + qdel(H.l_ear) + qdel(H.r_ear) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_pda) + qdel(H.wear_suit) + qdel(H.w_uniform) + + SSticker.mode.equip_syndicate(current) + +/datum/mind/proc/make_Vampire() + if(!(src in SSticker.mode.vampires)) + SSticker.mode.vampires += src + SSticker.mode.grant_vampire_powers(current) + special_role = SPECIAL_ROLE_VAMPIRE + SSticker.mode.forge_vampire_objectives(src) + SSticker.mode.greet_vampire(src) + SSticker.mode.update_vampire_icons_added(src) + +/datum/mind/proc/make_Changeling() + if(!(src in SSticker.mode.changelings)) + SSticker.mode.changelings += src + SSticker.mode.grant_changeling_powers(current) + special_role = SPECIAL_ROLE_CHANGELING + SSticker.mode.forge_changeling_objectives(src) + SSticker.mode.greet_changeling(src) + SSticker.mode.update_change_icons_added(src) + +/datum/mind/proc/make_Overmind() + if(!(src in SSticker.mode.blob_overminds)) + SSticker.mode.blob_overminds += src + special_role = SPECIAL_ROLE_BLOB_OVERMIND + +/datum/mind/proc/make_Wizard() + if(!(src in SSticker.mode.wizards)) + SSticker.mode.wizards += src + special_role = SPECIAL_ROLE_WIZARD + assigned_role = SPECIAL_ROLE_WIZARD + //ticker.mode.learn_basic_spells(current) + if(!GLOB.wizardstart.len) + current.loc = pick(GLOB.latejoin) + to_chat(current, "HOT INSERTION, GO GO GO") + else + current.loc = pick(GLOB.wizardstart) + + SSticker.mode.equip_wizard(current) + for(var/obj/item/spellbook/S in current.contents) + S.op = 0 + SSticker.mode.name_wizard(current) + SSticker.mode.forge_wizard_objectives(src) + SSticker.mode.greet_wizard(src) + SSticker.mode.update_wiz_icons_added(src) + +/datum/mind/proc/make_Rev() + if(SSticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries + if(valid_head) + for(var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]." + objectives += rev_obj + SSticker.mode.greet_revolutionary(src,0) + SSticker.mode.head_revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = SPECIAL_ROLE_HEAD_REV + + SSticker.mode.forge_revolutionary_objectives(src) + SSticker.mode.greet_revolutionary(src,0) + + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + qdel(flash) + take_uplink() + var/fail = 0 +// fail |= !ticker.mode.equip_traitor(current, 1) + fail |= !SSticker.mode.equip_revolutionary(current) + +/datum/mind/proc/make_Abductor() + var/role = alert("Abductor Role ?","Role","Agent","Scientist") + var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4) + var/teleport = alert("Teleport to ship ?","Teleport","Yes","No") + + if(!role || !team || !teleport) + return + + if(!ishuman(current)) + return + + SSticker.mode.abductors |= src + + var/datum/objective/stay_hidden/hidden_obj = new + hidden_obj.owner = src + objectives += hidden_obj + + var/datum/objective/experiment/O = new + O.owner = src + objectives += O + + var/mob/living/carbon/human/H = current + + H.set_species(/datum/species/abductor) + var/datum/species/abductor/S = H.dna.species + + if(role == "Scientist") + S.scientist = TRUE + + S.team = team + + var/list/obj/effect/landmark/abductor/agent_landmarks = new + var/list/obj/effect/landmark/abductor/scientist_landmarks = new + agent_landmarks.len = 4 + scientist_landmarks.len = 4 + for(var/obj/effect/landmark/abductor/A in GLOB.landmarks_list) + if(istype(A, /obj/effect/landmark/abductor/agent)) + agent_landmarks[text2num(A.team)] = A + else if(istype(A, /obj/effect/landmark/abductor/scientist)) + scientist_landmarks[text2num(A.team)] = A + + var/obj/effect/landmark/L + if(teleport == "Yes") + switch(role) + if("Agent") + L = agent_landmarks[team] + if("Scientist") + L = agent_landmarks[team] + H.forceMove(L.loc) + + +// check whether this mind's mob has been brigged for the given duration +// have to call this periodically for the duration to work properly +/datum/mind/proc/is_brigged(duration) + var/turf/T = current.loc + if(!istype(T)) + brigged_since = -1 + return 0 + + var/is_currently_brigged = current.is_in_brig() + if(!is_currently_brigged) + brigged_since = -1 + return 0 + + if(brigged_since == -1) + brigged_since = world.time + + return (duration <= world.time - brigged_since) + +/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S) + spell_list += S + S.action.Grant(current) + +/datum/mind/proc/RemoveSpell(obj/effect/proc_holder/spell/spell) //To remove a specific spell from a mind + if(!spell) + return + for(var/obj/effect/proc_holder/spell/S in spell_list) + if(istype(S, spell)) + qdel(S) + spell_list -= S + +/datum/mind/proc/transfer_actions(mob/living/new_character) + if(current && current.actions) + for(var/datum/action/A in current.actions) + A.Grant(new_character) + transfer_mindbound_actions(new_character) + +/datum/mind/proc/transfer_mindbound_actions(mob/living/new_character) + for(var/X in spell_list) + var/obj/effect/proc_holder/spell/S = X + S.action.Grant(new_character) + +/datum/mind/proc/disrupt_spells(delay, list/exceptions = New()) + for(var/X in spell_list) + var/obj/effect/proc_holder/spell/S = X + for(var/type in exceptions) + if(istype(S, type)) + continue + S.charge_counter = delay + spawn(0) + S.start_recharge() + S.updateButtonIcon() + +/datum/mind/proc/get_ghost(even_if_they_cant_reenter) + for(var/mob/dead/observer/G in GLOB.dead_mob_list) + if(G.mind == src) + if(G.can_reenter_corpse || even_if_they_cant_reenter) + return G + break + +/datum/mind/proc/grab_ghost(force) + var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force) + . = G + if(G) + G.reenter_corpse() + + +/datum/mind/proc/make_zealot(mob/living/carbon/human/missionary, convert_duration = 6000, team_color = "red") + + zealot_master = missionary + + var/list/implanters + if(!(missionary.mind in SSticker.mode.implanter)) + SSticker.mode.implanter[missionary.mind] = list() + implanters = SSticker.mode.implanter[missionary.mind] + implanters.Add(src) + SSticker.mode.implanted.Add(src) + SSticker.mode.implanted[src] = missionary.mind + SSticker.mode.implanter[missionary.mind] = implanters + SSticker.mode.traitors += src + + + var/datum/objective/protect/zealot_objective = new + zealot_objective.target = missionary.mind + zealot_objective.owner = src + zealot_objective.explanation_text = "Obey every order from and protect [missionary.real_name], the [missionary.mind.assigned_role == missionary.mind.special_role ? (missionary.mind.special_role) : (missionary.mind.assigned_role)]." + objectives += zealot_objective + add_antag_datum(/datum/antagonist/mindslave) + + var/datum/antagonist/traitor/T = missionary.mind.has_antag_datum(/datum/antagonist) + T.update_traitor_icons_added(missionary.mind) + + to_chat(current, "You're now a loyal zealot of [missionary.name]! You now must lay down your life to protect [missionary.p_them()] and assist in [missionary.p_their()] goals at any cost.") + + var/datum/mindslaves/slaved = missionary.mind.som + som = slaved + slaved.serv += current + slaved.add_serv_hud(missionary.mind, "master") //handles master servent icons + slaved.add_serv_hud(src, "mindslave") + + var/obj/item/clothing/under/jumpsuit = null + if(ishuman(current)) //only bother with the jumpsuit stuff if we are a human type, since we won't have the slot otherwise + var/mob/living/carbon/human/H = current + if(H.w_uniform) + jumpsuit = H.w_uniform + jumpsuit.color = team_color + H.update_inv_w_uniform(0,0) + + add_attack_logs(missionary, current, "Converted to a zealot for [convert_duration/600] minutes") + addtimer(CALLBACK(src, .proc/remove_zealot, jumpsuit), convert_duration) //deconverts after the timer expires + return 1 + +/datum/mind/proc/remove_zealot(obj/item/clothing/under/jumpsuit = null) + if(!zealot_master) //if they aren't a zealot, we can't remove their zealot status, obviously. don't bother with the rest so we don't confuse them with the messages + return + remove_antag_datum(/datum/antagonist/mindslave) + add_attack_logs(zealot_master, current, "Lost control of zealot") + zealot_master = null + + if(jumpsuit) + jumpsuit.color = initial(jumpsuit.color) //reset the jumpsuit no matter where our mind is + if(ishuman(current)) //but only try updating us if we are still a human type since it is a human proc + var/mob/living/carbon/human/H = current + H.update_inv_w_uniform(0,0) + + to_chat(current, "You seem to have forgotten the events of the past 10 minutes or so, and your head aches a bit as if someone beat it savagely with a stick.") + to_chat(current, "This means you don't remember who you were working for or what you were doing.") + +/datum/mind/proc/is_revivable() //Note, this ONLY checks the mind. + if(damnation_type) + return FALSE + return TRUE + +// returns a mob to message to produce something visible for the target mind +/datum/mind/proc/messageable_mob() + if(!QDELETED(current) && current.client) + return current + else + return get_ghost(even_if_they_cant_reenter = TRUE) + +//Initialisation procs +/mob/proc/mind_initialize() + if(mind) + mind.key = key + else + mind = new /datum/mind(key) + if(SSticker) + SSticker.minds += mind + else + error("mind_initialize(): No ticker ready yet! Please inform Carn") + if(!mind.name) + mind.name = real_name + mind.current = src + +//HUMAN +/mob/living/carbon/human/mind_initialize() + ..() + if(!mind.assigned_role) + mind.assigned_role = "Civilian" //defualt + +/mob/proc/sync_mind() + mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) + mind.active = 1 //indicates that the mind is currently synced with a client + +//slime +/mob/living/simple_animal/slime/mind_initialize() + ..() + mind.assigned_role = "slime" + +//XENO +/mob/living/carbon/alien/mind_initialize() + ..() + mind.assigned_role = "Alien" + //XENO HUMANOID +/mob/living/carbon/alien/humanoid/queen/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_QUEEN + +/mob/living/carbon/alien/humanoid/hunter/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_HUNTER + +/mob/living/carbon/alien/humanoid/drone/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_DRONE + +/mob/living/carbon/alien/humanoid/sentinel/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_SENTINEL + //XENO LARVA +/mob/living/carbon/alien/larva/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_LARVA + +//AI +/mob/living/silicon/ai/mind_initialize() + ..() + mind.assigned_role = "AI" + +//BORG +/mob/living/silicon/robot/mind_initialize() + ..() + mind.assigned_role = "Cyborg" + +//PAI +/mob/living/silicon/pai/mind_initialize() + ..() + mind.assigned_role = "pAI" + mind.special_role = null + +//BLOB +/mob/camera/overmind/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_BLOB + +//Animals +/mob/living/simple_animal/mind_initialize() + ..() + mind.assigned_role = "Animal" + +/mob/living/simple_animal/pet/dog/corgi/mind_initialize() + ..() + mind.assigned_role = "Corgi" + +/mob/living/simple_animal/shade/mind_initialize() + ..() + mind.assigned_role = "Shade" + +/mob/living/simple_animal/construct/builder/mind_initialize() + ..() + mind.assigned_role = "Artificer" + mind.special_role = SPECIAL_ROLE_CULTIST + +/mob/living/simple_animal/construct/wraith/mind_initialize() + ..() + mind.assigned_role = "Wraith" + mind.special_role = SPECIAL_ROLE_CULTIST + +/mob/living/simple_animal/construct/armoured/mind_initialize() + ..() + mind.assigned_role = "Juggernaut" + mind.special_role = SPECIAL_ROLE_CULTIST diff --git a/code/datums/mixed.dm b/code/datums/mixed.dm index a40a9f9a34d..a5ff38a26b0 100644 --- a/code/datums/mixed.dm +++ b/code/datums/mixed.dm @@ -1,40 +1,40 @@ -/datum/data - var/name = "data" - var/size = 1.0 - - -/datum/data/function - name = "function" - size = 2.0 - - -/datum/data/function/data_control - name = "data control" - - -/datum/data/function/id_changer - name = "id changer" - - -/datum/data/record - name = "record" - size = 5.0 - var/list/fields = list( ) - -/datum/data/record/Destroy() - if(src in data_core.medical) - data_core.medical -= src - if(src in data_core.security) - data_core.security -= src - if(src in data_core.general) - data_core.general -= src - if(src in data_core.locked) - data_core.locked -= src - return ..() - -/datum/data/text - name = "text" - var/data = null - -/datum/debug - var/list/debuglist +/datum/data + var/name = "data" + var/size = 1.0 + + +/datum/data/function + name = "function" + size = 2.0 + + +/datum/data/function/data_control + name = "data control" + + +/datum/data/function/id_changer + name = "id changer" + + +/datum/data/record + name = "record" + size = 5.0 + var/list/fields = list( ) + +/datum/data/record/Destroy() + if(src in GLOB.data_core.medical) + GLOB.data_core.medical -= src + if(src in GLOB.data_core.security) + GLOB.data_core.security -= src + if(src in GLOB.data_core.general) + GLOB.data_core.general -= src + if(src in GLOB.data_core.locked) + GLOB.data_core.locked -= src + return ..() + +/datum/data/text + name = "text" + var/data = null + +/datum/debug + var/list/debuglist diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index 31c20315508..4d870e1296c 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -22,5 +22,4 @@ /mutable_appearance/clean/New() . = ..() alpha = 255 - opacity = 1 - transform = null \ No newline at end of file + transform = null diff --git a/code/datums/outfits/outfit.dm b/code/datums/outfits/outfit.dm index db5cca6e9ec..416c4f9c02d 100644 --- a/code/datums/outfits/outfit.dm +++ b/code/datums/outfits/outfit.dm @@ -1,5 +1,5 @@ /datum/outfit - var/name = "Naked" + var/name = "SOMEBODY FORGOT TO SET A NAME, NOTIFY A CODER" var/collect_not_del = FALSE var/uniform = null @@ -33,6 +33,9 @@ var/can_be_admin_equipped = TRUE // Set to FALSE if your outfit requires runtime parameters +/datum/outfit/naked + name = "Naked" + /datum/outfit/proc/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) //to be overriden for customization depending on client prefs,species etc return diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index 93e4015233b..732c55e3fbe 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -291,7 +291,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), name) + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), name) /datum/outfit/admin/pirate/first_mate name = "Space Pirate First Mate" @@ -375,7 +375,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_clown, access_theatre, access_maint_tunnels), "Tunnel Clown") + apply_to_card(I, H, list(ACCESS_CLOWN, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS), "Tunnel Clown") /datum/outfit/admin/mime_assassin name = "Mime Assassin" @@ -424,7 +424,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_mime, access_theatre, access_maint_tunnels), "Mime") + apply_to_card(I, H, list(ACCESS_MIME, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS), "Mime") H.sec_hud_set_ID() /datum/outfit/admin/greytide @@ -450,7 +450,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Greytide") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Greytide") /datum/outfit/admin/greytide/leader name = "Greytide Leader" @@ -471,7 +471,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Greytide Leader") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Greytide Leader") /datum/outfit/admin/greytide/xeno name = "Greytide Xeno" @@ -499,7 +499,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Legit Xenomorph") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Legit Xenomorph") @@ -534,14 +534,14 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Bard") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Bard") var/obj/item/clothing/ears/headphones/P = r_ear if(istype(P)) P.attack_self(H) // activate them, display musical notes effect /datum/outfit/admin/soviet - + name = "Soviet Generic" gloves = /obj/item/clothing/gloves/combat uniform = /obj/item/clothing/under/soviet back = /obj/item/storage/backpack/satchel @@ -555,7 +555,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), name) + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), name) /datum/outfit/admin/soviet/tourist name = "Soviet Tourist" @@ -719,7 +719,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_trade_sol, access_maint_tunnels, access_external_airlocks), name) + apply_to_card(I, H, list(ACCESS_TRADE_SOL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS), name) /datum/outfit/admin/chrono name = "Chrono Legionnaire" @@ -771,6 +771,7 @@ apply_to_card(I, H, get_all_accesses(), "Space Explorer") /datum/outfit/admin/hardsuit + name = "Hardsuit Generic" back = /obj/item/tank/jetpack/oxygen mask = /obj/item/clothing/mask/breath shoes = /obj/item/clothing/shoes/magboots @@ -823,6 +824,7 @@ /datum/outfit/admin/tournament + name = "Tournament Generic" suit = /obj/item/clothing/suit/armor/vest shoes = /obj/item/clothing/shoes/black head = /obj/item/clothing/head/helmet/thunderdome @@ -907,7 +909,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Survivor") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Survivor") /datum/outfit/admin/masked_killer name = "Masked Killer" @@ -942,7 +944,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Masked Killer", "syndie") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Masked Killer", "syndie") /datum/outfit/admin/singuloth_knight name = "Singuloth Knight" @@ -1048,8 +1050,8 @@ to_chat(H, "You have gained the ability to shapeshift into lesser hellhound form. This is a combat form with different abilities, tough but not invincible. It can regenerate itself over time by resting.") H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/raise_vampires) to_chat(H, "You have gained the ability to Raise Vampires. This extremely powerful AOE ability affects all humans near you. Vampires/thralls are healed. Corpses are raised as vampires. Others are stunned, then brain damaged, then killed.") - H.dna.SetSEState(JUMPBLOCK, 1) - genemutcheck(H, JUMPBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.jumpblock, 1) + genemutcheck(H, GLOB.jumpblock, null, MUTCHK_FORCED) H.update_mutations() H.gene_stability = 100 @@ -1100,7 +1102,7 @@ l_hand = null backpack_contents = list( /obj/item/storage/box/engineer = 1, - /obj/item/clothing/suit/space/hardsuit/shielded/wizard = 1, + /obj/item/clothing/suit/space/hardsuit/shielded/wizard/arch = 1, /obj/item/clothing/shoes/magboots = 1, /obj/item/kitchen/knife/ritual = 1, /obj/item/clothing/suit/wizrobe/red = 1, diff --git a/code/datums/outfits/plasmamen.dm b/code/datums/outfits/plasmamen.dm index c14eafd08f2..94206c0ae3a 100644 --- a/code/datums/outfits/plasmamen.dm +++ b/code/datums/outfits/plasmamen.dm @@ -175,4 +175,10 @@ name = "Blueshield Plasmaman" head = /obj/item/clothing/head/helmet/space/plasmaman/blueshield - uniform = /obj/item/clothing/under/plasmaman/blueshield \ No newline at end of file + uniform = /obj/item/clothing/under/plasmaman/blueshield + +/datum/outfit/plasmaman/wizard + name = "Wizard Plasmaman" + + head = /obj/item/clothing/head/helmet/space/plasmaman/wizard + uniform = /obj/item/clothing/under/plasmaman/wizard diff --git a/code/datums/outfits/vv_outfit.dm b/code/datums/outfits/vv_outfit.dm index 7a07b087ae8..95c91318bb8 100644 --- a/code/datums/outfits/vv_outfit.dm +++ b/code/datums/outfits/vv_outfit.dm @@ -187,4 +187,4 @@ . = ..() stored_access = outfit_data["stored_access"] vv_values = outfit_data["vv_values"] - update_id_name = outfit_data["update_id_name"] \ No newline at end of file + update_id_name = outfit_data["update_id_name"] diff --git a/code/datums/periodic_news.dm b/code/datums/periodic_news.dm index 9a09ec4a7ec..d1cb9fc014d 100644 --- a/code/datums/periodic_news.dm +++ b/code/datums/periodic_news.dm @@ -1,6 +1,7 @@ // This system defines news that will be displayed in the course of a round. // Uses BYOND's type system to put everything into a nice format +// THIS ISNT PROPERLY PATHED AND I AM GOING TO FUCKING SCREAM AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA /datum/news_announcement var round_time // time of the round at which this should be announced, in seconds @@ -114,24 +115,24 @@ the riots. More on this at 6."} round_time = 60 * 60 - -var/global/list/newscaster_standard_feeds = list(/datum/news_announcement/bluespace_research, /datum/news_announcement/lotus_tree, /datum/news_announcement/random_junk, /datum/news_announcement/food_riots) +GLOBAL_LIST_INIT(newscaster_standard_feeds, list(/datum/news_announcement/bluespace_research, /datum/news_announcement/lotus_tree, /datum/news_announcement/random_junk, /datum/news_announcement/food_riots)) proc/process_newscaster() check_for_newscaster_updates(SSticker.mode.newscaster_announcements) -var/global/tmp/announced_news_types = list() +GLOBAL_LIST_EMPTY(announced_news_types) + proc/check_for_newscaster_updates(type) for(var/subtype in subtypesof(type)) var/datum/news_announcement/news = new subtype() - if(news.round_time * 10 <= world.time && !(subtype in announced_news_types)) - announced_news_types += subtype + if(news.round_time * 10 <= world.time && !(subtype in GLOB.announced_news_types)) + GLOB.announced_news_types += subtype announce_newscaster_news(news) proc/announce_newscaster_news(datum/news_announcement/news) var/datum/feed_channel/sendto - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == news.channel_name) sendto = FC break @@ -142,7 +143,7 @@ proc/announce_newscaster_news(datum/news_announcement/news) sendto.author = news.author sendto.locked = 1 sendto.is_admin_channel = 1 - news_network.network_channels += sendto + GLOB.news_network.network_channels += sendto var/datum/feed_message/newMsg = new /datum/feed_message newMsg.author = news.author ? news.author : sendto.author @@ -152,5 +153,5 @@ proc/announce_newscaster_news(datum/news_announcement/news) sendto.messages += newMsg - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) NEWSCASTER.newsAlert(news.channel_name) diff --git a/code/datums/radio.dm b/code/datums/radio.dm index 7670bc76e32..efeebc7c973 100644 --- a/code/datums/radio.dm +++ b/code/datums/radio.dm @@ -1,114 +1,114 @@ - -/datum/radio_frequency - var/frequency as num - var/list/list/obj/devices = list() - -/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null) - var/turf/start_point - if(range) - start_point = get_turf(source) - if(!start_point) - qdel(signal) - return 0 - if(filter) - send_to_filter(source, signal, filter, start_point, range) - send_to_filter(source, signal, RADIO_DEFAULT, start_point, range) - else - //Broadcast the signal to everyone! - for(var/next_filter in devices) - send_to_filter(source, signal, next_filter, start_point, range) - -//Sends a signal to all machines belonging to a given filter. Should be called by post_signal() -/datum/radio_frequency/proc/send_to_filter(obj/source, datum/signal/signal, var/filter, var/turf/start_point = null, var/range = null) - if(range && !start_point) - return - - for(var/obj/device in devices[filter]) - if(device == source) - continue - if(range) - var/turf/end_point = get_turf(device) - if(!end_point) - continue - if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range) - continue - - device.receive_signal(signal, TRANSMISSION_RADIO, frequency) - -/datum/radio_frequency/proc/add_listener(obj/device as obj, var/filter as text|null) - if(!filter) - filter = RADIO_DEFAULT - //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]") - var/list/obj/devices_line = devices[filter] - if(!devices_line) - devices_line = new - devices[filter] = devices_line - devices_line+=device -// var/list/obj/devices_line___ = devices[filter_str] -// var/l = devices_line___.len - //log_admin("DEBUG: devices_line.len=[devices_line.len]") - //log_admin("DEBUG: devices(filter_str).len=[l]") - -/datum/radio_frequency/proc/remove_listener(obj/device) - for(var/devices_filter in devices) - var/list/devices_line = devices[devices_filter] - devices_line-=device - while(null in devices_line) - devices_line -= null - if(devices_line.len==0) - devices -= devices_filter - qdel(devices_line) - -/datum/signal - var/obj/source - - var/transmission_method = 0 //unused at the moment - //0 = wire - //1 = radio transmission - //2 = subspace transmission - - var/list/data = list() - var/encryption - - var/frequency = 0 - -/datum/signal/proc/copy_from(datum/signal/model) - source = model.source - transmission_method = model.transmission_method - data = model.data - encryption = model.encryption - frequency = model.frequency - -/datum/signal/proc/debug_print() - if(source) - . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n" - else - . = "signal = {source = '[source]' ()\n" - for(var/i in data) - . += "data\[\"[i]\"\] = \"[data[i]]\"\n" - if(islist(data[i])) - var/list/L = data[i] - for(var/t in L) - . += "data\[\"[i]\"\] list has: [t]" - -/datum/signal/proc/get_race(mob/M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - . = H.dna.species.name - else if(isbrain(M)) - var/mob/living/carbon/brain/B = M - . = B.get_race() - else if(issilicon(M)) - . = "Artificial Life" - else if(isslime(M)) - . = "Slime" - else if(isbot(M)) - . = "Bot" - else if(isanimal(M)) - . = "Domestic Animal" - else - . = "Unidentifiable" - -//callback used by objects to react to incoming radio signals -/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param) - return null + +/datum/radio_frequency + var/frequency as num + var/list/obj/devices = list() + +/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null) + var/turf/start_point + if(range) + start_point = get_turf(source) + if(!start_point) + qdel(signal) + return 0 + if(filter) + send_to_filter(source, signal, filter, start_point, range) + send_to_filter(source, signal, RADIO_DEFAULT, start_point, range) + else + //Broadcast the signal to everyone! + for(var/next_filter in devices) + send_to_filter(source, signal, next_filter, start_point, range) + +//Sends a signal to all machines belonging to a given filter. Should be called by post_signal() +/datum/radio_frequency/proc/send_to_filter(obj/source, datum/signal/signal, var/filter, var/turf/start_point = null, var/range = null) + if(range && !start_point) + return + + for(var/obj/device in devices[filter]) + if(device == source) + continue + if(range) + var/turf/end_point = get_turf(device) + if(!end_point) + continue + if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range) + continue + + device.receive_signal(signal, TRANSMISSION_RADIO, frequency) + +/datum/radio_frequency/proc/add_listener(obj/device as obj, var/filter as text|null) + if(!filter) + filter = RADIO_DEFAULT + //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]") + var/list/obj/devices_line = devices[filter] + if(!devices_line) + devices_line = new + devices[filter] = devices_line + devices_line+=device +// var/list/obj/devices_line___ = devices[filter_str] +// var/l = devices_line___.len + //log_admin("DEBUG: devices_line.len=[devices_line.len]") + //log_admin("DEBUG: devices(filter_str).len=[l]") + +/datum/radio_frequency/proc/remove_listener(obj/device) + for(var/devices_filter in devices) + var/list/devices_line = devices[devices_filter] + devices_line-=device + while(null in devices_line) + devices_line -= null + if(devices_line.len==0) + devices -= devices_filter + qdel(devices_line) + +/datum/signal + var/obj/source + + var/transmission_method = 0 //unused at the moment + //0 = wire + //1 = radio transmission + //2 = subspace transmission + + var/list/data = list() + var/encryption + + var/frequency = 0 + +/datum/signal/proc/copy_from(datum/signal/model) + source = model.source + transmission_method = model.transmission_method + data = model.data + encryption = model.encryption + frequency = model.frequency + +/datum/signal/proc/debug_print() + if(source) + . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n" + else + . = "signal = {source = '[source]' ()\n" + for(var/i in data) + . += "data\[\"[i]\"\] = \"[data[i]]\"\n" + if(islist(data[i])) + var/list/L = data[i] + for(var/t in L) + . += "data\[\"[i]\"\] list has: [t]" + +/datum/signal/proc/get_race(mob/M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + . = H.dna.species.name + else if(isbrain(M)) + var/mob/living/carbon/brain/B = M + . = B.get_race() + else if(issilicon(M)) + . = "Artificial Life" + else if(isslime(M)) + . = "Slime" + else if(isbot(M)) + . = "Bot" + else if(isanimal(M)) + . = "Domestic Animal" + else + . = "Unidentifiable" + +//callback used by objects to react to incoming radio signals +/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param) + return null diff --git a/code/datums/recipe.dm b/code/datums/recipe.dm index 99d97e45626..52189fe527e 100644 --- a/code/datums/recipe.dm +++ b/code/datums/recipe.dm @@ -1,131 +1,131 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * - * /datum/recipe by rastaf0 13 apr 2011 * - * * * * * * * * * * * * * * * * * * * * * * * * * * - * This is powerful and flexible recipe system. - * It exists not only for food. - * supports both reagents and objects as prerequisites. - * In order to use this system you have to define a deriative from /datum/recipe - * * reagents are reagents. Acid, milc, booze, etc. - * * items are objects. Fruits, tools, circuit boards. - * * result is type to create as new object - * * time is optional parameter, you shall use in in your machine, - default /datum/recipe/ procs does not rely on this parameter. - * - * Functions you need: - * /datum/recipe/proc/make(var/obj/container as obj) - * Creates result inside container, - * deletes prerequisite reagents, - * transfers reagents from prerequisite objects, - * deletes all prerequisite objects (even not needed for recipe at the moment). - * - * /proc/select_recipe(list/datum/recipe/avaiable_recipes, obj/obj as obj, exact = 1) - * Wonderful function that select suitable recipe for you. - * obj is a machine (or magik hat) with prerequisites, - * exact = 0 forces algorithm to ignore superfluous stuff. - * - * - * Functions you do not need to call directly but could: - * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) - * //1=precisely, 0=insufficiently, -1=superfluous - * - * /datum/recipe/proc/check_items(var/obj/container as obj) - * //1=precisely, 0=insufficiently, -1=superfluous - * - * */ - -/datum/recipe - var/list/reagents // example: = list("berryjuice" = 5) // do not list same reagent twice - var/list/items // example: =list(/obj/item/crowbar, /obj/item/welder) // place /foo/bar before /foo - var/result //example: = /obj/item/reagent_containers/food/snacks/donut - var/time = 100 // 1/10 part of second - var/byproduct // example: = /obj/item/kitchen/mould // byproduct to return, such as a mould or trash - -/datum/recipe/proc/check_reagents(datum/reagents/avail_reagents) //1=precisely, 0=insufficiently, -1=superfluous - . = 1 - for(var/r_r in reagents) - var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) - if(!(abs(aval_r_amnt - reagents[r_r])<0.5)) //if NOT equals - if(aval_r_amnt>reagents[r_r]) - . = -1 - else - return 0 - if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) - return -1 - return . - -/datum/recipe/proc/check_items(obj/container, list/ignored_items = null) //1=precisely, 0=insufficiently, -1=superfluous - . = 1 - var/list/checklist = items ? items.Copy() : list() - for(var/obj/O in container) - if(ignored_items && is_type_in_list(O, ignored_items)) //skip if this is something we are ignoring - continue - if(!items) - return -1 - var/found = 0 - for(var/type in checklist) - if(istype(O,type)) - checklist -= type - found = 1 - break - if(!found) - . = -1 - if(checklist.len) - return 0 - return . - -//general version -/datum/recipe/proc/make(obj/container) - var/obj/result_obj = new result(container) - for(var/obj/O in (container.contents-result_obj)) - O.reagents.trans_to(result_obj, O.reagents.total_volume) - qdel(O) - container.reagents.clear_reagents() - return result_obj - -// food-related -/datum/recipe/proc/make_food(obj/container) - var/obj/result_obj = new result(container) - for(var/obj/O in (container.contents-result_obj)) - if(O.reagents) - O.reagents.del_reagent("nutriment") - O.reagents.update_total() - O.reagents.trans_to(result_obj, O.reagents.total_volume) - qdel(O) - container.reagents.clear_reagents() - return result_obj - -/proc/select_recipe(list/datum/recipe/available_recipes, obj/obj, exact = 1 as num, list/ignored_items = null) - if(!exact) - exact = -1 - var/list/datum/recipe/possible_recipes = new - for(var/datum/recipe/recipe in available_recipes) - if(recipe.check_reagents(obj.reagents) == exact && recipe.check_items(obj, ignored_items) == exact) - possible_recipes += recipe - if(possible_recipes.len == 0) - return null - else if(possible_recipes.len == 1) - return possible_recipes[1] - else //okay, let's select the most complicated recipe - var/r_count = 0 - var/i_count = 0 - . = possible_recipes[1] - for(var/datum/recipe/recipe in possible_recipes) - var/N_i = (recipe.items)?(recipe.items.len):0 - var/N_r = (recipe.reagents)?(recipe.reagents.len):0 - if(N_i > i_count || (N_i== i_count && N_r > r_count )) - r_count = N_r - i_count = N_i - . = recipe - return . - -/datum/recipe/proc/get_byproduct() - if(byproduct) - return byproduct - else - return null - -/datum/recipe/proc/count_n_items() - var/count = 0 - if(items && items.len) - count += items.len - return count \ No newline at end of file +/* * * * * * * * * * * * * * * * * * * * * * * * * * + * /datum/recipe by rastaf0 13 apr 2011 * + * * * * * * * * * * * * * * * * * * * * * * * * * * + * This is powerful and flexible recipe system. + * It exists not only for food. + * supports both reagents and objects as prerequisites. + * In order to use this system you have to define a deriative from /datum/recipe + * * reagents are reagents. Acid, milc, booze, etc. + * * items are objects. Fruits, tools, circuit boards. + * * result is type to create as new object + * * time is optional parameter, you shall use in in your machine, + default /datum/recipe/ procs does not rely on this parameter. + * + * Functions you need: + * /datum/recipe/proc/make(var/obj/container as obj) + * Creates result inside container, + * deletes prerequisite reagents, + * transfers reagents from prerequisite objects, + * deletes all prerequisite objects (even not needed for recipe at the moment). + * + * /proc/select_recipe(list/datum/recipe/avaiable_recipes, obj/obj as obj, exact = 1) + * Wonderful function that select suitable recipe for you. + * obj is a machine (or magik hat) with prerequisites, + * exact = 0 forces algorithm to ignore superfluous stuff. + * + * + * Functions you do not need to call directly but could: + * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) + * //1=precisely, 0=insufficiently, -1=superfluous + * + * /datum/recipe/proc/check_items(var/obj/container as obj) + * //1=precisely, 0=insufficiently, -1=superfluous + * + * */ + +/datum/recipe + var/list/reagents // example: = list("berryjuice" = 5) // do not list same reagent twice + var/list/items // example: =list(/obj/item/crowbar, /obj/item/welder) // place /foo/bar before /foo + var/result //example: = /obj/item/reagent_containers/food/snacks/donut + var/time = 100 // 1/10 part of second + var/byproduct // example: = /obj/item/kitchen/mould // byproduct to return, such as a mould or trash + +/datum/recipe/proc/check_reagents(datum/reagents/avail_reagents) //1=precisely, 0=insufficiently, -1=superfluous + . = 1 + for(var/r_r in reagents) + var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) + if(!(abs(aval_r_amnt - reagents[r_r])<0.5)) //if NOT equals + if(aval_r_amnt>reagents[r_r]) + . = -1 + else + return 0 + if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) + return -1 + return . + +/datum/recipe/proc/check_items(obj/container, list/ignored_items = null) //1=precisely, 0=insufficiently, -1=superfluous + . = 1 + var/list/checklist = items ? items.Copy() : list() + for(var/obj/O in container) + if(ignored_items && is_type_in_list(O, ignored_items)) //skip if this is something we are ignoring + continue + if(!items) + return -1 + var/found = 0 + for(var/type in checklist) + if(istype(O,type)) + checklist -= type + found = 1 + break + if(!found) + . = -1 + if(checklist.len) + return 0 + return . + +//general version +/datum/recipe/proc/make(obj/container) + var/obj/result_obj = new result(container) + for(var/obj/O in (container.contents-result_obj)) + O.reagents.trans_to(result_obj, O.reagents.total_volume) + qdel(O) + container.reagents.clear_reagents() + return result_obj + +// food-related +/datum/recipe/proc/make_food(obj/container) + var/obj/result_obj = new result(container) + for(var/obj/O in (container.contents-result_obj)) + if(O.reagents) + O.reagents.del_reagent("nutriment") + O.reagents.update_total() + O.reagents.trans_to(result_obj, O.reagents.total_volume) + qdel(O) + container.reagents.clear_reagents() + return result_obj + +/proc/select_recipe(list/datum/recipe/available_recipes, obj/obj, exact = 1 as num, list/ignored_items = null) + if(!exact) + exact = -1 + var/list/datum/recipe/possible_recipes = new + for(var/datum/recipe/recipe in available_recipes) + if(recipe.check_reagents(obj.reagents) == exact && recipe.check_items(obj, ignored_items) == exact) + possible_recipes += recipe + if(possible_recipes.len == 0) + return null + else if(possible_recipes.len == 1) + return possible_recipes[1] + else //okay, let's select the most complicated recipe + var/r_count = 0 + var/i_count = 0 + . = possible_recipes[1] + for(var/datum/recipe/recipe in possible_recipes) + var/N_i = (recipe.items)?(recipe.items.len):0 + var/N_r = (recipe.reagents)?(recipe.reagents.len):0 + if(N_i > i_count || (N_i== i_count && N_r > r_count )) + r_count = N_r + i_count = N_i + . = recipe + return . + +/datum/recipe/proc/get_byproduct() + if(byproduct) + return byproduct + else + return null + +/datum/recipe/proc/count_n_items() + var/count = 0 + if(items && items.len) + count += items.len + return count diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index bb80fe84d01..f826b6ff0d2 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -217,4 +217,4 @@ datum/map_template/ruin/lavaland/ash_walker id = "puzzle" description = "Mystery to be solved." suffix = "lavaland_surface_puzzle.dmm" - cost = 5 \ No newline at end of file + cost = 5 diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm index 5cc76721377..a651edbc79c 100644 --- a/code/datums/spawners_menu.dm +++ b/code/datums/spawners_menu.dm @@ -6,7 +6,7 @@ qdel(src) owner = new_owner -/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = FALSE, datum/topic_state/state = ghost_state, datum/nanoui/master_ui = null) +/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = FALSE, datum/topic_state/state = GLOB.ghost_state, datum/nanoui/master_ui = null) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "spawners_menu.tmpl", "Spawners Menu", 700, 600, master_ui, state = state) diff --git a/code/datums/spell.dm b/code/datums/spell.dm index f19f8100553..6b114971258 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -1,499 +1,499 @@ -#define TARGET_CLOSEST 1 -#define TARGET_RANDOM 2 - -/obj/effect/proc_holder - var/panel = "Debug"//What panel the proc holder needs to go on. - var/active = FALSE //Used by toggle based abilities. - var/ranged_mousepointer - var/mob/living/ranged_ability_user - -/obj/effect/proc_holder/singularity_act() - return - -/obj/effect/proc_holder/singularity_pull() - return - -var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin verb for now - -/obj/effect/proc_holder/proc/InterceptClickOn(mob/living/user, params, atom/A) - if(user.ranged_ability != src) - to_chat(user, "[user.ranged_ability.name] has been disabled.") - user.ranged_ability.remove_ranged_ability(user) - return TRUE //TRUE for failed, FALSE for passed. - user.changeNext_click(CLICK_CD_CLICK_ABILITY) - user.face_atom(A) - return FALSE - -/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, var/msg) - if(!user || !user.client) - return - if(user.ranged_ability && user.ranged_ability != src) - to_chat(user, "[user.ranged_ability.name] has been replaced by [name].") - user.ranged_ability.remove_ranged_ability(user) - user.ranged_ability = src - ranged_ability_user = user - user.client.click_intercept = user.ranged_ability - add_mousepointer(user.client) - active = TRUE - if(msg) - to_chat(user, msg) - update_icon() - -/obj/effect/proc_holder/proc/add_mousepointer(client/C) - if(C && ranged_mousepointer && C.mouse_pointer_icon == initial(C.mouse_pointer_icon)) - C.mouse_pointer_icon = ranged_mousepointer - -/obj/effect/proc_holder/proc/remove_mousepointer(client/C) - if(C && ranged_mousepointer && C.mouse_pointer_icon == ranged_mousepointer) - C.mouse_pointer_icon = initial(C.mouse_pointer_icon) - -/obj/effect/proc_holder/proc/remove_ranged_ability(mob/living/user, var/msg) - if(!user || !user.client || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability - return - user.ranged_ability = null - ranged_ability_user = null - user.client.click_intercept = null - remove_mousepointer(user.client) - active = FALSE - if(msg) - to_chat(user, msg) - update_icon() - -/obj/effect/proc_holder/spell - name = "Spell" - desc = "A wizard spell" - panel = "Spells"//What panel the proc holder needs to go on. - density = 0 - opacity = 0 - - var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? - - var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that - - var/charge_max = 100 //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges" - var/starts_charged = TRUE //Does this spell start ready to go? - var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges" - var/still_recharging_msg = "The spell is still recharging." - - var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" - var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used - - var/ghost = 0 // Skip life check. - var/clothes_req = 1 //see if it requires clothes - var/human_req = 0 //spell can only be cast by humans - var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities - var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells - var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell - var/invocation_emote_self = null - var/invocation_type = "none" //can be none, whisper and shout - var/range = 7 //the range of the spell; outer radius for aoe spells - var/message = "" //whatever it says to the guy affected by it - var/selection_type = "view" //can be "range" or "view" - var/spell_level = 0 //if a spell can be taken multiple times, this raises - var/level_max = 4 //The max possible level_max is 4 - var/cooldown_min = 0 //This defines what spell quickened four timeshas as a cooldown. Make sure to set this for every spell - - var/overlay = 0 - var/overlay_icon = 'icons/obj/wizard.dmi' - var/overlay_icon_state = "spell" - var/overlay_lifespan = 0 - - var/sparks_spread = 0 - var/sparks_amt = 0 //cropped at 10 - var/smoke_spread = 0 //1 - harmless, 2 - harmful - var/smoke_amt = 0 //cropped at 10 - - var/critfailchance = 0 - var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2 - - var/datum/action/spell_action/action = null - var/action_icon = 'icons/mob/actions/actions.dmi' - var/action_icon_state = "spell_default" - var/action_background_icon_state = "bg_spell" - var/special_availability_check = 0//Whether the spell needs to bypass the action button's IsAvailable() - - var/sound = null //The sound the spell makes when it is cast - -/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell - if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) - to_chat(user, "You shouldn't have this spell! Something's wrong.") - return 0 - - if(ishuman(user)) - var/mob/living/carbon/human/caster = user - if(caster.remoteview_target) - caster.remoteview_target = null - caster.reset_perspective(0) - return 0 - - if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel - return 0 - - if(!skipcharge) - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - to_chat(user, still_recharging_msg) - return 0 - if("charges") - if(!charge_counter) - to_chat(user, "[name] has no charges left.") - return 0 - - if(!ghost) - if(user.stat && !stat_allowed) - to_chat(user, "You can't cast this spell while incapacitated.") - return 0 - if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled()) - to_chat(user, "Mmmf mrrfff!") - return 0 - - var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list())) - if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check - var/mob/living/carbon/human/H = user - var/obj/item/clothing/robe = H.wear_suit - var/obj/item/clothing/hat = H.head - var/obj/item/clothing/shoes = H.shoes - if(!robe || !hat || !shoes) - to_chat(user, "Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.") - return 0 - if(!robe.magical || !hat.magical || !shoes.magical) - to_chat(user, "Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.") - return 0 - else if(!ishuman(user)) - if(clothes_req || human_req) - to_chat(user, "This spell can only be cast by humans!") - return 0 - if(nonabstract_req && (isbrain(user) || ispAI(user))) - to_chat(user, "This spell can only be cast by physical beings!") - return 0 - - if(!skipcharge) - switch(charge_type) - if("recharge") - charge_counter = 0 //doesn't start recharging until the targets selecting ends - if("charges") - charge_counter-- //returns the charge if the targets selecting fails - if("holdervar") - adjust_var(user, holder_var_type, holder_var_amount) - - if(action) - action.UpdateButtonIcon() - return 1 - -/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount - switch(invocation_type) - if("shout") - if(!user.IsVocal()) - user.custom_emote(1, "makes frantic gestures!") - else - if(prob(50))//Auto-mute? Fuck that noise - user.say(invocation) - else - user.say(replacetext(invocation," ","`")) - if("whisper") - if(prob(50)) - user.whisper(invocation) - else - user.whisper(replacetext(invocation," ","`")) - if("emote") - user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm - -/obj/effect/proc_holder/spell/proc/playMagSound() - playsound(get_turf(usr), sound,50,1) - -/obj/effect/proc_holder/spell/New() - ..() - action = new(src) - still_recharging_msg = "[name] is still recharging." - if(starts_charged) - charge_counter = charge_max - else - start_recharge() - -/obj/effect/proc_holder/spell/Destroy() - QDEL_NULL(action) - return ..() - -/obj/effect/proc_holder/spell/Click() - if(cast_check()) - choose_targets() - return 1 - -/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf - return - -/obj/effect/proc_holder/spell/proc/start_recharge() - if(action) - action.UpdateButtonIcon() - START_PROCESSING(SSfastprocess, src) - -/obj/effect/proc_holder/spell/process() - charge_counter += 2 - if(charge_counter < charge_max) - return - STOP_PROCESSING(SSfastprocess, src) - charge_counter = charge_max - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr) //if recharge is started is important for the trigger spells - before_cast(targets) - invocation() - if(user && user.ckey) - user.create_attack_log("[key_name(user)] cast the spell [name].") - spawn(0) - if(charge_type == "recharge" && recharge) - start_recharge() - - if(sound) - playMagSound() - - if(prob(critfailchance)) - critfail(targets) - else - cast(targets, user = user) - after_cast(targets) - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/before_cast(list/targets) - if(overlay) - for(var/atom/target in targets) - var/location - if(istype(target,/mob/living)) - location = target.loc - else if(istype(target,/turf)) - location = target - var/obj/effect/overlay/spell = new /obj/effect/overlay(location) - spell.icon = overlay_icon - spell.icon_state = overlay_icon_state - spell.anchored = 1 - spell.density = 0 - spawn(overlay_lifespan) - qdel(spell) - -/obj/effect/proc_holder/spell/proc/after_cast(list/targets) - for(var/atom/target in targets) - var/location - if(istype(target,/mob/living)) - location = target.loc - else if(istype(target,/turf)) - location = target - if(istype(target,/mob/living) && message) - to_chat(target, text("[message]")) - if(sparks_spread) - do_sparks(sparks_amt, 0, location) - if(smoke_spread) - if(smoke_spread == 1) - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is - smoke.start() - else if(smoke_spread == 2) - var/datum/effect_system/smoke_spread/bad/smoke = new - smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is - smoke.start() - else if(smoke_spread == 3) - var/datum/effect_system/smoke_spread/sleeping/smoke = new - smoke.set_up(smoke_amt, 0, location) // same here - smoke.start() - -/obj/effect/proc_holder/spell/proc/cast(list/targets, mob/user = usr) - return - -/obj/effect/proc_holder/spell/proc/critfail(list/targets) - return - -/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge - switch(charge_type) - if("recharge") - charge_counter = charge_max - if("charges") - charge_counter++ - if("holdervar") - adjust_var(user, holder_var_type, -holder_var_amount) - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/updateButtonIcon() - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types - switch(type) - if("bruteloss") - target.adjustBruteLoss(amount) - if("fireloss") - target.adjustFireLoss(amount) - if("toxloss") - target.adjustToxLoss(amount) - if("oxyloss") - target.adjustOxyLoss(amount) - if("stunned") - target.AdjustStunned(amount) - if("weakened") - target.AdjustWeakened(amount) - if("paralysis") - target.AdjustParalysis(amount) - else - target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars - return - -/obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob - var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range - var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast - var/include_user = 0 //if it includes usr in the target list - var/random_target = 0 // chooses random viable target instead of asking the caster - var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target - var/humans_only = 0 //for avoiding simple animals and only doing "human" mobs, 0 = all mobs, 1 = humans only - -/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends) - var/inner_radius = -1 //for all your ring spell needs - -/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr) - var/list/targets = list() - - switch(max_targets) - if(0) //unlimited - - if(!humans_only) - for(var/mob/living/target in view_or_range(range, user, selection_type)) - targets += target - else - for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) - targets += target - - if(1) //single target can be picked - if(range < 0) - targets += user - else - var/possible_targets = list() - - if(!humans_only) - for(var/mob/living/M in view_or_range(range, user, selection_type)) - if(!include_user && user == M) - continue - possible_targets += M - else - for(var/mob/living/carbon/human/M in view_or_range(range, user, selection_type)) - if(!include_user && user == M) - continue - possible_targets += M - - //targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets - //Adds a safety check post-input to make sure those targets are actually in range. - var/mob/M - if(!random_target) - M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets - else - switch(random_target_priority) - if(TARGET_RANDOM) - M = pick(possible_targets) - if(TARGET_CLOSEST) - for(var/mob/living/L in possible_targets) - if(M) - if(get_dist(user,L) < get_dist(user,M)) - if(los_check(user,L)) - M = L - else - if(los_check(user,L)) - M = L - if(M in view_or_range(range, user, selection_type)) targets += M - - else - var/list/possible_targets = list() - if(!humans_only) - for(var/mob/living/target in view_or_range(range, user, selection_type)) - possible_targets += target - else - for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) - possible_targets += target - for(var/i=1,i<=max_targets,i++) - if(!possible_targets.len) - break - if(target_ignore_prev) - var/target = pick(possible_targets) - possible_targets -= target - targets += target - else - targets += pick(possible_targets) - - if(!include_user && (user in targets)) - targets -= user - - if(!targets.len) //doesn't waste the spell - revert_cast(user) - return - - perform(targets, user=user) - - return - -/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr) - var/list/targets = list() - - for(var/turf/target in view_or_range(range,user,selection_type)) - if(!(target in view_or_range(inner_radius,user,selection_type))) - targets += target - - if(!targets.len) //doesn't waste the spell - revert_cast() - return - - perform(targets, user=user) - - return - - -/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) - //Checks for obstacles from A to B - var/obj/dummy = new(A.loc) - dummy.pass_flags |= PASSTABLE - for(var/turf/turf in getline(A,B)) - for(var/atom/movable/AM in turf) - if(!AM.CanPass(dummy,turf,1)) - qdel(dummy) - return 0 - qdel(dummy) - return 1 - -/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr) - if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) - return 0 - - if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel - return 0 - - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - return 0 - if("charges") - if(!charge_counter) - return 0 - - if(user.stat && !stat_allowed) - return 0 - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - - if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) - return 0 - - var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list - var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list) - if(clothes_req && !clothcheck && !clothcheck2) //clothes check - var/obj/item/clothing/robe = H.wear_suit - var/obj/item/clothing/hat = H.head - var/obj/item/clothing/shoes = H.shoes - if(!robe || !hat || !shoes) - return 0 - if(!robe.magical || !hat.magical || !shoes.magical) - return 0 - else - if(clothes_req || human_req) - return 0 - if(nonabstract_req && (isbrain(user) || ispAI(user))) - return 0 - return 1 +#define TARGET_CLOSEST 1 +#define TARGET_RANDOM 2 + +/obj/effect/proc_holder + var/panel = "Debug"//What panel the proc holder needs to go on. + var/active = FALSE //Used by toggle based abilities. + var/ranged_mousepointer + var/mob/living/ranged_ability_user + +/obj/effect/proc_holder/singularity_act() + return + +/obj/effect/proc_holder/singularity_pull() + return + +GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) + +/obj/effect/proc_holder/proc/InterceptClickOn(mob/living/user, params, atom/A) + if(user.ranged_ability != src) + to_chat(user, "[user.ranged_ability.name] has been disabled.") + user.ranged_ability.remove_ranged_ability(user) + return TRUE //TRUE for failed, FALSE for passed. + user.changeNext_click(CLICK_CD_CLICK_ABILITY) + user.face_atom(A) + return FALSE + +/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, var/msg) + if(!user || !user.client) + return + if(user.ranged_ability && user.ranged_ability != src) + to_chat(user, "[user.ranged_ability.name] has been replaced by [name].") + user.ranged_ability.remove_ranged_ability(user) + user.ranged_ability = src + ranged_ability_user = user + user.client.click_intercept = user.ranged_ability + add_mousepointer(user.client) + active = TRUE + if(msg) + to_chat(user, msg) + update_icon() + +/obj/effect/proc_holder/proc/add_mousepointer(client/C) + if(C && ranged_mousepointer && C.mouse_pointer_icon == initial(C.mouse_pointer_icon)) + C.mouse_pointer_icon = ranged_mousepointer + +/obj/effect/proc_holder/proc/remove_mousepointer(client/C) + if(C && ranged_mousepointer && C.mouse_pointer_icon == ranged_mousepointer) + C.mouse_pointer_icon = initial(C.mouse_pointer_icon) + +/obj/effect/proc_holder/proc/remove_ranged_ability(mob/living/user, var/msg) + if(!user || !user.client || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability + return + user.ranged_ability = null + ranged_ability_user = null + user.client.click_intercept = null + remove_mousepointer(user.client) + active = FALSE + if(msg) + to_chat(user, msg) + update_icon() + +/obj/effect/proc_holder/spell + name = "Spell" + desc = "A wizard spell" + panel = "Spells"//What panel the proc holder needs to go on. + density = 0 + opacity = 0 + + var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? + + var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that + + var/charge_max = 100 //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges" + var/starts_charged = TRUE //Does this spell start ready to go? + var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges" + var/still_recharging_msg = "The spell is still recharging." + + var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" + var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used + + var/ghost = 0 // Skip life check. + var/clothes_req = 1 //see if it requires clothes + var/human_req = 0 //spell can only be cast by humans + var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities + var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells + var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell + var/invocation_emote_self = null + var/invocation_type = "none" //can be none, whisper and shout + var/range = 7 //the range of the spell; outer radius for aoe spells + var/message = "" //whatever it says to the guy affected by it + var/selection_type = "view" //can be "range" or "view" + var/spell_level = 0 //if a spell can be taken multiple times, this raises + var/level_max = 4 //The max possible level_max is 4 + var/cooldown_min = 0 //This defines what spell quickened four timeshas as a cooldown. Make sure to set this for every spell + + var/overlay = 0 + var/overlay_icon = 'icons/obj/wizard.dmi' + var/overlay_icon_state = "spell" + var/overlay_lifespan = 0 + + var/sparks_spread = 0 + var/sparks_amt = 0 //cropped at 10 + var/smoke_spread = 0 //1 - harmless, 2 - harmful + var/smoke_amt = 0 //cropped at 10 + + var/critfailchance = 0 + var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2 + + var/datum/action/spell_action/action = null + var/action_icon = 'icons/mob/actions/actions.dmi' + var/action_icon_state = "spell_default" + var/action_background_icon_state = "bg_spell" + var/special_availability_check = 0//Whether the spell needs to bypass the action button's IsAvailable() + + var/sound = null //The sound the spell makes when it is cast + +/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell + if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) + to_chat(user, "You shouldn't have this spell! Something's wrong.") + return 0 + + if(ishuman(user)) + var/mob/living/carbon/human/caster = user + if(caster.remoteview_target) + caster.remoteview_target = null + caster.reset_perspective(0) + return 0 + + if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel + return 0 + + if(!skipcharge) + switch(charge_type) + if("recharge") + if(charge_counter < charge_max) + to_chat(user, still_recharging_msg) + return 0 + if("charges") + if(!charge_counter) + to_chat(user, "[name] has no charges left.") + return 0 + + if(!ghost) + if(user.stat && !stat_allowed) + to_chat(user, "You can't cast this spell while incapacitated.") + return 0 + if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled()) + to_chat(user, "Mmmf mrrfff!") + return 0 + + var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list())) + if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check + var/mob/living/carbon/human/H = user + var/obj/item/clothing/robe = H.wear_suit + var/obj/item/clothing/hat = H.head + var/obj/item/clothing/shoes = H.shoes + if(!robe || !hat || !shoes) + to_chat(user, "Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.") + return 0 + if(!robe.magical || !hat.magical || !shoes.magical) + to_chat(user, "Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.") + return 0 + else if(!ishuman(user)) + if(clothes_req || human_req) + to_chat(user, "This spell can only be cast by humans!") + return 0 + if(nonabstract_req && (isbrain(user) || ispAI(user))) + to_chat(user, "This spell can only be cast by physical beings!") + return 0 + + if(!skipcharge) + switch(charge_type) + if("recharge") + charge_counter = 0 //doesn't start recharging until the targets selecting ends + if("charges") + charge_counter-- //returns the charge if the targets selecting fails + if("holdervar") + adjust_var(user, holder_var_type, holder_var_amount) + + if(action) + action.UpdateButtonIcon() + return 1 + +/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount + switch(invocation_type) + if("shout") + if(!user.IsVocal()) + user.custom_emote(1, "makes frantic gestures!") + else + if(prob(50))//Auto-mute? Fuck that noise + user.say(invocation) + else + user.say(replacetext(invocation," ","`")) + if("whisper") + if(prob(50)) + user.whisper(invocation) + else + user.whisper(replacetext(invocation," ","`")) + if("emote") + user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm + +/obj/effect/proc_holder/spell/proc/playMagSound() + playsound(get_turf(usr), sound,50,1) + +/obj/effect/proc_holder/spell/New() + ..() + action = new(src) + still_recharging_msg = "[name] is still recharging." + if(starts_charged) + charge_counter = charge_max + else + start_recharge() + +/obj/effect/proc_holder/spell/Destroy() + QDEL_NULL(action) + return ..() + +/obj/effect/proc_holder/spell/Click() + if(cast_check()) + choose_targets() + return 1 + +/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf + return + +/obj/effect/proc_holder/spell/proc/start_recharge() + if(action) + action.UpdateButtonIcon() + START_PROCESSING(SSfastprocess, src) + +/obj/effect/proc_holder/spell/process() + charge_counter += 2 + if(charge_counter < charge_max) + return + STOP_PROCESSING(SSfastprocess, src) + charge_counter = charge_max + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr) //if recharge is started is important for the trigger spells + before_cast(targets) + invocation() + if(user && user.ckey) + add_attack_logs(user, targets, "cast the spell [name]", ATKLOG_ALL) + spawn(0) + if(charge_type == "recharge" && recharge) + start_recharge() + + if(sound) + playMagSound() + + if(prob(critfailchance)) + critfail(targets) + else + cast(targets, user = user) + after_cast(targets) + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/before_cast(list/targets) + if(overlay) + for(var/atom/target in targets) + var/location + if(istype(target,/mob/living)) + location = target.loc + else if(istype(target,/turf)) + location = target + var/obj/effect/overlay/spell = new /obj/effect/overlay(location) + spell.icon = overlay_icon + spell.icon_state = overlay_icon_state + spell.anchored = 1 + spell.density = 0 + spawn(overlay_lifespan) + qdel(spell) + +/obj/effect/proc_holder/spell/proc/after_cast(list/targets) + for(var/atom/target in targets) + var/location + if(istype(target,/mob/living)) + location = target.loc + else if(istype(target,/turf)) + location = target + if(istype(target,/mob/living) && message) + to_chat(target, text("[message]")) + if(sparks_spread) + do_sparks(sparks_amt, 0, location) + if(smoke_spread) + if(smoke_spread == 1) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is + smoke.start() + else if(smoke_spread == 2) + var/datum/effect_system/smoke_spread/bad/smoke = new + smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is + smoke.start() + else if(smoke_spread == 3) + var/datum/effect_system/smoke_spread/sleeping/smoke = new + smoke.set_up(smoke_amt, 0, location) // same here + smoke.start() + +/obj/effect/proc_holder/spell/proc/cast(list/targets, mob/user = usr) + return + +/obj/effect/proc_holder/spell/proc/critfail(list/targets) + return + +/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge + switch(charge_type) + if("recharge") + charge_counter = charge_max + if("charges") + charge_counter++ + if("holdervar") + adjust_var(user, holder_var_type, -holder_var_amount) + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/updateButtonIcon() + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types + switch(type) + if("bruteloss") + target.adjustBruteLoss(amount) + if("fireloss") + target.adjustFireLoss(amount) + if("toxloss") + target.adjustToxLoss(amount) + if("oxyloss") + target.adjustOxyLoss(amount) + if("stunned") + target.AdjustStunned(amount) + if("weakened") + target.AdjustWeakened(amount) + if("paralysis") + target.AdjustParalysis(amount) + else + target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars + return + +/obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob + var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range + var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast + var/include_user = 0 //if it includes usr in the target list + var/random_target = 0 // chooses random viable target instead of asking the caster + var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target + var/humans_only = 0 //for avoiding simple animals and only doing "human" mobs, 0 = all mobs, 1 = humans only + +/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends) + var/inner_radius = -1 //for all your ring spell needs + +/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr) + var/list/targets = list() + + switch(max_targets) + if(0) //unlimited + + if(!humans_only) + for(var/mob/living/target in view_or_range(range, user, selection_type)) + targets += target + else + for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) + targets += target + + if(1) //single target can be picked + if(range < 0) + targets += user + else + var/possible_targets = list() + + if(!humans_only) + for(var/mob/living/M in view_or_range(range, user, selection_type)) + if(!include_user && user == M) + continue + possible_targets += M + else + for(var/mob/living/carbon/human/M in view_or_range(range, user, selection_type)) + if(!include_user && user == M) + continue + possible_targets += M + + //targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets + //Adds a safety check post-input to make sure those targets are actually in range. + var/mob/M + if(!random_target) + M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets + else + switch(random_target_priority) + if(TARGET_RANDOM) + M = pick(possible_targets) + if(TARGET_CLOSEST) + for(var/mob/living/L in possible_targets) + if(M) + if(get_dist(user,L) < get_dist(user,M)) + if(los_check(user,L)) + M = L + else + if(los_check(user,L)) + M = L + if(M in view_or_range(range, user, selection_type)) targets += M + + else + var/list/possible_targets = list() + if(!humans_only) + for(var/mob/living/target in view_or_range(range, user, selection_type)) + possible_targets += target + else + for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) + possible_targets += target + for(var/i=1,i<=max_targets,i++) + if(!possible_targets.len) + break + if(target_ignore_prev) + var/target = pick(possible_targets) + possible_targets -= target + targets += target + else + targets += pick(possible_targets) + + if(!include_user && (user in targets)) + targets -= user + + if(!targets.len) //doesn't waste the spell + revert_cast(user) + return + + perform(targets, user=user) + + return + +/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr) + var/list/targets = list() + + for(var/turf/target in view_or_range(range,user,selection_type)) + if(!(target in view_or_range(inner_radius,user,selection_type))) + targets += target + + if(!targets.len) //doesn't waste the spell + revert_cast() + return + + perform(targets, user=user) + + return + + +/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) + //Checks for obstacles from A to B + var/obj/dummy = new(A.loc) + dummy.pass_flags |= PASSTABLE + for(var/turf/turf in getline(A,B)) + for(var/atom/movable/AM in turf) + if(!AM.CanPass(dummy,turf,1)) + qdel(dummy) + return 0 + qdel(dummy) + return 1 + +/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr) + if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) + return 0 + + if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel + return 0 + + switch(charge_type) + if("recharge") + if(charge_counter < charge_max) + return 0 + if("charges") + if(!charge_counter) + return 0 + + if(user.stat && !stat_allowed) + return 0 + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + + if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) + return 0 + + var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list + var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list) + if(clothes_req && !clothcheck && !clothcheck2) //clothes check + var/obj/item/clothing/robe = H.wear_suit + var/obj/item/clothing/hat = H.head + var/obj/item/clothing/shoes = H.shoes + if(!robe || !hat || !shoes) + return 0 + if(!robe.magical || !hat.magical || !shoes.magical) + return 0 + else + if(clothes_req || human_req) + return 0 + if(nonabstract_req && (isbrain(user) || ispAI(user))) + return 0 + return 1 diff --git a/code/datums/spells/area_teleport.dm b/code/datums/spells/area_teleport.dm index 4aa7eafbed6..0d24a984014 100644 --- a/code/datums/spells/area_teleport.dm +++ b/code/datums/spells/area_teleport.dm @@ -1,98 +1,100 @@ -/obj/effect/proc_holder/spell/targeted/area_teleport - name = "Area teleport" - desc = "This spell teleports you to a type of area of your selection." - nonabstract_req = 1 - - var/randomise_selection = 0 //if it lets the usr choose the teleport loc or picks it from the list - var/invocation_area = 1 //if the invocation appends the selected area - - var/sound1 = 'sound/weapons/zapbang.ogg' - var/sound2 = 'sound/weapons/zapbang.ogg' - -/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr) - var/thearea = before_cast(targets) - if(!thearea || !cast_check(1)) - revert_cast() - return - invocation(thearea) - spawn(0) - if(charge_type == "recharge" && recharge) - start_recharge() - cast(targets,thearea) - after_cast(targets) - -/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets) - var/A = null - - if(!randomise_selection) - A = input("Area to teleport to", "Teleport", A) as null|anything in teleportlocs - else - A = pick(teleportlocs) - - if(!A) - return - - var/area/thearea = teleportlocs[A] - - if(thearea.tele_proof && !istype(thearea, /area/wizard_station)) - to_chat(usr, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.") - return - - return thearea - -/obj/effect/proc_holder/spell/targeted/area_teleport/cast(list/targets,area/thearea,mob/living/user = usr) - playsound(get_turf(user), sound1, 50,1) - for(var/mob/living/target in targets) - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - if(!L.len) - to_chat(usr, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") - return - - if(target && target.buckled) - target.buckled.unbuckle_mob(target, force = TRUE) - - if(target && target.has_buckled_mobs()) - target.unbuckle_all_mobs(force = TRUE) - - var/list/tempL = L - var/attempt = null - var/success = 0 - while(tempL.len) - attempt = pick(tempL) - success = target.Move(attempt) - if(!success) - tempL.Remove(attempt) - else - break - - if(!success) - target.forceMove(pick(L)) - playsound(get_turf(user), sound2, 50,1) - - return - -/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null) - if(!invocation_area || !chosenarea) - ..() - else - switch(invocation_type) - if("shout") - usr.say("[invocation] [uppertext(chosenarea.name)]") - if(usr.gender==MALE) - playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) - else - playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) - if("whisper") - usr.whisper("[invocation] [uppertext(chosenarea.name)]") - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/area_teleport + name = "Area teleport" + desc = "This spell teleports you to a type of area of your selection." + nonabstract_req = 1 + + var/randomise_selection = 0 //if it lets the usr choose the teleport loc or picks it from the list + var/invocation_area = 1 //if the invocation appends the selected area + + var/sound1 = 'sound/weapons/zapbang.ogg' + var/sound2 = 'sound/weapons/zapbang.ogg' + +/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr) + var/thearea = before_cast(targets) + if(!thearea || !cast_check(1)) + revert_cast() + return + invocation(thearea) + spawn(0) + if(charge_type == "recharge" && recharge) + start_recharge() + cast(targets,thearea) + after_cast(targets) + +/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets) + var/A = null + + if(!randomise_selection) + A = input("Area to teleport to", "Teleport", A) as null|anything in GLOB.teleportlocs + else + A = pick(GLOB.teleportlocs) + + if(!A) + return + + var/area/thearea = GLOB.teleportlocs[A] + + if(thearea.tele_proof && !istype(thearea, /area/wizard_station)) + to_chat(usr, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.") + return + + return thearea + +/obj/effect/proc_holder/spell/targeted/area_teleport/cast(list/targets,area/thearea,mob/living/user = usr) + playsound(get_turf(user), sound1, 50,1) + for(var/mob/living/target in targets) + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + if(!T.density) + var/clear = 1 + for(var/obj/O in T) + if(O.density) + clear = 0 + break + if(clear) + L+=T + + if(!L.len) + to_chat(usr, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") + return + + if(target && target.buckled) + target.buckled.unbuckle_mob(target, force = TRUE) + + if(target && target.has_buckled_mobs()) + target.unbuckle_all_mobs(force = TRUE) + + var/list/tempL = L + var/attempt = null + var/success = 0 + while(tempL.len) + attempt = pick(tempL) + success = target.Move(attempt) + if(!success) + tempL.Remove(attempt) + else + break + + if(!success) + target.forceMove(pick(L)) + playsound(get_turf(user), sound2, 50,1) + + user.update_action_buttons_icon() //Update action buttons as some spells might now be castable + + return + +/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null) + if(!invocation_area || !chosenarea) + ..() + else + switch(invocation_type) + if("shout") + usr.say("[invocation] [uppertext(chosenarea.name)]") + if(usr.gender==MALE) + playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) + else + playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) + if("whisper") + usr.whisper("[invocation] [uppertext(chosenarea.name)]") + + return diff --git a/code/datums/spells/banana_touch.dm b/code/datums/spells/banana_touch.dm index 4e2f1d44e51..e8b7f27b0da 100644 --- a/code/datums/spells/banana_touch.dm +++ b/code/datums/spells/banana_touch.dm @@ -55,10 +55,10 @@ equip_to_slot_if_possible(new /obj/item/clothing/under/rank/clown/nodrop, slot_w_uniform, TRUE, TRUE) equip_to_slot_if_possible(new /obj/item/clothing/shoes/clown_shoes/nodrop, slot_shoes, TRUE, TRUE) equip_to_slot_if_possible(new /obj/item/clothing/mask/gas/clown_hat/nodrop, slot_wear_mask, TRUE, TRUE) - dna.SetSEState(CLUMSYBLOCK, TRUE, TRUE) - dna.SetSEState(COMICBLOCK, TRUE, TRUE) - genemutcheck(src, CLUMSYBLOCK, null, MUTCHK_FORCED) - genemutcheck(src, COMICBLOCK, null, MUTCHK_FORCED) + dna.SetSEState(GLOB.clumsyblock, TRUE, TRUE) + dna.SetSEState(GLOB.comicblock, TRUE, TRUE) + genemutcheck(src, GLOB.clumsyblock, null, MUTCHK_FORCED) + genemutcheck(src, GLOB.comicblock, null, MUTCHK_FORCED) if(!(iswizard(src) || (mind && mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE))) //Mutations are permanent on non-wizards. Can still be removed by genetics fuckery but not mutadone. - dna.default_blocks.Add(CLUMSYBLOCK) - dna.default_blocks.Add(COMICBLOCK) \ No newline at end of file + dna.default_blocks.Add(GLOB.clumsyblock) + dna.default_blocks.Add(GLOB.comicblock) diff --git a/code/datums/spells/cluwne.dm b/code/datums/spells/cluwne.dm index 58a33e31a44..91df261e9c2 100644 --- a/code/datums/spells/cluwne.dm +++ b/code/datums/spells/cluwne.dm @@ -27,8 +27,8 @@ var/obj/item/organ/internal/honktumor/cursed/tumor = new tumor.insert(src) mutations.Add(NERVOUS) - dna.SetSEState(NERVOUSBLOCK, 1, 1) - genemutcheck(src, NERVOUSBLOCK, null, MUTCHK_FORCED) + dna.SetSEState(GLOB.nervousblock, 1, 1) + genemutcheck(src, GLOB.nervousblock, null, MUTCHK_FORCED) rename_character(real_name, "cluwne") unEquip(w_uniform, 1) @@ -56,14 +56,14 @@ tumor.remove(src) else mutations.Remove(CLUMSY) - mutations.Remove(COMICBLOCK) - dna.SetSEState(CLUMSYBLOCK,0) - dna.SetSEState(COMICBLOCK,0) - genemutcheck(src, CLUMSYBLOCK, null, MUTCHK_FORCED) - genemutcheck(src, COMICBLOCK, null, MUTCHK_FORCED) + mutations.Remove(GLOB.comicblock) + dna.SetSEState(GLOB.clumsyblock,0) + dna.SetSEState(GLOB.comicblock,0) + genemutcheck(src, GLOB.clumsyblock, null, MUTCHK_FORCED) + genemutcheck(src, GLOB.comicblock, null, MUTCHK_FORCED) mutations.Remove(NERVOUS) - dna.SetSEState(NERVOUSBLOCK, 0) - genemutcheck(src, NERVOUSBLOCK, null, MUTCHK_FORCED) + dna.SetSEState(GLOB.nervousblock, 0) + genemutcheck(src, GLOB.nervousblock, null, MUTCHK_FORCED) var/obj/item/clothing/under/U = w_uniform unEquip(w_uniform, 1) diff --git a/code/datums/spells/conjure.dm b/code/datums/spells/conjure.dm index ebf9fa59fe8..306ec8b9987 100644 --- a/code/datums/spells/conjure.dm +++ b/code/datums/spells/conjure.dm @@ -1,70 +1,70 @@ -/obj/effect/proc_holder/spell/aoe_turf/conjure - name = "Conjure" - desc = "This spell conjures objs of the specified types in range." - - var/list/summon_type = list() //determines what exactly will be summoned - //should be text, like list("/mob/simple_animal/bot/ed209") - - var/summon_lifespan = 0 // 0=permanent, any other time in deciseconds - var/summon_amt = 1 //amount of objects summoned - var/summon_ignore_density = 0 //if set to 1, adds dense tiles to possible spawn places - var/summon_ignore_prev_spawn_points = 0 //if set to 1, each new object is summoned on a new spawn point - - var/list/newVars = list() //vars of the summoned objects will be replaced with those where they meet - //should have format of list("emagged" = 1,"name" = "Wizard's Justicebot"), for example - var/delay = 1//Go Go Gadget Inheritance - - var/cast_sound = 'sound/items/welder.ogg' - -/obj/effect/proc_holder/spell/aoe_turf/conjure/cast(list/targets,mob/living/user = usr) - playsound(get_turf(user), cast_sound, 50,1) - for(var/turf/T in targets) - if(T.density && !summon_ignore_density) - targets -= T - playsound(get_turf(src), cast_sound, 50, 1) - - if(do_after(user, delay, target = user)) - for(var/i=0,iYou can't build things on shuttles!") - break - var/turf/O = spawn_place - var/N = summoned_object_type - O.ChangeTurf(N) - else - var/atom/summoned_object = new summoned_object_type(spawn_place) - - for(var/varName in newVars) - if(varName in summoned_object.vars) - summoned_object.vars[varName] = newVars[varName] - summoned_object.admin_spawned = TRUE - - if(summon_lifespan) - spawn(summon_lifespan) - if(summoned_object) - qdel(summoned_object) - else - switch(charge_type) - if("recharge") - charge_counter = charge_max - 5//So you don't lose charge for a failed spell(Also prevents most over-fill) - if("charges") - charge_counter++//Ditto, just for different spell types - - - return - -/obj/effect/proc_holder/spell/aoe_turf/conjure/summonEdSwarm //test purposes - name = "Dispense Wizard Justice" - desc = "This spell dispenses wizard justice." - - summon_type = list(/mob/living/simple_animal/bot/ed209) - summon_amt = 10 - range = 3 - newVars = list("emagged" = 1,"name" = "Wizard's Justicebot") \ No newline at end of file +/obj/effect/proc_holder/spell/aoe_turf/conjure + name = "Conjure" + desc = "This spell conjures objs of the specified types in range." + + var/list/summon_type = list() //determines what exactly will be summoned + //should be text, like list("/mob/simple_animal/bot/ed209") + + var/summon_lifespan = 0 // 0=permanent, any other time in deciseconds + var/summon_amt = 1 //amount of objects summoned + var/summon_ignore_density = 0 //if set to 1, adds dense tiles to possible spawn places + var/summon_ignore_prev_spawn_points = 0 //if set to 1, each new object is summoned on a new spawn point + + var/list/newVars = list() //vars of the summoned objects will be replaced with those where they meet + //should have format of list("emagged" = 1,"name" = "Wizard's Justicebot"), for example + var/delay = 1//Go Go Gadget Inheritance + + var/cast_sound = 'sound/items/welder.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/conjure/cast(list/targets,mob/living/user = usr) + playsound(get_turf(user), cast_sound, 50,1) + for(var/turf/T in targets) + if(T.density && !summon_ignore_density) + targets -= T + playsound(get_turf(src), cast_sound, 50, 1) + + if(do_after(user, delay, target = user)) + for(var/i=0,iYou can't build things on shuttles!") + break + var/turf/O = spawn_place + var/N = summoned_object_type + O.ChangeTurf(N) + else + var/atom/summoned_object = new summoned_object_type(spawn_place) + + for(var/varName in newVars) + if(varName in summoned_object.vars) + summoned_object.vars[varName] = newVars[varName] + summoned_object.admin_spawned = TRUE + + if(summon_lifespan) + spawn(summon_lifespan) + if(summoned_object) + qdel(summoned_object) + else + switch(charge_type) + if("recharge") + charge_counter = charge_max - 5//So you don't lose charge for a failed spell(Also prevents most over-fill) + if("charges") + charge_counter++//Ditto, just for different spell types + + + return + +/obj/effect/proc_holder/spell/aoe_turf/conjure/summonEdSwarm //test purposes + name = "Dispense Wizard Justice" + desc = "This spell dispenses wizard justice." + + summon_type = list(/mob/living/simple_animal/bot/ed209) + summon_amt = 10 + range = 3 + newVars = list("emagged" = 1,"name" = "Wizard's Justicebot") diff --git a/code/datums/spells/dumbfire.dm b/code/datums/spells/dumbfire.dm index ca8cc43df9c..9efb93a0e7b 100644 --- a/code/datums/spells/dumbfire.dm +++ b/code/datums/spells/dumbfire.dm @@ -1,84 +1,84 @@ -/obj/effect/proc_holder/spell/dumbfire - - var/projectile_type = "" - var/activate_on_collision = 1 - - var/proj_icon = 'icons/obj/projectiles.dmi' - var/proj_icon_state = "spell" - var/proj_name = "a spell projectile" - - var/proj_trail = 0 //if it leaves a trail - var/proj_trail_lifespan = 0 //deciseconds - var/proj_trail_icon = 'icons/obj/wizard.dmi' - var/proj_trail_icon_state = "trail" - - var/proj_type = /obj/effect/proc_holder/spell //IMPORTANT use only subtypes of this - - var/proj_insubstantial = 0 //if it can pass through dense objects or not - var/proj_trigger_range = 1 //the range from target at which the projectile triggers cast(target) - - var/proj_lifespan = 100 //in deciseconds * proj_step_delay - var/proj_step_delay = 1 //lower = faster - -/obj/effect/proc_holder/spell/dumbfire/choose_targets(mob/user = usr) - - var/turf/T = get_turf(usr) - for(var/i = 1; i < range; i++) - var/turf/new_turf = get_step(T, usr.dir) - if(new_turf.density) - break - T = new_turf - perform(list(T), user = user) - -/obj/effect/proc_holder/spell/dumbfire/cast(list/targets, mob/user = usr) - - for(var/turf/target in targets) - spawn(0) - var/obj/effect/proc_holder/spell/targeted/projectile - projectile = new proj_type(user) - projectile.icon = proj_icon - projectile.icon_state = proj_icon_state - projectile.dir = get_dir(projectile, target) - projectile.name = proj_name - - var/current_loc = user.loc - - projectile.loc = current_loc - - for(var/i = 0,i < proj_lifespan,i++) - if(!projectile) - break - - if(proj_insubstantial) - projectile.loc = get_step(projectile, projectile.dir) - else - step(projectile, projectile.dir) - - if(projectile.loc == current_loc || i == proj_lifespan) - projectile.cast(current_loc) - break - - var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - user - if(L && L.stat != DEAD) - projectile.cast(L.loc) - break - - if(proj_trail && projectile) - spawn(0) - if(projectile) - var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) - trail.icon = proj_trail_icon - trail.icon_state = proj_trail_icon_state - trail.density = 0 - spawn(proj_trail_lifespan) - qdel(trail) - - current_loc = projectile.loc - var/matrix/M = new - M.Turn(dir2angle(projectile.dir)) - projectile.transform = M - - sleep(proj_step_delay) - - if(projectile) - qdel(projectile) +/obj/effect/proc_holder/spell/dumbfire + + var/projectile_type = "" + var/activate_on_collision = 1 + + var/proj_icon = 'icons/obj/projectiles.dmi' + var/proj_icon_state = "spell" + var/proj_name = "a spell projectile" + + var/proj_trail = 0 //if it leaves a trail + var/proj_trail_lifespan = 0 //deciseconds + var/proj_trail_icon = 'icons/obj/wizard.dmi' + var/proj_trail_icon_state = "trail" + + var/proj_type = /obj/effect/proc_holder/spell //IMPORTANT use only subtypes of this + + var/proj_insubstantial = 0 //if it can pass through dense objects or not + var/proj_trigger_range = 1 //the range from target at which the projectile triggers cast(target) + + var/proj_lifespan = 100 //in deciseconds * proj_step_delay + var/proj_step_delay = 1 //lower = faster + +/obj/effect/proc_holder/spell/dumbfire/choose_targets(mob/user = usr) + + var/turf/T = get_turf(usr) + for(var/i = 1; i < range; i++) + var/turf/new_turf = get_step(T, usr.dir) + if(new_turf.density) + break + T = new_turf + perform(list(T), user = user) + +/obj/effect/proc_holder/spell/dumbfire/cast(list/targets, mob/user = usr) + + for(var/turf/target in targets) + spawn(0) + var/obj/effect/proc_holder/spell/targeted/projectile + projectile = new proj_type(user) + projectile.icon = proj_icon + projectile.icon_state = proj_icon_state + projectile.dir = get_dir(projectile, target) + projectile.name = proj_name + + var/current_loc = user.loc + + projectile.loc = current_loc + + for(var/i = 0,i < proj_lifespan,i++) + if(!projectile) + break + + if(proj_insubstantial) + projectile.loc = get_step(projectile, projectile.dir) + else + step(projectile, projectile.dir) + + if(projectile.loc == current_loc || i == proj_lifespan) + projectile.cast(current_loc) + break + + var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - user + if(L && L.stat != DEAD) + projectile.cast(L.loc) + break + + if(proj_trail && projectile) + spawn(0) + if(projectile) + var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) + trail.icon = proj_trail_icon + trail.icon_state = proj_trail_icon_state + trail.density = 0 + spawn(proj_trail_lifespan) + qdel(trail) + + current_loc = projectile.loc + var/matrix/M = new + M.Turn(dir2angle(projectile.dir)) + projectile.transform = M + + sleep(proj_step_delay) + + if(projectile) + qdel(projectile) diff --git a/code/datums/spells/emplosion.dm b/code/datums/spells/emplosion.dm index b2f0a228282..cd84432aba8 100644 --- a/code/datums/spells/emplosion.dm +++ b/code/datums/spells/emplosion.dm @@ -1,15 +1,15 @@ -/obj/effect/proc_holder/spell/targeted/emplosion - name = "Emplosion" - desc = "This spell emplodes an area." - - var/emp_heavy = 2 - var/emp_light = 3 - - action_icon_state = "emp" - -/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - empulse(target.loc, emp_heavy, emp_light, 1) - - return +/obj/effect/proc_holder/spell/targeted/emplosion + name = "Emplosion" + desc = "This spell emplodes an area." + + var/emp_heavy = 2 + var/emp_light = 3 + + action_icon_state = "emp" + +/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + empulse(target.loc, emp_heavy, emp_light, 1) + + return diff --git a/code/datums/spells/ethereal_jaunt.dm b/code/datums/spells/ethereal_jaunt.dm index 3103d8d5e5d..f7d7a9bfb8d 100644 --- a/code/datums/spells/ethereal_jaunt.dm +++ b/code/datums/spells/ethereal_jaunt.dm @@ -1,104 +1,104 @@ -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt - name = "Ethereal Jaunt" - desc = "This spell creates your ethereal form, temporarily making you invisible and able to pass through walls." - - school = "transmutation" - charge_max = 300 - clothes_req = 1 - invocation = "none" - invocation_type = "none" - range = -1 - cooldown_min = 100 //50 deciseconds reduction per rank - include_user = 1 - nonabstract_req = 1 - centcom_cancast = 0 //Prevent people from getting to centcom - - var/jaunt_duration = 50 //in deciseconds - var/jaunt_in_time = 5 - var/jaunt_in_type = /obj/effect/temp_visual/wizard - var/jaunt_out_type = /obj/effect/temp_visual/wizard/out - - action_icon_state = "jaunt" - -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets, mob/user = usr) //magnets, so mostly hardcoded - playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1) - for(var/mob/living/target in targets) - if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains - to_chat(target, "You are somehow too bound to your current location to abandon it.") - continue - INVOKE_ASYNC(src, .proc/do_jaunt, target) - -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target) - target.notransform = 1 - var/turf/mobloc = get_turf(target) - var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt(mobloc) - new jaunt_out_type(mobloc, target.dir) - target.ExtinguishMob() - target.forceMove(holder) - target.reset_perspective(holder) - target.notransform = 0 //mob is safely inside holder now, no need for protection. - jaunt_steam(mobloc) - - sleep(jaunt_duration) - - if(target.loc != holder) //mob warped out of the warp - qdel(holder) - return - mobloc = get_turf(target.loc) - jaunt_steam(mobloc) - target.canmove = 0 - holder.reappearing = 1 - playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, 1, -1) - sleep(25 - jaunt_in_time) - new jaunt_in_type(mobloc, holder.dir) - target.setDir(holder.dir) - sleep(jaunt_in_time) - qdel(holder) - if(!QDELETED(target)) - if(mobloc.density) - for(var/direction in alldirs) - var/turf/T = get_step(mobloc, direction) - if(T) - if(target.Move(T)) - break - target.canmove = 1 - -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc) - var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() - steam.set_up(10, 0, mobloc) - steam.start() - -/obj/effect/dummy/spell_jaunt - name = "water" - icon = 'icons/effects/effects.dmi' - icon_state = "nothing" - var/reappearing = 0 - var/movedelay = 0 - var/movespeed = 2 - density = 0 - anchored = 1 - invisibility = 60 - resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/effect/dummy/spell_jaunt/Destroy() - // Eject contents if deleted somehow - for(var/atom/movable/AM in src) - AM.forceMove(get_turf(src)) - return ..() - -/obj/effect/dummy/spell_jaunt/relaymove(mob/user, direction) - if((movedelay > world.time) || reappearing || !direction) - return - var/turf/newLoc = get_step(src,direction) - setDir(direction) - if(!(newLoc.flags & NOJAUNT)) - forceMove(newLoc) - else - to_chat(user, "Some strange aura is blocking the way!") - movedelay = world.time + movespeed - -/obj/effect/dummy/spell_jaunt/ex_act(blah) - return - -/obj/effect/dummy/spell_jaunt/bullet_act(blah) - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt + name = "Ethereal Jaunt" + desc = "This spell creates your ethereal form, temporarily making you invisible and able to pass through walls." + + school = "transmutation" + charge_max = 300 + clothes_req = 1 + invocation = "none" + invocation_type = "none" + range = -1 + cooldown_min = 100 //50 deciseconds reduction per rank + include_user = 1 + nonabstract_req = 1 + centcom_cancast = 0 //Prevent people from getting to centcom + + var/jaunt_duration = 50 //in deciseconds + var/jaunt_in_time = 5 + var/jaunt_in_type = /obj/effect/temp_visual/wizard + var/jaunt_out_type = /obj/effect/temp_visual/wizard/out + + action_icon_state = "jaunt" + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets, mob/user = usr) //magnets, so mostly hardcoded + playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1) + for(var/mob/living/target in targets) + if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains + to_chat(target, "You are somehow too bound to your current location to abandon it.") + continue + INVOKE_ASYNC(src, .proc/do_jaunt, target) + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target) + target.notransform = 1 + var/turf/mobloc = get_turf(target) + var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt(mobloc) + new jaunt_out_type(mobloc, target.dir) + target.ExtinguishMob() + target.forceMove(holder) + target.reset_perspective(holder) + target.notransform = 0 //mob is safely inside holder now, no need for protection. + jaunt_steam(mobloc) + + sleep(jaunt_duration) + + if(target.loc != holder) //mob warped out of the warp + qdel(holder) + return + mobloc = get_turf(target.loc) + jaunt_steam(mobloc) + target.canmove = 0 + holder.reappearing = 1 + playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, 1, -1) + sleep(25 - jaunt_in_time) + new jaunt_in_type(mobloc, holder.dir) + target.setDir(holder.dir) + sleep(jaunt_in_time) + qdel(holder) + if(!QDELETED(target)) + if(mobloc.density) + for(var/direction in GLOB.alldirs) + var/turf/T = get_step(mobloc, direction) + if(T) + if(target.Move(T)) + break + target.canmove = 1 + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc) + var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() + steam.set_up(10, 0, mobloc) + steam.start() + +/obj/effect/dummy/spell_jaunt + name = "water" + icon = 'icons/effects/effects.dmi' + icon_state = "nothing" + var/reappearing = 0 + var/movedelay = 0 + var/movespeed = 2 + density = 0 + anchored = 1 + invisibility = 60 + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/effect/dummy/spell_jaunt/Destroy() + // Eject contents if deleted somehow + for(var/atom/movable/AM in src) + AM.forceMove(get_turf(src)) + return ..() + +/obj/effect/dummy/spell_jaunt/relaymove(mob/user, direction) + if((movedelay > world.time) || reappearing || !direction) + return + var/turf/newLoc = get_step(src,direction) + setDir(direction) + if(!(newLoc.flags & NOJAUNT)) + forceMove(newLoc) + else + to_chat(user, "Some strange aura is blocking the way!") + movedelay = world.time + movespeed + +/obj/effect/dummy/spell_jaunt/ex_act(blah) + return + +/obj/effect/dummy/spell_jaunt/bullet_act(blah) + return diff --git a/code/datums/spells/explosion.dm b/code/datums/spells/explosion.dm index 4beeae1206a..22580d9a896 100644 --- a/code/datums/spells/explosion.dm +++ b/code/datums/spells/explosion.dm @@ -1,15 +1,15 @@ -/obj/effect/proc_holder/spell/targeted/explosion - name = "Explosion" - desc = "This spell explodes an area." - - var/ex_severe = 1 - var/ex_heavy = 2 - var/ex_light = 3 - var/ex_flash = 4 - -/obj/effect/proc_holder/spell/targeted/explosion/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - explosion(target.loc,ex_severe,ex_heavy,ex_light,ex_flash) - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/explosion + name = "Explosion" + desc = "This spell explodes an area." + + var/ex_severe = 1 + var/ex_heavy = 2 + var/ex_light = 3 + var/ex_flash = 4 + +/obj/effect/proc_holder/spell/targeted/explosion/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + explosion(target.loc,ex_severe,ex_heavy,ex_light,ex_flash) + + return diff --git a/code/datums/spells/fake_gib.dm b/code/datums/spells/fake_gib.dm index 6e2f31ea158..dc187576496 100644 --- a/code/datums/spells/fake_gib.dm +++ b/code/datums/spells/fake_gib.dm @@ -8,4 +8,4 @@ clothes_req = 0 cooldown_min = 200 //100 deciseconds reduction per rank - action_icon_state = "gib" \ No newline at end of file + action_icon_state = "gib" diff --git a/code/datums/spells/genetic.dm b/code/datums/spells/genetic.dm index 697337d85e0..3c2f997e461 100644 --- a/code/datums/spells/genetic.dm +++ b/code/datums/spells/genetic.dm @@ -1,37 +1,37 @@ -/obj/effect/proc_holder/spell/targeted/genetic - name = "Genetic" - desc = "This spell inflicts a set of mutations and disabilities upon the target." - - var/disabilities = 0 //bits - var/list/mutations = list() //mutation strings - var/duration = 100 //deciseconds - /* - Disabilities - 1st bit - ? - 2nd bit - ? - 3rd bit - ? - 4th bit - ? - 5th bit - ? - 6th bit - ? - */ - -/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - for(var/x in mutations) - target.mutations.Add(x) - /* if(x == HULK && ishuman(target)) - target:hulk_time=world.time + duration */ - target.disabilities |= disabilities - target.update_mutations() //update target's mutation overlays - var/mob/living/carbon/human/H = target - if(ishuman(target)) - H.update_body() - spawn(duration) - target.mutations.Remove(mutations) - target.disabilities &= ~disabilities - target.update_mutations() - if(ishuman(target)) - H.update_body() - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/genetic + name = "Genetic" + desc = "This spell inflicts a set of mutations and disabilities upon the target." + + var/disabilities = 0 //bits + var/list/mutations = list() //mutation strings + var/duration = 100 //deciseconds + /* + Disabilities + 1st bit - ? + 2nd bit - ? + 3rd bit - ? + 4th bit - ? + 5th bit - ? + 6th bit - ? + */ + +/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + for(var/x in mutations) + target.mutations.Add(x) + /* if(x == HULK && ishuman(target)) + target:hulk_time=world.time + duration */ + target.disabilities |= disabilities + target.update_mutations() //update target's mutation overlays + var/mob/living/carbon/human/H = target + if(ishuman(target)) + H.update_body() + spawn(duration) + target.mutations.Remove(mutations) + target.disabilities &= ~disabilities + target.update_mutations() + if(ishuman(target)) + H.update_body() + + return diff --git a/code/datums/spells/horsemask.dm b/code/datums/spells/horsemask.dm index f536041e807..2c000a0d232 100644 --- a/code/datums/spells/horsemask.dm +++ b/code/datums/spells/horsemask.dm @@ -1,48 +1,48 @@ -/obj/effect/proc_holder/spell/targeted/horsemask - name = "Curse of the Horseman" - desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes." - school = "transmutation" - charge_type = "recharge" - charge_max = 150 - charge_counter = 0 - clothes_req = 0 - stat_allowed = 0 - invocation = "KN'A FTAGHU, PUCK 'BTHNK!" - invocation_type = "shout" - range = 7 - cooldown_min = 30 //30 deciseconds reduction per rank - selection_type = "range" - - action_icon_state = "barn" - sound = 'sound/magic/HorseHead_curse.ogg' - -/obj/effect/proc_holder/spell/targeted/horsemask/cast(list/targets, mob/user = usr) - if(!targets.len) - to_chat(user, "No target found in range.") - return - - var/mob/living/carbon/target = targets[1] - - if(!target) - return - - - if(!ishuman(target)) - to_chat(user, "It'd be stupid to curse [target] with a horse's head!") - return - - if(!(target in oview(range)))//If they are not in overview after selection. - to_chat(user, "They are too far away!") - return - - var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead - magichead.flags |= NODROP | DROPDEL //curses! - magichead.flags_inv = null //so you can still see their face - magichead.voicechange = 1 //NEEEEIIGHH - target.visible_message( "[target]'s face lights up in fire, and after the event a horse's head takes its place!", \ - "Your face burns up, and shortly after the fire you realise you have the face of a horse!") - if(!target.unEquip(target.wear_mask)) - qdel(target.wear_mask) - target.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) - - target.flash_eyes() \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/horsemask + name = "Curse of the Horseman" + desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes." + school = "transmutation" + charge_type = "recharge" + charge_max = 150 + charge_counter = 0 + clothes_req = 0 + stat_allowed = 0 + invocation = "KN'A FTAGHU, PUCK 'BTHNK!" + invocation_type = "shout" + range = 7 + cooldown_min = 30 //30 deciseconds reduction per rank + selection_type = "range" + + action_icon_state = "barn" + sound = 'sound/magic/HorseHead_curse.ogg' + +/obj/effect/proc_holder/spell/targeted/horsemask/cast(list/targets, mob/user = usr) + if(!targets.len) + to_chat(user, "No target found in range.") + return + + var/mob/living/carbon/target = targets[1] + + if(!target) + return + + + if(!ishuman(target)) + to_chat(user, "It'd be stupid to curse [target] with a horse's head!") + return + + if(!(target in oview(range)))//If they are not in overview after selection. + to_chat(user, "They are too far away!") + return + + var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead + magichead.flags |= NODROP | DROPDEL //curses! + magichead.flags_inv = null //so you can still see their face + magichead.voicechange = 1 //NEEEEIIGHH + target.visible_message( "[target]'s face lights up in fire, and after the event a horse's head takes its place!", \ + "Your face burns up, and shortly after the fire you realise you have the face of a horse!") + if(!target.unEquip(target.wear_mask)) + qdel(target.wear_mask) + target.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) + + target.flash_eyes() diff --git a/code/datums/spells/inflict_handler.dm b/code/datums/spells/inflict_handler.dm index 896e399187a..7615a37cfff 100644 --- a/code/datums/spells/inflict_handler.dm +++ b/code/datums/spells/inflict_handler.dm @@ -1,57 +1,57 @@ -/obj/effect/proc_holder/spell/targeted/inflict_handler - name = "Inflict Handler" - desc = "This spell blinds and/or destroys/damages/heals and/or weakens/stuns the target." - - var/amt_weakened = 0 - var/amt_paralysis = 0 - var/amt_stunned = 0 - - //set to negatives for healing - var/amt_dam_fire = 0 - var/amt_dam_brute = 0 - var/amt_dam_oxy = 0 - var/amt_dam_tox = 0 - - var/amt_eye_blind = 0 - var/amt_eye_blurry = 0 - - var/destroys = "none" //can be "none", "gib" or "disintegrate" - - var/summon_type = null //this will put an obj at the target's location - -/obj/effect/proc_holder/spell/targeted/inflict_handler/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - switch(destroys) - if("gib") - target.gib() - if("disintegrate") - target.dust() - - if(!target) - continue - //damage - if(amt_dam_brute > 0) - if(amt_dam_fire >= 0) - target.take_overall_damage(amt_dam_brute,amt_dam_fire) - else if(amt_dam_fire < 0) - target.take_overall_damage(amt_dam_brute,0) - target.heal_overall_damage(0,amt_dam_fire) - else if(amt_dam_brute < 0) - if(amt_dam_fire > 0) - target.take_overall_damage(0,amt_dam_fire) - target.heal_overall_damage(amt_dam_brute,0) - else if(amt_dam_fire <= 0) - target.heal_overall_damage(amt_dam_brute,amt_dam_fire) - target.adjustToxLoss(amt_dam_tox) - target.adjustOxyLoss(amt_dam_oxy) - //disabling - target.Weaken(amt_weakened) - target.Paralyse(amt_paralysis) - target.Stun(amt_stunned) - - target.AdjustEyeBlind(amt_eye_blind) - target.AdjustEyeBlurry(amt_eye_blurry) - //summoning - if(summon_type) - new summon_type(target.loc, target) +/obj/effect/proc_holder/spell/targeted/inflict_handler + name = "Inflict Handler" + desc = "This spell blinds and/or destroys/damages/heals and/or weakens/stuns the target." + + var/amt_weakened = 0 + var/amt_paralysis = 0 + var/amt_stunned = 0 + + //set to negatives for healing + var/amt_dam_fire = 0 + var/amt_dam_brute = 0 + var/amt_dam_oxy = 0 + var/amt_dam_tox = 0 + + var/amt_eye_blind = 0 + var/amt_eye_blurry = 0 + + var/destroys = "none" //can be "none", "gib" or "disintegrate" + + var/summon_type = null //this will put an obj at the target's location + +/obj/effect/proc_holder/spell/targeted/inflict_handler/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + switch(destroys) + if("gib") + target.gib() + if("disintegrate") + target.dust() + + if(!target) + continue + //damage + if(amt_dam_brute > 0) + if(amt_dam_fire >= 0) + target.take_overall_damage(amt_dam_brute,amt_dam_fire) + else if(amt_dam_fire < 0) + target.take_overall_damage(amt_dam_brute,0) + target.heal_overall_damage(0,amt_dam_fire) + else if(amt_dam_brute < 0) + if(amt_dam_fire > 0) + target.take_overall_damage(0,amt_dam_fire) + target.heal_overall_damage(amt_dam_brute,0) + else if(amt_dam_fire <= 0) + target.heal_overall_damage(amt_dam_brute,amt_dam_fire) + target.adjustToxLoss(amt_dam_tox) + target.adjustOxyLoss(amt_dam_oxy) + //disabling + target.Weaken(amt_weakened) + target.Paralyse(amt_paralysis) + target.Stun(amt_stunned) + + target.AdjustEyeBlind(amt_eye_blind) + target.AdjustEyeBlurry(amt_eye_blurry) + //summoning + if(summon_type) + new summon_type(target.loc, target) diff --git a/code/datums/spells/knock.dm b/code/datums/spells/knock.dm index b8fafb58bac..dc2efcc1e91 100644 --- a/code/datums/spells/knock.dm +++ b/code/datums/spells/knock.dm @@ -1,57 +1,57 @@ -/obj/effect/proc_holder/spell/aoe_turf/knock - name = "Knock" - desc = "This spell opens nearby doors and does not require wizard garb." - - school = "transmutation" - charge_max = 100 - clothes_req = 0 - invocation = "AULIE OXIN FIERA" - invocation_type = "whisper" - range = 3 - cooldown_min = 20 //20 deciseconds reduction per rank - - action_icon_state = "knock" - sound = 'sound/magic/knock.ogg' - -/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr) - for(var/turf/T in targets) - for(var/obj/machinery/door/door in T.contents) - spawn(1) - if(istype(door,/obj/machinery/door/airlock/hatch/gamma)) - return - if(istype(door,/obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/A = door - A.unlock(1) //forced because it's magic! - door.open() - for(var/obj/structure/closet/C in T.contents) - spawn(1) - if(istype(C, /obj/structure/closet/secure_closet)) - var/obj/structure/closet/secure_closet/SC = C - SC.locked = 0 - C.open() - - return - -/obj/effect/proc_holder/spell/aoe_turf/knock/greater - name = "Greater Knock" - desc = "On first cast, will remove access restrictions on all airlocks on the station, and announce this spell's use to the station. On any further cast, will open all doors in sight. Cannot be refunded once bought!" - - charge_max = 200 - invocation = "MAIOR OXIN FIERA" - invocation_type = "shout" - range = 7 - level_max = 0 //Cannot be improved, quality of life since can't be refunded - cooldown_min = 200 - var/used = FALSE - -/obj/effect/proc_holder/spell/aoe_turf/knock/greater/cast(list/targets, mob/user = usr) - if(!used) - used = TRUE - for(var/obj/machinery/door/airlock/A in GLOB.airlocks) - if(is_station_level(A.z)) - A.req_access = list() - A.req_one_access = list() - command_announcement.Announce("We have removed all access requirements on your station's airlocks. You can thank us later!", "Greetings!", 'sound/misc/notice2.ogg', , , "Space Wizard Federation Message") - else - ..() - return +/obj/effect/proc_holder/spell/aoe_turf/knock + name = "Knock" + desc = "This spell opens nearby doors and does not require wizard garb." + + school = "transmutation" + charge_max = 100 + clothes_req = 0 + invocation = "AULIE OXIN FIERA" + invocation_type = "whisper" + range = 3 + cooldown_min = 20 //20 deciseconds reduction per rank + + action_icon_state = "knock" + sound = 'sound/magic/knock.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr) + for(var/turf/T in targets) + for(var/obj/machinery/door/door in T.contents) + spawn(1) + if(istype(door,/obj/machinery/door/airlock/hatch/gamma)) + return + if(istype(door,/obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/A = door + A.unlock(1) //forced because it's magic! + door.open() + for(var/obj/structure/closet/C in T.contents) + spawn(1) + if(istype(C, /obj/structure/closet/secure_closet)) + var/obj/structure/closet/secure_closet/SC = C + SC.locked = 0 + C.open() + + return + +/obj/effect/proc_holder/spell/aoe_turf/knock/greater + name = "Greater Knock" + desc = "On first cast, will remove access restrictions on all airlocks on the station, and announce this spell's use to the station. On any further cast, will open all doors in sight. Cannot be refunded once bought!" + + charge_max = 200 + invocation = "MAIOR OXIN FIERA" + invocation_type = "shout" + range = 7 + level_max = 0 //Cannot be improved, quality of life since can't be refunded + cooldown_min = 200 + var/used = FALSE + +/obj/effect/proc_holder/spell/aoe_turf/knock/greater/cast(list/targets, mob/user = usr) + if(!used) + used = TRUE + for(var/obj/machinery/door/airlock/A in GLOB.airlocks) + if(is_station_level(A.z)) + A.req_access = list() + A.req_one_access = list() + GLOB.command_announcement.Announce("We have removed all access requirements on your station's airlocks. You can thank us later!", "Greetings!", 'sound/misc/notice2.ogg', , , "Space Wizard Federation Message") + else + ..() + return diff --git a/code/datums/spells/lichdom.dm b/code/datums/spells/lichdom.dm index f37d48c0a10..9175fd677fb 100644 --- a/code/datums/spells/lichdom.dm +++ b/code/datums/spells/lichdom.dm @@ -28,7 +28,7 @@ config.continuous_rounds = 0 return ..() -/obj/effect/proc_holder/spell/targeted/lichdom/cast(list/targets,mob/user = usr) +/obj/effect/proc_holder/spell/targeted/lichdom/cast(list/targets, mob/user = usr) if(!config.continuous_rounds) existence_stops_round_end = 1 config.continuous_rounds = 1 @@ -36,7 +36,7 @@ for(var/mob/M in targets) var/list/hand_items = list() if(iscarbon(M)) - hand_items = list(M.get_active_hand(),M.get_inactive_hand()) + hand_items = list(M.get_active_hand(), M.get_inactive_hand()) if(marked_item && !stat_allowed) //sanity, shouldn't happen without badminry marked_item = null @@ -74,12 +74,9 @@ var/mob/old_body = current_body var/turf/body_turf = get_turf(old_body) current_body = lich - lich.Weaken(10+10*resurrections) + lich.Weaken(10 + 10 * resurrections) ++resurrections - lich.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(lich), slot_shoes) - lich.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(lich), slot_w_uniform) - lich.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(lich), slot_wear_suit) - lich.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(lich), slot_head) + equip_lich(lich) if(old_body && old_body.loc) if(iscarbon(old_body)) @@ -95,7 +92,7 @@ if(!marked_item) //linking item to the spell message = "" for(var/obj/item in hand_items) - if(ABSTRACT in item.flags || NODROP in item.flags) + if((ABSTRACT in item.flags) || (NODROP in item.flags)) continue marked_item = item to_chat(M, "You begin to focus your very being into the [item.name]...") @@ -127,7 +124,10 @@ H.unEquip(H.head) H.unEquip(H.shoes) H.unEquip(H.head) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(H), slot_head) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), slot_w_uniform) \ No newline at end of file + equip_lich(H) + +/obj/effect/proc_holder/spell/targeted/lichdom/proc/equip_lich(mob/living/carbon/human/H) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(H), slot_head) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), slot_w_uniform) diff --git a/code/datums/spells/lightning.dm b/code/datums/spells/lightning.dm index dcca0cfc919..e7821aed6c2 100644 --- a/code/datums/spells/lightning.dm +++ b/code/datums/spells/lightning.dm @@ -117,4 +117,4 @@ obj/effect/proc_holder/spell/targeted/lightning/proc/Reset(mob/user = usr) return var/mob/living/next = pick(possible_targets) if(next) - Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces \ No newline at end of file + Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces diff --git a/code/datums/spells/magnet.dm b/code/datums/spells/magnet.dm index 8a6e9c525c4..fea6780085c 100644 --- a/code/datums/spells/magnet.dm +++ b/code/datums/spells/magnet.dm @@ -122,4 +122,4 @@ obj/effect/proc_holder/spell/targeted/magnet/proc/Reset(mob/user = usr) return var/mob/living/next = pick(possible_targets) if(next) - Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces \ No newline at end of file + Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm index f11fb5b9572..11b0a75b275 100644 --- a/code/datums/spells/mime.dm +++ b/code/datums/spells/mime.dm @@ -141,6 +141,7 @@ else user.mind.AddSpell(S) to_chat(user, "You flip through the pages. Your understanding of the boundaries of reality increases. You can cast [spellname]!") + user.create_log(MISC_LOG, "learned the spell [spellname] ([S])") user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).") onlearned(user) @@ -164,4 +165,4 @@ /obj/item/spellbook/oneuse/mime/greaterwall spell = /obj/effect/proc_holder/spell/targeted/forcewall/mime spellname = "Invisible Greater Wall" - desc = "It contains illustrations of the great walls of human history." \ No newline at end of file + desc = "It contains illustrations of the great walls of human history." diff --git a/code/datums/spells/mime_malaise.dm b/code/datums/spells/mime_malaise.dm index a5afd26d916..1765b2f5741 100644 --- a/code/datums/spells/mime_malaise.dm +++ b/code/datums/spells/mime_malaise.dm @@ -49,6 +49,6 @@ equip_to_slot_if_possible(new /obj/item/clothing/mask/gas/mime/nodrop, slot_wear_mask, TRUE, TRUE) equip_to_slot_if_possible(new /obj/item/clothing/under/mime/nodrop, slot_w_uniform, TRUE, TRUE) equip_to_slot_if_possible(new /obj/item/clothing/suit/suspenders/nodrop, slot_wear_suit, TRUE, TRUE) - dna.SetSEState(MUTEBLOCK , TRUE, TRUE) - genemutcheck(src, MUTEBLOCK , null, MUTCHK_FORCED) - dna.default_blocks.Add(MUTEBLOCK) \ No newline at end of file + dna.SetSEState(GLOB.muteblock , TRUE, TRUE) + genemutcheck(src, GLOB.muteblock , null, MUTCHK_FORCED) + dna.default_blocks.Add(GLOB.muteblock) diff --git a/code/datums/spells/mind_transfer.dm b/code/datums/spells/mind_transfer.dm index e45d27ec28e..3d9031a8e03 100644 --- a/code/datums/spells/mind_transfer.dm +++ b/code/datums/spells/mind_transfer.dm @@ -1,82 +1,82 @@ -/obj/effect/proc_holder/spell/targeted/mind_transfer - name = "Mind Transfer" - desc = "This spell allows the user to switch bodies with a target." - - school = "transmutation" - charge_max = 600 - clothes_req = 0 - invocation = "GIN'YU CAPAN" - invocation_type = "whisper" - range = 1 - cooldown_min = 200 //100 deciseconds reduction per rank - var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell - var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell - var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell - action_icon_state = "mindswap" - -/* -Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do. -Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering. -Also, you never added distance checking after target is selected. I've went ahead and did that. -*/ -/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride) - - var/mob/living/target = targets[range] - - if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. - to_chat(user, "They are too far away!") - return - - if(target.stat == DEAD) - to_chat(user, "You don't particularly want to be dead.") - return - - if(!target.key || !target.mind) - to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.") - return - - if(user.suiciding) - to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") - return - - if(target.mind.special_role in protected_roles) - to_chat(user, "Their mind is resisting your spell.") - return - - if(istype(target, /mob/living/silicon)) - to_chat(user, "You feel this enslaved being is just as dead as its cold, hard exoskeleton.") - return - - var/mob/living/victim = target//The target of the spell whos body will be transferred to. - var/mob/caster = user//The wizard/whomever doing the body transferring. - - //MIND TRANSFER BEGIN - if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list. - for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot. - caster.verbs -= V//But a safety nontheless. - - if(victim.mind.special_verbs.len)//Now remove all of the victim's verbs. - for(var/V in victim.mind.special_verbs) - victim.verbs -= V - - var/mob/dead/observer/ghost = victim.ghostize(0) - caster.mind.transfer_to(victim) - - if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster. - for(var/V in caster.mind.special_verbs)//Not too important but could come into play. - caster.verbs += V - - ghost.mind.transfer_to(caster) - if(ghost.key) - GLOB.non_respawnable_keys -= ghost.ckey //ghostizing with an argument of 0 will make them unable to respawn forever, which is bad - caster.key = ghost.key //have to transfer the key since the mind was not active - qdel(ghost) - - if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here. - for(var/V in caster.mind.special_verbs) - caster.verbs += V - //MIND TRANSFER END - - //Here we paralyze both mobs and knock them out for a time. - caster.Paralyse(paralysis_amount_caster) - victim.Paralyse(paralysis_amount_victim) +/obj/effect/proc_holder/spell/targeted/mind_transfer + name = "Mind Transfer" + desc = "This spell allows the user to switch bodies with a target." + + school = "transmutation" + charge_max = 600 + clothes_req = 0 + invocation = "GIN'YU CAPAN" + invocation_type = "whisper" + range = 1 + cooldown_min = 200 //100 deciseconds reduction per rank + var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell + var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell + var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell + action_icon_state = "mindswap" + +/* +Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do. +Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering. +Also, you never added distance checking after target is selected. I've went ahead and did that. +*/ +/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride) + + var/mob/living/target = targets[range] + + if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. + to_chat(user, "They are too far away!") + return + + if(target.stat == DEAD) + to_chat(user, "You don't particularly want to be dead.") + return + + if(!target.key || !target.mind) + to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.") + return + + if(user.suiciding) + to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") + return + + if(target.mind.special_role in protected_roles) + to_chat(user, "Their mind is resisting your spell.") + return + + if(istype(target, /mob/living/silicon)) + to_chat(user, "You feel this enslaved being is just as dead as its cold, hard exoskeleton.") + return + + var/mob/living/victim = target//The target of the spell whos body will be transferred to. + var/mob/caster = user//The wizard/whomever doing the body transferring. + + //MIND TRANSFER BEGIN + if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list. + for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot. + caster.verbs -= V//But a safety nontheless. + + if(victim.mind.special_verbs.len)//Now remove all of the victim's verbs. + for(var/V in victim.mind.special_verbs) + victim.verbs -= V + + var/mob/dead/observer/ghost = victim.ghostize(0) + caster.mind.transfer_to(victim) + + if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster. + for(var/V in caster.mind.special_verbs)//Not too important but could come into play. + caster.verbs += V + + ghost.mind.transfer_to(caster) + if(ghost.key) + GLOB.non_respawnable_keys -= ghost.ckey //ghostizing with an argument of 0 will make them unable to respawn forever, which is bad + caster.key = ghost.key //have to transfer the key since the mind was not active + qdel(ghost) + + if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here. + for(var/V in caster.mind.special_verbs) + caster.verbs += V + //MIND TRANSFER END + + //Here we paralyze both mobs and knock them out for a time. + caster.Paralyse(paralysis_amount_caster) + victim.Paralyse(paralysis_amount_victim) diff --git a/code/datums/spells/projectile.dm b/code/datums/spells/projectile.dm index 258925653d8..955aa6e3537 100644 --- a/code/datums/spells/projectile.dm +++ b/code/datums/spells/projectile.dm @@ -1,86 +1,86 @@ -/obj/effect/proc_holder/spell/targeted/projectile - name = "Projectile" - desc = "This spell summons projectiles which try to hit the targets." - - var/proj_icon = 'icons/obj/projectiles.dmi' - var/proj_icon_state = "spell" - var/proj_name = "a spell projectile" - - var/proj_trail = 0 //if it leaves a trail - var/proj_trail_lifespan = 0 //deciseconds - var/proj_trail_icon = 'icons/obj/wizard.dmi' - var/proj_trail_icon_state = "trail" - - var/proj_type = "/obj/effect/proc_holder/spell/targeted" //IMPORTANT use only subtypes of this - - var/proj_lingering = 0 //if it lingers or disappears upon hitting an obstacle - var/proj_homing = 1 //if it follows the target - var/proj_insubstantial = 0 //if it can pass through dense objects or not - var/proj_trigger_range = 0 //the range from target at which the projectile triggers cast(target) - - var/proj_lifespan = 15 //in deciseconds * proj_step_delay - var/proj_step_delay = 1 //lower = faster - -/obj/effect/proc_holder/spell/targeted/projectile/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - spawn(0) - var/obj/effect/proc_holder/spell/targeted/projectile - if(istext(proj_type)) - var/projectile_type = text2path(proj_type) - projectile = new projectile_type(user) - if(istype(proj_type,/obj/effect/proc_holder/spell)) - projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user) - projectile:linked_spells += proj_type - projectile.icon = proj_icon - projectile.icon_state = proj_icon_state - projectile.dir = get_dir(target,projectile) - projectile.name = proj_name - - var/current_loc = user.loc - - projectile.loc = current_loc - - for(var/i = 0,i < proj_lifespan,i++) - if(!projectile) - break - - if(proj_homing) - if(proj_insubstantial) - projectile.dir = get_dir(projectile,target) - projectile.loc = get_step_to(projectile,target) - else - step_to(projectile,target) - else - if(proj_insubstantial) - projectile.loc = get_step(projectile,dir) - else - step(projectile,dir) - - if(!projectile) // step and step_to sleeps so we'll have to check again. - break - - if(!proj_lingering && projectile.loc == current_loc) //if it didn't move since last time - qdel(projectile) - break - - if(proj_trail && projectile) - spawn(0) - if(projectile) - var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) - trail.icon = proj_trail_icon - trail.icon_state = proj_trail_icon_state - trail.density = 0 - spawn(proj_trail_lifespan) - qdel(trail) - - if(projectile.loc in range(target.loc,proj_trigger_range)) - projectile.perform(list(target), user = user) - break - - current_loc = projectile.loc - - sleep(proj_step_delay) - - if(projectile) - qdel(projectile) \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/projectile + name = "Projectile" + desc = "This spell summons projectiles which try to hit the targets." + + var/proj_icon = 'icons/obj/projectiles.dmi' + var/proj_icon_state = "spell" + var/proj_name = "a spell projectile" + + var/proj_trail = 0 //if it leaves a trail + var/proj_trail_lifespan = 0 //deciseconds + var/proj_trail_icon = 'icons/obj/wizard.dmi' + var/proj_trail_icon_state = "trail" + + var/proj_type = "/obj/effect/proc_holder/spell/targeted" //IMPORTANT use only subtypes of this + + var/proj_lingering = 0 //if it lingers or disappears upon hitting an obstacle + var/proj_homing = 1 //if it follows the target + var/proj_insubstantial = 0 //if it can pass through dense objects or not + var/proj_trigger_range = 0 //the range from target at which the projectile triggers cast(target) + + var/proj_lifespan = 15 //in deciseconds * proj_step_delay + var/proj_step_delay = 1 //lower = faster + +/obj/effect/proc_holder/spell/targeted/projectile/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + spawn(0) + var/obj/effect/proc_holder/spell/targeted/projectile + if(istext(proj_type)) + var/projectile_type = text2path(proj_type) + projectile = new projectile_type(user) + if(istype(proj_type,/obj/effect/proc_holder/spell)) + projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user) + projectile:linked_spells += proj_type + projectile.icon = proj_icon + projectile.icon_state = proj_icon_state + projectile.dir = get_dir(target,projectile) + projectile.name = proj_name + + var/current_loc = user.loc + + projectile.loc = current_loc + + for(var/i = 0,i < proj_lifespan,i++) + if(!projectile) + break + + if(proj_homing) + if(proj_insubstantial) + projectile.dir = get_dir(projectile,target) + projectile.loc = get_step_to(projectile,target) + else + step_to(projectile,target) + else + if(proj_insubstantial) + projectile.loc = get_step(projectile,dir) + else + step(projectile,dir) + + if(!projectile) // step and step_to sleeps so we'll have to check again. + break + + if(!proj_lingering && projectile.loc == current_loc) //if it didn't move since last time + qdel(projectile) + break + + if(proj_trail && projectile) + spawn(0) + if(projectile) + var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) + trail.icon = proj_trail_icon + trail.icon_state = proj_trail_icon_state + trail.density = 0 + spawn(proj_trail_lifespan) + qdel(trail) + + if(projectile.loc in range(target.loc,proj_trigger_range)) + projectile.perform(list(target), user = user) + break + + current_loc = projectile.loc + + sleep(proj_step_delay) + + if(projectile) + qdel(projectile) diff --git a/code/datums/spells/rathens.dm b/code/datums/spells/rathens.dm index b2d35ff5393..4d98be324b2 100644 --- a/code/datums/spells/rathens.dm +++ b/code/datums/spells/rathens.dm @@ -21,14 +21,14 @@ A.remove(H) A.forceMove(get_turf(H)) spawn() - A.throw_at(get_edge_target_turf(H, pick(alldirs)), rand(1, 10), 5) + A.throw_at(get_edge_target_turf(H, pick(GLOB.alldirs)), rand(1, 10), 5) H.visible_message("[H]'s [A.name] flies out of their body in a magical explosion!",\ "Your [A.name] flies out of your body in a magical explosion!") H.Weaken(2) else var/obj/effect/decal/cleanable/blood/gibs/G = new/obj/effect/decal/cleanable/blood/gibs(get_turf(H)) spawn() - G.throw_at(get_edge_target_turf(H, pick(alldirs)), rand(1, 10), 5) + G.throw_at(get_edge_target_turf(H, pick(GLOB.alldirs)), rand(1, 10), 5) H.apply_damage(10, BRUTE, "chest") to_chat(H, "You have no appendix, but something had to give! Holy shit, what was that?") H.Weaken(3) diff --git a/code/datums/spells/shapeshift.dm b/code/datums/spells/shapeshift.dm index ff303ce750b..7b72b5a42c4 100644 --- a/code/datums/spells/shapeshift.dm +++ b/code/datums/spells/shapeshift.dm @@ -111,4 +111,4 @@ shapeshift_type = /mob/living/simple_animal/hostile/hellhound/greater current_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater) current_casters = list() - possible_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater) \ No newline at end of file + possible_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater) diff --git a/code/datums/spells/summonitem.dm b/code/datums/spells/summonitem.dm index aaef2229856..c3301f4d6ab 100644 --- a/code/datums/spells/summonitem.dm +++ b/code/datums/spells/summonitem.dm @@ -41,7 +41,7 @@ else message = "You must hold the desired item in your hands to mark it for recall." - else if(marked_item && marked_item in hand_items) //unlinking item to the spell + else if(marked_item && (marked_item in hand_items)) //unlinking item to the spell message = "You remove the mark on [marked_item] to use elsewhere." name = "Instant Summons" marked_item = null @@ -75,7 +75,7 @@ B.transfer_identity(C) C.death() add_attack_logs(target, C, "Magically debrained INTENT: [uppertext(target.a_intent)]")*/ - if(C.stomach_contents && item_to_retrieve in C.stomach_contents) + if(C.stomach_contents && (item_to_retrieve in C.stomach_contents)) C.stomach_contents -= item_to_retrieve for(var/X in C.bodyparts) var/obj/item/organ/external/part = X diff --git a/code/datums/spells/trigger.dm b/code/datums/spells/trigger.dm index bba1f22cb1a..bc768cef57b 100644 --- a/code/datums/spells/trigger.dm +++ b/code/datums/spells/trigger.dm @@ -1,29 +1,29 @@ -/obj/effect/proc_holder/spell/targeted/trigger - name = "Trigger" - desc = "This spell triggers another spell or a few." - - var/list/linked_spells = list() //those are just referenced by the trigger spell and are unaffected by it directly - var/list/starting_spells = list() //those are added on New() to contents from default spells and are deleted when the trigger spell is deleted to prevent memory leaks - -/obj/effect/proc_holder/spell/targeted/trigger/New() - ..() - - for(var/spell in starting_spells) - var/spell_to_add = text2path(spell) - new spell_to_add(src) //should result in adding to contents, needs testing - -/obj/effect/proc_holder/spell/targeted/trigger/Destroy() - for(var/spell in contents) - qdel(spell) - linked_spells = null - starting_spells = null - return ..() - -/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets, mob/user = usr) - for(var/mob/living/target in targets) - for(var/obj/effect/proc_holder/spell/spell in contents) - spell.perform(list(target), 0, user = user) - for(var/obj/effect/proc_holder/spell/spell in linked_spells) - spell.perform(list(target), 0, user = user) - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/trigger + name = "Trigger" + desc = "This spell triggers another spell or a few." + + var/list/linked_spells = list() //those are just referenced by the trigger spell and are unaffected by it directly + var/list/starting_spells = list() //those are added on New() to contents from default spells and are deleted when the trigger spell is deleted to prevent memory leaks + +/obj/effect/proc_holder/spell/targeted/trigger/New() + ..() + + for(var/spell in starting_spells) + var/spell_to_add = text2path(spell) + new spell_to_add(src) //should result in adding to contents, needs testing + +/obj/effect/proc_holder/spell/targeted/trigger/Destroy() + for(var/spell in contents) + qdel(spell) + linked_spells = null + starting_spells = null + return ..() + +/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets, mob/user = usr) + for(var/mob/living/target in targets) + for(var/obj/effect/proc_holder/spell/spell in contents) + spell.perform(list(target), 0, user = user) + for(var/obj/effect/proc_holder/spell/spell in linked_spells) + spell.perform(list(target), 0, user = user) + + return diff --git a/code/datums/spells/turf_teleport.dm b/code/datums/spells/turf_teleport.dm index 6ecc9d693d8..adaa4afc94c 100644 --- a/code/datums/spells/turf_teleport.dm +++ b/code/datums/spells/turf_teleport.dm @@ -1,40 +1,40 @@ -/obj/effect/proc_holder/spell/targeted/turf_teleport - name = "Turf Teleport" - desc = "This spell teleports the target to the turf in range." - nonabstract_req = 1 - - var/inner_tele_radius = 1 - var/outer_tele_radius = 2 - - var/include_space = 0 //whether it includes space tiles in possible teleport locations - var/include_dense = 0 //whether it includes dense tiles in possible teleport locations - - var/sound1 = 'sound/weapons/zapbang.ogg' - var/sound2 = 'sound/weapons/zapbang.ogg' - -/obj/effect/proc_holder/spell/targeted/turf_teleport/cast(list/targets,mob/living/user = usr) - playsound(get_turf(user), sound1, 50,1) - for(var/mob/living/target in targets) - var/list/turfs = new/list() - for(var/turf/T in range(target,outer_tele_radius)) - if(T in range(target,inner_tele_radius)) continue - if(istype(T,/turf/space) && !include_space) continue - if(T.density && !include_dense) continue - if(T.x>world.maxx-outer_tele_radius || T.xworld.maxy-outer_tele_radius || T.yworld.maxx-outer_tele_radius || T.xworld.maxy-outer_tele_radius || T.yYou're slammed into the floor by a mystical force!") - else - new sparkle_path(get_turf(AM), get_dir(user, AM)) //created sparkles will disappear on their own - if(isliving(AM)) - var/mob/living/M = AM - M.Weaken(stun_amt) - to_chat(M, "You're thrown back by a mystical force!") - spawn(0) - AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time. - -/obj/effect/proc_holder/spell/targeted/sacred_flame - name = "Sacred Flame" - desc = "Makes everyone around you more flammable, and lights yourself on fire." - charge_max = 60 - clothes_req = 0 - invocation = "FI'RAN DADISKO" - invocation_type = "shout" - max_targets = 0 - range = 6 - include_user = 1 - selection_type = "view" - action_icon_state = "sacredflame" - sound = 'sound/magic/fireball.ogg' - -/obj/effect/proc_holder/spell/targeted/sacred_flame/cast(list/targets, mob/user = usr) - for(var/mob/living/L in targets) - L.adjust_fire_stacks(20) - if(isliving(user)) - var/mob/living/U = user - U.IgniteMob() +/obj/effect/proc_holder/spell/targeted/projectile/magic_missile + name = "Magic Missile" + desc = "This spell fires several, slow moving, magic projectiles at nearby targets." + + school = "evocation" + charge_max = 200 + clothes_req = 1 + invocation = "FORTI GY AMA" + invocation_type = "shout" + range = 7 + cooldown_min = 60 //35 deciseconds reduction per rank + + max_targets = 0 + + proj_icon_state = "magicm" + proj_name = "a magic missile" + proj_lingering = 1 + proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile" + + proj_lifespan = 20 + proj_step_delay = 5 + + proj_trail = 1 + proj_trail_lifespan = 5 + proj_trail_icon_state = "magicmd" + + action_icon_state = "magicm" + + sound = 'sound/magic/magic_missile.ogg' + +/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile + amt_weakened = 3 + sound = 'sound/magic/mm_hit.ogg' + + +/obj/effect/proc_holder/spell/targeted/projectile/honk_missile + name = "Honk Missile" + desc = "This spell fires several, slow moving, magic bikehorns at nearby targets." + + school = "evocation" + charge_max = 60 + clothes_req = 0 + invocation = "HONK GY AMA" + invocation_type = "shout" + range = 7 + cooldown_min = 60 //35 deciseconds reduction per rank + + max_targets = 0 + + proj_icon = 'icons/obj/items.dmi' + proj_icon_state = "bike_horn" + proj_name = "A bike horn" + proj_lingering = 1 + proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/honk_missile" + + proj_lifespan = 20 + proj_step_delay = 5 + + proj_trail_icon = 'icons/obj/items.dmi' + proj_trail = 1 + proj_trail_lifespan = 5 + proj_trail_icon_state = "bike_horn" + + action_icon_state = "magicm" + + sound = 'sound/items/bikehorn.ogg' + +/obj/effect/proc_holder/spell/targeted/inflict_handler/honk_missile + amt_weakened = 3 + sound = 'sound/items/bikehorn.ogg' + +/obj/effect/proc_holder/spell/noclothes + name = "No Clothes" + desc = "This always-on spell allows you to cast magic without your garments." + action_icon_state = "no_clothes" + +/obj/effect/proc_holder/spell/targeted/genetic/mutate + name = "Mutate" + desc = "This spell causes you to turn into a hulk and gain laser vision for a short while." + + school = "transmutation" + charge_max = 400 + clothes_req = 1 + invocation = "BIRUZ BENNAR" + invocation_type = "shout" + message = "You feel strong! You feel a pressure building behind your eyes!" + range = -1 + include_user = 1 + centcom_cancast = 0 + + mutations = list(LASER, HULK) + duration = 300 + cooldown_min = 300 //25 deciseconds reduction per rank + + action_icon_state = "mutate" + sound = 'sound/magic/mutate.ogg' + +/obj/effect/proc_holder/spell/targeted/genetic/mutate/cast(list/targets, mob/user = usr) + for(var/mob/living/target in targets) + target.dna.SetSEState(GLOB.hulkblock, 1) + genemutcheck(target, GLOB.hulkblock, null, MUTCHK_FORCED) + spawn(duration) + target.dna.SetSEState(GLOB.hulkblock, 0) + genemutcheck(target, GLOB.hulkblock, null, MUTCHK_FORCED) + ..() + +/obj/effect/proc_holder/spell/targeted/smoke + name = "Smoke" + desc = "This spell spawns a cloud of choking smoke at your location and does not require wizard garb." + + school = "conjuration" + charge_max = 120 + clothes_req = 0 + invocation = "none" + invocation_type = "none" + range = -1 + include_user = 1 + cooldown_min = 20 //25 deciseconds reduction per rank + + smoke_spread = 2 + smoke_amt = 10 + + action_icon_state = "smoke" + +/obj/effect/proc_holder/spell/targeted/emplosion/disable_tech + name = "Disable Tech" + desc = "This spell disables all weapons, cameras and most other technology in range." + charge_max = 400 + clothes_req = 1 + invocation = "NEC CANTIO" + invocation_type = "shout" + range = -1 + include_user = 1 + cooldown_min = 200 //50 deciseconds reduction per rank + + emp_heavy = 6 + emp_light = 10 + + sound = 'sound/magic/disable_tech.ogg' + +/obj/effect/proc_holder/spell/targeted/turf_teleport/blink + name = "Blink" + desc = "This spell randomly teleports you a short distance." + + school = "abjuration" + charge_max = 20 + clothes_req = 1 + invocation = "none" + invocation_type = "none" + range = -1 + include_user = 1 + cooldown_min = 5 //4 deciseconds reduction per rank + + + smoke_spread = 1 + smoke_amt = 1 + + inner_tele_radius = 0 + outer_tele_radius = 6 + + centcom_cancast = 0 //prevent people from getting to centcom + + action_icon_state = "blink" + + sound1 = 'sound/magic/blink.ogg' + sound2 = 'sound/magic/blink.ogg' + +/obj/effect/proc_holder/spell/targeted/area_teleport/teleport + name = "Teleport" + desc = "This spell teleports you to a type of area of your selection." + + school = "abjuration" + charge_max = 600 + clothes_req = 1 + invocation = "SCYAR NILA" + invocation_type = "shout" + range = -1 + include_user = 1 + cooldown_min = 200 //100 deciseconds reduction per rank + + smoke_spread = 1 + smoke_amt = 5 + + action_icon_state = "spell_teleport" + + sound1 = 'sound/magic/teleport_diss.ogg' + sound2 = 'sound/magic/teleport_app.ogg' + +/obj/effect/proc_holder/spell/targeted/forcewall + name = "Force Wall" + desc = "This spell creates a small unbreakable wall that only you can pass through, and does not need wizard garb. Lasts 30 seconds." + + school = "transmutation" + charge_max = 100 + clothes_req = FALSE + invocation = "TARCOL MINTI ZHERI" + invocation_type = "whisper" + sound = 'sound/magic/forcewall.ogg' + action_icon_state = "shield" + range = -1 + include_user = TRUE + cooldown_min = 50 //12 deciseconds reduction per rank + var/wall_type = /obj/effect/forcefield/wizard + var/large = FALSE + +/obj/effect/proc_holder/spell/targeted/forcewall/cast(list/targets, mob/user = usr) + new wall_type(get_turf(user), user) + if(large) //Extra THICK + if(user.dir == SOUTH || user.dir == NORTH) + new wall_type(get_step(user, EAST), user) + new wall_type(get_step(user, WEST), user) + else + new wall_type(get_step(user, NORTH), user) + new wall_type(get_step(user, SOUTH), user) + +/obj/effect/proc_holder/spell/targeted/forcewall/greater + name = "Greater Force Wall" + desc = "Create a larger magical barrier that only you can pass through, but requires wizard garb. Lasts 30 seconds." + + clothes_req = TRUE + invocation = "TARCOL GRANDI ZHERI" + invocation_type = "shout" + large = TRUE + +/obj/effect/proc_holder/spell/aoe_turf/conjure/timestop + name = "Stop Time" + desc = "This spell stops time for everyone except for you, allowing you to move freely while your enemies and even projectiles are frozen." + charge_max = 500 + clothes_req = 1 + invocation = "TOKI WO TOMARE" + invocation_type = "shout" + range = 0 + cooldown_min = 100 + summon_amt = 1 + action_icon_state = "time" + + summon_type = list(/obj/effect/timestop/wizard) + +/obj/effect/proc_holder/spell/aoe_turf/conjure/carp + name = "Summon Carp" + desc = "This spell conjures a simple carp." + + school = "conjuration" + charge_max = 1200 + clothes_req = 1 + invocation = "NOUK FHUNMM SACP RISSKA" + invocation_type = "shout" + range = 1 + + summon_type = list(/mob/living/simple_animal/hostile/carp) + + cast_sound = 'sound/magic/summon_karp.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/conjure/construct + name = "Artificer" + desc = "This spell conjures a construct which may be controlled by Shades" + + school = "conjuration" + charge_max = 600 + clothes_req = 0 + invocation = "none" + invocation_type = "none" + range = 0 + + summon_type = list(/obj/structure/constructshell) + + action_icon_state = "artificer" + cast_sound = 'sound/magic/summonitems_generic.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/conjure/creature + name = "Summon Creature Swarm" + desc = "This spell tears the fabric of reality, allowing horrific daemons to spill forth" + + school = "conjuration" + charge_max = 1200 + clothes_req = 0 + invocation = "IA IA" + invocation_type = "shout" + summon_amt = 10 + range = 3 + + summon_type = list(/mob/living/simple_animal/hostile/creature) + cast_sound = 'sound/magic/summonitems_generic.ogg' + +/obj/effect/proc_holder/spell/targeted/trigger/blind + name = "Blind" + desc = "This spell temporarily blinds a single person and does not require wizard garb." + + school = "transmutation" + charge_max = 300 + clothes_req = 0 + invocation = "STI KALY" + invocation_type = "whisper" + message = "Your eyes cry out in pain!" + cooldown_min = 50 //12 deciseconds reduction per rank + + starting_spells = list("/obj/effect/proc_holder/spell/targeted/inflict_handler/blind","/obj/effect/proc_holder/spell/targeted/genetic/blind") + + action_icon_state = "blind" + +/obj/effect/proc_holder/spell/targeted/inflict_handler/blind + amt_eye_blind = 10 + amt_eye_blurry = 20 + sound = 'sound/magic/blind.ogg' + +/obj/effect/proc_holder/spell/targeted/genetic/blind + disabilities = BLIND + duration = 300 + sound = 'sound/magic/blind.ogg' + +/obj/effect/proc_holder/spell/fireball + name = "Fireball" + desc = "This spell fires a fireball at a target and does not require wizard garb." + + school = "evocation" + charge_max = 60 + clothes_req = 0 + invocation = "ONI SOMA" + invocation_type = "shout" + range = 20 + cooldown_min = 20 //10 deciseconds reduction per rank + var/fireball_type = /obj/item/projectile/magic/fireball + action_icon_state = "fireball0" + sound = 'sound/magic/fireball.ogg' + + active = FALSE + +/obj/effect/proc_holder/spell/fireball/Click() + var/mob/living/user = usr + if(!istype(user)) + return + + var/msg + + if(!can_cast(user)) + msg = "You can no longer cast Fireball." + remove_ranged_ability(user, msg) + return + + if(active) + msg = "You extinguish your fireball...for now." + remove_ranged_ability(user, msg) + else + msg = "Your prepare to cast your fireball spell! Left-click to cast at a target!" + add_ranged_ability(user, msg) + +/obj/effect/proc_holder/spell/fireball/update_icon() + if(!action) + return + action.button_icon_state = "fireball[active]" + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/fireball/InterceptClickOn(mob/living/user, params, atom/target) + if(..()) + return FALSE + + if(!cast_check(0, user)) + remove_ranged_ability(user) + return FALSE + + var/list/targets = list(target) + perform(targets, user = user) + + return TRUE + +/obj/effect/proc_holder/spell/fireball/cast(list/targets, mob/living/user = usr) + var/target = targets[1] //There is only ever one target for fireball + var/turf/T = user.loc + var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction + if(!isturf(U) || !isturf(T)) + return 0 + + var/obj/item/projectile/magic/fireball/FB = new fireball_type(user.loc) + FB.current = get_turf(user) + FB.preparePixelProjectile(target, get_turf(target), user) + FB.fire() + user.newtonian_move(get_dir(U, T)) + remove_ranged_ability(user) + + return 1 + +/obj/effect/proc_holder/spell/aoe_turf/repulse + name = "Repulse" + desc = "This spell throws everything around the user away." + charge_max = 400 + clothes_req = TRUE + invocation = "GITTAH WEIGH" + invocation_type = "shout" + range = 5 + cooldown_min = 150 + selection_type = "view" + sound = 'sound/magic/repulse.ogg' + var/maxthrow = 5 + var/sparkle_path = /obj/effect/temp_visual/gravpush + action_icon_state = "repulse" + +/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets, mob/user = usr, stun_amt = 2) + var/list/thrownatoms = list() + var/atom/throwtarget + var/distfromcaster + playMagSound() + for(var/turf/T in targets) //Done this way so things don't get thrown all around hilariously. + for(var/atom/movable/AM in T) + thrownatoms += AM + + for(var/am in thrownatoms) + var/atom/movable/AM = am + if(AM == user || AM.anchored) + continue + + throwtarget = get_edge_target_turf(user, get_dir(user, get_step_away(AM, user))) + distfromcaster = get_dist(user, AM) + if(distfromcaster == 0) + if(isliving(AM)) + var/mob/living/M = AM + M.Weaken(5) + M.adjustBruteLoss(5) + to_chat(M, "You're slammed into the floor by a mystical force!") + else + new sparkle_path(get_turf(AM), get_dir(user, AM)) //created sparkles will disappear on their own + if(isliving(AM)) + var/mob/living/M = AM + M.Weaken(stun_amt) + to_chat(M, "You're thrown back by a mystical force!") + spawn(0) + AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time. + +/obj/effect/proc_holder/spell/targeted/sacred_flame + name = "Sacred Flame" + desc = "Makes everyone around you more flammable, and lights yourself on fire." + charge_max = 60 + clothes_req = 0 + invocation = "FI'RAN DADISKO" + invocation_type = "shout" + max_targets = 0 + range = 6 + include_user = 1 + selection_type = "view" + action_icon_state = "sacredflame" + sound = 'sound/magic/fireball.ogg' + +/obj/effect/proc_holder/spell/targeted/sacred_flame/cast(list/targets, mob/user = usr) + for(var/mob/living/L in targets) + L.adjust_fire_stacks(20) + if(isliving(user)) + var/mob/living/U = user + U.IgniteMob() diff --git a/code/datums/statclick.dm b/code/datums/statclick.dm index 11a3ed41ce3..2c83e57fb45 100644 --- a/code/datums/statclick.dm +++ b/code/datums/statclick.dm @@ -30,4 +30,4 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick) class = "unknown" usr.client.debug_variables(target) - message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") \ No newline at end of file + message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index c4ba55552fa..61decede9bc 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -113,13 +113,13 @@ /datum/status_effect/hippocraticOath/on_apply() //Makes the user passive, it's in their oath not to harm! ADD_TRAIT(owner, TRAIT_PACIFISM, "hippocraticOath") - var/datum/atom_hud/H = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/H = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] H.add_hud_to(owner) return ..() /datum/status_effect/hippocraticOath/on_remove() REMOVE_TRAIT(owner, TRAIT_PACIFISM, "hippocraticOath") - var/datum/atom_hud/H = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/H = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] H.remove_hud_from(owner) /datum/status_effect/hippocraticOath/tick() @@ -190,7 +190,7 @@ L.adjustBrainLoss(-3.5) L.adjustCloneLoss(-1) //Becasue apparently clone damage is the bastion of all health if(ishuman(L)) - var/var/mob/living/carbon/human/H = L + var/mob/living/carbon/human/H = L for(var/obj/item/organ/external/E in H.bodyparts) if(prob(10)) E.mend_fracture() @@ -229,4 +229,4 @@ return TRUE /datum/status_effect/regenerative_core/on_remove() - owner.status_flags &= ~IGNORESLOWDOWN \ No newline at end of file + owner.status_flags &= ~IGNORESLOWDOWN diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 3ce68f85250..d8b5e4d9cbe 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -107,9 +107,9 @@ if(needs_to_bleed) var/turf/T = get_turf(owner) new /obj/effect/temp_visual/bleed/explode(T) - for(var/d in alldirs) + for(var/d in GLOB.alldirs) new /obj/effect/temp_visual/dir_setting/bloodsplatter(T, d) playsound(T, "desceration", 200, 1, -1) owner.adjustBruteLoss(bleed_damage) else - new /obj/effect/temp_visual/bleed(get_turf(owner)) \ No newline at end of file + new /obj/effect/temp_visual/bleed(get_turf(owner)) diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm index bdd80e7fc32..00417993144 100644 --- a/code/datums/status_effects/gas.dm +++ b/code/datums/status_effects/gas.dm @@ -43,4 +43,4 @@ /datum/status_effect/freon/watcher duration = 8 - can_melt = FALSE \ No newline at end of file + can_melt = FALSE diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index b2365ad4e1c..7a86a00ccd7 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -44,4 +44,4 @@ alert_type = null /datum/status_effect/high_five/on_remove() - owner.visible_message("[owner] was left hanging....") \ No newline at end of file + owner.visible_message("[owner] was left hanging....") diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index 8ed41c559b5..74abf104465 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -41,6 +41,9 @@ LAZYREMOVE(owner.status_effects, src) on_remove() owner = null + if(linked_alert) + linked_alert.attached_effect = null + linked_alert = null return ..() /datum/status_effect/process() @@ -85,6 +88,13 @@ desc = "You don't feel any different..." var/datum/status_effect/attached_effect +/obj/screen/alert/status_effect/Destroy() + if(attached_effect) + attached_effect.linked_alert = null + attached_effect = null + return ..() + + ////////////////// // HELPER PROCS // ////////////////// @@ -234,4 +244,4 @@ owner.cut_overlay(status_overlay) owner.underlays -= status_underlay QDEL_NULL(status_overlay) - return ..() \ No newline at end of file + return ..() diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 8e20ebb7a93..15c6f3fb2c0 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -6,37 +6,37 @@ //NEW NOTE: Do NOT set the price of any crates below 7 points. Doing so allows infinite points. // Supply Groups -var/const/supply_emergency = 1 -var/const/supply_security = 2 -var/const/supply_engineer = 3 -var/const/supply_medical = 4 -var/const/supply_science = 5 -var/const/supply_organic = 6 -var/const/supply_materials = 7 -var/const/supply_misc = 8 -var/const/supply_vend = 9 +#define SUPPLY_EMERGENCY 1 +#define SUPPLY_SECURITY 2 +#define SUPPLY_ENGINEER 3 +#define SUPPLY_MEDICAL 4 +#define SUPPLY_SCIENCE 5 +#define SUPPLY_ORGANIC 6 +#define SUPPLY_MATERIALS 7 +#define SUPPLY_MISC 8 +#define SUPPLY_VEND 9 -var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engineer,supply_medical,supply_science,supply_organic,supply_materials,supply_misc,supply_vend) +GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY_ENGINEER,SUPPLY_MEDICAL,SUPPLY_SCIENCE,SUPPLY_ORGANIC,SUPPLY_MATERIALS,SUPPLY_MISC,SUPPLY_VEND)) /proc/get_supply_group_name(var/cat) switch(cat) - if(1) + if(SUPPLY_EMERGENCY) return "Emergency" - if(2) + if(SUPPLY_SECURITY) return "Security" - if(3) + if(SUPPLY_ENGINEER) return "Engineering" - if(4) + if(SUPPLY_MEDICAL) return "Medical" - if(5) + if(SUPPLY_SCIENCE) return "Science" - if(6) + if(SUPPLY_ORGANIC) return "Food and Livestock" - if(7) + if(SUPPLY_MATERIALS) return "Raw Materials" - if(8) + if(SUPPLY_MISC) return "Miscellaneous" - if(9) + if(SUPPLY_VEND) return "Vending" @@ -51,7 +51,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine var/access = null var/hidden = 0 var/contraband = 0 - var/group = supply_misc + var/group = SUPPLY_MISC var/list/announce_beacons = list() // Particular beacons that we'll notify the relevant department when we reach var/special = FALSE //Event/Station Goals/Admin enabled packs var/special_enabled = FALSE @@ -74,7 +74,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/emergency // Section header - use these to set default supply group and crate type for sections name = "HEADER" // Use "HEADER" to denote section headers, this is needed for the supply computers to filter them containertype = /obj/structure/closet/crate/internals - group = supply_emergency + group = SUPPLY_EMERGENCY /datum/supply_packs/emergency/evac @@ -98,7 +98,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 35 containertype = /obj/structure/closet/crate/internals containername = "emergency crate" - group = supply_emergency + group = SUPPLY_EMERGENCY /datum/supply_packs/emergency/internals name = "Internals Crate" @@ -135,7 +135,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure containername = "firefighting watertank crate" - access = access_atmospherics + access = ACCESS_ATMOSPHERICS /datum/supply_packs/emergency/weedcontrol name = "Weed Control Crate" @@ -146,7 +146,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 15 containertype = /obj/structure/closet/crate/secure/hydrosec containername = "weed control crate" - access = access_hydroponics + access = ACCESS_HYDROPONICS announce_beacons = list("Hydroponics" = list("Hydroponics")) /datum/supply_packs/emergency/voxsupport @@ -172,7 +172,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 20 containertype = /obj/structure/closet/crate/secure/plasma containername = "plasmaman life support supplies crate" - access = access_eva + access = ACCESS_EVA /datum/supply_packs/emergency/specialops name = "Special Ops Supplies" @@ -202,8 +202,8 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/security name = "HEADER" containertype = /obj/structure/closet/crate/secure/gear - access = access_security - group = supply_security + access = ACCESS_SECURITY + group = SUPPLY_SECURITY announce_beacons = list("Security" = list("Head of Security's Desk", "Warden", "Security")) @@ -299,7 +299,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/security/armory name = "HEADER" containertype = /obj/structure/closet/crate/secure/weapon - access = access_armory + access = ACCESS_ARMORY announce_beacons = list("Security" = list("Warden", "Head of Security's Desk")) ///// Armor: Specialist @@ -442,7 +442,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 15 // its a fecking flamethrower and some plasma, why the shit did this cost so much before!? containertype = /obj/structure/closet/crate/secure/plasma containername = "incendiary weapons crate" - access = access_heads + access = ACCESS_HEADS /datum/supply_packs/security/armory/wt550 name = "WT-550 Auto Rifle Crate" @@ -534,7 +534,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/engineering name = "HEADER" - group = supply_engineer + group = SUPPLY_ENGINEER announce_beacons = list("Engineering" = list("Engineering", "Chief Engineer's Desk")) containertype = /obj/structure/closet/crate/engineering @@ -635,7 +635,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure containername = "emitter crate" - access = access_ce + access = ACCESS_CE containertype = /obj/structure/closet/crate/secure/engineering /datum/supply_packs/engineering/engine/field_gen @@ -703,7 +703,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 30 containertype = /obj/structure/closet/crate/secure containername = "space suit crate" - access = access_eva + access = ACCESS_EVA /datum/supply_packs/engineering/inflatable name = "Inflatable barriers Crate" @@ -719,7 +719,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 50 //So cargo thinks twice before killing themselves with it containertype = /obj/structure/closet/crate/secure containername = "supermatter shard crate" - access = access_ce + access = ACCESS_CE /datum/supply_packs/engineering/engine/teg name = "Thermo-Electric Generator Crate" @@ -730,7 +730,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 25 containertype = /obj/structure/closet/crate/secure containername = "thermo-electric generator crate" - access = access_ce + access = ACCESS_CE announce_beacons = list("Engineering" = list("Chief Engineer's Desk", "Atmospherics")) /datum/supply_packs/engineering/conveyor @@ -753,7 +753,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/medical name = "HEADER" containertype = /obj/structure/closet/crate/medical - group = supply_medical + group = SUPPLY_MEDICAL announce_beacons = list("Medbay" = list("Medbay", "Chief Medical Officer's Desk"), "Security" = list("Brig Medbay")) @@ -858,7 +858,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 25 containertype = /obj/structure/closet/crate/secure/plasma containername = "virus crate" - access = access_cmo + access = ACCESS_CMO announce_beacons = list("Medbay" = list("Virology", "Chief Medical Officer's Desk")) /datum/supply_packs/medical/vending @@ -888,7 +888,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 30 containertype = /obj/structure/closet/crate/secure containername = "IV drip crate" - access = access_medical + access = ACCESS_MEDICAL /datum/supply_packs/medical/surgery name = "Surgery Crate" @@ -906,7 +906,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 25 containertype = /obj/structure/closet/crate/secure containername = "surgery crate" - access = access_medical + access = ACCESS_MEDICAL ////////////////////////////////////////////////////////////////////////////// @@ -915,7 +915,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/science name = "HEADER" - group = supply_science + group = SUPPLY_SCIENCE announce_beacons = list("Research Division" = list("Science", "Research Director's Desk")) containertype = /obj/structure/closet/crate/sci @@ -931,7 +931,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure/scisec containername = "robotics assembly crate" - access = access_robotics + access = ACCESS_ROBOTICS announce_beacons = list("Research Division" = list("Robotics", "Research Director's Desk")) @@ -967,8 +967,8 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure/plasma containername = "plasma assembly crate" - access = access_tox_storage - group = supply_science + access = ACCESS_TOX_STORAGE + group = SUPPLY_SCIENCE /datum/supply_packs/science/shieldwalls name = "Shield Generators Crate" @@ -979,7 +979,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 20 containertype = /obj/structure/closet/crate/secure/scisec containername = "shield generators crate" - access = access_teleporter + access = ACCESS_TELEPORTER /datum/supply_packs/science/modularpc name = "Deluxe Silicate Selections restocking unit" @@ -994,7 +994,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 60 containertype = /obj/structure/closet/crate/secure/scisec containername = "tank transfer valves crate" - access = access_rd + access = ACCESS_RD /datum/supply_packs/science/prototype name = "Machine Prototype Crate" @@ -1002,7 +1002,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 80 containertype = /obj/structure/closet/crate/secure/scisec containername = "machine prototype crate" - access = access_research + access = ACCESS_RESEARCH /datum/supply_packs/science/oil name = "Oil Tank Crate" @@ -1018,7 +1018,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/organic name = "HEADER" - group = supply_organic + group = SUPPLY_ORGANIC containertype = /obj/structure/closet/crate/freezer @@ -1204,13 +1204,13 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine containername = "hydroponics crate" announce_beacons = list("Hydroponics" = list("Hydroponics")) -/datum/supply_packs/misc/hydroponics/hydrotank +/datum/supply_packs/organic/hydroponics/hydrotank name = "Hydroponics Watertank Crate" contains = list(/obj/item/watertank) cost = 10 containertype = /obj/structure/closet/crate/secure containername = "hydroponics watertank crate" - access = access_hydroponics + access = ACCESS_HYDROPONICS announce_beacons = list("Hydroponics" = list("Hydroponics")) /datum/supply_packs/organic/hydroponics/seeds @@ -1296,7 +1296,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/materials name = "HEADER" - group = supply_materials + group = SUPPLY_MATERIALS announce_beacons = list("Engineering" = list("Engineering", "Chief Engineer's Desk", "Atmospherics")) @@ -1363,7 +1363,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/misc name = "HEADER" - group = supply_misc + group = SUPPLY_MISC /datum/supply_packs/misc/mule name = "MULEbot Crate" @@ -1421,7 +1421,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/misc/minerkit name = "Shaft Miner Starter Kit" cost = 30 - access = access_qm + access = ACCESS_QM contains = list(/obj/item/storage/backpack/duffel/mining_conscript) containertype = /obj/structure/closet/crate/secure containername = "shaft miner starter kit" @@ -1550,7 +1550,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure containername = "janitor watertank crate" - access = access_janitor + access = ACCESS_JANITOR /datum/supply_packs/misc/janitor/lightbulbs name = "Replacement Lights Crate" @@ -1588,7 +1588,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure containername = "standard costumes" - access = access_theatre + access = ACCESS_THEATRE /datum/supply_packs/misc/wizard name = "Wizard Costume Crate" @@ -1855,7 +1855,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/vending name = "HEADER" - group = supply_vend + group = SUPPLY_VEND /datum/supply_packs/vending/autodrobe name = "Autodrobe Supply Crate" diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index a5155cceb4a..a0c9a19e4b0 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -108,7 +108,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) return desc /datum/uplink_item/proc/buy(var/obj/item/uplink/hidden/U, var/mob/user) - ..() if(!istype(U)) return 0 diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm index dbc75cb9bf0..558669df3c6 100644 --- a/code/datums/weather/weather_types/ash_storm.dm +++ b/code/datums/weather/weather_types/ash_storm.dm @@ -36,7 +36,7 @@ var/list/outside_areas = list() var/list/eligible_areas = list() for(var/z in impacted_z_levels) - eligible_areas += space_manager.areas_in_z["[z]"] + eligible_areas += GLOB.space_manager.areas_in_z["[z]"] for(var/i in 1 to eligible_areas.len) var/area/place = eligible_areas[i] if(place.outdoors) @@ -105,4 +105,4 @@ aesthetic = TRUE - probability = 10 \ No newline at end of file + probability = 10 diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm index 08b30ecef48..2bfefa5624e 100644 --- a/code/datums/weather/weather_types/radiation_storm.dm +++ b/code/datums/weather/weather_types/radiation_storm.dm @@ -26,8 +26,8 @@ /datum/weather/rad_storm/telegraph() ..() status_alarm(TRUE) - pre_maint_all_access = maint_all_access - if(!maint_all_access) + pre_maint_all_access = GLOB.maint_all_access + if(!GLOB.maint_all_access) make_maint_all_access() @@ -51,7 +51,7 @@ /datum/weather/rad_storm/end() if(..()) return - priority_announcement.Announce("The radiation threat has passed. Please return to your workplaces.", "Anomaly Alert") + GLOB.priority_announcement.Announce("The radiation threat has passed. Please return to your workplaces.", "Anomaly Alert") status_alarm(FALSE) if(!pre_maint_all_access) revoke_maint_all_access() @@ -61,4 +61,4 @@ post_status("alert", "radiation") else post_status("blank") - post_status("shuttle") \ No newline at end of file + post_status("shuttle") diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index 3e9f488bb29..e3d90b8d075 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -9,16 +9,16 @@ window_x = 410 window_y = 570 -var/const/AIRLOCK_WIRE_IDSCAN = 1 -var/const/AIRLOCK_WIRE_MAIN_POWER1 = 2 -var/const/AIRLOCK_WIRE_DOOR_BOLTS = 4 -var/const/AIRLOCK_WIRE_BACKUP_POWER1 = 8 -var/const/AIRLOCK_WIRE_OPEN_DOOR = 16 -var/const/AIRLOCK_WIRE_AI_CONTROL = 32 -var/const/AIRLOCK_WIRE_ELECTRIFY = 64 -var/const/AIRLOCK_WIRE_SAFETY = 128 -var/const/AIRLOCK_WIRE_SPEED = 256 -var/const/AIRLOCK_WIRE_LIGHT = 512 +#define AIRLOCK_WIRE_IDSCAN 1 +#define AIRLOCK_WIRE_MAIN_POWER1 2 +#define AIRLOCK_WIRE_DOOR_BOLTS 4 +#define AIRLOCK_WIRE_BACKUP_POWER1 8 +#define AIRLOCK_WIRE_OPEN_DOOR 16 +#define AIRLOCK_WIRE_AI_CONTROL 32 +#define AIRLOCK_WIRE_ELECTRIFY 64 +#define AIRLOCK_WIRE_SAFETY 128 +#define AIRLOCK_WIRE_SPEED 256 +#define AIRLOCK_WIRE_LIGHT 512 /datum/wires/airlock/GetWireName(index) switch(index) diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm index daf8a0dcd48..f6b49b0dd99 100644 --- a/code/datums/wires/alarm.dm +++ b/code/datums/wires/alarm.dm @@ -3,11 +3,11 @@ holder_type = /obj/machinery/alarm wire_count = 5 -var/const/AALARM_WIRE_IDSCAN = 1 -var/const/AALARM_WIRE_POWER = 2 -var/const/AALARM_WIRE_SYPHON = 4 -var/const/AALARM_WIRE_AI_CONTROL = 8 -var/const/AALARM_WIRE_AALARM = 16 +#define AALARM_WIRE_IDSCAN 1 +#define AALARM_WIRE_POWER 2 +#define AALARM_WIRE_SYPHON 4 +#define AALARM_WIRE_AI_CONTROL 8 +#define AALARM_WIRE_AALARM 16 /datum/wires/alarm/GetWireName(index) switch(index) diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm index df014edd4f4..b1076c8e29b 100644 --- a/code/datums/wires/apc.dm +++ b/code/datums/wires/apc.dm @@ -2,10 +2,10 @@ holder_type = /obj/machinery/power/apc wire_count = 4 -var/const/APC_WIRE_IDSCAN = 1 -var/const/APC_WIRE_MAIN_POWER1 = 2 -var/const/APC_WIRE_MAIN_POWER2 = 4 -var/const/APC_WIRE_AI_CONTROL = 8 +#define APC_WIRE_IDSCAN 1 +#define APC_WIRE_MAIN_POWER1 2 +#define APC_WIRE_MAIN_POWER2 4 +#define APC_WIRE_AI_CONTROL 8 /datum/wires/apc/GetWireName(index) switch(index) @@ -91,4 +91,4 @@ var/const/APC_WIRE_AI_CONTROL = 8 else if(A.aidisabled == 1) A.aidisabled = 0 - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/autolathe.dm b/code/datums/wires/autolathe.dm index a901695c880..c1413abd0cc 100644 --- a/code/datums/wires/autolathe.dm +++ b/code/datums/wires/autolathe.dm @@ -2,9 +2,9 @@ holder_type = /obj/machinery/autolathe wire_count = 10 -var/const/AUTOLATHE_HACK_WIRE = 1 -var/const/AUTOLATHE_SHOCK_WIRE = 2 -var/const/AUTOLATHE_DISABLE_WIRE = 4 +#define AUTOLATHE_HACK_WIRE 1 +#define AUTOLATHE_SHOCK_WIRE 2 +#define AUTOLATHE_DISABLE_WIRE 4 /datum/wires/autolathe/GetWireName(index) switch(index) @@ -71,4 +71,4 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4 /datum/wires/autolathe/proc/updateUIs() SSnanoui.update_uis(src) if(holder) - SSnanoui.update_uis(holder) \ No newline at end of file + SSnanoui.update_uis(holder) diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm index 609dce59e84..a6b1549fbe1 100644 --- a/code/datums/wires/camera.dm +++ b/code/datums/wires/camera.dm @@ -17,8 +17,8 @@ return FALSE return TRUE -var/const/CAMERA_WIRE_FOCUS = 1 -var/const/CAMERA_WIRE_POWER = 2 +#define CAMERA_WIRE_FOCUS 1 +#define CAMERA_WIRE_POWER 2 /datum/wires/camera/GetWireName(index) switch(index) @@ -58,4 +58,4 @@ var/const/CAMERA_WIRE_POWER = 2 if(IsIndexCut(CAMERA_WIRE_POWER) && IsIndexCut(CAMERA_WIRE_FOCUS)) return TRUE else - return FALSE \ No newline at end of file + return FALSE diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm index 045923c7dcf..47fd627d597 100644 --- a/code/datums/wires/explosive.dm +++ b/code/datums/wires/explosive.dm @@ -1,7 +1,7 @@ /datum/wires/explosive wire_count = 1 -var/const/WIRE_EXPLODE = 1 +#define WIRE_EXPLODE 1 /datum/wires/explosive/GetWireName(index) switch(index) diff --git a/code/datums/wires/mulebot.dm b/code/datums/wires/mulebot.dm index cbc92497f33..988b520854f 100644 --- a/code/datums/wires/mulebot.dm +++ b/code/datums/wires/mulebot.dm @@ -4,43 +4,43 @@ wire_count = 10 window_x = 410 -var/const/WIRE_POWER1 = 1 // power connections -var/const/WIRE_POWER2 = 2 -var/const/WIRE_AVOIDANCE = 4 // mob avoidance -var/const/WIRE_LOADCHECK = 8 // load checking (non-crate) -var/const/WIRE_MOTOR1 = 16 // motor wires -var/const/WIRE_MOTOR2 = 32 // -var/const/WIRE_REMOTE_RX = 64 // remote recv functions -var/const/WIRE_REMOTE_TX = 128 // remote trans status -var/const/WIRE_BEACON_RX = 256 // beacon ping recv +#define MULEBOT_WIRE_POWER1 1 // power connections +#define MULEBOT_WIRE_POWER2 2 +#define MULEBOT_WIRE_AVOIDANCE 4 // mob avoidance +#define MULEBOT_WIRE_LOADCHECK 8 // load checking (non-crate) +#define MULEBOT_WIRE_MOTOR1 16 // motor wires +#define MULEBOT_WIRE_MOTOR2 32 // +#define MULEBOT_WIRE_REMOTE_RX 64 // remote recv functions +#define MULEBOT_WIRE_REMOTE_TX 128 // remote trans status +#define MULEBOT_WIRE_BEACON_RX 256 // beacon ping recv /datum/wires/mulebot/GetWireName(index) switch(index) - if(WIRE_POWER1) + if(MULEBOT_WIRE_POWER1) return "Primary Power" - if(WIRE_POWER2) + if(MULEBOT_WIRE_POWER2) return "Secondary Power" - if(WIRE_AVOIDANCE) + if(MULEBOT_WIRE_AVOIDANCE) return "Mob Avoidance" - if(WIRE_LOADCHECK) + if(MULEBOT_WIRE_LOADCHECK) return "Load Checking" - if(WIRE_MOTOR1) + if(MULEBOT_WIRE_MOTOR1) return "Primary Motor" - if(WIRE_MOTOR2) + if(MULEBOT_WIRE_MOTOR2) return "Secondary Motor" - if(WIRE_REMOTE_RX) + if(MULEBOT_WIRE_REMOTE_RX) return "Remote Signal Receiver" - if(WIRE_REMOTE_TX) + if(MULEBOT_WIRE_REMOTE_TX) return "Remote Signal Sender" - if(WIRE_BEACON_RX) + if(MULEBOT_WIRE_BEACON_RX) return "Navigation Beacon Receiver" /datum/wires/mulebot/CanUse(mob/living/L) @@ -51,13 +51,13 @@ var/const/WIRE_BEACON_RX = 256 // beacon ping recv /datum/wires/mulebot/UpdatePulsed(index) switch(index) - if(WIRE_POWER1, WIRE_POWER2) + if(MULEBOT_WIRE_POWER1, MULEBOT_WIRE_POWER2) holder.visible_message("[bicon(holder)] The charge light flickers.") - if(WIRE_AVOIDANCE) + if(MULEBOT_WIRE_AVOIDANCE) holder.visible_message("[bicon(holder)] The external warning lights flash briefly.") - if(WIRE_LOADCHECK) + if(MULEBOT_WIRE_LOADCHECK) holder.visible_message("[bicon(holder)] The load platform clunks.") - if(WIRE_MOTOR1, WIRE_MOTOR2) + if(MULEBOT_WIRE_MOTOR1, MULEBOT_WIRE_MOTOR2) holder.visible_message("[bicon(holder)] The drive motor whines briefly.") else holder.visible_message("[bicon(holder)] You hear a radio crackle.") @@ -66,25 +66,25 @@ var/const/WIRE_BEACON_RX = 256 // beacon ping recv // HELPER PROCS /datum/wires/mulebot/proc/Motor1() - return !(wires_status & WIRE_MOTOR1) + return !(wires_status & MULEBOT_WIRE_MOTOR1) /datum/wires/mulebot/proc/Motor2() - return !(wires_status & WIRE_MOTOR2) + return !(wires_status & MULEBOT_WIRE_MOTOR2) /datum/wires/mulebot/proc/HasPower() - return !(wires_status & WIRE_POWER1) && !(wires_status & WIRE_POWER2) + return !(wires_status & MULEBOT_WIRE_POWER1) && !(wires_status & MULEBOT_WIRE_POWER2) /datum/wires/mulebot/proc/LoadCheck() - return !(wires_status & WIRE_LOADCHECK) + return !(wires_status & MULEBOT_WIRE_LOADCHECK) /datum/wires/mulebot/proc/MobAvoid() - return !(wires_status & WIRE_AVOIDANCE) + return !(wires_status & MULEBOT_WIRE_AVOIDANCE) /datum/wires/mulebot/proc/RemoteTX() - return !(wires_status & WIRE_REMOTE_TX) + return !(wires_status & MULEBOT_WIRE_REMOTE_TX) /datum/wires/mulebot/proc/RemoteRX() - return !(wires_status & WIRE_REMOTE_RX) + return !(wires_status & MULEBOT_WIRE_REMOTE_RX) /datum/wires/mulebot/proc/BeaconRX() - return !(wires_status & WIRE_BEACON_RX) \ No newline at end of file + return !(wires_status & MULEBOT_WIRE_BEACON_RX) diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm index 5b704a4526b..3b8ff5db098 100644 --- a/code/datums/wires/nuclearbomb.dm +++ b/code/datums/wires/nuclearbomb.dm @@ -3,9 +3,9 @@ random = 1 wire_count = 7 -var/const/NUCLEARBOMB_WIRE_LIGHT = 1 -var/const/NUCLEARBOMB_WIRE_TIMING = 2 -var/const/NUCLEARBOMB_WIRE_SAFETY = 4 +#define NUCLEARBOMB_WIRE_LIGHT 1 +#define NUCLEARBOMB_WIRE_TIMING 2 +#define NUCLEARBOMB_WIRE_SAFETY 4 /datum/wires/nuclearbomb/GetWireName(index) switch(index) @@ -72,11 +72,11 @@ var/const/NUCLEARBOMB_WIRE_SAFETY = 4 if(N.icon_state == "nuclearbomb2") N.icon_state = "nuclearbomb1" N.timing = 0 - bomb_set = 0 + GLOB.bomb_set = 0 if(NUCLEARBOMB_WIRE_LIGHT) N.lighthack = !N.lighthack /datum/wires/nuclearbomb/proc/updateUIs() SSnanoui.update_uis(src) if(holder) - SSnanoui.update_uis(holder) \ No newline at end of file + SSnanoui.update_uis(holder) diff --git a/code/datums/wires/particle_accelerator.dm b/code/datums/wires/particle_accelerator.dm index 1ed68b799f4..d6e68a79cfb 100644 --- a/code/datums/wires/particle_accelerator.dm +++ b/code/datums/wires/particle_accelerator.dm @@ -2,10 +2,10 @@ wire_count = 5 holder_type = /obj/machinery/particle_accelerator/control_box -var/const/PARTICLE_TOGGLE_WIRE = 1 // Toggles whether the PA is on or not. -var/const/PARTICLE_STRENGTH_WIRE = 2 // Determines the strength of the PA. -var/const/PARTICLE_INTERFACE_WIRE = 4 // Determines the interface showing up. -var/const/PARTICLE_LIMIT_POWER_WIRE = 8 // Determines how strong the PA can be. +#define PARTICLE_TOGGLE_WIRE 1 // Toggles whether the PA is on or not. +#define PARTICLE_STRENGTH_WIRE 2 // Determines the strength of the PA. +#define PARTICLE_INTERFACE_WIRE 4 // Determines the interface showing up. +#define PARTICLE_LIMIT_POWER_WIRE 8 // Determines how strong the PA can be. /datum/wires/particle_acc/control_box/GetWireName(index) switch(index) @@ -64,4 +64,4 @@ var/const/PARTICLE_LIMIT_POWER_WIRE = 8 // Determines how strong the PA can be. C.strength_upper_limit = (mended ? 2 : 3) if(C.strength_upper_limit < C.strength) C.remove_strength() - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/radio.dm b/code/datums/wires/radio.dm index 18a2ef73f65..90a72935c53 100644 --- a/code/datums/wires/radio.dm +++ b/code/datums/wires/radio.dm @@ -2,19 +2,19 @@ holder_type = /obj/item/radio wire_count = 3 -var/const/WIRE_SIGNAL = 1 -var/const/WIRE_RECEIVE = 2 -var/const/WIRE_TRANSMIT = 4 +#define RADIO_WIRE_SIGNAL 1 +#define RADIO_WIRE_RECEIVE 2 +#define RADIO_WIRE_TRANSMIT 4 /datum/wires/radio/GetWireName(index) switch(index) - if(WIRE_SIGNAL) + if(RADIO_WIRE_SIGNAL) return "Signal" - if(WIRE_RECEIVE) + if(RADIO_WIRE_RECEIVE) return "Receiver" - if(WIRE_TRANSMIT) + if(RADIO_WIRE_TRANSMIT) return "Transmitter" /datum/wires/radio/CanUse(mob/living/L) @@ -26,27 +26,27 @@ var/const/WIRE_TRANSMIT = 4 /datum/wires/radio/UpdatePulsed(index) var/obj/item/radio/R = holder switch(index) - if(WIRE_SIGNAL) - R.listening = !R.listening && !IsIndexCut(WIRE_RECEIVE) - R.broadcasting = R.listening && !IsIndexCut(WIRE_TRANSMIT) + if(RADIO_WIRE_SIGNAL) + R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_RECEIVE) + R.broadcasting = R.listening && !IsIndexCut(RADIO_WIRE_TRANSMIT) - if(WIRE_RECEIVE) - R.listening = !R.listening && !IsIndexCut(WIRE_SIGNAL) + if(RADIO_WIRE_RECEIVE) + R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_SIGNAL) - if(WIRE_TRANSMIT) - R.broadcasting = !R.broadcasting && !IsIndexCut(WIRE_SIGNAL) + if(RADIO_WIRE_TRANSMIT) + R.broadcasting = !R.broadcasting && !IsIndexCut(RADIO_WIRE_SIGNAL) ..() /datum/wires/radio/UpdateCut(index, mended) var/obj/item/radio/R = holder switch(index) - if(WIRE_SIGNAL) - R.listening = mended && !IsIndexCut(WIRE_RECEIVE) - R.broadcasting = mended && !IsIndexCut(WIRE_TRANSMIT) + if(RADIO_WIRE_SIGNAL) + R.listening = mended && !IsIndexCut(RADIO_WIRE_RECEIVE) + R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_TRANSMIT) - if(WIRE_RECEIVE) - R.listening = mended && !IsIndexCut(WIRE_SIGNAL) + if(RADIO_WIRE_RECEIVE) + R.listening = mended && !IsIndexCut(RADIO_WIRE_SIGNAL) - if(WIRE_TRANSMIT) - R.broadcasting = mended && !IsIndexCut(WIRE_SIGNAL) + if(RADIO_WIRE_TRANSMIT) + R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_SIGNAL) ..() diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index bc03e80c327..789a0472869 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -3,21 +3,13 @@ holder_type = /mob/living/silicon/robot wire_count = 5 -/* /tg/ ordering doesn't work for us, we need lawsync at the end for MoMMIs. -var/const/BORG_WIRE_LAWCHECK = 1 -var/const/BORG_WIRE_MAIN_POWER = 2 // The power wires do nothing whyyyyyyyyyyyyy -var/const/BORG_WIRE_LOCKED_DOWN = 4 -var/const/BORG_WIRE_AI_CONTROL = 8 -var/const/BORG_WIRE_CAMERA = 16 -*/ - // /vg/ ordering -var/const/BORG_WIRE_MAIN_POWER = 1 // The power wires do nothing whyyyyyyyyyyyyy -var/const/BORG_WIRE_LOCKED_DOWN = 2 -var/const/BORG_WIRE_CAMERA = 4 -var/const/BORG_WIRE_AI_CONTROL = 8 // Not used on MoMMIs -var/const/BORG_WIRE_LAWCHECK = 16 // Not used on MoMMIs +#define BORG_WIRE_MAIN_POWER 1 // The power wires do nothing whyyyyyyyyyyyyy +#define BORG_WIRE_LOCKED_DOWN 2 +#define BORG_WIRE_CAMERA 4 +#define BORG_WIRE_AI_CONTROL 8 // Not used on MoMMIs +#define BORG_WIRE_LAWCHECK 16 // Not used on MoMMIs /datum/wires/robot/GetWireName(index) switch(index) @@ -111,4 +103,4 @@ var/const/BORG_WIRE_LAWCHECK = 16 // Not used on MoMMIs return wires_status & BORG_WIRE_LAWCHECK /datum/wires/robot/proc/AIHasControl() - return wires_status & BORG_WIRE_AI_CONTROL \ No newline at end of file + return wires_status & BORG_WIRE_AI_CONTROL diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm index 53b33e0fd72..a65377ead1a 100644 --- a/code/datums/wires/smartfridge.dm +++ b/code/datums/wires/smartfridge.dm @@ -6,9 +6,9 @@ random = 1 wire_count = 4 -var/const/SMARTFRIDGE_WIRE_ELECTRIFY = 1 -var/const/SMARTFRIDGE_WIRE_THROW = 2 -var/const/SMARTFRIDGE_WIRE_IDSCAN = 4 +#define SMARTFRIDGE_WIRE_ELECTRIFY 1 +#define SMARTFRIDGE_WIRE_THROW 2 +#define SMARTFRIDGE_WIRE_IDSCAN 4 /datum/wires/smartfridge/GetWireName(index) switch(index) diff --git a/code/datums/wires/suitstorage.dm b/code/datums/wires/suitstorage.dm index b519b449c86..175b36e6081 100644 --- a/code/datums/wires/suitstorage.dm +++ b/code/datums/wires/suitstorage.dm @@ -2,10 +2,10 @@ holder_type = /obj/machinery/suit_storage_unit wire_count = 8 -var/const/SSU_WIRE_ID = 1 -var/const/SSU_WIRE_SHOCK = 2 -var/const/SSU_WIRE_SAFETY = 4 -var/const/SSU_WIRE_UV = 8 +#define SSU_WIRE_ID 1 +#define SSU_WIRE_SHOCK 2 +#define SSU_WIRE_SAFETY 4 +#define SSU_WIRE_UV 8 /datum/wires/suitstorage/GetWireName(index) @@ -66,4 +66,4 @@ datum/wires/suitstorage/UpdatePulsed(index) A.shocked = FALSE if(SSU_WIRE_UV) A.uv_super = !A.uv_super - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm index 616429b8cd8..b490d150efe 100644 --- a/code/datums/wires/syndicatebomb.dm +++ b/code/datums/wires/syndicatebomb.dm @@ -3,27 +3,27 @@ holder_type = /obj/machinery/syndicatebomb wire_count = 5 -var/const/WIRE_BOOM = 1 // Explodes if pulsed or cut while active, defuses a bomb that isn't active on cut -var/const/WIRE_UNBOLT = 2 // Unbolts the bomb if cut, hint on pulsed -var/const/WIRE_DELAY = 4 // Raises the timer on pulse, does nothing on cut -var/const/WIRE_PROCEED = 8 // Lowers the timer, explodes if cut while the bomb is active -var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut +#define BOMB_WIRE_BOOM 1 // Explodes if pulsed or cut while active, defuses a bomb that isn't active on cut +#define BOMB_WIRE_UNBOLT 2 // Unbolts the bomb if cut, hint on pulsed +#define BOMB_WIRE_DELAY 4 // Raises the timer on pulse, does nothing on cut +#define BOMB_WIRE_PROCEED 8 // Lowers the timer, explodes if cut while the bomb is active +#define BOMB_WIRE_ACTIVATE 16 // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut /datum/wires/syndicatebomb/GetWireName(index) switch(index) - if(WIRE_BOOM) + if(BOMB_WIRE_BOOM) return "Explode" - if(WIRE_UNBOLT) + if(BOMB_WIRE_UNBOLT) return "Unbolt" - if(WIRE_DELAY) + if(BOMB_WIRE_DELAY) return "Delay" - if(WIRE_PROCEED) + if(BOMB_WIRE_PROCEED) return "Proceed" - if(WIRE_ACTIVATE) + if(BOMB_WIRE_ACTIVATE) return "Activate" /datum/wires/syndicatebomb/CanUse(mob/living/L) @@ -35,13 +35,13 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if /datum/wires/syndicatebomb/UpdatePulsed(index) var/obj/machinery/syndicatebomb/B = holder switch(index) - if(WIRE_BOOM) + if(BOMB_WIRE_BOOM) if(B.active) holder.visible_message("[bicon(B)] An alarm sounds! It's go-") B.explode_now = TRUE - if(WIRE_UNBOLT) + if(BOMB_WIRE_UNBOLT) holder.visible_message("[bicon(holder)] The bolts spin in place for a moment.") - if(WIRE_DELAY) + if(BOMB_WIRE_DELAY) if(B.delayedbig) holder.visible_message("[bicon(B)] The bomb has already been delayed.") else @@ -49,7 +49,7 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if playsound(B, 'sound/machines/chime.ogg', 30, 1) B.detonation_timer += 300 B.delayedbig = TRUE - if(WIRE_PROCEED) + if(BOMB_WIRE_PROCEED) holder.visible_message("[bicon(B)] The bomb buzzes ominously!") playsound(B, 'sound/machines/buzz-sigh.ogg', 30, 1) var/seconds = B.seconds_remaining() @@ -59,7 +59,7 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if B.detonation_timer -= 100 else if(seconds >= 11) // Both to prevent negative timers and to have a little mercy. B.detonation_timer = world.time + 100 - if(WIRE_ACTIVATE) + if(BOMB_WIRE_ACTIVATE) if(!B.active && !B.defused) holder.visible_message("[bicon(B)] You hear the bomb start ticking!") B.activate() @@ -75,7 +75,7 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if /datum/wires/syndicatebomb/UpdateCut(index, mended) var/obj/machinery/syndicatebomb/B = holder switch(index) - if(WIRE_EXPLODE) + if(BOMB_WIRE_BOOM) if(mended) B.defused = FALSE // Cutting and mending all the wires of an inactive bomb will thus cure any sabotage. else @@ -84,19 +84,19 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if B.explode_now = TRUE else B.defused = TRUE - if(WIRE_UNBOLT) + if(BOMB_WIRE_UNBOLT) if(!mended && B.anchored) holder.visible_message("[bicon(B)] The bolts lift out of the ground!") playsound(B, 'sound/effects/stealthoff.ogg', 30, 1) B.anchored = FALSE - if(WIRE_PROCEED) + if(BOMB_WIRE_PROCEED) if(!mended && B.active) holder.visible_message("[bicon(B)] An alarm sounds! It's go-") B.explode_now = TRUE - if(WIRE_ACTIVATE) + if(BOMB_WIRE_ACTIVATE) if(!mended && B.active) holder.visible_message("[bicon(B)] The timer stops! The bomb has been defused!") B.active = FALSE B.defused = TRUE B.update_icon() - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/tesla_coil.dm b/code/datums/wires/tesla_coil.dm index ed60e927f45..bc513c6c6c0 100644 --- a/code/datums/wires/tesla_coil.dm +++ b/code/datums/wires/tesla_coil.dm @@ -2,11 +2,11 @@ wire_count = 1 holder_type = /obj/machinery/power/tesla_coil -var/const/WIRE_ZAP = 1 +#define TESLACOIL_WIRE_ZAP 1 /datum/wires/tesla_coil/GetWireName(index) switch(index) - if(WIRE_ZAP) + if(TESLACOIL_WIRE_ZAP) return "Zap" /datum/wires/tesla_coil/CanUse(mob/living/L) @@ -18,6 +18,6 @@ var/const/WIRE_ZAP = 1 /datum/wires/tesla_coil/UpdatePulsed(index) var/obj/machinery/power/tesla_coil/T = holder switch(index) - if(WIRE_ZAP) + if(TESLACOIL_WIRE_ZAP) T.zap() - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm index 3861cc68203..c9e6032afee 100644 --- a/code/datums/wires/vending.dm +++ b/code/datums/wires/vending.dm @@ -2,10 +2,10 @@ holder_type = /obj/machinery/vending wire_count = 4 -var/const/VENDING_WIRE_THROW = 1 -var/const/VENDING_WIRE_CONTRABAND = 2 -var/const/VENDING_WIRE_ELECTRIFY = 4 -var/const/VENDING_WIRE_IDSCAN = 8 +#define VENDING_WIRE_THROW 1 +#define VENDING_WIRE_CONTRABAND 2 +#define VENDING_WIRE_ELECTRIFY 4 +#define VENDING_WIRE_IDSCAN 8 /datum/wires/vending/GetWireName(index) switch(index) @@ -66,4 +66,4 @@ var/const/VENDING_WIRE_IDSCAN = 8 V.seconds_electrified = -1 if(VENDING_WIRE_IDSCAN) V.scan_id = 1 - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index a201617682d..1a407f2ec50 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -5,9 +5,9 @@ #define MAX_FLAG 65535 -var/list/same_wires = list() +GLOBAL_LIST_EMPTY(same_wires) // 12 colours, if you're adding more than 12 wires then add more colours here -var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink") +GLOBAL_LIST_INIT(wireColours, list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink")) /datum/wires @@ -39,11 +39,11 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", // Get the same wires else // We don't have any wires to copy yet, generate some and then copy it. - if(!same_wires[holder_type]) + if(!GLOB.same_wires[holder_type]) GenerateWires() - same_wires[holder_type] = src.wires.Copy() + GLOB.same_wires[holder_type] = src.wires.Copy() else - var/list/wires = same_wires[holder_type] + var/list/wires = GLOB.same_wires[holder_type] src.wires = wires // Reference the wires list. /datum/wires/Destroy() @@ -51,7 +51,7 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", return ..() /datum/wires/proc/GenerateWires() - var/list/colours_to_pick = wireColours.Copy() // Get a copy, not a reference. + var/list/colours_to_pick = GLOB.wireColours.Copy() // Get a copy, not a reference. var/list/indexes_to_pick = list() //Generate our indexes for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) @@ -81,7 +81,7 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", ui = new(user, src, ui_key, "wires.tmpl", holder.name, window_x, window_y) ui.open() -/datum/wires/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) +/datum/wires/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) var/data[0] var/list/replace_colours = null if(ishuman(user)) @@ -202,20 +202,20 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", // Example of use: /* -var/const/BOLTED= 1 -var/const/SHOCKED = 2 -var/const/SAFETY = 4 -var/const/POWER = 8 +#define NAME_WIRE_BOLTED 1 +#define NAME_WIRE_SHOCKED 2 +#define NAME_WIRE_SAFETY 4 +#define NAME_WIRE_POWER 8 /datum/wires/door/UpdateCut(var/index, var/mended) var/obj/machinery/door/airlock/A = holder switch(index) - if(BOLTED) + if(NAME_WIRE_BOLTED) if(!mended) A.bolt() - if(SHOCKED) + if(NAME_WIRE_SHOCKED) A.shock() - if(SAFETY ) + if(NAME_WIRE_SAFETY) A.safety() */ @@ -329,4 +329,4 @@ var/const/POWER = 8 /datum/wires/proc/Shuffle() wires_status = 0 - GenerateWires() \ No newline at end of file + GenerateWires() diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm index 1ec4f4eb7fc..1f8c83f5f04 100644 --- a/code/defines/procs/AStar.dm +++ b/code/defines/procs/AStar.dm @@ -1,184 +1,184 @@ -/* -A Star pathfinding algorithm -Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of -windows along the route into account. -Use: -your_list = AStar(start location, end location, moving atom, distance proc, max nodes, maximum node depth, minimum distance to target, adjacent proc, atom id, turfs to exclude, check only simulated) - -Optional extras to add on (in order): -Distance proc : the distance used in every A* calculation (length of path and heuristic) -MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) -Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) -Mintargetdist: Minimum distance to the target before path returns, could be used to get -near a target, but not right to it - for an AI mob with a gun, for example. -Adjacent proc : returns the turfs to consider around the actually processed node -Simulated only : whether to consider unsimulated turfs or not (used by some Adjacent proc) - -Also added 'exclude' turf to avoid travelling over; defaults to null - -Actual Adjacent procs : - - /turf/proc/reachableAdjacentTurfs : returns reachable turfs in cardinal directions (uses simulated_only) - - /turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos - -*/ - -////////////////////// -//PathNode object -////////////////////// - -//A* nodes variables -/PathNode - var/turf/source //turf associated with the PathNode - var/PathNode/prevNode //link to the parent PathNode - var/f //A* Node weight (f = g + h) - var/g //A* movement cost variable - var/h //A* heuristic variable - var/nt //count the number of Nodes traversed - -/PathNode/New(s,p,pg,ph,pnt) - source = s - prevNode = p - g = pg - h = ph - f = g + h - source.PNode = src - nt = pnt - -/PathNode/proc/calc_f() - f = g + h - -////////////////////// -//A* procs -////////////////////// - -//the weighting function, used in the A* algorithm -/proc/PathWeightCompare(PathNode/a, PathNode/b) - return a.f - b.f - -//reversed so that the Heap is a MinHeap rather than a MaxHeap -/proc/HeapPathWeightCompare(PathNode/a, PathNode/b) - return b.f - a.f - -//wrapper that returns an empty list if A* failed to find a path -/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) - var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent, id, exclude, simulated_only) - if(!path) - path = list() - return path - -//the actual algorithm -/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) - - //sanitation - var/start = get_turf(caller) - if(!start) - return 0 - - if(maxnodes) - //if start turf is farther than maxnodes from end turf, no need to do anything - if(call(start, dist)(end) > maxnodes) - return 0 - maxnodedepth = maxnodes //no need to consider path longer than maxnodes - - var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list - var/list/closed = new() //the closed list - var/list/path = null //the returned path, if any - var/PathNode/cur //current processed turf - - //initialization - open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0)) - - //then run the main loop - while(!open.IsEmpty() && !path) - { - //get the lower f node on the open list - cur = open.Pop() //get the lower f turf in the open list - closed.Add(cur.source) //and tell we've processed it - - //if we only want to get near the target, check if we're close enough - var/closeenough - if(mintargetdist) - closeenough = call(cur.source,dist)(end) <= mintargetdist - - //if too many steps, abandon that path - if(maxnodedepth && (cur.nt > maxnodedepth)) - continue - - //found the target turf (or close enough), let's create the path to it - if(cur.source == end || closeenough) - path = new() - path.Add(cur.source) - - while(cur.prevNode) - cur = cur.prevNode - path.Add(cur.source) - - break - - //get adjacents turfs using the adjacent proc, checking for access with id - var/list/L = call(cur.source,adjacent)(caller, id, simulated_only) - for(var/turf/T in L) - if(T == exclude || (T in closed)) - continue - - var/newg = cur.g + call(cur.source,dist)(T) - if(!T.PNode) //is not already in open list, so add it - open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1)) - else //is already in open list, check if it's a better way from the current turf - if(newg < T.PNode.g) - T.PNode.prevNode = cur - T.PNode.g = newg - T.PNode.calc_f() - T.PNode.nt = cur.nt + 1 - open.ReSort(T.PNode)//reorder the changed element in the list - - } - - //cleaning after us - for(var/PathNode/PN in open.L) - PN.source.PNode = null - for(var/turf/T in closed) - T.PNode = null - - //reverse the path to get it from start to finish - if(path) - for(var/i = 1; i <= path.len/2; i++) - path.Swap(i,path.len-i+1) - - return path - -//Returns adjacent turfs in cardinal directions that are reachable -//simulated_only controls whether only simulated turfs are considered or not -/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only) - var/list/L = new() - var/turf/simulated/T - - for(var/dir in cardinal) - T = get_step(src,dir) - if(!T || (simulated_only && !istype(T))) - continue - if(!T.density && !LinkBlockedWithAccess(T,caller, ID)) - L.Add(T) - return L - -//Returns adjacent turfs in cardinal directions that are reachable via atmos -/turf/proc/reachableAdjacentAtmosTurfs() - return atmos_adjacent_turfs - -/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID) - var/adir = get_dir(src, T) - var/rdir = get_dir(T, src) - - for(var/obj/structure/window/W in src) - if(!W.CanAStarPass(ID, adir)) - return 1 - for(var/obj/machinery/door/window/W in src) - if(!W.CanAStarPass(ID, adir)) - return 1 - for(var/obj/O in T) - if(!O.CanAStarPass(ID, rdir, caller)) - return 1 - - return 0 +/* +A Star pathfinding algorithm +Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of +windows along the route into account. +Use: +your_list = AStar(start location, end location, moving atom, distance proc, max nodes, maximum node depth, minimum distance to target, adjacent proc, atom id, turfs to exclude, check only simulated) + +Optional extras to add on (in order): +Distance proc : the distance used in every A* calculation (length of path and heuristic) +MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) +Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) +Mintargetdist: Minimum distance to the target before path returns, could be used to get +near a target, but not right to it - for an AI mob with a gun, for example. +Adjacent proc : returns the turfs to consider around the actually processed node +Simulated only : whether to consider unsimulated turfs or not (used by some Adjacent proc) + +Also added 'exclude' turf to avoid travelling over; defaults to null + +Actual Adjacent procs : + + /turf/proc/reachableAdjacentTurfs : returns reachable turfs in cardinal directions (uses simulated_only) + + /turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos + +*/ + +////////////////////// +//PathNode object +////////////////////// + +//A* nodes variables +/PathNode + var/turf/source //turf associated with the PathNode + var/PathNode/prevNode //link to the parent PathNode + var/f //A* Node weight (f = g + h) + var/g //A* movement cost variable + var/h //A* heuristic variable + var/nt //count the number of Nodes traversed + +/PathNode/New(s,p,pg,ph,pnt) + source = s + prevNode = p + g = pg + h = ph + f = g + h + source.PNode = src + nt = pnt + +/PathNode/proc/calc_f() + f = g + h + +////////////////////// +//A* procs +////////////////////// + +//the weighting function, used in the A* algorithm +/proc/PathWeightCompare(PathNode/a, PathNode/b) + return a.f - b.f + +//reversed so that the Heap is a MinHeap rather than a MaxHeap +/proc/HeapPathWeightCompare(PathNode/a, PathNode/b) + return b.f - a.f + +//wrapper that returns an empty list if A* failed to find a path +/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) + var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent, id, exclude, simulated_only) + if(!path) + path = list() + return path + +//the actual algorithm +/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) + + //sanitation + var/start = get_turf(caller) + if(!start) + return 0 + + if(maxnodes) + //if start turf is farther than maxnodes from end turf, no need to do anything + if(call(start, dist)(end) > maxnodes) + return 0 + maxnodedepth = maxnodes //no need to consider path longer than maxnodes + + var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list + var/list/closed = new() //the closed list + var/list/path = null //the returned path, if any + var/PathNode/cur //current processed turf + + //initialization + open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0)) + + //then run the main loop + while(!open.IsEmpty() && !path) + { + //get the lower f node on the open list + cur = open.Pop() //get the lower f turf in the open list + closed.Add(cur.source) //and tell we've processed it + + //if we only want to get near the target, check if we're close enough + var/closeenough + if(mintargetdist) + closeenough = call(cur.source,dist)(end) <= mintargetdist + + //if too many steps, abandon that path + if(maxnodedepth && (cur.nt > maxnodedepth)) + continue + + //found the target turf (or close enough), let's create the path to it + if(cur.source == end || closeenough) + path = new() + path.Add(cur.source) + + while(cur.prevNode) + cur = cur.prevNode + path.Add(cur.source) + + break + + //get adjacents turfs using the adjacent proc, checking for access with id + var/list/L = call(cur.source,adjacent)(caller, id, simulated_only) + for(var/turf/T in L) + if(T == exclude || (T in closed)) + continue + + var/newg = cur.g + call(cur.source,dist)(T) + if(!T.PNode) //is not already in open list, so add it + open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1)) + else //is already in open list, check if it's a better way from the current turf + if(newg < T.PNode.g) + T.PNode.prevNode = cur + T.PNode.g = newg + T.PNode.calc_f() + T.PNode.nt = cur.nt + 1 + open.ReSort(T.PNode)//reorder the changed element in the list + + } + + //cleaning after us + for(var/PathNode/PN in open.L) + PN.source.PNode = null + for(var/turf/T in closed) + T.PNode = null + + //reverse the path to get it from start to finish + if(path) + for(var/i = 1; i <= path.len/2; i++) + path.Swap(i,path.len-i+1) + + return path + +//Returns adjacent turfs in cardinal directions that are reachable +//simulated_only controls whether only simulated turfs are considered or not +/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only) + var/list/L = new() + var/turf/simulated/T + + for(var/dir in GLOB.cardinal) + T = get_step(src,dir) + if(!T || (simulated_only && !istype(T))) + continue + if(!T.density && !LinkBlockedWithAccess(T,caller, ID)) + L.Add(T) + return L + +//Returns adjacent turfs in cardinal directions that are reachable via atmos +/turf/proc/reachableAdjacentAtmosTurfs() + return atmos_adjacent_turfs + +/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID) + var/adir = get_dir(src, T) + var/rdir = get_dir(T, src) + + for(var/obj/structure/window/W in src) + if(!W.CanAStarPass(ID, adir)) + return 1 + for(var/obj/machinery/door/window/W in src) + if(!W.CanAStarPass(ID, adir)) + return 1 + for(var/obj/O in T) + if(!O.CanAStarPass(ID, rdir, caller)) + return 1 + + return 0 diff --git a/code/defines/procs/admin.dm b/code/defines/procs/admin.dm index b7036f9fbdc..fb8ca665906 100644 --- a/code/defines/procs/admin.dm +++ b/code/defines/procs/admin.dm @@ -7,7 +7,7 @@ /proc/key_name_helper(whom, include_name, include_link = FALSE, type = null) if(include_link != FALSE && include_link != TRUE) - log_runtime(EXCEPTION("Key_name was called with an incorrect include_link [include_link]"), src) + log_runtime(EXCEPTION("Key_name was called with an incorrect include_link [include_link]")) var/mob/M var/client/C diff --git a/code/defines/procs/announce.dm b/code/defines/procs/announce.dm index 3a70d05029e..a2b80440ecb 100644 --- a/code/defines/procs/announce.dm +++ b/code/defines/procs/announce.dm @@ -1,7 +1,7 @@ -/var/datum/announcement/minor/minor_announcement = new() -/var/datum/announcement/priority/priority_announcement = new(do_log = 0) -/var/datum/announcement/priority/command/command_announcement = new(do_log = 0, do_newscast = 0) -/var/datum/announcement/priority/command/event/event_announcement = new(do_log = 0, do_newscast = 0) +GLOBAL_DATUM_INIT(minor_announcement, /datum/announcement/minor, new()) +GLOBAL_DATUM_INIT(priority_announcement, /datum/announcement/priority, new(do_log = 0)) +GLOBAL_DATUM_INIT(command_announcement, /datum/announcement/priority/command, new(do_log = 0, do_newscast = 0)) +GLOBAL_DATUM_INIT(event_announcement, /datum/announcement/priority/command/event, new(do_log = 0, do_newscast = 0)) /datum/announcement var/title = "Attention" diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm index b04a4c3b93a..76e5a1fd0a6 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -1,206 +1,199 @@ -//cursors -#define Default_Cursor 0 -#define Client_Cursor 1 -#define Server_Cursor 2 -//conversions -#define TEXT_CONV 1 -#define RSC_FILE_CONV 2 -#define NUMBER_CONV 3 -//column flag values: -#define IS_NUMERIC 1 -#define IS_BINARY 2 -#define IS_NOT_NULL 4 -#define IS_PRIMARY_KEY 8 -#define IS_UNSIGNED 16 -//types -#define TINYINT 1 -#define SMALLINT 2 -#define MEDIUMINT 3 -#define INTEGER 4 -#define BIGINT 5 -#define DECIMAL 6 -#define FLOAT 7 -#define DOUBLE 8 -#define DATE 9 -#define DATETIME 10 -#define TIMESTAMP 11 -#define TIME 12 -#define STRING 13 -#define BLOB 14 -// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew - - -// Deprecated! See global.dm for new configuration vars -/* -var/DB_SERVER = "" // This is the location of your MySQL server (localhost is USUALLY fine) -var/DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default) -*/ - -DBConnection - var/_db_con // This variable contains a reference to the actual database connection. - var/dbi // This variable is a string containing the DBI MySQL requires. - var/user // This variable contains the username data. - var/password // This variable contains the password data. - var/default_cursor // This contains the default database cursor data. - // - var/server = "" - var/port = 3306 - -DBConnection/New(dbi_handler,username,password_handler,cursor_handler) - src.dbi = dbi_handler - src.user = username - src.password = password_handler - src.default_cursor = cursor_handler - _db_con = _dm_db_new_con() - -DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) - if(!config.sql_enabled) - return 0 - if(!src) return 0 - cursor_handler = src.default_cursor - if(!cursor_handler) cursor_handler = Default_Cursor - return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) - -DBConnection/proc/Disconnect() return _dm_db_close(_db_con) - -DBConnection/proc/IsConnected() - if(!config.sql_enabled) return 0 - var/success = _dm_db_is_connected(_db_con) - return success - -DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) - -DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) -DBConnection/proc/SelectDB(database_name,dbi) - if(IsConnected()) Disconnect() - //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) - return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) -DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) - - -DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) - if(sql_query) src.sql = sql_query - if(connection_handler) src.db_connection = connection_handler - if(cursor_handler) src.default_cursor = cursor_handler - _db_query = _dm_db_new_query() - return ..() - - -DBQuery - var/sql // The sql query being executed. - var/default_cursor - var/list/columns //list of DB Columns populated by Columns() - var/list/conversions - var/list/item[0] //list of data values populated by NextRow() - - var/DBConnection/db_connection - var/_db_query - -DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler - -DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) - Close() - return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) - -DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) - -DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) - -DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) - -DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) - -DBQuery/proc/Columns() - if(!columns) - columns = _dm_db_columns(_db_query,/DBColumn) - return columns - -DBQuery/proc/GetRowData() - var/list/columns = Columns() - var/list/results - if(columns.len) - results = list() - for(var/C in columns) - results+=C - var/DBColumn/cur_col = columns[C] - results[C] = src.item[(cur_col.position+1)] - return results - -DBQuery/proc/Close() - item.len = 0 - columns = null - conversions = null - return _dm_db_close(_db_query) - -DBQuery/proc/Quote(str) - return db_connection.Quote(str) - -DBQuery/proc/SetConversion(column,conversion) - if(istext(column)) column = columns.Find(column) - if(!conversions) conversions = new/list(column) - else if(conversions.len < column) conversions.len = column - conversions[column] = conversion - - -DBColumn - var/name - var/table - var/position //1-based index into item data - var/sql_type - var/flags - var/length - var/max_length - -DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) - src.name = name_handler - src.table = table_handler - src.position = position_handler - src.sql_type = type_handler - src.flags = flag_handler - src.length = length_handler - src.max_length = max_length_handler - return ..() - - -DBColumn/proc/SqlTypeName(type_handler=src.sql_type) - switch(type_handler) - if(TINYINT) return "TINYINT" - if(SMALLINT) return "SMALLINT" - if(MEDIUMINT) return "MEDIUMINT" - if(INTEGER) return "INTEGER" - if(BIGINT) return "BIGINT" - if(FLOAT) return "FLOAT" - if(DOUBLE) return "DOUBLE" - if(DATE) return "DATE" - if(DATETIME) return "DATETIME" - if(TIMESTAMP) return "TIMESTAMP" - if(TIME) return "TIME" - if(STRING) return "STRING" - if(BLOB) return "BLOB" - - -#undef Default_Cursor -#undef Client_Cursor -#undef Server_Cursor -#undef TEXT_CONV -#undef RSC_FILE_CONV -#undef NUMBER_CONV -#undef IS_NUMERIC -#undef IS_BINARY -#undef IS_NOT_NULL -#undef IS_PRIMARY_KEY -#undef IS_UNSIGNED -#undef TINYINT -#undef SMALLINT -#undef MEDIUMINT -#undef INTEGER -#undef BIGINT -#undef DECIMAL -#undef FLOAT -#undef DOUBLE -#undef DATE -#undef DATETIME -#undef TIMESTAMP -#undef TIME -#undef STRING -#undef BLOB +//cursors +#define Default_Cursor 0 +#define Client_Cursor 1 +#define Server_Cursor 2 +//conversions +#define TEXT_CONV 1 +#define RSC_FILE_CONV 2 +#define NUMBER_CONV 3 +//column flag values: +#define IS_NUMERIC 1 +#define IS_BINARY 2 +#define IS_NOT_NULL 4 +#define IS_PRIMARY_KEY 8 +#define IS_UNSIGNED 16 +//types +#define TINYINT 1 +#define SMALLINT 2 +#define MEDIUMINT 3 +#define INTEGER 4 +#define BIGINT 5 +#define DECIMAL 6 +#define FLOAT 7 +#define DOUBLE 8 +#define DATE 9 +#define DATETIME 10 +#define TIMESTAMP 11 +#define TIME 12 +#define STRING 13 +#define BLOB 14 +// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew + +DBConnection + var/_db_con // This variable contains a reference to the actual database connection. + var/dbi // This variable is a string containing the DBI MySQL requires. + var/user // This variable contains the username data. + var/password // This variable contains the password data. + var/default_cursor // This contains the default database cursor data. + // + var/server = "" + var/port = 3306 + +DBConnection/New(dbi_handler,username,password_handler,cursor_handler) + src.dbi = dbi_handler + src.user = username + src.password = password_handler + src.default_cursor = cursor_handler + _db_con = _dm_db_new_con() + +DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) + if(!config.sql_enabled) + return 0 + if(!src) return 0 + cursor_handler = src.default_cursor + if(!cursor_handler) cursor_handler = Default_Cursor + return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) + +DBConnection/proc/Disconnect() return _dm_db_close(_db_con) + +DBConnection/proc/IsConnected() + if(!config.sql_enabled) return 0 + var/success = _dm_db_is_connected(_db_con) + return success + +DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) + +DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) +DBConnection/proc/SelectDB(database_name,dbi) + if(IsConnected()) Disconnect() + //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) + return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) +DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) + + +DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) + if(sql_query) src.sql = sql_query + if(connection_handler) src.db_connection = connection_handler + if(cursor_handler) src.default_cursor = cursor_handler + _db_query = _dm_db_new_query() + return ..() + + +DBQuery + var/sql // The sql query being executed. + var/default_cursor + var/list/columns //list of DB Columns populated by Columns() + var/list/conversions + var/list/item[0] //list of data values populated by NextRow() + + var/DBConnection/db_connection + var/_db_query + +DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler + +DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) + Close() + return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) + +DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) + +DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) + +DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) + +DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) + +DBQuery/proc/Columns() + if(!columns) + columns = _dm_db_columns(_db_query,/DBColumn) + return columns + +DBQuery/proc/GetRowData() + var/list/columns = Columns() + var/list/results + if(columns.len) + results = list() + for(var/C in columns) + results+=C + var/DBColumn/cur_col = columns[C] + results[C] = src.item[(cur_col.position+1)] + return results + +DBQuery/proc/Close() + item.len = 0 + columns = null + conversions = null + return _dm_db_close(_db_query) + +DBQuery/proc/Quote(str) + return db_connection.Quote(str) + +DBQuery/proc/SetConversion(column,conversion) + if(istext(column)) column = columns.Find(column) + if(!conversions) conversions = new/list(column) + else if(conversions.len < column) conversions.len = column + conversions[column] = conversion + + +DBColumn + var/name + var/table + var/position //1-based index into item data + var/sql_type + var/flags + var/length + var/max_length + +DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) + src.name = name_handler + src.table = table_handler + src.position = position_handler + src.sql_type = type_handler + src.flags = flag_handler + src.length = length_handler + src.max_length = max_length_handler + return ..() + + +DBColumn/proc/SqlTypeName(type_handler=src.sql_type) + switch(type_handler) + if(TINYINT) return "TINYINT" + if(SMALLINT) return "SMALLINT" + if(MEDIUMINT) return "MEDIUMINT" + if(INTEGER) return "INTEGER" + if(BIGINT) return "BIGINT" + if(FLOAT) return "FLOAT" + if(DOUBLE) return "DOUBLE" + if(DATE) return "DATE" + if(DATETIME) return "DATETIME" + if(TIMESTAMP) return "TIMESTAMP" + if(TIME) return "TIME" + if(STRING) return "STRING" + if(BLOB) return "BLOB" + + +#undef Default_Cursor +#undef Client_Cursor +#undef Server_Cursor +#undef TEXT_CONV +#undef RSC_FILE_CONV +#undef NUMBER_CONV +#undef IS_NUMERIC +#undef IS_BINARY +#undef IS_NOT_NULL +#undef IS_PRIMARY_KEY +#undef IS_UNSIGNED +#undef TINYINT +#undef SMALLINT +#undef MEDIUMINT +#undef INTEGER +#undef BIGINT +#undef DECIMAL +#undef FLOAT +#undef DOUBLE +#undef DATE +#undef DATETIME +#undef TIMESTAMP +#undef TIME +#undef STRING +#undef BLOB diff --git a/code/defines/procs/radio.dm b/code/defines/procs/radio.dm index 84d3ddaae78..a3d6647ef41 100644 --- a/code/defines/procs/radio.dm +++ b/code/defines/procs/radio.dm @@ -35,8 +35,8 @@ var/list/receiver_reception = new /proc/get_message_server() - if(message_servers) - for(var/obj/machinery/message_server/MS in message_servers) + if(GLOB.message_servers) + for(var/obj/machinery/message_server/MS in GLOB.message_servers) if(MS.active) return MS return null diff --git a/code/defines/procs/records.dm b/code/defines/procs/records.dm index 256ae51dbf2..a74680ec578 100644 --- a/code/defines/procs/records.dm +++ b/code/defines/procs/records.dm @@ -20,7 +20,7 @@ G.fields["religion"] = "Unknown" G.fields["photo_front"] = front G.fields["photo_side"] = side - data_core.general += G + GLOB.data_core.general += G qdel(dummy) return G @@ -36,11 +36,11 @@ R.fields["ma_crim"] = "None" R.fields["ma_crim_d"] = "No major crime convictions." R.fields["notes"] = "No notes." - data_core.security += R + GLOB.data_core.security += R return R /proc/find_security_record(field, value) - return find_record(field, value, data_core.security) + return find_record(field, value, GLOB.data_core.security) /proc/find_record(field, value, list/L) for(var/datum/data/record/R in L) diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index e68bb7ecc63..e6e111880fb 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -1,163 +1,163 @@ -/proc/sql_poll_players() - if(!config.sql_enabled) - return - var/playercount = 0 - for(var/mob/M in GLOB.player_list) - if(M.client) - playercount += 1 - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during player polling. Failed to connect.") - else - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, time) VALUES ([playercount], '[sqltime]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during player polling. Error : \[[err]\]\n") - - -/proc/sql_poll_admins() - if(!config.sql_enabled) - return - var/admincount = GLOB.admins.len - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during admin polling. Failed to connect.") - else - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (admincount, time) VALUES ([admincount], '[sqltime]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during admin polling. Error : \[[err]\]\n") - -/proc/sql_report_round_start() - // TODO - if(!config.sql_enabled) - return - -/proc/sql_report_round_end() - // TODO - if(!config.sql_enabled) - return - -/proc/sql_report_death(mob/living/carbon/human/H) - if(!config.sql_enabled) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/area/placeofdeath = get_area(H.loc) - var/podname = "Unknown" - if(placeofdeath) - podname = sanitizeSQL(placeofdeath.name) - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" -// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - - -/proc/sql_report_cyborg_death(mob/living/silicon/robot/H) - if(!config.sql_enabled) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/turf/T = H.loc - var/area/placeofdeath = get_area(T.loc) - var/podname = sanitizeSQL(placeofdeath.name) - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" -// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - -/proc/statistic_cycle() - if(!config.sql_enabled) - return - while(1) - sql_poll_players() - sleep(600) - sql_poll_admins() - sleep(6000) //Poll every ten minutes - -//This proc is used for feedback. It is executed at round end. -/proc/sql_commit_feedback() - if(!blackbox) - log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") - return - - //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. - var/list/datum/feedback_variable/content = blackbox.get_round_feedback() - - if(!content) - log_game("Round ended without any feedback being generated. No feedback was sent to the database.") - return - - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during feedback reporting. Failed to connect.") - else - - var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM [format_table_name("feedback")]") - max_query.Execute() - - var/newroundid - - while(max_query.NextRow()) - newroundid = max_query.item[1] - - if(!(isnum(newroundid))) - newroundid = text2num(newroundid) - - if(isnum(newroundid)) - newroundid++ - else - newroundid = 1 - - for(var/datum/feedback_variable/item in content) - var/variable = item.get_variable() - var/value = item.get_value() - - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("feedback")] (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during feedback reporting. Error : \[[err]\]\n") +/proc/sql_poll_players() + if(!config.sql_enabled) + return + var/playercount = 0 + for(var/mob/M in GLOB.player_list) + if(M.client) + playercount += 1 + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during player polling. Failed to connect.") + else + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, time) VALUES ([playercount], '[sqltime]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during player polling. Error : \[[err]\]\n") + + +/proc/sql_poll_admins() + if(!config.sql_enabled) + return + var/admincount = GLOB.admins.len + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during admin polling. Failed to connect.") + else + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (admincount, time) VALUES ([admincount], '[sqltime]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during admin polling. Error : \[[err]\]\n") + +/proc/sql_report_round_start() + // TODO + if(!config.sql_enabled) + return + +/proc/sql_report_round_end() + // TODO + if(!config.sql_enabled) + return + +/proc/sql_report_death(mob/living/carbon/human/H) + if(!config.sql_enabled) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/area/placeofdeath = get_area(H.loc) + var/podname = "Unknown" + if(placeofdeath) + podname = sanitizeSQL(placeofdeath.name) + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" +// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()])") + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + + +/proc/sql_report_cyborg_death(mob/living/silicon/robot/H) + if(!config.sql_enabled) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/turf/T = H.loc + var/area/placeofdeath = get_area(T.loc) + var/podname = sanitizeSQL(placeofdeath.name) + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" +// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + +/proc/statistic_cycle() + if(!config.sql_enabled) + return + while(1) + sql_poll_players() + sleep(600) + sql_poll_admins() + sleep(6000) //Poll every ten minutes + +//This proc is used for feedback. It is executed at round end. +/proc/sql_commit_feedback() + if(!GLOB.blackbox) + log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") + return + + //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. + var/list/datum/feedback_variable/content = GLOB.blackbox.get_round_feedback() + + if(!content) + log_game("Round ended without any feedback being generated. No feedback was sent to the database.") + return + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during feedback reporting. Failed to connect.") + else + + var/DBQuery/max_query = GLOB.dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM [format_table_name("feedback")]") + max_query.Execute() + + var/newroundid + + while(max_query.NextRow()) + newroundid = max_query.item[1] + + if(!(isnum(newroundid))) + newroundid = text2num(newroundid) + + if(isnum(newroundid)) + newroundid++ + else + newroundid = 1 + + for(var/datum/feedback_variable/item in content) + var/variable = item.get_variable() + var/value = item.get_value() + + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("feedback")] (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during feedback reporting. Error : \[[err]\]\n") diff --git a/code/defines/vox_sounds.dm b/code/defines/vox_sounds.dm index 794a259ea4e..1d7bb9753a0 100644 --- a/code/defines/vox_sounds.dm +++ b/code/defines/vox_sounds.dm @@ -1,7 +1,7 @@ // List is required to compile the resources into the game when it loads. // Dynamically loading it has bad results with sounds overtaking each other, even with the wait variable. -var/list/vox_sounds = list("," = 'sound/vox_fem/,.ogg', +GLOBAL_LIST_INIT(vox_sounds, list("," = 'sound/vox_fem/,.ogg', "." = 'sound/vox_fem/..ogg', "a" = 'sound/vox_fem/a.ogg', "abortions" = 'sound/vox_fem/abortions.ogg', @@ -1267,4 +1267,4 @@ var/list/vox_sounds = list("," = 'sound/vox_fem/,.ogg', "zero" = 'sound/vox_fem/zero.ogg', "zone" = 'sound/vox_fem/zone.ogg', "zulu" = 'sound/vox_fem/zulu.ogg' -) \ No newline at end of file +)) diff --git a/code/game/area/Dynamic areas.dm b/code/game/area/Dynamic areas.dm index c6d1154847b..10957e11d2a 100644 --- a/code/game/area/Dynamic areas.dm +++ b/code/game/area/Dynamic areas.dm @@ -41,4 +41,4 @@ match_width = 5 match_height = 4 requires_power = FALSE - dynamic_lighting = DYNAMIC_LIGHTING_FORCED \ No newline at end of file + dynamic_lighting = DYNAMIC_LIGHTING_FORCED diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 97eca30cbfe..bbfcc915c79 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -15,30 +15,30 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /*Adding a wizard area teleport list because motherfucking lag -- Urist*/ /*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/ -var/list/teleportlocs = list() +GLOBAL_LIST_EMPTY(teleportlocs) /hook/startup/proc/process_teleport_locs() for(var/area/AR in world) if(AR.no_teleportlocs) continue - if(teleportlocs.Find(AR.name)) continue + if(GLOB.teleportlocs.Find(AR.name)) continue var/turf/picked = safepick(get_area_turfs(AR.type)) if(picked && is_station_level(picked.z)) - teleportlocs += AR.name - teleportlocs[AR.name] = AR + GLOB.teleportlocs += AR.name + GLOB.teleportlocs[AR.name] = AR - teleportlocs = sortAssoc(teleportlocs) + GLOB.teleportlocs = sortAssoc(GLOB.teleportlocs) return 1 -var/list/ghostteleportlocs = list() +GLOBAL_LIST_EMPTY(ghostteleportlocs) /hook/startup/proc/process_ghost_teleport_locs() for(var/area/AR in world) - if(ghostteleportlocs.Find(AR.name)) continue + if(GLOB.ghostteleportlocs.Find(AR.name)) continue var/list/turfs = get_area_turfs(AR.type) if(turfs.len) - ghostteleportlocs += AR.name - ghostteleportlocs[AR.name] = AR + GLOB.ghostteleportlocs += AR.name + GLOB.ghostteleportlocs[AR.name] = AR - ghostteleportlocs = sortAssoc(ghostteleportlocs) + GLOB.ghostteleportlocs = sortAssoc(GLOB.ghostteleportlocs) return 1 @@ -2285,7 +2285,7 @@ var/list/ghostteleportlocs = list() */ // CENTCOM -var/list/centcom_areas = list ( +GLOBAL_LIST_INIT(centcom_areas, list( /area/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, @@ -2294,10 +2294,10 @@ var/list/centcom_areas = list ( /area/shuttle/transport1, /area/shuttle/administration/centcom, /area/shuttle/specops/centcom, -) +)) //SPACE STATION 13 -var/list/the_station_areas = list ( +GLOBAL_LIST_INIT(the_station_areas, list( /area/shuttle/arrival, /area/shuttle/escape, /area/shuttle/escape_pod1/station, @@ -2344,4 +2344,4 @@ var/list/the_station_areas = list ( /area/turret_protected/ai_upload, //do not try to simplify to "/area/turret_protected" --rastaf0 /area/turret_protected/ai_upload_foyer, /area/turret_protected/ai, -) +)) diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm index 0bc7b5f001b..8f65ac634f4 100644 --- a/code/game/area/ai_monitored.dm +++ b/code/game/area/ai_monitored.dm @@ -1,26 +1,24 @@ -/area/ai_monitored - name = "AI Monitored Area" - var/obj/machinery/camera/motioncamera = null - - -/area/ai_monitored/New() - ..() - // locate and store the motioncamera - spawn (20) // spawn on a delay to let turfs/objs load - for(var/obj/machinery/camera/M in src) - if(M.isMotion()) - motioncamera = M - M.area_motion = src - return - return - -/area/ai_monitored/Entered(atom/movable/O) - ..() - if(ismob(O) && motioncamera) - motioncamera.newTarget(O) - -/area/ai_monitored/Exited(atom/movable/O) - if(ismob(O) && motioncamera) - motioncamera.lostTarget(O) - - +/area/ai_monitored + name = "AI Monitored Area" + var/obj/machinery/camera/motioncamera = null + + +/area/ai_monitored/LateInitialize() + . = ..() + // locate and store the motioncamera + for(var/obj/machinery/camera/M in src) + if(M.isMotion()) + motioncamera = M + M.area_motion = src + break + +/area/ai_monitored/Entered(atom/movable/O) + ..() + if(ismob(O) && motioncamera) + motioncamera.newTarget(O) + +/area/ai_monitored/Exited(atom/movable/O) + if(ismob(O) && motioncamera) + motioncamera.lostTarget(O) + + diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 26dc6ddbf56..6ad45532641 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -1,453 +1,453 @@ -/area - var/fire = null - var/atmosalm = ATMOS_ALARM_NONE - var/poweralm = TRUE - var/party = null - var/report_alerts = TRUE // Should atmos alerts notify the AI/computers - level = null - name = "Space" - icon = 'icons/turf/areas.dmi' - icon_state = "unknown" - layer = AREA_LAYER - plane = BLACKNESS_PLANE //Keeping this on the default plane, GAME_PLANE, will make area overlays fail to render on FLOOR_PLANE. - luminosity = 0 - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - invisibility = INVISIBILITY_LIGHTING - var/valid_territory = TRUE //used for cult summoning areas on station zlevel - var/map_name // Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints. - var/lightswitch = TRUE - - var/eject = null - - var/debug = FALSE - var/requires_power = TRUE - var/always_unpowered = FALSE //this gets overriden to 1 for space in area/New() - - var/power_equip = TRUE - var/power_light = TRUE - var/power_environ = TRUE - var/music = null - var/used_equip = FALSE - var/used_light = FALSE - var/used_environ = FALSE - var/static_equip - var/static_light = FALSE - var/static_environ - - var/has_gravity = TRUE - var/list/apc = list() - var/no_air = null - - var/air_doors_activated = FALSE - - var/tele_proof = FALSE - var/no_teleportlocs = FALSE - - var/outdoors = FALSE //For space, the asteroid, lavaland, etc. Used with blueprints to determine if we are adding a new area (vs editing a station room) - var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default? - var/nad_allowed = FALSE //is the station NAD allowed on this area? - - // This var is used with the maploader (modules/awaymissions/maploader/reader.dm) - // if this is 1, when used in a map snippet, this will instantiate a unique - // area from any other instances already present (meaning you can have - // separate APCs, and so on) - var/there_can_be_many = FALSE - - var/global/global_uid = 0 - var/uid - - var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\ - 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\ - 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\ - 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\ - 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\ - 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg') - - var/fast_despawn = FALSE - var/can_get_auto_cryod = TRUE - var/hide_attacklogs = FALSE // For areas such as thunderdome, lavaland syndiebase, etc which generate a lot of spammy attacklogs. Reduces log priority. - - var/parallax_movedir = 0 - var/moving = FALSE - -/area/Initialize(mapload) - GLOB.all_areas += src - icon_state = "" - layer = AREA_LAYER - uid = ++global_uid - - map_name = name // Save the initial (the name set in the map) name of the area. - - if(requires_power) - luminosity = 0 - else - power_light = TRUE - power_equip = TRUE - power_environ = TRUE - - if(dynamic_lighting == DYNAMIC_LIGHTING_FORCED) - dynamic_lighting = DYNAMIC_LIGHTING_ENABLED - luminosity = 0 - else if(dynamic_lighting != DYNAMIC_LIGHTING_IFSTARLIGHT) - dynamic_lighting = DYNAMIC_LIGHTING_DISABLED - if(dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT) - dynamic_lighting = config.starlight ? DYNAMIC_LIGHTING_ENABLED : DYNAMIC_LIGHTING_DISABLED - - . = ..() - - blend_mode = BLEND_MULTIPLY // Putting this in the constructor so that it stops the icons being screwed up in the map editor. - - if(!IS_DYNAMIC_LIGHTING(src)) - add_overlay(/obj/effect/fullbright) - - reg_in_areas_in_z() - - return INITIALIZE_HINT_LATELOAD - -/area/LateInitialize() - . = ..() - power_change() // all machines set to current power level, also updates lighting icon - -/area/proc/reg_in_areas_in_z() - if(contents.len) - var/list/areas_in_z = space_manager.areas_in_z - var/z - for(var/i in 1 to contents.len) - var/atom/thing = contents[i] - if(!thing) - continue - z = thing.z - break - if(!z) - WARNING("No z found for [src]") - return - if(!areas_in_z["[z]"]) - areas_in_z["[z]"] = list() - areas_in_z["[z]"] += src - -/area/proc/get_cameras() - var/list/cameras = list() - for(var/obj/machinery/camera/C in src) - cameras += C - return cameras - - -/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE) - if(report_alerts) - if(danger_level == ATMOS_ALARM_NONE) - SSalarms.atmosphere_alarm.clearAlarm(src, alarm_source) - else - SSalarms.atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) - - //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care. - for(var/obj/machinery/alarm/AA in src) - if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force) - danger_level = max(danger_level, AA.danger_level) - - if(danger_level != atmosalm) - if(danger_level < ATMOS_ALARM_WARNING && atmosalm >= ATMOS_ALARM_WARNING) - //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise - air_doors_open() - else if(danger_level >= ATMOS_ALARM_DANGER && atmosalm < ATMOS_ALARM_DANGER) - air_doors_close() - - atmosalm = danger_level - for(var/obj/machinery/alarm/AA in src) - AA.update_icon() - - air_alarm_repository.update_cache(src) - return 1 - air_alarm_repository.update_cache(src) - return 0 - -/area/proc/air_doors_close() - if(!air_doors_activated) - air_doors_activated = TRUE - for(var/obj/machinery/door/firedoor/D in src) - if(!D.welded) - D.activate_alarm() - if(D.operating) - D.nextstate = FD_CLOSED - else if(!D.density) - spawn(0) - D.close() - -/area/proc/air_doors_open() - if(air_doors_activated) - air_doors_activated = FALSE - for(var/obj/machinery/door/firedoor/D in src) - if(!D.welded) - D.deactivate_alarm() - if(D.operating) - D.nextstate = OPEN - else if(D.density) - spawn(0) - D.open() - - -/area/proc/fire_alert() - if(!fire) - fire = 1 //used for firedoor checks - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - air_doors_close() - -/area/proc/fire_reset() - if(fire) - fire = 0 //used for firedoor checks - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - air_doors_open() - - return - -/area/proc/burglaralert(var/obj/trigger) - if(always_unpowered == 1) //no burglar alarms in space/asteroid - return - - //Trigger alarm effect - set_fire_alarm_effect() - - //Lockdown airlocks - for(var/obj/machinery/door/airlock/A in src) - spawn(0) - A.close() - if(A.density) - A.lock() - - SSalarms.burglar_alarm.triggerAlarm(src, trigger) - spawn(600) - SSalarms.burglar_alarm.clearAlarm(src, trigger) - -/area/proc/set_fire_alarm_effect() - fire = 1 - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/area/proc/readyalert() - if(!eject) - eject = 1 - updateicon() - -/area/proc/readyreset() - if(eject) - eject = 0 - updateicon() - -/area/proc/partyalert() - if(!party) - party = 1 - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/area/proc/partyreset() - if(party) - party = 0 - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - updateicon() - -/area/proc/updateicon() - if((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. - if(fire && !eject && !party) - icon_state = "red" - else if(!fire && eject && !party) - icon_state = "red" - else if(party && !fire && !eject) - icon_state = "party" - else - icon_state = "blue-red" - else - var/weather_icon - for(var/V in SSweather.processing) - var/datum/weather/W = V - if(W.stage != END_STAGE && (src in W.impacted_areas)) - W.update_areas() - weather_icon = TRUE - if(!weather_icon) - icon_state = null - -/area/space/updateicon() - icon_state = null - -/* -#define EQUIP 1 -#define LIGHT 2 -#define ENVIRON 3 -*/ - -/area/proc/powered(var/chan) // return true if the area has power to given channel - - if(!requires_power) - return 1 - if(always_unpowered) - return 0 - switch(chan) - if(EQUIP) - return power_equip - if(LIGHT) - return power_light - if(ENVIRON) - return power_environ - - return 0 - -/area/space/powered(chan) //Nope.avi - return 0 - -// called when power status changes - -/area/proc/power_change() - for(var/obj/machinery/M in src) // for each machine in the area - M.power_change() // reverify power status (to update icons etc.) - updateicon() - -/area/proc/usage(var/chan) - var/used = 0 - switch(chan) - if(LIGHT) - used += used_light - if(EQUIP) - used += used_equip - if(ENVIRON) - used += used_environ - if(TOTAL) - used += used_light + used_equip + used_environ - if(STATIC_EQUIP) - used += static_equip - if(STATIC_LIGHT) - used += static_light - if(STATIC_ENVIRON) - used += static_environ - return used - -/area/proc/addStaticPower(value, powerchannel) - switch(powerchannel) - if(STATIC_EQUIP) - static_equip += value - if(STATIC_LIGHT) - static_light += value - if(STATIC_ENVIRON) - static_environ += value - -/area/proc/clear_usage() - - used_equip = 0 - used_light = 0 - used_environ = 0 - -/area/proc/use_power(var/amount, var/chan) - switch(chan) - if(EQUIP) - used_equip += amount - if(LIGHT) - used_light += amount - if(ENVIRON) - used_environ += amount - -/area/proc/use_battery_power(var/amount, var/chan) - switch(chan) - if(EQUIP) - used_equip += amount - if(LIGHT) - used_light += amount - if(ENVIRON) - used_environ += amount - - -/area/Entered(A) - var/area/newarea - var/area/oldarea - - if(istype(A,/mob)) - var/mob/M=A - - if(!M.lastarea) - M.lastarea = get_area(M) - newarea = get_area(M) - oldarea = M.lastarea - - if(newarea==oldarea) return - - M.lastarea = src - - if(!istype(A,/mob/living)) return - - var/mob/living/L = A - if(!L.ckey) return - if((oldarea.has_gravity == 0) && (newarea.has_gravity == 1) && (L.m_intent == MOVE_INTENT_RUN)) // Being ready when you change areas gives you a chance to avoid falling all together. - thunk(L) - - // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch - if(L && L.client && !L.client.ambience_playing && (L.client.prefs.sound & SOUND_BUZZ)) //split off the white noise from the rest of the ambience because of annoyance complaints - Kluys - L.client.ambience_playing = 1 - L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ) - else if(L && L.client && !(L.client.prefs.sound & SOUND_BUZZ)) - L.client.ambience_playing = 0 - - if(prob(35) && L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE)) - var/sound = pick(ambientsounds) - - if(!L.client.played) - L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE) - L.client.played = 1 - spawn(600) //ewww - this is very very bad - if(L && L.client) - L.client.played = 0 - -/area/proc/gravitychange(var/gravitystate = 0, var/area/A) - A.has_gravity = gravitystate - - if(gravitystate) - for(var/mob/living/carbon/human/M in A) - thunk(M) - -/area/proc/thunk(var/mob/living/carbon/human/M) - if(istype(M,/mob/living/carbon/human/)) // Only humans can wear magboots, so we give them a chance to. - if(istype(M.shoes, /obj/item/clothing/shoes/magboots) && (M.shoes.flags & NOSLIP)) - return - - if(M.buckled) //Cam't fall down if you are buckled - return - - if(istype(get_turf(M), /turf/space)) // Can't fall onto nothing. - return - - if((istype(M,/mob/living/carbon/human/)) && (M.m_intent == MOVE_INTENT_RUN)) - M.Stun(5) - M.Weaken(5) - - else if(istype(M,/mob/living/carbon/human/)) - M.Stun(2) - M.Weaken(2) - - - to_chat(M, "Gravity!") - -/proc/has_gravity(atom/AT, turf/T) - if(!T) - T = get_turf(AT) - var/area/A = get_area(T) - if(istype(T, /turf/space)) // Turf never has gravity - return 0 - else if(A && A.has_gravity) // Areas which always has gravity - return 1 - else - // There's a gravity generator on our z level - // This would do well when integrated with the z level manager - if(T && gravity_generators["[T.z]"] && length(gravity_generators["[T.z]"])) - return 1 - return 0 - -/area/proc/prison_break() - for(var/obj/machinery/power/apc/temp_apc in src) - temp_apc.overload_lighting(70) - for(var/obj/machinery/door/airlock/temp_airlock in src) - temp_airlock.prison_open() - for(var/obj/machinery/door/window/temp_windoor in src) - temp_windoor.open() - -/area/AllowDrop() - CRASH("Bad op: area/AllowDrop() called") - -/area/drop_location() - CRASH("Bad op: area/drop_location() called") +/area + var/fire = null + var/atmosalm = ATMOS_ALARM_NONE + var/poweralm = TRUE + var/party = null + var/report_alerts = TRUE // Should atmos alerts notify the AI/computers + level = null + name = "Space" + icon = 'icons/turf/areas.dmi' + icon_state = "unknown" + layer = AREA_LAYER + plane = BLACKNESS_PLANE //Keeping this on the default plane, GAME_PLANE, will make area overlays fail to render on FLOOR_PLANE. + luminosity = 0 + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + invisibility = INVISIBILITY_LIGHTING + var/valid_territory = TRUE //used for cult summoning areas on station zlevel + var/map_name // Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints. + var/lightswitch = TRUE + + var/eject = null + + var/debug = FALSE + var/requires_power = TRUE + var/always_unpowered = FALSE //this gets overriden to 1 for space in area/New() + + var/power_equip = TRUE + var/power_light = TRUE + var/power_environ = TRUE + var/music = null + var/used_equip = FALSE + var/used_light = FALSE + var/used_environ = FALSE + var/static_equip + var/static_light = FALSE + var/static_environ + + var/has_gravity = TRUE + var/list/apc = list() + var/no_air = null + + var/air_doors_activated = FALSE + + var/tele_proof = FALSE + var/no_teleportlocs = FALSE + + var/outdoors = FALSE //For space, the asteroid, lavaland, etc. Used with blueprints to determine if we are adding a new area (vs editing a station room) + var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default? + var/nad_allowed = FALSE //is the station NAD allowed on this area? + + // This var is used with the maploader (modules/awaymissions/maploader/reader.dm) + // if this is 1, when used in a map snippet, this will instantiate a unique + // area from any other instances already present (meaning you can have + // separate APCs, and so on) + var/there_can_be_many = FALSE + + var/global/global_uid = 0 + var/uid + + var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\ + 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\ + 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\ + 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\ + 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\ + 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg') + + var/fast_despawn = FALSE + var/can_get_auto_cryod = TRUE + var/hide_attacklogs = FALSE // For areas such as thunderdome, lavaland syndiebase, etc which generate a lot of spammy attacklogs. Reduces log priority. + + var/parallax_movedir = 0 + var/moving = FALSE + +/area/Initialize(mapload) + GLOB.all_areas += src + icon_state = "" + layer = AREA_LAYER + uid = ++global_uid + + map_name = name // Save the initial (the name set in the map) name of the area. + + if(requires_power) + luminosity = 0 + else + power_light = TRUE + power_equip = TRUE + power_environ = TRUE + + if(dynamic_lighting == DYNAMIC_LIGHTING_FORCED) + dynamic_lighting = DYNAMIC_LIGHTING_ENABLED + luminosity = 0 + else if(dynamic_lighting != DYNAMIC_LIGHTING_IFSTARLIGHT) + dynamic_lighting = DYNAMIC_LIGHTING_DISABLED + if(dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT) + dynamic_lighting = config.starlight ? DYNAMIC_LIGHTING_ENABLED : DYNAMIC_LIGHTING_DISABLED + + . = ..() + + blend_mode = BLEND_MULTIPLY // Putting this in the constructor so that it stops the icons being screwed up in the map editor. + + if(!IS_DYNAMIC_LIGHTING(src)) + add_overlay(/obj/effect/fullbright) + + reg_in_areas_in_z() + + return INITIALIZE_HINT_LATELOAD + +/area/LateInitialize() + . = ..() + power_change() // all machines set to current power level, also updates lighting icon + +/area/proc/reg_in_areas_in_z() + if(contents.len) + var/list/areas_in_z = GLOB.space_manager.areas_in_z + var/z + for(var/i in 1 to contents.len) + var/atom/thing = contents[i] + if(!thing) + continue + z = thing.z + break + if(!z) + WARNING("No z found for [src]") + return + if(!areas_in_z["[z]"]) + areas_in_z["[z]"] = list() + areas_in_z["[z]"] += src + +/area/proc/get_cameras() + var/list/cameras = list() + for(var/obj/machinery/camera/C in src) + cameras += C + return cameras + + +/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE) + if(report_alerts) + if(danger_level == ATMOS_ALARM_NONE) + SSalarms.atmosphere_alarm.clearAlarm(src, alarm_source) + else + SSalarms.atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) + + //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care. + for(var/obj/machinery/alarm/AA in src) + if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force) + danger_level = max(danger_level, AA.danger_level) + + if(danger_level != atmosalm) + if(danger_level < ATMOS_ALARM_WARNING && atmosalm >= ATMOS_ALARM_WARNING) + //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise + air_doors_open() + else if(danger_level >= ATMOS_ALARM_DANGER && atmosalm < ATMOS_ALARM_DANGER) + air_doors_close() + + atmosalm = danger_level + for(var/obj/machinery/alarm/AA in src) + AA.update_icon() + + GLOB.air_alarm_repository.update_cache(src) + return 1 + GLOB.air_alarm_repository.update_cache(src) + return 0 + +/area/proc/air_doors_close() + if(!air_doors_activated) + air_doors_activated = TRUE + for(var/obj/machinery/door/firedoor/D in src) + if(!D.welded) + D.activate_alarm() + if(D.operating) + D.nextstate = FD_CLOSED + else if(!D.density) + spawn(0) + D.close() + +/area/proc/air_doors_open() + if(air_doors_activated) + air_doors_activated = FALSE + for(var/obj/machinery/door/firedoor/D in src) + if(!D.welded) + D.deactivate_alarm() + if(D.operating) + D.nextstate = OPEN + else if(D.density) + spawn(0) + D.open() + + +/area/proc/fire_alert() + if(!fire) + fire = 1 //used for firedoor checks + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + air_doors_close() + +/area/proc/fire_reset() + if(fire) + fire = 0 //used for firedoor checks + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + air_doors_open() + + return + +/area/proc/burglaralert(var/obj/trigger) + if(always_unpowered == 1) //no burglar alarms in space/asteroid + return + + //Trigger alarm effect + set_fire_alarm_effect() + + //Lockdown airlocks + for(var/obj/machinery/door/airlock/A in src) + spawn(0) + A.close() + if(A.density) + A.lock() + + SSalarms.burglar_alarm.triggerAlarm(src, trigger) + spawn(600) + SSalarms.burglar_alarm.clearAlarm(src, trigger) + +/area/proc/set_fire_alarm_effect() + fire = 1 + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/area/proc/readyalert() + if(!eject) + eject = 1 + updateicon() + +/area/proc/readyreset() + if(eject) + eject = 0 + updateicon() + +/area/proc/partyalert() + if(!party) + party = 1 + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/area/proc/partyreset() + if(party) + party = 0 + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + updateicon() + +/area/proc/updateicon() + if((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. + if(fire && !eject && !party) + icon_state = "red" + else if(!fire && eject && !party) + icon_state = "red" + else if(party && !fire && !eject) + icon_state = "party" + else + icon_state = "blue-red" + else + var/weather_icon + for(var/V in SSweather.processing) + var/datum/weather/W = V + if(W.stage != END_STAGE && (src in W.impacted_areas)) + W.update_areas() + weather_icon = TRUE + if(!weather_icon) + icon_state = null + +/area/space/updateicon() + icon_state = null + +/* +#define EQUIP 1 +#define LIGHT 2 +#define ENVIRON 3 +*/ + +/area/proc/powered(var/chan) // return true if the area has power to given channel + + if(!requires_power) + return 1 + if(always_unpowered) + return 0 + switch(chan) + if(EQUIP) + return power_equip + if(LIGHT) + return power_light + if(ENVIRON) + return power_environ + + return 0 + +/area/space/powered(chan) //Nope.avi + return 0 + +// called when power status changes + +/area/proc/power_change() + for(var/obj/machinery/M in src) // for each machine in the area + M.power_change() // reverify power status (to update icons etc.) + updateicon() + +/area/proc/usage(var/chan) + var/used = 0 + switch(chan) + if(LIGHT) + used += used_light + if(EQUIP) + used += used_equip + if(ENVIRON) + used += used_environ + if(TOTAL) + used += used_light + used_equip + used_environ + if(STATIC_EQUIP) + used += static_equip + if(STATIC_LIGHT) + used += static_light + if(STATIC_ENVIRON) + used += static_environ + return used + +/area/proc/addStaticPower(value, powerchannel) + switch(powerchannel) + if(STATIC_EQUIP) + static_equip += value + if(STATIC_LIGHT) + static_light += value + if(STATIC_ENVIRON) + static_environ += value + +/area/proc/clear_usage() + + used_equip = 0 + used_light = 0 + used_environ = 0 + +/area/proc/use_power(var/amount, var/chan) + switch(chan) + if(EQUIP) + used_equip += amount + if(LIGHT) + used_light += amount + if(ENVIRON) + used_environ += amount + +/area/proc/use_battery_power(var/amount, var/chan) + switch(chan) + if(EQUIP) + used_equip += amount + if(LIGHT) + used_light += amount + if(ENVIRON) + used_environ += amount + + +/area/Entered(A) + var/area/newarea + var/area/oldarea + + if(istype(A,/mob)) + var/mob/M=A + + if(!M.lastarea) + M.lastarea = get_area(M) + newarea = get_area(M) + oldarea = M.lastarea + + if(newarea==oldarea) return + + M.lastarea = src + + if(!istype(A,/mob/living)) return + + var/mob/living/L = A + if(!L.ckey) return + if((oldarea.has_gravity == 0) && (newarea.has_gravity == 1) && (L.m_intent == MOVE_INTENT_RUN)) // Being ready when you change areas gives you a chance to avoid falling all together. + thunk(L) + + // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch + if(L && L.client && !L.client.ambience_playing && (L.client.prefs.sound & SOUND_BUZZ)) //split off the white noise from the rest of the ambience because of annoyance complaints - Kluys + L.client.ambience_playing = 1 + L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ) + else if(L && L.client && !(L.client.prefs.sound & SOUND_BUZZ)) + L.client.ambience_playing = 0 + + if(prob(35) && L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE)) + var/sound = pick(ambientsounds) + + if(!L.client.played) + L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE) + L.client.played = 1 + spawn(600) //ewww - this is very very bad + if(L && L.client) + L.client.played = 0 + +/area/proc/gravitychange(var/gravitystate = 0, var/area/A) + A.has_gravity = gravitystate + + if(gravitystate) + for(var/mob/living/carbon/human/M in A) + thunk(M) + +/area/proc/thunk(var/mob/living/carbon/human/M) + if(istype(M,/mob/living/carbon/human/)) // Only humans can wear magboots, so we give them a chance to. + if(istype(M.shoes, /obj/item/clothing/shoes/magboots) && (M.shoes.flags & NOSLIP)) + return + + if(M.buckled) //Cam't fall down if you are buckled + return + + if(istype(get_turf(M), /turf/space)) // Can't fall onto nothing. + return + + if((istype(M,/mob/living/carbon/human/)) && (M.m_intent == MOVE_INTENT_RUN)) + M.Stun(5) + M.Weaken(5) + + else if(istype(M,/mob/living/carbon/human/)) + M.Stun(2) + M.Weaken(2) + + + to_chat(M, "Gravity!") + +/proc/has_gravity(atom/AT, turf/T) + if(!T) + T = get_turf(AT) + var/area/A = get_area(T) + if(istype(T, /turf/space)) // Turf never has gravity + return 0 + else if(A && A.has_gravity) // Areas which always has gravity + return 1 + else + // There's a gravity generator on our z level + // This would do well when integrated with the z level manager + if(T && GLOB.gravity_generators["[T.z]"] && length(GLOB.gravity_generators["[T.z]"])) + return 1 + return 0 + +/area/proc/prison_break() + for(var/obj/machinery/power/apc/temp_apc in src) + temp_apc.overload_lighting(70) + for(var/obj/machinery/door/airlock/temp_airlock in src) + temp_airlock.prison_open() + for(var/obj/machinery/door/window/temp_windoor in src) + temp_windoor.open() + +/area/AllowDrop() + CRASH("Bad op: area/AllowDrop() called") + +/area/drop_location() + CRASH("Bad op: area/drop_location() called") diff --git a/code/game/area/areas/depot-areas.dm b/code/game/area/areas/depot-areas.dm index 86940fcb412..7a7c934df35 100644 --- a/code/game/area/areas/depot-areas.dm +++ b/code/game/area/areas/depot-areas.dm @@ -174,7 +174,7 @@ L.close() if(!L.locked) L.locked = !L.locked - L.req_access = list(access_syndicate_leader) + L.req_access = list(ACCESS_SYNDICATE_LEADER) L.update_icon() else log_game("Depot visit: ended") @@ -182,7 +182,7 @@ for(var/mob/living/simple_animal/hostile/syndicate/N in src) N.a_intent = INTENT_HARM for(var/obj/machinery/door/airlock/A in src) - A.req_access_txt = "[access_syndicate_leader]" + A.req_access_txt = "[ACCESS_SYNDICATE_LEADER]" for(var/obj/structure/closet/secure_closet/syndicate/depot/L in src) if(L.locked) L.locked = !L.locked @@ -476,4 +476,4 @@ var/obj/machinery/shieldwall/syndicate/S = locateUID(shuid) if(S) qdel(S) - shield_list = list() \ No newline at end of file + shield_list = list() diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm index 4a2317889fa..0b07fffda5e 100644 --- a/code/game/area/areas/mining.dm +++ b/code/game/area/areas/mining.dm @@ -127,4 +127,4 @@ icon_state = "danger" /area/lavaland/surface/outdoors/explored - name = "Lavaland Labor Camp" \ No newline at end of file + name = "Lavaland Labor Camp" diff --git a/code/game/area/areas/ruins/lavaland.dm b/code/game/area/areas/ruins/lavaland.dm index ac4c41b0c42..5baf1fe315e 100644 --- a/code/game/area/areas/ruins/lavaland.dm +++ b/code/game/area/areas/ruins/lavaland.dm @@ -87,4 +87,4 @@ //ash walker nest /area/ruin/unpowered/ash_walkers - icon_state = "red" \ No newline at end of file + icon_state = "red" diff --git a/code/game/atoms.dm b/code/game/atoms.dm index cd7e1e45015..1cb0ee3baad 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,884 +1,884 @@ -/atom - layer = TURF_LAYER - plane = GAME_PLANE - var/level = 2 - var/flags = NONE - var/flags_2 = NONE - var/list/fingerprints - var/list/fingerprintshidden - var/fingerprintslast = null - var/list/blood_DNA - var/blood_color - var/last_bumped = 0 - var/pass_flags = 0 - var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. - var/simulated = TRUE //filter for actions - used by lighting overlays - var/atom_say_verb = "says" - var/dont_save = 0 // For atoms that are temporary by necessity - like lighting overlays - - - ///Chemistry. - var/container_type = NONE - var/datum/reagents/reagents = null - - //This atom's HUD (med/sec, etc) images. Associative list. - var/list/image/hud_list = list() - //HUD images that this atom can provide. - var/list/hud_possible - - ///Chemistry. - - - //Value used to increment ex_act() if reactionary_explosions is on - var/explosion_block = 0 - - //Detective Work, used for the duplicate data points kept in the scanners - var/list/original_atom - - //Detective Work, used for allowing a given atom to leave its fibers on stuff. Allowed by default - var/can_leave_fibers = TRUE - - var/allow_spin = 1 //Set this to 1 for a _target_ that is being thrown at; if an atom has this set to 1 then atoms thrown AT it will not spin; currently used for the singularity. -Fox - - var/admin_spawned = 0 //was this spawned by an admin? used for stat tracking stuff. - - var/initialized = FALSE - - var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. - var/list/remove_overlays // a very temporary list of overlays to remove - var/list/add_overlays // a very temporary list of overlays to add - - var/list/atom_colours //used to store the different colors on an atom - //its inherent color, the colored paint applied on it, special color effect etc... - -/atom/New(loc, ...) - if(use_preloader && (src.type == _preloader.target_path))//in case the instanciated atom is creating other atoms in New() - _preloader.load(src) - . = ..() - attempt_init(arglist(args)) - -// This is distinct from /tg/ because of our space management system -// This is overriden in /atom/movable and the parent isn't called if the SMS wants to deal with it's init -/atom/proc/attempt_init(...) - var/do_initialize = SSatoms.initialized - if(do_initialize != INITIALIZATION_INSSATOMS) - args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD - if(SSatoms.InitAtom(src, args)) - // we were deleted - return - - -//Called after New if the map is being loaded. mapload = TRUE -//Called from base of New if the map is not being loaded. mapload = FALSE -//This base must be called or derivatives must set initialized to TRUE -//must not sleep -//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE -//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm - -//Note: the following functions don't call the base for optimization and must copypasta: -// /turf/Initialize -// /turf/open/space/Initialize - -/atom/proc/Initialize(mapload, ...) - if(initialized) - stack_trace("Warning: [src]([type]) initialized multiple times!") - initialized = TRUE - - if(color) - add_atom_colour(color, FIXED_COLOUR_PRIORITY) - - if(light_power && light_range) - update_light() - - if(opacity && isturf(loc)) - var/turf/T = loc - T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways. - - if(loc) - loc.InitializedOn(src) // Used for poolcontroller / pool to improve performance greatly. However it also open up path to other usage of observer pattern on turfs. - - ComponentInitialize() - - return INITIALIZE_HINT_NORMAL - - -//called if Initialize returns INITIALIZE_HINT_LATELOAD -/atom/proc/LateInitialize() - return - -// Put your AddComponent() calls here -/atom/proc/ComponentInitialize() - return - -/atom/proc/InitializedOn(atom/A) // Proc for when something is initialized on a atom - Optional to call. Useful for observer pattern etc. - return - -/atom/proc/onCentcom() - var/turf/T = get_turf(src) - if(!T) - return 0 - - if(!is_admin_level(T.z))//if not, don't bother - return 0 - - //check for centcomm shuttles - for(var/centcom_shuttle in list("emergency", "pod1", "pod2", "pod3", "pod4", "ferry")) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle(centcom_shuttle) - if(T in M.areaInstance) - return 1 - - //finally check for centcom itself - return istype(T.loc,/area/centcom) - -/atom/proc/onSyndieBase() - var/turf/T = get_turf(src) - if(!T) - return 0 - - if(!is_admin_level(T.z))//if not, don't bother - return 0 - - if(istype(T.loc, /area/shuttle/syndicate_elite) || istype(T.loc, /area/syndicate_mothership)) - return 1 - - return 0 - -/atom/Destroy() - if(alternate_appearances) - for(var/aakey in alternate_appearances) - var/datum/alternate_appearance/AA = alternate_appearances[aakey] - qdel(AA) - alternate_appearances = null - - QDEL_NULL(reagents) - invisibility = INVISIBILITY_MAXIMUM - LAZYCLEARLIST(overlays) - LAZYCLEARLIST(priority_overlays) - - QDEL_NULL(light) - - return ..() - -//Hook for running code when a dir change occurs -/atom/proc/setDir(newdir) - SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) - dir = newdir - -///Handle melee attack by a mech -/atom/proc/mech_melee_attack(obj/mecha/M) - return - -/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) - if(does_attack_animation) - user.changeNext_move(CLICK_CD_MELEE) - add_attack_logs(user, src, "Punched with hulk powers") - user.do_attack_animation(src, ATTACK_EFFECT_SMASH) - -/atom/proc/CheckParts(list/parts_list) - for(var/A in parts_list) - if(istype(A, /datum/reagent)) - if(!reagents) - reagents = new() - reagents.reagent_list.Add(A) - reagents.conditional_update() - else if(istype(A, /atom/movable)) - var/atom/movable/M = A - if(istype(M.loc, /mob/living)) - var/mob/living/L = M.loc - L.unEquip(M) - M.forceMove(src) - -/atom/proc/assume_air(datum/gas_mixture/giver) - qdel(giver) - return null - -/atom/proc/remove_air(amount) - return null - -/atom/proc/return_air() - if(loc) - return loc.return_air() - else - return null - -/atom/proc/check_eye(user as mob) - if(istype(user, /mob/living/silicon/ai)) // WHYYYY - return 1 - return - -/atom/proc/on_reagent_change() - return - -/atom/proc/Bumped(AM as mob|obj) - return - -/// Convenience proc to see if a container is open for chemistry handling -/atom/proc/is_open_container() - return is_refillable() && is_drainable() - -/// Is this atom injectable into other atoms -/atom/proc/is_injectable(mob/user, allowmobs = TRUE) - return reagents && (container_type & (INJECTABLE | REFILLABLE)) - -/// Can we draw from this atom with an injectable atom -/atom/proc/is_drawable(mob/user, allowmobs = TRUE) - return reagents && (container_type & (DRAWABLE | DRAINABLE)) - -/// Can this atoms reagents be refilled -/atom/proc/is_refillable() - return reagents && (container_type & REFILLABLE) - -/// Is this atom drainable of reagents -/atom/proc/is_drainable() - return reagents && (container_type & DRAINABLE) - -/atom/proc/CheckExit() - return 1 - -/atom/proc/HasProximity(atom/movable/AM as mob|obj) - return - -/atom/proc/emp_act(var/severity) - return - -/atom/proc/bullet_act(obj/item/projectile/P, def_zone) - SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) - . = P.on_hit(src, 0, def_zone) - -/atom/proc/in_contents_of(container)//can take class or object instance as argument - if(ispath(container)) - if(istype(src.loc, container)) - return 1 - else if(src in container) - return 1 - return - -/* - * atom/proc/search_contents_for(path,list/filter_path=null) - * Recursevly searches all atom contens (including contents contents and so on). - * - * ARGS: path - search atom contents for atoms of this type - * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. - * - * RETURNS: list of found atoms - */ - -/atom/proc/search_contents_for(path,list/filter_path=null) - var/list/found = list() - for(var/atom/A in src) - if(istype(A, path)) - found += A - if(filter_path) - var/pass = 0 - for(var/type in filter_path) - pass |= istype(A, type) - if(!pass) - continue - if(A.contents.len) - found += A.search_contents_for(path,filter_path) - return found - - -//All atoms -/atom/proc/examine(mob/user, infix = "", suffix = "") - //This reformat names to get a/an properly working on item descriptions when they are bloody - var/f_name = "\a [src][infix]." - if(src.blood_DNA && !istype(src, /obj/effect/decal)) - if(gender == PLURAL) - f_name = "some " - else - f_name = "a " - if(blood_color != "#030303") - f_name += "blood-stained [name][infix]!" - else - f_name += "oil-stained [name][infix]." - . = list("[bicon(src)] That's [f_name] [suffix]") - if(desc) - . += desc - - if(reagents) - if(container_type & TRANSPARENT) - . += "It contains:" - if(reagents.reagent_list.len) - if(user.can_see_reagents()) //Show each individual reagent - for(var/I in reagents.reagent_list) - var/datum/reagent/R = I - . += "[R.volume] units of [R.name]" - else //Otherwise, just show the total volume - if(reagents && reagents.reagent_list.len) - . += "[reagents.total_volume] units of various reagents." - else - . += "Nothing." - else if(container_type & AMOUNT_VISIBLE) - if(reagents.total_volume) - . += "It has [reagents.total_volume] unit\s left." - else - . += "It's empty." - - SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) - -/atom/proc/relaymove() - return - -/atom/proc/ex_act() - return - -/atom/proc/blob_act(obj/structure/blob/B) - SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) - -/atom/proc/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) - if(reagents) - reagents.temperature_reagents(exposed_temperature) - -/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type) - switch(tool_type) - if(TOOL_CROWBAR) - return crowbar_act(user, I) - if(TOOL_MULTITOOL) - return multitool_act(user, I) - if(TOOL_SCREWDRIVER) - return screwdriver_act(user, I) - if(TOOL_WRENCH) - return wrench_act(user, I) - if(TOOL_WIRECUTTER) - return wirecutter_act(user, I) - if(TOOL_WELDER) - return welder_act(user, I) - - -// Tool-specific behavior procs. To be overridden in subtypes. -/atom/proc/crowbar_act(mob/living/user, obj/item/I) - return - -/atom/proc/multitool_act(mob/living/user, obj/item/I) - return - -//Check if the multitool has an item in its data buffer -/atom/proc/multitool_check_buffer(user, silent = FALSE) - if(!silent) - to_chat(user, "[src] has no data buffer!") - return FALSE - -/atom/proc/screwdriver_act(mob/living/user, obj/item/I) - return - -/atom/proc/wrench_act(mob/living/user, obj/item/I) - return - -/atom/proc/wirecutter_act(mob/living/user, obj/item/I) - return - -/atom/proc/welder_act(mob/living/user, obj/item/I) - return - -/atom/proc/emag_act() - return - -/atom/proc/fart_act(mob/living/M) - return FALSE - -/atom/proc/rpd_act() - return - -/atom/proc/rpd_blocksusage() - // Atoms that return TRUE prevent RPDs placing any kind of pipes on their turf. - return FALSE - -/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). - addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) - -/atom/proc/hitby_react(atom/movable/AM) - if(AM && isturf(AM.loc)) - step(AM, turn(AM.dir, 180)) - -/atom/proc/get_spooked() - return - -/atom/proc/add_hiddenprint(mob/living/M as mob) - if(isnull(M)) return - if(isnull(M.key)) return - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(!istype(H.dna, /datum/dna)) - return 0 - if(H.gloves) - if(fingerprintslast != H.ckey) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) - fingerprintslast = H.ckey - return 0 - if(!fingerprints) - if(fingerprintslast != H.ckey) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) - fingerprintslast = H.ckey - return 1 - else - if(fingerprintslast != M.ckey) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) - fingerprintslast = M.ckey - return - - -//Set ignoregloves to add prints irrespective of the mob having gloves on. -/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) - if(isnull(M)) return - if(isnull(M.key)) return - if(ishuman(M)) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - - //Fibers~ - add_fibers(M) - - //He has no prints! - if(FINGERPRINTS in M.mutations) - if(fingerprintslast != M.key) - fingerprintshidden += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]" - fingerprintslast = M.key - return 0 //Now, lets get to the dirty work. - //First, make sure their DNA makes sense. - var/mob/living/carbon/human/H = M - if(!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) - if(!istype(H.dna, /datum/dna)) - H.dna = new /datum/dna(null) - H.dna.real_name = H.real_name - H.check_dna() - - //Check if the gloves (if any) hide fingerprints - if(H.gloves) - var/obj/item/clothing/gloves/G = H.gloves - if(G.transfer_prints) - ignoregloves = 1 - - //Now, deal with gloves. - if(!ignoregloves) - if(H.gloves && H.gloves != src) - if(fingerprintslast != H.ckey) - fingerprintshidden += text("\[[]\](Wearing gloves). Real name: [], Key: []",time_stamp(), H.real_name, H.key) - fingerprintslast = H.ckey - H.gloves.add_fingerprint(M) - return 0 - - //More adminstuffz - if(fingerprintslast != H.ckey) - fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), H.real_name, H.key) - fingerprintslast = H.ckey - - //Make the list if it does not exist. - if(!fingerprints) - fingerprints = list() - - //Hash this shit. - var/full_print = H.get_full_print() - - // Add the fingerprints - fingerprints[full_print] = full_print - - return 1 - else - //Smudge up dem prints some - if(fingerprintslast != M.ckey) - fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), M.real_name, M.key) - fingerprintslast = M.ckey - - return - - -/atom/proc/transfer_fingerprints_to(var/atom/A) - - // Make sure everything are lists. - if(!islist(A.fingerprints)) - A.fingerprints = list() - if(!islist(A.fingerprintshidden)) - A.fingerprintshidden = list() - - if(!islist(fingerprints)) - fingerprints = list() - if(!islist(fingerprintshidden)) - fingerprintshidden = list() - - // Transfer - if(fingerprints) - A.fingerprints |= fingerprints.Copy() //detective - if(fingerprintshidden) - A.fingerprintshidden |= fingerprintshidden.Copy() //admin - A.fingerprintslast = fingerprintslast - -var/list/blood_splatter_icons = list() - -/atom/proc/blood_splatter_index() - return "\ref[initial(icon)]-[initial(icon_state)]" - -//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list -/mob/living/proc/get_blood_dna_list() - if(get_blood_id() != "blood") - return - return list("ANIMAL DNA" = "Y-") - -/mob/living/carbon/get_blood_dna_list() - if(get_blood_id() != "blood") - return - var/list/blood_dna = list() - if(dna) - blood_dna[dna.unique_enzymes] = dna.blood_type - else - blood_dna["UNKNOWN DNA"] = "X*" - return blood_dna - -/mob/living/carbon/alien/get_blood_dna_list() - return list("UNKNOWN DNA" = "X*") - -//to add a mob's dna info into an object's blood_DNA list. -/atom/proc/transfer_mob_blood_dna(mob/living/L) - var/new_blood_dna = L.get_blood_dna_list() - if(!new_blood_dna) - return 0 - return transfer_blood_dna(new_blood_dna) - -/obj/effect/decal/cleanable/blood/splatter/transfer_mob_blood_dna(mob/living/L) - ..(L) - var/list/b_data = L.get_blood_data(L.get_blood_id()) - if(b_data) - basecolor = b_data["blood_color"] - else - basecolor = "#A10808" - update_icon() - -/obj/effect/decal/cleanable/blood/footprints/transfer_mob_blood_dna(mob/living/L) - ..(L) - var/list/b_data = L.get_blood_data(L.get_blood_id()) - if(b_data) - basecolor = b_data["blood_color"] - else - basecolor = "#A10808" - update_icon() - -//to add blood dna info to the object's blood_DNA list -/atom/proc/transfer_blood_dna(list/blood_dna) - if(!blood_DNA) - blood_DNA = list() - var/old_length = blood_DNA.len - blood_DNA |= blood_dna - if(blood_DNA.len > old_length) - return 1//some new blood DNA was added - - -//to add blood from a mob onto something, and transfer their dna info -/atom/proc/add_mob_blood(mob/living/M) - var/list/blood_dna = M.get_blood_dna_list() - if(!blood_dna) - return 0 - var/bloodcolor = "#A10808" - var/list/b_data = M.get_blood_data(M.get_blood_id()) - if(b_data) - bloodcolor = b_data["blood_color"] - - return add_blood(blood_dna, bloodcolor) - -//to add blood onto something, with blood dna info to include. -/atom/proc/add_blood(list/blood_dna, color) - return 0 - -/obj/add_blood(list/blood_dna, color) - return transfer_blood_dna(blood_dna) - -/obj/item/add_blood(list/blood_dna, color) - var/blood_count = !blood_DNA ? 0 : blood_DNA.len - if(!..()) - return 0 - if(!blood_count)//apply the blood-splatter overlay if it isn't already in there - add_blood_overlay(color) - return 1 //we applied blood to the item - -/obj/item/clothing/gloves/add_blood(list/blood_dna, color) - . = ..() - transfer_blood = rand(2, 4) - -/turf/add_blood(list/blood_dna, color) - var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src - if(!B) - B = new /obj/effect/decal/cleanable/blood/splatter(src) - B.transfer_blood_dna(blood_dna) //give blood info to the blood decal. - B.basecolor = color - return 1 //we bloodied the floor - -/mob/living/carbon/human/add_blood(list/blood_dna, color) - if(wear_suit) - wear_suit.add_blood(blood_dna, color) - wear_suit.blood_color = color - update_inv_wear_suit(1) - else if(w_uniform) - w_uniform.add_blood(blood_dna, color) - w_uniform.blood_color = color - update_inv_w_uniform(1) - if(head) - head.add_blood(blood_dna, color) - head.blood_color = color - update_inv_head(0,0) - if(glasses) - glasses.add_blood(blood_dna, color) - glasses.blood_color = color - update_inv_glasses(0) - if(gloves) - var/obj/item/clothing/gloves/G = gloves - G.add_blood(blood_dna, color) - G.blood_color = color - verbs += /mob/living/carbon/human/proc/bloody_doodle - else - hand_blood_color = color - bloody_hands = rand(2, 4) - transfer_blood_dna(blood_dna) - verbs += /mob/living/carbon/human/proc/bloody_doodle - - update_inv_gloves(1) //handles bloody hands overlays and updating - return 1 - -/obj/item/proc/add_blood_overlay(color) - if(initial(icon) && initial(icon_state)) - //try to find a pre-processed blood-splatter. otherwise, make a new one - var/index = blood_splatter_index() - var/icon/blood_splatter_icon = blood_splatter_icons[index] - if(!blood_splatter_icon) - blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object - blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) - blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant - blood_splatter_icon = fcopy_rsc(blood_splatter_icon) - blood_splatter_icons[index] = blood_splatter_icon - - blood_overlay = image(blood_splatter_icon) - blood_overlay.color = color - overlays += blood_overlay - -/atom/proc/clean_blood() - germ_level = 0 - if(islist(blood_DNA)) - blood_DNA = null - return TRUE - -/obj/effect/decal/cleanable/blood/clean_blood() - return // While this seems nonsensical, clean_blood isn't supposed to be used like this on a blood decal. - -/obj/item/clean_blood() - . = ..() - if(.) - if(blood_overlay) - overlays -= blood_overlay - -/obj/item/clothing/gloves/clean_blood() - . = ..() - if(.) - transfer_blood = 0 - - -/obj/item/clothing/shoes/clean_blood() - ..() - bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) - blood_state = BLOOD_STATE_NOT_BLOODY - if(ismob(loc)) - var/mob/M = loc - M.update_inv_shoes() - - -/mob/living/carbon/human/clean_blood() - if(gloves) - if(gloves.clean_blood()) - clean_blood() - update_inv_gloves() - gloves.germ_level = 0 - else - ..() // Clear the Blood_DNA list - if(bloody_hands) - bloody_hands = 0 - update_inv_gloves() - update_icons() //apply the now updated overlays to the mob - - -/atom/proc/add_vomit_floor(toxvomit = 0, green = FALSE) - playsound(src, 'sound/effects/splat.ogg', 50, 1) - if(!isspaceturf(src)) - var/type = green ? /obj/effect/decal/cleanable/vomit/green : /obj/effect/decal/cleanable/vomit - var/vomit_reagent = green ? "green_vomit" : "vomit" - for(var/obj/effect/decal/cleanable/vomit/V in get_turf(src)) - if(V.type == type) - V.reagents.add_reagent(vomit_reagent, 5) - return - - var/obj/effect/decal/cleanable/vomit/this = new type(src) - - // Make toxins vomit look different - if(toxvomit) - this.icon_state = "vomittox_[pick(1,4)]" - -/atom/proc/get_global_map_pos() - if(!islist(global_map) || isemptylist(global_map)) return - var/cur_x = null - var/cur_y = null - var/list/y_arr = null - for(cur_x=1,cur_x<=global_map.len,cur_x++) - y_arr = global_map[cur_x] - cur_y = y_arr.Find(src.z) - if(cur_y) - break -// to_chat(world, "X = [cur_x]; Y = [cur_y]") - if(cur_x && cur_y) - return list("x"=cur_x,"y"=cur_y) - else - return 0 - -// Used to provide overlays when using this atom as a viewing focus -// (cameras, locker tint, etc.) -/atom/proc/get_remote_view_fullscreens(mob/user) - return - -//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) -/atom/proc/update_remote_sight(mob/living/user) - user.sync_lighting_plane_alpha() - return - -/atom/proc/checkpass(passflag) - return pass_flags&passflag - -/atom/proc/isinspace() - if(isspaceturf(get_turf(src))) - return TRUE - else - return FALSE - -/atom/proc/handle_fall() - return - -/atom/proc/singularity_act() - return - -/atom/proc/singularity_pull(obj/singularity/S, current_size) - SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) - -/** - * Respond to acid being used on our atom - * - * Default behaviour is to send COMSIG_ATOM_ACID_ACT and return - */ -/atom/proc/acid_act(acidpwr, acid_volume) - SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) - -/atom/proc/narsie_act() - return - -/atom/proc/ratvar_act() - return - -/atom/proc/handle_ricochet(obj/item/projectile/P) - return - -//This proc is called on the location of an atom when the atom is Destroy()'d -/atom/proc/handle_atom_del(atom/A) - return - -/atom/proc/atom_say(message) - if(!message) - return - audible_message("[src] [atom_say_verb], \"[message]\"") - -/atom/proc/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - return - -/atom/vv_edit_var(var_name, var_value) - if(!Debug2) - admin_spawned = TRUE - . = ..() - switch(var_name) - if("light_power", "light_range", "light_color") - update_light() - if("color") - add_atom_colour(color, ADMIN_COLOUR_PRIORITY) - - -/atom/vv_get_dropdown() - . = ..() - var/turf/curturf = get_turf(src) - if(curturf) - .["Jump to turf"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" - .["Add reagent"] = "?_src_=vars;addreagent=[UID()]" - .["Trigger explosion"] = "?_src_=vars;explode=[UID()]" - .["Trigger EM pulse"] = "?_src_=vars;emp=[UID()]" - -/atom/proc/AllowDrop() - return FALSE - -/atom/proc/drop_location() - var/atom/L = loc - if(!L) - return null - return L.AllowDrop() ? L : get_turf(L) - -/atom/Entered(atom/movable/AM, atom/oldLoc) - SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) - -/atom/Exit(atom/movable/AM, atom/newLoc) - . = ..() - if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) - return FALSE - -/atom/Exited(atom/movable/AM, atom/newLoc) - SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) - -/* - Adds an instance of colour_type to the atom's atom_colours list -*/ -/atom/proc/add_atom_colour(coloration, colour_priority) - if(!atom_colours || !atom_colours.len) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(!coloration) - return - if(colour_priority > atom_colours.len) - return - atom_colours[colour_priority] = coloration - update_atom_colour() - - -/* - Removes an instance of colour_type from the atom's atom_colours list -*/ -/atom/proc/remove_atom_colour(colour_priority, coloration) - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(colour_priority > atom_colours.len) - return - if(coloration && atom_colours[colour_priority] != coloration) - return //if we don't have the expected color (for a specific priority) to remove, do nothing - atom_colours[colour_priority] = null - update_atom_colour() - - -/* - Resets the atom's color to null, and then sets it to the highest priority - colour available -*/ -/atom/proc/update_atom_colour() - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - color = null - for(var/C in atom_colours) - if(islist(C)) - var/list/L = C - if(L.len) - color = L - return - else if(C) - color = C - return \ No newline at end of file +/atom + layer = TURF_LAYER + plane = GAME_PLANE + var/level = 2 + var/flags = NONE + var/flags_2 = NONE + var/list/fingerprints + var/list/fingerprintshidden + var/fingerprintslast = null + var/list/blood_DNA + var/blood_color + var/last_bumped = 0 + var/pass_flags = 0 + var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. + var/simulated = TRUE //filter for actions - used by lighting overlays + var/atom_say_verb = "says" + var/dont_save = 0 // For atoms that are temporary by necessity - like lighting overlays + + + ///Chemistry. + var/container_type = NONE + var/datum/reagents/reagents = null + + //This atom's HUD (med/sec, etc) images. Associative list. + var/list/image/hud_list = list() + //HUD images that this atom can provide. + var/list/hud_possible + + ///Chemistry. + + + //Value used to increment ex_act() if reactionary_explosions is on + var/explosion_block = 0 + + //Detective Work, used for the duplicate data points kept in the scanners + var/list/original_atom + + //Detective Work, used for allowing a given atom to leave its fibers on stuff. Allowed by default + var/can_leave_fibers = TRUE + + var/allow_spin = 1 //Set this to 1 for a _target_ that is being thrown at; if an atom has this set to 1 then atoms thrown AT it will not spin; currently used for the singularity. -Fox + + var/admin_spawned = 0 //was this spawned by an admin? used for stat tracking stuff. + + var/initialized = FALSE + + var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. + var/list/remove_overlays // a very temporary list of overlays to remove + var/list/add_overlays // a very temporary list of overlays to add + + var/list/atom_colours //used to store the different colors on an atom + //its inherent color, the colored paint applied on it, special color effect etc... + +/atom/New(loc, ...) + if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() + GLOB._preloader.load(src) + . = ..() + attempt_init(arglist(args)) + +// This is distinct from /tg/ because of our space management system +// This is overriden in /atom/movable and the parent isn't called if the SMS wants to deal with it's init +/atom/proc/attempt_init(...) + var/do_initialize = SSatoms.initialized + if(do_initialize != INITIALIZATION_INSSATOMS) + args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD + if(SSatoms.InitAtom(src, args)) + // we were deleted + return + + +//Called after New if the map is being loaded. mapload = TRUE +//Called from base of New if the map is not being loaded. mapload = FALSE +//This base must be called or derivatives must set initialized to TRUE +//must not sleep +//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE +//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm + +//Note: the following functions don't call the base for optimization and must copypasta: +// /turf/Initialize +// /turf/open/space/Initialize + +/atom/proc/Initialize(mapload, ...) + if(initialized) + stack_trace("Warning: [src]([type]) initialized multiple times!") + initialized = TRUE + + if(color) + add_atom_colour(color, FIXED_COLOUR_PRIORITY) + + if(light_power && light_range) + update_light() + + if(opacity && isturf(loc)) + var/turf/T = loc + T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways. + + if(loc) + loc.InitializedOn(src) // Used for poolcontroller / pool to improve performance greatly. However it also open up path to other usage of observer pattern on turfs. + + ComponentInitialize() + + return INITIALIZE_HINT_NORMAL + + +//called if Initialize returns INITIALIZE_HINT_LATELOAD +/atom/proc/LateInitialize() + return + +// Put your AddComponent() calls here +/atom/proc/ComponentInitialize() + return + +/atom/proc/InitializedOn(atom/A) // Proc for when something is initialized on a atom - Optional to call. Useful for observer pattern etc. + return + +/atom/proc/onCentcom() + var/turf/T = get_turf(src) + if(!T) + return 0 + + if(!is_admin_level(T.z))//if not, don't bother + return 0 + + //check for centcomm shuttles + for(var/centcom_shuttle in list("emergency", "pod1", "pod2", "pod3", "pod4", "ferry")) + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(centcom_shuttle) + if(T in M.areaInstance) + return 1 + + //finally check for centcom itself + return istype(T.loc,/area/centcom) + +/atom/proc/onSyndieBase() + var/turf/T = get_turf(src) + if(!T) + return 0 + + if(!is_admin_level(T.z))//if not, don't bother + return 0 + + if(istype(T.loc, /area/shuttle/syndicate_elite) || istype(T.loc, /area/syndicate_mothership)) + return 1 + + return 0 + +/atom/Destroy() + if(alternate_appearances) + for(var/aakey in alternate_appearances) + var/datum/alternate_appearance/AA = alternate_appearances[aakey] + qdel(AA) + alternate_appearances = null + + QDEL_NULL(reagents) + invisibility = INVISIBILITY_MAXIMUM + LAZYCLEARLIST(overlays) + LAZYCLEARLIST(priority_overlays) + + QDEL_NULL(light) + + return ..() + +//Hook for running code when a dir change occurs +/atom/proc/setDir(newdir) + SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) + dir = newdir + +///Handle melee attack by a mech +/atom/proc/mech_melee_attack(obj/mecha/M) + return + +/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) + if(does_attack_animation) + user.changeNext_move(CLICK_CD_MELEE) + add_attack_logs(user, src, "Punched with hulk powers") + user.do_attack_animation(src, ATTACK_EFFECT_SMASH) + +/atom/proc/CheckParts(list/parts_list) + for(var/A in parts_list) + if(istype(A, /datum/reagent)) + if(!reagents) + reagents = new() + reagents.reagent_list.Add(A) + reagents.conditional_update() + else if(istype(A, /atom/movable)) + var/atom/movable/M = A + if(istype(M.loc, /mob/living)) + var/mob/living/L = M.loc + L.unEquip(M) + M.forceMove(src) + +/atom/proc/assume_air(datum/gas_mixture/giver) + qdel(giver) + return null + +/atom/proc/remove_air(amount) + return null + +/atom/proc/return_air() + if(loc) + return loc.return_air() + else + return null + +/atom/proc/check_eye(user as mob) + if(istype(user, /mob/living/silicon/ai)) // WHYYYY + return 1 + return + +/atom/proc/on_reagent_change() + return + +/atom/proc/Bumped(AM as mob|obj) + return + +/// Convenience proc to see if a container is open for chemistry handling +/atom/proc/is_open_container() + return is_refillable() && is_drainable() + +/// Is this atom injectable into other atoms +/atom/proc/is_injectable(mob/user, allowmobs = TRUE) + return reagents && (container_type & (INJECTABLE | REFILLABLE)) + +/// Can we draw from this atom with an injectable atom +/atom/proc/is_drawable(mob/user, allowmobs = TRUE) + return reagents && (container_type & (DRAWABLE | DRAINABLE)) + +/// Can this atoms reagents be refilled +/atom/proc/is_refillable() + return reagents && (container_type & REFILLABLE) + +/// Is this atom drainable of reagents +/atom/proc/is_drainable() + return reagents && (container_type & DRAINABLE) + +/atom/proc/CheckExit() + return 1 + +/atom/proc/HasProximity(atom/movable/AM as mob|obj) + return + +/atom/proc/emp_act(var/severity) + return + +/atom/proc/bullet_act(obj/item/projectile/P, def_zone) + SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) + . = P.on_hit(src, 0, def_zone) + +/atom/proc/in_contents_of(container)//can take class or object instance as argument + if(ispath(container)) + if(istype(src.loc, container)) + return 1 + else if(src in container) + return 1 + return + +/* + * atom/proc/search_contents_for(path,list/filter_path=null) + * Recursevly searches all atom contens (including contents contents and so on). + * + * ARGS: path - search atom contents for atoms of this type + * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. + * + * RETURNS: list of found atoms + */ + +/atom/proc/search_contents_for(path,list/filter_path=null) + var/list/found = list() + for(var/atom/A in src) + if(istype(A, path)) + found += A + if(filter_path) + var/pass = 0 + for(var/type in filter_path) + pass |= istype(A, type) + if(!pass) + continue + if(A.contents.len) + found += A.search_contents_for(path,filter_path) + return found + + +//All atoms +/atom/proc/examine(mob/user, infix = "", suffix = "") + //This reformat names to get a/an properly working on item descriptions when they are bloody + var/f_name = "\a [src][infix]." + if(src.blood_DNA && !istype(src, /obj/effect/decal)) + if(gender == PLURAL) + f_name = "some " + else + f_name = "a " + if(blood_color != "#030303") + f_name += "blood-stained [name][infix]!" + else + f_name += "oil-stained [name][infix]." + . = list("[bicon(src)] That's [f_name] [suffix]") + if(desc) + . += desc + + if(reagents) + if(container_type & TRANSPARENT) + . += "It contains:" + if(reagents.reagent_list.len) + if(user.can_see_reagents()) //Show each individual reagent + for(var/I in reagents.reagent_list) + var/datum/reagent/R = I + . += "[R.volume] units of [R.name]" + else //Otherwise, just show the total volume + if(reagents && reagents.reagent_list.len) + . += "[reagents.total_volume] units of various reagents." + else + . += "Nothing." + else if(container_type & AMOUNT_VISIBLE) + if(reagents.total_volume) + . += "It has [reagents.total_volume] unit\s left." + else + . += "It's empty." + + SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) + +/atom/proc/relaymove() + return + +/atom/proc/ex_act() + return + +/atom/proc/blob_act(obj/structure/blob/B) + SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) + +/atom/proc/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) + if(reagents) + reagents.temperature_reagents(exposed_temperature) + +/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type) + switch(tool_type) + if(TOOL_CROWBAR) + return crowbar_act(user, I) + if(TOOL_MULTITOOL) + return multitool_act(user, I) + if(TOOL_SCREWDRIVER) + return screwdriver_act(user, I) + if(TOOL_WRENCH) + return wrench_act(user, I) + if(TOOL_WIRECUTTER) + return wirecutter_act(user, I) + if(TOOL_WELDER) + return welder_act(user, I) + + +// Tool-specific behavior procs. To be overridden in subtypes. +/atom/proc/crowbar_act(mob/living/user, obj/item/I) + return + +/atom/proc/multitool_act(mob/living/user, obj/item/I) + return + +//Check if the multitool has an item in its data buffer +/atom/proc/multitool_check_buffer(user, silent = FALSE) + if(!silent) + to_chat(user, "[src] has no data buffer!") + return FALSE + +/atom/proc/screwdriver_act(mob/living/user, obj/item/I) + return + +/atom/proc/wrench_act(mob/living/user, obj/item/I) + return + +/atom/proc/wirecutter_act(mob/living/user, obj/item/I) + return + +/atom/proc/welder_act(mob/living/user, obj/item/I) + return + +/atom/proc/emag_act() + return + +/atom/proc/fart_act(mob/living/M) + return FALSE + +/atom/proc/rpd_act() + return + +/atom/proc/rpd_blocksusage() + // Atoms that return TRUE prevent RPDs placing any kind of pipes on their turf. + return FALSE + +/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). + addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) + +/atom/proc/hitby_react(atom/movable/AM) + if(AM && isturf(AM.loc)) + step(AM, turn(AM.dir, 180)) + +/atom/proc/get_spooked() + return + +/atom/proc/add_hiddenprint(mob/living/M as mob) + if(isnull(M)) return + if(isnull(M.key)) return + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!istype(H.dna, /datum/dna)) + return 0 + if(H.gloves) + if(fingerprintslast != H.ckey) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) + fingerprintslast = H.ckey + return 0 + if(!fingerprints) + if(fingerprintslast != H.ckey) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) + fingerprintslast = H.ckey + return 1 + else + if(fingerprintslast != M.ckey) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) + fingerprintslast = M.ckey + return + + +//Set ignoregloves to add prints irrespective of the mob having gloves on. +/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) + if(isnull(M)) return + if(isnull(M.key)) return + if(ishuman(M)) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + + //Fibers~ + add_fibers(M) + + //He has no prints! + if(FINGERPRINTS in M.mutations) + if(fingerprintslast != M.key) + fingerprintshidden += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]" + fingerprintslast = M.key + return 0 //Now, lets get to the dirty work. + //First, make sure their DNA makes sense. + var/mob/living/carbon/human/H = M + if(!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) + if(!istype(H.dna, /datum/dna)) + H.dna = new /datum/dna(null) + H.dna.real_name = H.real_name + H.check_dna() + + //Check if the gloves (if any) hide fingerprints + if(H.gloves) + var/obj/item/clothing/gloves/G = H.gloves + if(G.transfer_prints) + ignoregloves = 1 + + //Now, deal with gloves. + if(!ignoregloves) + if(H.gloves && H.gloves != src) + if(fingerprintslast != H.ckey) + fingerprintshidden += text("\[[]\](Wearing gloves). Real name: [], Key: []",time_stamp(), H.real_name, H.key) + fingerprintslast = H.ckey + H.gloves.add_fingerprint(M) + return 0 + + //More adminstuffz + if(fingerprintslast != H.ckey) + fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), H.real_name, H.key) + fingerprintslast = H.ckey + + //Make the list if it does not exist. + if(!fingerprints) + fingerprints = list() + + //Hash this shit. + var/full_print = H.get_full_print() + + // Add the fingerprints + fingerprints[full_print] = full_print + + return 1 + else + //Smudge up dem prints some + if(fingerprintslast != M.ckey) + fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), M.real_name, M.key) + fingerprintslast = M.ckey + + return + + +/atom/proc/transfer_fingerprints_to(var/atom/A) + + // Make sure everything are lists. + if(!islist(A.fingerprints)) + A.fingerprints = list() + if(!islist(A.fingerprintshidden)) + A.fingerprintshidden = list() + + if(!islist(fingerprints)) + fingerprints = list() + if(!islist(fingerprintshidden)) + fingerprintshidden = list() + + // Transfer + if(fingerprints) + A.fingerprints |= fingerprints.Copy() //detective + if(fingerprintshidden) + A.fingerprintshidden |= fingerprintshidden.Copy() //admin + A.fingerprintslast = fingerprintslast + +GLOBAL_LIST_EMPTY(blood_splatter_icons) + +/atom/proc/blood_splatter_index() + return "\ref[initial(icon)]-[initial(icon_state)]" + +//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list +/mob/living/proc/get_blood_dna_list() + if(get_blood_id() != "blood") + return + return list("ANIMAL DNA" = "Y-") + +/mob/living/carbon/get_blood_dna_list() + if(get_blood_id() != "blood") + return + var/list/blood_dna = list() + if(dna) + blood_dna[dna.unique_enzymes] = dna.blood_type + else + blood_dna["UNKNOWN DNA"] = "X*" + return blood_dna + +/mob/living/carbon/alien/get_blood_dna_list() + return list("UNKNOWN DNA" = "X*") + +//to add a mob's dna info into an object's blood_DNA list. +/atom/proc/transfer_mob_blood_dna(mob/living/L) + var/new_blood_dna = L.get_blood_dna_list() + if(!new_blood_dna) + return 0 + return transfer_blood_dna(new_blood_dna) + +/obj/effect/decal/cleanable/blood/splatter/transfer_mob_blood_dna(mob/living/L) + ..(L) + var/list/b_data = L.get_blood_data(L.get_blood_id()) + if(b_data) + basecolor = b_data["blood_color"] + else + basecolor = "#A10808" + update_icon() + +/obj/effect/decal/cleanable/blood/footprints/transfer_mob_blood_dna(mob/living/L) + ..(L) + var/list/b_data = L.get_blood_data(L.get_blood_id()) + if(b_data) + basecolor = b_data["blood_color"] + else + basecolor = "#A10808" + update_icon() + +//to add blood dna info to the object's blood_DNA list +/atom/proc/transfer_blood_dna(list/blood_dna) + if(!blood_DNA) + blood_DNA = list() + var/old_length = blood_DNA.len + blood_DNA |= blood_dna + if(blood_DNA.len > old_length) + return 1//some new blood DNA was added + + +//to add blood from a mob onto something, and transfer their dna info +/atom/proc/add_mob_blood(mob/living/M) + var/list/blood_dna = M.get_blood_dna_list() + if(!blood_dna) + return 0 + var/bloodcolor = "#A10808" + var/list/b_data = M.get_blood_data(M.get_blood_id()) + if(b_data) + bloodcolor = b_data["blood_color"] + + return add_blood(blood_dna, bloodcolor) + +//to add blood onto something, with blood dna info to include. +/atom/proc/add_blood(list/blood_dna, color) + return 0 + +/obj/add_blood(list/blood_dna, color) + return transfer_blood_dna(blood_dna) + +/obj/item/add_blood(list/blood_dna, color) + var/blood_count = !blood_DNA ? 0 : blood_DNA.len + if(!..()) + return 0 + if(!blood_count)//apply the blood-splatter overlay if it isn't already in there + add_blood_overlay(color) + return 1 //we applied blood to the item + +/obj/item/clothing/gloves/add_blood(list/blood_dna, color) + . = ..() + transfer_blood = rand(2, 4) + +/turf/add_blood(list/blood_dna, color) + var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src + if(!B) + B = new /obj/effect/decal/cleanable/blood/splatter(src) + B.transfer_blood_dna(blood_dna) //give blood info to the blood decal. + B.basecolor = color + return 1 //we bloodied the floor + +/mob/living/carbon/human/add_blood(list/blood_dna, color) + if(wear_suit) + wear_suit.add_blood(blood_dna, color) + wear_suit.blood_color = color + update_inv_wear_suit(1) + else if(w_uniform) + w_uniform.add_blood(blood_dna, color) + w_uniform.blood_color = color + update_inv_w_uniform(1) + if(head) + head.add_blood(blood_dna, color) + head.blood_color = color + update_inv_head(0,0) + if(glasses) + glasses.add_blood(blood_dna, color) + glasses.blood_color = color + update_inv_glasses(0) + if(gloves) + var/obj/item/clothing/gloves/G = gloves + G.add_blood(blood_dna, color) + G.blood_color = color + verbs += /mob/living/carbon/human/proc/bloody_doodle + else + hand_blood_color = color + bloody_hands = rand(2, 4) + transfer_blood_dna(blood_dna) + verbs += /mob/living/carbon/human/proc/bloody_doodle + + update_inv_gloves(1) //handles bloody hands overlays and updating + return 1 + +/obj/item/proc/add_blood_overlay(color) + if(initial(icon) && initial(icon_state)) + //try to find a pre-processed blood-splatter. otherwise, make a new one + var/index = blood_splatter_index() + var/icon/blood_splatter_icon = GLOB.blood_splatter_icons[index] + if(!blood_splatter_icon) + blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object + blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) + blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant + blood_splatter_icon = fcopy_rsc(blood_splatter_icon) + GLOB.blood_splatter_icons[index] = blood_splatter_icon + + blood_overlay = image(blood_splatter_icon) + blood_overlay.color = color + overlays += blood_overlay + +/atom/proc/clean_blood() + germ_level = 0 + if(islist(blood_DNA)) + blood_DNA = null + return TRUE + +/obj/effect/decal/cleanable/blood/clean_blood() + return // While this seems nonsensical, clean_blood isn't supposed to be used like this on a blood decal. + +/obj/item/clean_blood() + . = ..() + if(.) + if(blood_overlay) + overlays -= blood_overlay + +/obj/item/clothing/gloves/clean_blood() + . = ..() + if(.) + transfer_blood = 0 + + +/obj/item/clothing/shoes/clean_blood() + ..() + bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) + blood_state = BLOOD_STATE_NOT_BLOODY + if(ismob(loc)) + var/mob/M = loc + M.update_inv_shoes() + + +/mob/living/carbon/human/clean_blood() + if(gloves) + if(gloves.clean_blood()) + clean_blood() + update_inv_gloves() + gloves.germ_level = 0 + else + ..() // Clear the Blood_DNA list + if(bloody_hands) + bloody_hands = 0 + update_inv_gloves() + update_icons() //apply the now updated overlays to the mob + + +/atom/proc/add_vomit_floor(toxvomit = 0, green = FALSE) + playsound(src, 'sound/effects/splat.ogg', 50, 1) + if(!isspaceturf(src)) + var/type = green ? /obj/effect/decal/cleanable/vomit/green : /obj/effect/decal/cleanable/vomit + var/vomit_reagent = green ? "green_vomit" : "vomit" + for(var/obj/effect/decal/cleanable/vomit/V in get_turf(src)) + if(V.type == type) + V.reagents.add_reagent(vomit_reagent, 5) + return + + var/obj/effect/decal/cleanable/vomit/this = new type(src) + + // Make toxins vomit look different + if(toxvomit) + this.icon_state = "vomittox_[pick(1,4)]" + +/atom/proc/get_global_map_pos() + if(!islist(GLOB.global_map) || isemptylist(GLOB.global_map)) return + var/cur_x = null + var/cur_y = null + var/list/y_arr = null + for(cur_x=1,cur_x<=GLOB.global_map.len,cur_x++) + y_arr = GLOB.global_map[cur_x] + cur_y = y_arr.Find(src.z) + if(cur_y) + break +// to_chat(world, "X = [cur_x]; Y = [cur_y]") + if(cur_x && cur_y) + return list("x"=cur_x,"y"=cur_y) + else + return 0 + +// Used to provide overlays when using this atom as a viewing focus +// (cameras, locker tint, etc.) +/atom/proc/get_remote_view_fullscreens(mob/user) + return + +//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) +/atom/proc/update_remote_sight(mob/living/user) + user.sync_lighting_plane_alpha() + return + +/atom/proc/checkpass(passflag) + return pass_flags&passflag + +/atom/proc/isinspace() + if(isspaceturf(get_turf(src))) + return TRUE + else + return FALSE + +/atom/proc/handle_fall() + return + +/atom/proc/singularity_act() + return + +/atom/proc/singularity_pull(obj/singularity/S, current_size) + SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) + +/** + * Respond to acid being used on our atom + * + * Default behaviour is to send COMSIG_ATOM_ACID_ACT and return + */ +/atom/proc/acid_act(acidpwr, acid_volume) + SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) + +/atom/proc/narsie_act() + return + +/atom/proc/ratvar_act() + return + +/atom/proc/handle_ricochet(obj/item/projectile/P) + return + +//This proc is called on the location of an atom when the atom is Destroy()'d +/atom/proc/handle_atom_del(atom/A) + return + +/atom/proc/atom_say(message) + if(!message) + return + audible_message("[src] [atom_say_verb], \"[message]\"") + +/atom/proc/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) + return + +/atom/vv_edit_var(var_name, var_value) + if(!GLOB.debug2) + admin_spawned = TRUE + . = ..() + switch(var_name) + if("light_power", "light_range", "light_color") + update_light() + if("color") + add_atom_colour(color, ADMIN_COLOUR_PRIORITY) + + +/atom/vv_get_dropdown() + . = ..() + var/turf/curturf = get_turf(src) + if(curturf) + .["Jump to turf"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" + .["Add reagent"] = "?_src_=vars;addreagent=[UID()]" + .["Trigger explosion"] = "?_src_=vars;explode=[UID()]" + .["Trigger EM pulse"] = "?_src_=vars;emp=[UID()]" + +/atom/proc/AllowDrop() + return FALSE + +/atom/proc/drop_location() + var/atom/L = loc + if(!L) + return null + return L.AllowDrop() ? L : get_turf(L) + +/atom/Entered(atom/movable/AM, atom/oldLoc) + SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) + +/atom/Exit(atom/movable/AM, atom/newLoc) + . = ..() + if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) + return FALSE + +/atom/Exited(atom/movable/AM, atom/newLoc) + SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) + +/* + Adds an instance of colour_type to the atom's atom_colours list +*/ +/atom/proc/add_atom_colour(coloration, colour_priority) + if(!atom_colours || !atom_colours.len) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(!coloration) + return + if(colour_priority > atom_colours.len) + return + atom_colours[colour_priority] = coloration + update_atom_colour() + + +/* + Removes an instance of colour_type from the atom's atom_colours list +*/ +/atom/proc/remove_atom_colour(colour_priority, coloration) + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(colour_priority > atom_colours.len) + return + if(coloration && atom_colours[colour_priority] != coloration) + return //if we don't have the expected color (for a specific priority) to remove, do nothing + atom_colours[colour_priority] = null + update_atom_colour() + + +/* + Resets the atom's color to null, and then sets it to the highest priority + colour available +*/ +/atom/proc/update_atom_colour() + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + color = null + for(var/C in atom_colours) + if(islist(C)) + var/list/L = C + if(L.len) + color = L + return + else if(C) + color = C + return diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 2fb8796dc10..ac77d3d4553 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,565 +1,576 @@ -/atom/movable - layer = 3 - appearance_flags = TILE_BOUND - var/last_move = null - var/anchored = 0 - var/move_resist = MOVE_RESIST_DEFAULT - var/move_force = MOVE_FORCE_DEFAULT - var/pull_force = PULL_FORCE_DEFAULT - // var/elevation = 2 - not used anywhere - var/move_speed = 10 - var/l_move_time = 1 - var/datum/thrownthing/throwing = null - var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported - var/throw_range = 7 - var/no_spin = 0 - var/no_spin_thrown = 0 - var/moved_recently = 0 - var/mob/pulledby = null - var/atom/movable/pulling - var/throwforce = 0 - var/canmove = 1 - - var/inertia_dir = 0 - var/atom/inertia_last_loc - var/inertia_moving = 0 - var/inertia_next_move = 0 - var/inertia_move_delay = 5 - - var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move - var/list/client_mobs_in_contents - var/area/areaMaster - -/atom/movable/New() - . = ..() - areaMaster = get_area(src) - -/atom/movable/attempt_init(loc, ...) - var/turf/T = get_turf(src) - if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && space_manager.is_zlevel_dirty(T.z)) - space_manager.postpone_init(T.z, src) - return - . = ..() - -/atom/movable/Destroy() - unbuckle_all_mobs(force = TRUE) - if(loc) - loc.handle_atom_del(src) - for(var/atom/movable/AM in contents) - qdel(AM) - loc = null - if(pulledby) - if(pulledby.pulling == src) - pulledby.pulling = null - pulledby = null - return ..() - -//Returns an atom's power cell, if it has one. Overload for individual items. -/atom/movable/proc/get_cell() - return - -/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) - if(QDELETED(AM)) - return FALSE - if(!(AM.can_be_pulled(src, state, force))) - return FALSE - - // if we're pulling something then drop what we're currently pulling and pull this instead. - if(pulling) - if(state == 0) - stop_pulling() - return FALSE - // Are we trying to pull something we are already pulling? Then enter grab cycle and end. - if(AM == pulling) - if(isliving(AM)) - var/mob/living/AMob = AM - AMob.grabbedby(src) - return TRUE - stop_pulling() - if(AM.pulledby) - add_attack_logs(AM, AM.pulledby, "pulled from", ATKLOG_ALMOSTALL) - AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. - pulling = AM - AM.pulledby = src - if(ismob(AM)) - var/mob/M = AM - add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL) - if(!supress_message) - visible_message("[src] has grabbed [M] passively!") - return TRUE - -/atom/movable/proc/stop_pulling() - if(pulling) - pulling.pulledby = null - var/mob/living/ex_pulled = pulling - pulling = null - pulledby = null - if(isliving(ex_pulled)) - var/mob/living/L = ex_pulled - L.update_canmove()// mob gets up if it was lyng down in a chokehold - -/atom/movable/proc/check_pulling() - if(pulling) - var/atom/movable/pullee = pulling - if(pullee && get_dist(src, pullee) > 1) - stop_pulling() - return - if(!isturf(loc)) - stop_pulling() - return - if(pullee && !isturf(pullee.loc) && pullee.loc != loc) //to be removed once all code that changes an object's loc uses forceMove(). - log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.") - stop_pulling() - return - if(pulling.anchored || pulling.move_resist > move_force) - stop_pulling() - return - if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move. - pulledby.stop_pulling() - -/atom/movable/proc/can_be_pulled(user, grab_state, force) - if(src == user || !isturf(loc)) - return FALSE - if(anchored || throwing) - return FALSE - if(force < (move_resist * MOVE_FORCE_PULL_RATIO)) - return FALSE - return TRUE - -// Used in shuttle movement and AI eye stuff. -// Primarily used to notify objects being moved by a shuttle/bluespace fuckup. -/atom/movable/proc/setLoc(var/T, var/teleported=0) - loc = T - -/atom/movable/Move(atom/newloc, direct = 0) - if(!loc || !newloc) return 0 - var/atom/oldloc = loc - - if(loc != newloc) - if(!(direct & (direct - 1))) //Cardinal move - . = ..() - else //Diagonal move, split it into cardinal moves - moving_diagonally = FIRST_DIAG_STEP - var/first_step_dir - // The `&& moving_diagonally` checks are so that a forceMove taking - // place due to a Crossed, Bumped, etc. call will interrupt - // the second half of the diagonal movement, or the second attempt - // at a first half if step() fails because we hit something. - if(direct & NORTH) - if(direct & EAST) - if(step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if(moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if(direct & WEST) - if(step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if(moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if(direct & SOUTH) - if(direct & EAST) - if(step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if(moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - else if(direct & WEST) - if(step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if(moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - if(moving_diagonally == SECOND_DIAG_STEP) - if(!.) - setDir(first_step_dir) - else if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay - newtonian_move(direct) - moving_diagonally = 0 - return - - if(!loc || (loc == oldloc && oldloc != newloc)) - last_move = 0 - return - - if(.) - Moved(oldloc, direct) - - last_move = direct - src.move_speed = world.time - src.l_move_time - src.l_move_time = world.time - - if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct)) //movement failed due to buckled mob - . = 0 - -// Called after a successful Move(). By this point, we've already moved -/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) - if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay - newtonian_move(Dir) - if(length(client_mobs_in_contents)) - update_parallax_contents() - return TRUE - -// Previously known as HasEntered() -// This is automatically called when something enters your square -/atom/movable/Crossed(atom/movable/AM, oldloc) - SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM) - SEND_SIGNAL(AM, COMSIG_CROSSED_MOVABLE, src) - -/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump(). - if(A && yes) - SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) - if(throwing) - throwing.hit_atom(A) - . = 1 - if(!A || QDELETED(A)) - return - A.Bumped(src) - -/atom/movable/proc/forceMove(atom/destination) - var/turf/old_loc = loc - loc = destination - moving_diagonally = 0 - - if(old_loc) - old_loc.Exited(src, destination) - for(var/atom/movable/AM in old_loc) - AM.Uncrossed(src) - - if(destination) - destination.Entered(src) - for(var/atom/movable/AM in destination) - if(AM == src) - continue - AM.Crossed(src, old_loc) - var/turf/oldturf = get_turf(old_loc) - var/turf/destturf = get_turf(destination) - var/old_z = (oldturf ? oldturf.z : null) - var/dest_z = (destturf ? destturf.z : null) - if(old_z != dest_z) - onTransitZ(old_z, dest_z) - if(isturf(destination) && opacity) - var/turf/new_loc = destination - new_loc.reconsider_lights() - - if(isturf(old_loc) && opacity) - old_loc.reconsider_lights() - - for(var/datum/light_source/L in light_sources) - L.source_atom.update_light() - - return 1 - -/atom/movable/proc/onTransitZ(old_z,new_z) - for(var/item in src) // Notify contents of Z-transition. This can be overridden if we know the items contents do not care. - var/atom/movable/AM = item - AM.onTransitZ(old_z,new_z) - -/mob/living/forceMove(atom/destination) - if(buckled) - addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE) - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - addtimer(CALLBACK(buckled_mob, .proc/check_buckled), 1, TIMER_UNIQUE) - if(pulling) - addtimer(CALLBACK(src, .proc/check_pull), 1, TIMER_UNIQUE) - . = ..() - if(client) - reset_perspective(destination) - update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall. - - -//Called whenever an object moves and by mobs when they attempt to move themselves through space -//And when an object or action applies a force on src, see newtonian_move() below -//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting -//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm -//movement_dir == 0 when stopping or any dir when trying to move -/atom/movable/proc/Process_Spacemove(var/movement_dir = 0) - if(has_gravity(src)) - return 1 - - if(pulledby && !pulledby.pulling) - return 1 - - if(throwing) - return 1 - - if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier - return 1 - - return 0 - -/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity - if(!loc || Process_Spacemove(0)) - inertia_dir = 0 - return 0 - - inertia_dir = direction - if(!direction) - return 1 - - inertia_last_loc = loc - SSspacedrift.processing[src] = src - return 1 - - -//called when src is thrown into hit_atom -/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum) - set waitfor = 0 - SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum) - if(!QDELETED(hit_atom)) - return hit_atom.hitby(src) - -/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum) - if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO)))) - step(src, AM.dir) - ..() - -/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY) - if(!target || (flags & NODROP) || speed <= 0) - return 0 - - if(pulledby) - pulledby.stop_pulling() - - // They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw? - if(thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag * 2) - var/user_momentum = thrower.movement_delay() - if(!user_momentum) // no movement_delay, this means they move once per byond tick, let's calculate from that instead - user_momentum = world.tick_lag - - user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses - - if(get_dir(thrower, target) & last_move) - user_momentum = user_momentum // basically a noop, but needed - else if(get_dir(target, thrower) & last_move) - user_momentum = -user_momentum // we are moving away from the target, lets slowdown the throw accordingly - else - user_momentum = 0 - - if(user_momentum) - // first lets add that momentum to range - range *= (user_momentum / speed) + 1 - //then lets add it to speed - speed += user_momentum - if(speed <= 0) - return //no throw speed, the user was moving too fast. - - var/datum/thrownthing/TT = new() - TT.thrownthing = src - TT.target = target - TT.target_turf = get_turf(target) - TT.init_dir = get_dir(src, target) - TT.maxrange = range - TT.speed = speed - TT.thrower = thrower - TT.diagonals_first = diagonals_first - TT.callback = callback - - var/dist_x = abs(target.x - src.x) - var/dist_y = abs(target.y - src.y) - var/dx = (target.x > src.x) ? EAST : WEST - var/dy = (target.y > src.y) ? NORTH : SOUTH - - if(dist_x == dist_y) - TT.pure_diagonal = 1 - - else if(dist_x <= dist_y) - var/olddist_x = dist_x - var/olddx = dx - dist_x = dist_y - dist_y = olddist_x - dx = dy - dy = olddx - TT.dist_x = dist_x - TT.dist_y = dist_y - TT.dx = dx - TT.dy = dy - TT.diagonal_error = dist_x / 2 - dist_y - TT.start_time = world.time - - if(pulledby) - pulledby.stop_pulling() - - throwing = TT - if(spin && !no_spin && !no_spin_thrown) - SpinAnimation(5, 1) - - SSthrowing.processing[src] = TT - TT.tick() - - return TRUE - -//Overlays -/atom/movable/overlay - var/atom/master = null - anchored = TRUE - simulated = FALSE - -/atom/movable/overlay/New() - verbs.Cut() - return - -/atom/movable/overlay/attackby(a, b, c) - if(master) - return master.attackby(a, b, c) - -/atom/movable/overlay/attack_hand(a, b, c) - if(master) - return master.attack_hand(a, b, c) - -/atom/movable/proc/water_act(volume, temperature, source, method = TOUCH) //amount of water acting : temperature of water in kelvin : object that called it (for shennagins) - return TRUE - -/atom/movable/proc/handle_buckled_mob_movement(newloc,direct) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - if(!buckled_mob.Move(newloc, direct)) - forceMove(buckled_mob.loc) - last_move = buckled_mob.last_move - inertia_dir = last_move - buckled_mob.inertia_dir = last_move - return 0 - return 1 - -/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) - return FALSE - -/atom/movable/proc/force_push(atom/movable/AM, force = move_force, direction, silent = FALSE) - . = AM.force_pushed(src, force, direction) - if(!silent && .) - visible_message("[src] forcefully pushes against [AM]!", "You forcefully push against [AM]!") - -/atom/movable/proc/move_crush(atom/movable/AM, force = move_force, direction, silent = FALSE) - . = AM.move_crushed(src, force, direction) - if(!silent && .) - visible_message("[src] crushes past [AM]!", "You crush [AM]!") - -/atom/movable/proc/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) - return FALSE - -/atom/movable/CanPass(atom/movable/mover, turf/target, height=1.5) - if(mover in buckled_mobs) - return 1 - return ..() - -/atom/movable/proc/get_spacemove_backup() - var/atom/movable/dense_object_backup - for(var/A in orange(1, get_turf(src))) - if(isarea(A)) - continue - else if(isturf(A)) - var/turf/turf = A - if(!turf.density) - continue - return turf - else - var/atom/movable/AM = A - if(!AM.CanPass(src) || AM.density) - if(AM.anchored) - return AM - dense_object_backup = AM - break - . = dense_object_backup - -/atom/movable/proc/transfer_prints_to(atom/movable/target = null, overwrite = FALSE) - if(!target) - return - if(overwrite) - target.fingerprints = fingerprints - target.fingerprintshidden = fingerprintshidden - else - target.fingerprints += fingerprints - target.fingerprintshidden += fingerprintshidden - target.fingerprintslast = fingerprintslast - -/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) - if(!no_effect && (visual_effect_icon || used_item)) - do_item_attack_animation(A, visual_effect_icon, used_item) - - if(A == src) - return //don't do an animation if attacking self - var/pixel_x_diff = 0 - var/pixel_y_diff = 0 - var/final_pixel_y = initial(pixel_y) - if(end_pixel_y) - final_pixel_y = end_pixel_y - - var/direction = get_dir(src, A) - if(direction & NORTH) - pixel_y_diff = 8 - else if(direction & SOUTH) - pixel_y_diff = -8 - - if(direction & EAST) - pixel_x_diff = 8 - else if(direction & WEST) - pixel_x_diff = -8 - - animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2) - animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2) - -/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item) - var/image/I - if(visual_effect_icon) - I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1) - else if(used_item) - I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1) - - // Scale the icon. - I.transform *= 0.75 - // The icon should not rotate. - I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - - // Set the direction of the icon animation. - var/direction = get_dir(src, A) - if(direction & NORTH) - I.pixel_y = -16 - else if(direction & SOUTH) - I.pixel_y = 16 - - if(direction & EAST) - I.pixel_x = -16 - else if(direction & WEST) - I.pixel_x = 16 - - if(!direction) // Attacked self?! - I.pixel_z = 16 - - if(!I) - return - - // Who can see the attack? - var/list/viewing = list() - for(var/mob/M in viewers(A)) - if(M.client && M.client.prefs.show_ghostitem_attack) - viewing |= M.client - - flick_overlay(I, viewing, 5) // 5 ticks/half a second - - // And animate the attack! - var/t_color = "#ffffff" - if(ismob(src) && ismob(A) && (!used_item)) - var/mob/M = src - t_color = M.a_intent == INTENT_HARM ? "#ff0000" : "#ffffff" - animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3, color = t_color) - -/atom/movable/proc/portal_destroyed(obj/effect/portal/P) - return +/atom/movable + layer = 3 + appearance_flags = TILE_BOUND + glide_size = 8 // Default, adjusted when mobs move based on their movement delays + var/last_move = null + var/anchored = 0 + var/move_resist = MOVE_RESIST_DEFAULT + var/move_force = MOVE_FORCE_DEFAULT + var/pull_force = PULL_FORCE_DEFAULT + // var/elevation = 2 - not used anywhere + var/move_speed = 10 + var/l_move_time = 1 + var/datum/thrownthing/throwing = null + var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported + var/throw_range = 7 + var/no_spin = 0 + var/no_spin_thrown = 0 + var/moved_recently = 0 + var/mob/pulledby = null + var/atom/movable/pulling + var/throwforce = 0 + var/canmove = 1 + + var/inertia_dir = 0 + var/atom/inertia_last_loc + var/inertia_moving = 0 + var/inertia_next_move = 0 + var/inertia_move_delay = 5 + + var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move + var/list/client_mobs_in_contents + var/area/areaMaster + +/atom/movable/New() + . = ..() + areaMaster = get_area(src) + +/atom/movable/attempt_init(loc, ...) + var/turf/T = get_turf(src) + if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && GLOB.space_manager.is_zlevel_dirty(T.z)) + GLOB.space_manager.postpone_init(T.z, src) + return + . = ..() + +/atom/movable/Destroy() + unbuckle_all_mobs(force = TRUE) + if(loc) + loc.handle_atom_del(src) + for(var/atom/movable/AM in contents) + qdel(AM) + loc = null + if(pulledby) + if(pulledby.pulling == src) + pulledby.pulling = null + pulledby = null + return ..() + +//Returns an atom's power cell, if it has one. Overload for individual items. +/atom/movable/proc/get_cell() + return + +/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) + if(QDELETED(AM)) + return FALSE + if(!(AM.can_be_pulled(src, state, force))) + return FALSE + + // if we're pulling something then drop what we're currently pulling and pull this instead. + if(pulling) + if(state == 0) + stop_pulling() + return FALSE + // Are we trying to pull something we are already pulling? Then enter grab cycle and end. + if(AM == pulling) + if(isliving(AM)) + var/mob/living/AMob = AM + AMob.grabbedby(src) + return TRUE + stop_pulling() + if(AM.pulledby) + add_attack_logs(AM, AM.pulledby, "pulled from", ATKLOG_ALMOSTALL) + AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. + pulling = AM + AM.pulledby = src + if(ismob(AM)) + var/mob/M = AM + add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL) + if(!supress_message) + visible_message("[src] has grabbed [M] passively!") + return TRUE + +/atom/movable/proc/stop_pulling() + if(pulling) + pulling.pulledby = null + var/mob/living/ex_pulled = pulling + pulling = null + pulledby = null + if(isliving(ex_pulled)) + var/mob/living/L = ex_pulled + L.update_canmove()// mob gets up if it was lyng down in a chokehold + +/atom/movable/proc/check_pulling() + if(pulling) + var/atom/movable/pullee = pulling + if(pullee && get_dist(src, pullee) > 1) + stop_pulling() + return + if(!isturf(loc)) + stop_pulling() + return + if(pullee && !isturf(pullee.loc) && pullee.loc != loc) //to be removed once all code that changes an object's loc uses forceMove(). + log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.") + stop_pulling() + return + if(pulling.anchored || pulling.move_resist > move_force) + stop_pulling() + return + if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move. + pulledby.stop_pulling() + +/atom/movable/proc/can_be_pulled(user, grab_state, force) + if(src == user || !isturf(loc)) + return FALSE + if(anchored || throwing) + return FALSE + if(force < (move_resist * MOVE_FORCE_PULL_RATIO)) + return FALSE + return TRUE + +// Used in shuttle movement and AI eye stuff. +// Primarily used to notify objects being moved by a shuttle/bluespace fuckup. +/atom/movable/proc/setLoc(var/T, var/teleported=0) + loc = T + +/atom/movable/Move(atom/newloc, direct = 0, movetime) + if(!loc || !newloc) return 0 + var/atom/oldloc = loc + + if(loc != newloc) + glide_for(movetime) + if(!(direct & (direct - 1))) //Cardinal move + . = ..(newloc, direct) // don't pass up movetime + else //Diagonal move, split it into cardinal moves + moving_diagonally = FIRST_DIAG_STEP + var/first_step_dir + // The `&& moving_diagonally` checks are so that a forceMove taking + // place due to a Crossed, Bumped, etc. call will interrupt + // the second half of the diagonal movement, or the second attempt + // at a first half if step() fails because we hit something. + if(direct & NORTH) + if(direct & EAST) + if(step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if(moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if(direct & WEST) + if(step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if(moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if(direct & SOUTH) + if(direct & EAST) + if(step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if(moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + else if(direct & WEST) + if(step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if(moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + if(moving_diagonally == SECOND_DIAG_STEP) + if(!.) + setDir(first_step_dir) + else if(!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(direct) + moving_diagonally = 0 + return + + if(!loc || (loc == oldloc && oldloc != newloc)) + last_move = 0 + return + + if(.) + Moved(oldloc, direct) + + last_move = direct + src.move_speed = world.time - src.l_move_time + src.l_move_time = world.time + + if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, movetime)) //movement failed due to buckled mob + . = 0 + +// Called after a successful Move(). By this point, we've already moved +/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) + if(!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(Dir) + if(length(client_mobs_in_contents)) + update_parallax_contents() + return TRUE + +// Change glide size for the duration of one movement +/atom/movable/proc/glide_for(movetime) + if(movetime) + glide_size = world.icon_size/max(DS2TICKS(movetime), 1) + spawn(movetime) + glide_size = initial(glide_size) + else + glide_size = initial(glide_size) + +// Previously known as HasEntered() +// This is automatically called when something enters your square +/atom/movable/Crossed(atom/movable/AM, oldloc) + SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM) + SEND_SIGNAL(AM, COMSIG_CROSSED_MOVABLE, src) + +/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump(). + if(A && yes) + SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) + if(throwing) + throwing.hit_atom(A) + . = 1 + if(!A || QDELETED(A)) + return + A.Bumped(src) + +/atom/movable/proc/forceMove(atom/destination) + var/turf/old_loc = loc + loc = destination + moving_diagonally = 0 + + if(old_loc) + old_loc.Exited(src, destination) + for(var/atom/movable/AM in old_loc) + AM.Uncrossed(src) + + if(destination) + destination.Entered(src) + for(var/atom/movable/AM in destination) + if(AM == src) + continue + AM.Crossed(src, old_loc) + var/turf/oldturf = get_turf(old_loc) + var/turf/destturf = get_turf(destination) + var/old_z = (oldturf ? oldturf.z : null) + var/dest_z = (destturf ? destturf.z : null) + if(old_z != dest_z) + onTransitZ(old_z, dest_z) + if(isturf(destination) && opacity) + var/turf/new_loc = destination + new_loc.reconsider_lights() + + if(isturf(old_loc) && opacity) + old_loc.reconsider_lights() + + for(var/datum/light_source/L in light_sources) + L.source_atom.update_light() + + return 1 + +/atom/movable/proc/onTransitZ(old_z,new_z) + for(var/item in src) // Notify contents of Z-transition. This can be overridden if we know the items contents do not care. + var/atom/movable/AM = item + AM.onTransitZ(old_z,new_z) + +/mob/living/forceMove(atom/destination) + if(buckled) + addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE) + if(has_buckled_mobs()) + for(var/m in buckled_mobs) + var/mob/living/buckled_mob = m + addtimer(CALLBACK(buckled_mob, .proc/check_buckled), 1, TIMER_UNIQUE) + if(pulling) + addtimer(CALLBACK(src, .proc/check_pull), 1, TIMER_UNIQUE) + . = ..() + if(client) + reset_perspective(destination) + update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall. + + +//Called whenever an object moves and by mobs when they attempt to move themselves through space +//And when an object or action applies a force on src, see newtonian_move() below +//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting +//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm +//movement_dir == 0 when stopping or any dir when trying to move +/atom/movable/proc/Process_Spacemove(var/movement_dir = 0) + if(has_gravity(src)) + return 1 + + if(pulledby && !pulledby.pulling) + return 1 + + if(throwing) + return 1 + + if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier + return 1 + + return 0 + +/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity + if(!loc || Process_Spacemove(0)) + inertia_dir = 0 + return 0 + + inertia_dir = direction + if(!direction) + return 1 + + inertia_last_loc = loc + SSspacedrift.processing[src] = src + return 1 + + +//called when src is thrown into hit_atom +/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum) + set waitfor = 0 + SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum) + if(!QDELETED(hit_atom)) + return hit_atom.hitby(src) + +/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum) + if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO)))) + step(src, AM.dir) + ..() + +/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY) + if(!target || (flags & NODROP) || speed <= 0) + return 0 + + if(pulledby) + pulledby.stop_pulling() + + // They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw? + if(thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag * 2) + var/user_momentum = thrower.movement_delay() + if(!user_momentum) // no movement_delay, this means they move once per byond tick, let's calculate from that instead + user_momentum = world.tick_lag + + user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses + + if(get_dir(thrower, target) & last_move) + user_momentum = user_momentum // basically a noop, but needed + else if(get_dir(target, thrower) & last_move) + user_momentum = -user_momentum // we are moving away from the target, lets slowdown the throw accordingly + else + user_momentum = 0 + + if(user_momentum) + // first lets add that momentum to range + range *= (user_momentum / speed) + 1 + //then lets add it to speed + speed += user_momentum + if(speed <= 0) + return //no throw speed, the user was moving too fast. + + var/datum/thrownthing/TT = new() + TT.thrownthing = src + TT.target = target + TT.target_turf = get_turf(target) + TT.init_dir = get_dir(src, target) + TT.maxrange = range + TT.speed = speed + TT.thrower = thrower + TT.diagonals_first = diagonals_first + TT.callback = callback + + var/dist_x = abs(target.x - src.x) + var/dist_y = abs(target.y - src.y) + var/dx = (target.x > src.x) ? EAST : WEST + var/dy = (target.y > src.y) ? NORTH : SOUTH + + if(dist_x == dist_y) + TT.pure_diagonal = 1 + + else if(dist_x <= dist_y) + var/olddist_x = dist_x + var/olddx = dx + dist_x = dist_y + dist_y = olddist_x + dx = dy + dy = olddx + TT.dist_x = dist_x + TT.dist_y = dist_y + TT.dx = dx + TT.dy = dy + TT.diagonal_error = dist_x / 2 - dist_y + TT.start_time = world.time + + if(pulledby) + pulledby.stop_pulling() + + throwing = TT + if(spin && !no_spin && !no_spin_thrown) + SpinAnimation(5, 1) + + SSthrowing.processing[src] = TT + TT.tick() + + return TRUE + +//Overlays +/atom/movable/overlay + var/atom/master = null + anchored = TRUE + simulated = FALSE + +/atom/movable/overlay/New() + verbs.Cut() + return + +/atom/movable/overlay/attackby(a, b, c) + if(master) + return master.attackby(a, b, c) + +/atom/movable/overlay/attack_hand(a, b, c) + if(master) + return master.attack_hand(a, b, c) + +/atom/movable/proc/water_act(volume, temperature, source, method = REAGENT_TOUCH) //amount of water acting : temperature of water in kelvin : object that called it (for shennagins) + return TRUE + +/atom/movable/proc/handle_buckled_mob_movement(newloc,direct,movetime) + for(var/m in buckled_mobs) + var/mob/living/buckled_mob = m + if(!buckled_mob.Move(newloc, direct, movetime)) + forceMove(buckled_mob.loc) + last_move = buckled_mob.last_move + inertia_dir = last_move + buckled_mob.inertia_dir = last_move + return 0 + return 1 + +/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + return FALSE + +/atom/movable/proc/force_push(atom/movable/AM, force = move_force, direction, silent = FALSE) + . = AM.force_pushed(src, force, direction) + if(!silent && .) + visible_message("[src] forcefully pushes against [AM]!", "You forcefully push against [AM]!") + +/atom/movable/proc/move_crush(atom/movable/AM, force = move_force, direction, silent = FALSE) + . = AM.move_crushed(src, force, direction) + if(!silent && .) + visible_message("[src] crushes past [AM]!", "You crush [AM]!") + +/atom/movable/proc/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + return FALSE + +/atom/movable/CanPass(atom/movable/mover, turf/target, height=1.5) + if(mover in buckled_mobs) + return 1 + return ..() + +/atom/movable/proc/get_spacemove_backup() + var/atom/movable/dense_object_backup + for(var/A in orange(1, get_turf(src))) + if(isarea(A)) + continue + else if(isturf(A)) + var/turf/turf = A + if(!turf.density) + continue + return turf + else + var/atom/movable/AM = A + if(!AM.CanPass(src) || AM.density) + if(AM.anchored) + return AM + dense_object_backup = AM + break + . = dense_object_backup + +/atom/movable/proc/transfer_prints_to(atom/movable/target = null, overwrite = FALSE) + if(!target) + return + if(overwrite) + target.fingerprints = fingerprints + target.fingerprintshidden = fingerprintshidden + else + target.fingerprints += fingerprints + target.fingerprintshidden += fingerprintshidden + target.fingerprintslast = fingerprintslast + +/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) + if(!no_effect && (visual_effect_icon || used_item)) + do_item_attack_animation(A, visual_effect_icon, used_item) + + if(A == src) + return //don't do an animation if attacking self + var/pixel_x_diff = 0 + var/pixel_y_diff = 0 + var/final_pixel_y = initial(pixel_y) + if(end_pixel_y) + final_pixel_y = end_pixel_y + + var/direction = get_dir(src, A) + if(direction & NORTH) + pixel_y_diff = 8 + else if(direction & SOUTH) + pixel_y_diff = -8 + + if(direction & EAST) + pixel_x_diff = 8 + else if(direction & WEST) + pixel_x_diff = -8 + + animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2) + animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2) + +/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item) + var/image/I + if(visual_effect_icon) + I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1) + else if(used_item) + I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1) + + // Scale the icon. + I.transform *= 0.75 + // The icon should not rotate. + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + + // Set the direction of the icon animation. + var/direction = get_dir(src, A) + if(direction & NORTH) + I.pixel_y = -16 + else if(direction & SOUTH) + I.pixel_y = 16 + + if(direction & EAST) + I.pixel_x = -16 + else if(direction & WEST) + I.pixel_x = 16 + + if(!direction) // Attacked self?! + I.pixel_z = 16 + + if(!I) + return + + // Who can see the attack? + var/list/viewing = list() + for(var/mob/M in viewers(A)) + if(M.client && M.client.prefs.show_ghostitem_attack) + viewing |= M.client + + flick_overlay(I, viewing, 5) // 5 ticks/half a second + + // And animate the attack! + var/t_color = "#ffffff" + if(ismob(src) && ismob(A) && (!used_item)) + var/mob/M = src + t_color = M.a_intent == INTENT_HARM ? "#ff0000" : "#ffffff" + animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3, color = t_color) + +/atom/movable/proc/portal_destroyed(obj/effect/portal/P) + return diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 4352e48ffe1..08a7b77daff 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -7,10 +7,10 @@ /* DATA HUD DATUMS */ /atom/proc/add_to_all_human_data_huds() - for(var/datum/atom_hud/data/human/hud in huds) hud.add_to_hud(src) + for(var/datum/atom_hud/data/human/hud in GLOB.huds) hud.add_to_hud(src) /atom/proc/remove_from_all_data_huds() - for(var/datum/atom_hud/data/hud in huds) hud.remove_from_hud(src) + for(var/datum/atom_hud/data/hud in GLOB.huds) hud.remove_from_hud(src) /datum/atom_hud/data @@ -142,7 +142,7 @@ //called when a human changes suit sensors /mob/living/carbon/proc/update_suit_sensors() - var/datum/atom_hud/data/human/medical/basic/B = huds[DATA_HUD_MEDICAL_BASIC] + var/datum/atom_hud/data/human/medical/basic/B = GLOB.huds[DATA_HUD_MEDICAL_BASIC] B.update_suit_sensors(src) @@ -220,22 +220,31 @@ var/perpname = get_visible_name(TRUE) //gets the name of the perp, works if they have an id or if their face is uncovered if(!SSticker) return //wait till the game starts or the monkeys runtime.... if(perpname) - var/datum/data/record/R = find_record("name", perpname, data_core.security) + var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security) if(R) switch(R.fields["criminal"]) - if("*Execute*") + if(SEC_RECORD_STATUS_EXECUTE) holder.icon_state = "hudexecute" return - if("*Arrest*") + if(SEC_RECORD_STATUS_ARREST) holder.icon_state = "hudwanted" return - if("Incarcerated") + if(SEC_RECORD_STATUS_SEARCH) + holder.icon_state = "hudsearch" + return + if(SEC_RECORD_STATUS_MONITOR) + holder.icon_state = "hudmonitor" + return + if(SEC_RECORD_STATUS_DEMOTE) + holder.icon_state = "huddemote" + return + if(SEC_RECORD_STATUS_INCARCERATED) holder.icon_state = "hudprisoner" return - if("Parolled") + if(SEC_RECORD_STATUS_PAROLLED) holder.icon_state = "hudparolled" return - if("Released") + if(SEC_RECORD_STATUS_RELEASED) holder.icon_state = "hudreleased" return holder.icon_state = null @@ -440,4 +449,4 @@ if(weedlevel < 1) // You don't want to see these icons if the value is small holder.icon_state = "" return - holder.icon_state = "hudweed[RoundPlantBar(weedlevel/10)]" \ No newline at end of file + holder.icon_state = "hudweed[RoundPlantBar(weedlevel/10)]" diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index e6d80529651..ceabae25973 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -4,76 +4,20 @@ * @author N3X15 */ -// What each index means: -#define DNA_OFF_LOWERBOUND 1 // changed as lists start at 1 not 0 -#define DNA_OFF_UPPERBOUND 2 -#define DNA_ON_LOWERBOUND 3 -#define DNA_ON_UPPERBOUND 4 - -// Define block bounds (off-low,off-high,on-low,on-high) -// Used in setupgame.dm -#define DNA_DEFAULT_BOUNDS list(1,2049,2050,4095) -#define DNA_HARDER_BOUNDS list(1,3049,3050,4095) -#define DNA_HARD_BOUNDS list(1,3490,3500,4095) - -// UI Indices (can change to mutblock style, if desired) -#define DNA_UI_HAIR_R 1 -#define DNA_UI_HAIR_G 2 -#define DNA_UI_HAIR_B 3 -#define DNA_UI_HAIR2_R 4 -#define DNA_UI_HAIR2_G 5 -#define DNA_UI_HAIR2_B 6 -#define DNA_UI_BEARD_R 7 -#define DNA_UI_BEARD_G 8 -#define DNA_UI_BEARD_B 9 -#define DNA_UI_BEARD2_R 10 -#define DNA_UI_BEARD2_G 11 -#define DNA_UI_BEARD2_B 12 -#define DNA_UI_SKIN_TONE 13 -#define DNA_UI_SKIN_R 14 -#define DNA_UI_SKIN_G 15 -#define DNA_UI_SKIN_B 16 -#define DNA_UI_HACC_R 17 -#define DNA_UI_HACC_G 18 -#define DNA_UI_HACC_B 19 -#define DNA_UI_HEAD_MARK_R 20 -#define DNA_UI_HEAD_MARK_G 21 -#define DNA_UI_HEAD_MARK_B 22 -#define DNA_UI_BODY_MARK_R 23 -#define DNA_UI_BODY_MARK_G 24 -#define DNA_UI_BODY_MARK_B 25 -#define DNA_UI_TAIL_MARK_R 26 -#define DNA_UI_TAIL_MARK_G 27 -#define DNA_UI_TAIL_MARK_B 28 -#define DNA_UI_EYES_R 29 -#define DNA_UI_EYES_G 30 -#define DNA_UI_EYES_B 31 -#define DNA_UI_GENDER 32 -#define DNA_UI_BEARD_STYLE 33 -#define DNA_UI_HAIR_STYLE 34 -/*#define DNA_UI_BACC_STYLE 23*/ -#define DNA_UI_HACC_STYLE 35 -#define DNA_UI_HEAD_MARK_STYLE 36 -#define DNA_UI_BODY_MARK_STYLE 37 -#define DNA_UI_TAIL_MARK_STYLE 38 -#define DNA_UI_LENGTH 38 // Update this when you add something, or you WILL break shit. - -#define DNA_SE_LENGTH 55 // Was STRUCDNASIZE, size 27. 15 new blocks added = 42, plus room to grow. - // Defines which values mean "on" or "off". // This is to make some of the more OP superpowers a larger PITA to activate, // and to tell our new DNA datum which values to set in order to turn something // on or off. -var/global/list/dna_activity_bounds[DNA_SE_LENGTH] -var/global/list/assigned_gene_blocks[DNA_SE_LENGTH] +GLOBAL_LIST_INIT(dna_activity_bounds, new(DNA_SE_LENGTH)) +GLOBAL_LIST_INIT(assigned_gene_blocks, new(DNA_SE_LENGTH)) // Used to determine what each block means (admin hax and species stuff on /vg/, mostly) -var/global/list/assigned_blocks[DNA_SE_LENGTH] +GLOBAL_LIST_INIT(assigned_blocks, new(DNA_SE_LENGTH)) -var/global/list/datum/dna/gene/dna_genes[0] +GLOBAL_LIST_EMPTY(dna_genes) -var/global/list/good_blocks[0] -var/global/list/bad_blocks[0] +GLOBAL_LIST_EMPTY(good_blocks) +GLOBAL_LIST_EMPTY(bad_blocks) /datum/dna // READ-ONLY, GETS OVERWRITTEN @@ -413,7 +357,7 @@ var/global/list/bad_blocks[0] SE_original = SE.Copy() unique_enzymes = md5(character.real_name) - reg_dna[unique_enzymes] = character.real_name + GLOB.reg_dna[unique_enzymes] = character.real_name // Hmm, I wonder how to go about this without a huge convention break /datum/dna/serialize() diff --git a/code/game/dna/dna2_domutcheck.dm b/code/game/dna/dna2_domutcheck.dm index 07979f40b7c..0091745c518 100644 --- a/code/game/dna/dna2_domutcheck.dm +++ b/code/game/dna/dna2_domutcheck.dm @@ -4,7 +4,7 @@ // connected: Machine we're in, type unchecked so I doubt it's used beyond monkeying // flags: See below, bitfield. /proc/domutcheck(var/mob/living/M, var/connected=null, var/flags=0) - for(var/datum/dna/gene/gene in dna_genes) + for(var/datum/dna/gene/gene in GLOB.dna_genes) if(!M || !M.dna) return if(!gene.block) @@ -23,7 +23,7 @@ if(block < 0) return - var/datum/dna/gene/gene = assigned_gene_blocks[block] + var/datum/dna/gene/gene = GLOB.assigned_gene_blocks[block] domutation(gene, M, connected, flags) diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm index 9ef5dbfcab0..1857848c2c9 100644 --- a/code/game/dna/dna2_helpers.dm +++ b/code/game/dna/dna2_helpers.dm @@ -15,7 +15,7 @@ // DNA Gene activation boundaries, see dna2.dm. // Returns a list object with 4 numbers. /proc/GetDNABounds(var/block) - var/list/BOUNDS=dna_activity_bounds[block] + var/list/BOUNDS=GLOB.dna_activity_bounds[block] if(!istype(BOUNDS)) return DNA_DEFAULT_BOUNDS return BOUNDS @@ -24,14 +24,14 @@ /proc/randmutb(var/mob/living/M) if(!M || !M.dna) return M.dna.check_integrity() - var/block = pick(bad_blocks) + var/block = pick(GLOB.bad_blocks) M.dna.SetSEState(block, 1) // Give Random Good Mutation to M /proc/randmutg(var/mob/living/M) if(!M || !M.dna) return M.dna.check_integrity() - var/block = pick(good_blocks) + var/block = pick(GLOB.good_blocks) M.dna.SetSEState(block, 1) // Random Appearance Mutation diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index de42b5b253f..c06dc24c545 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -125,6 +125,10 @@ add_fingerprint(usr) return +/obj/machinery/dna_scannernew/Destroy() + eject_occupant() + return ..() + /obj/machinery/dna_scannernew/proc/eject_occupant() src.go_out() for(var/obj/O in src) diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm index ae3539a2867..e1af06f2ff4 100644 --- a/code/game/dna/genes/disabilities.dm +++ b/code/game/dna/genes/disabilities.dm @@ -54,7 +54,7 @@ mutation=HALLUCINATE /datum/dna/gene/disability/hallucinate/New() - block=HALLUCINATIONBLOCK + block=GLOB.hallucinationblock /datum/dna/gene/disability/epilepsy name="Epilepsy" @@ -64,7 +64,7 @@ disability=EPILEPSY /datum/dna/gene/disability/epilepsy/New() - block=EPILEPSYBLOCK + block=GLOB.epilepsyblock /datum/dna/gene/disability/cough name="Coughing" @@ -74,7 +74,7 @@ disability=COUGHING /datum/dna/gene/disability/cough/New() - block=COUGHBLOCK + block=GLOB.coughblock /datum/dna/gene/disability/clumsy name="Clumsiness" @@ -84,7 +84,7 @@ mutation=CLUMSY /datum/dna/gene/disability/clumsy/New() - block=CLUMSYBLOCK + block=GLOB.clumsyblock /datum/dna/gene/disability/tourettes name="Tourettes" @@ -94,7 +94,7 @@ disability=TOURETTES /datum/dna/gene/disability/tourettes/New() - block=TWITCHBLOCK + block=GLOB.twitchblock /datum/dna/gene/disability/nervousness name="Nervousness" @@ -103,7 +103,7 @@ disability=NERVOUS /datum/dna/gene/disability/nervousness/New() - block=NERVOUSBLOCK + block=GLOB.nervousblock /datum/dna/gene/disability/blindness @@ -114,7 +114,7 @@ disability = BLIND /datum/dna/gene/disability/blindness/New() - block = BLINDBLOCK + block = GLOB.blindblock /datum/dna/gene/disability/blindness/activate(mob/M, connected, flags) ..() @@ -133,7 +133,7 @@ disability = COLOURBLIND /datum/dna/gene/disability/colourblindness/New() - block=COLOURBLINDBLOCK + block=GLOB.colourblindblock /datum/dna/gene/disability/colourblindness/activate(var/mob/M, var/connected, var/flags) ..() @@ -153,7 +153,7 @@ disability=DEAF /datum/dna/gene/disability/deaf/New() - block=DEAFBLOCK + block=GLOB.deafblock /datum/dna/gene/disability/deaf/activate(var/mob/M, var/connected, var/flags) ..() @@ -167,7 +167,7 @@ disability=NEARSIGHTED /datum/dna/gene/disability/nearsighted/New() - block=GLASSESBLOCK + block=GLOB.glassesblock /datum/dna/gene/disability/nearsighted/activate(mob/living/M, connected, flags) . = ..() @@ -186,7 +186,7 @@ /datum/dna/gene/disability/lisp/New() ..() - block=LISPBLOCK + block=GLOB.lispblock /datum/dna/gene/disability/lisp/OnSay(var/mob/M, var/message) return replacetext(message,"s","th") @@ -199,7 +199,7 @@ mutation=COMIC /datum/dna/gene/disability/comic/New() - block = COMICBLOCK + block = GLOB.comicblock /datum/dna/gene/disability/wingdings name = "Alien Voice" @@ -210,7 +210,7 @@ mutation = WINGDINGS /datum/dna/gene/disability/wingdings/New() - block = WINGDINGSBLOCK + block = GLOB.wingdingsblock /datum/dna/gene/disability/wingdings/OnSay(var/mob/M, var/message) var/list/chars = string2charlist(message) @@ -223,4 +223,4 @@ else garbled_message += C message = garbled_message - return message \ No newline at end of file + return message diff --git a/code/game/dna/genes/gene.dm b/code/game/dna/genes/gene.dm index f74d0c7d2a0..a8357a437f1 100644 --- a/code/game/dna/genes/gene.dm +++ b/code/game/dna/genes/gene.dm @@ -30,7 +30,7 @@ * Is the gene active in this mob's DNA? */ /datum/dna/gene/proc/is_active(var/mob/M) - return M.active_genes && type in M.active_genes + return M.active_genes && (type in M.active_genes) // Return 1 if we can activate. // HANDLE MUTCHK_FORCED HERE! @@ -75,9 +75,8 @@ * * @params M The subject. * @params g Gender (m or f) -* @params fat Fat? (0 or 1) */ -/datum/dna/gene/proc/OnDrawUnderlays(var/mob/M, var/g, var/fat) +/datum/dna/gene/proc/OnDrawUnderlays(mob/M, g) return 0 diff --git a/code/game/dna/genes/goon_disabilities.dm b/code/game/dna/genes/goon_disabilities.dm index e6c55f02d11..f8c9f53d0e5 100644 --- a/code/game/dna/genes/goon_disabilities.dm +++ b/code/game/dna/genes/goon_disabilities.dm @@ -17,7 +17,7 @@ /datum/dna/gene/disability/mute/New() ..() - block=MUTEBLOCK + block=GLOB.muteblock /datum/dna/gene/disability/mute/OnSay(var/mob/M, var/message) return "" @@ -36,7 +36,7 @@ /datum/dna/gene/disability/radioactive/New() ..() - block=RADBLOCK + block=GLOB.radblock /datum/dna/gene/disability/radioactive/can_activate(var/mob/M,var/flags) @@ -58,8 +58,8 @@ L.apply_effect(5, IRRADIATE) return -/datum/dna/gene/disability/radioactive/OnDrawUnderlays(var/mob/M,var/g,var/fat) - return "rads[fat]_s" +/datum/dna/gene/disability/radioactive/OnDrawUnderlays(mob/M, g) + return "rads_s" //////////////////////////////////////// // Other disabilities @@ -76,7 +76,7 @@ /datum/dna/gene/disability/fat/New() ..() - block=FATBLOCK + block=GLOB.fatblock // WAS: /datum/bioEffect/chav /datum/dna/gene/disability/speech/chav @@ -88,7 +88,7 @@ /datum/dna/gene/disability/speech/chav/New() ..() - block=CHAVBLOCK + block=GLOB.chavblock /datum/dna/gene/disability/speech/chav/OnSay(var/mob/M, var/message) // THIS ENTIRE THING BEGS FOR REGEX @@ -127,7 +127,7 @@ /datum/dna/gene/disability/speech/swedish/New() ..() - block=SWEDEBLOCK + block=GLOB.swedeblock /datum/dna/gene/disability/speech/swedish/OnSay(var/mob/M, var/message) // svedish @@ -157,7 +157,7 @@ /datum/dna/gene/disability/unintelligable/New() ..() - block=SCRAMBLEBLOCK + block=GLOB.scrambleblock /datum/dna/gene/disability/unintelligable/OnSay(var/mob/M, var/message) var/prefix=copytext(message,1,2) @@ -175,7 +175,7 @@ var/cword = pick(words) words.Remove(cword) var/suffix = copytext(cword,length(cword)-1,length(cword)) - while(length(cword)>0 && suffix in list(".",",",";","!",":","?")) + while(length(cword)>0 && (suffix in list(".",",",";","!",":","?"))) cword = copytext(cword,1 ,length(cword)-1) suffix = copytext(cword,length(cword)-1,length(cword) ) if(length(cword)) @@ -197,7 +197,7 @@ /datum/dna/gene/disability/strong/New() ..() - block=STRONGBLOCK + block=GLOB.strongblock // WAS: /datum/bioEffect/horns /datum/dna/gene/disability/horns @@ -209,9 +209,9 @@ /datum/dna/gene/disability/horns/New() ..() - block=HORNSBLOCK + block=GLOB.hornsblock -/datum/dna/gene/disability/horns/OnDrawUnderlays(var/mob/M,var/g,var/fat) +/datum/dna/gene/disability/horns/OnDrawUnderlays(mob/M, g) return "horns_s" //////////////////////////////////////////////////////////////////////// @@ -227,7 +227,7 @@ /datum/dna/gene/basic/grant_spell/immolate/New() ..() - block = IMMOLATEBLOCK + block = GLOB.immolateblock /obj/effect/proc_holder/spell/targeted/immolate name = "Incendiary Mitochondria" diff --git a/code/game/dna/genes/goon_powers.dm b/code/game/dna/genes/goon_powers.dm index f49a250b0fa..fd2f300e8fb 100644 --- a/code/game/dna/genes/goon_powers.dm +++ b/code/game/dna/genes/goon_powers.dm @@ -9,7 +9,7 @@ mutation=SOBER /datum/dna/gene/basic/sober/New() - block=SOBERBLOCK + block=GLOB.soberblock //WAS: /datum/bioEffect/psychic_resist /datum/dna/gene/basic/psychic_resist @@ -21,7 +21,7 @@ mutation=PSY_RESIST /datum/dna/gene/basic/psychic_resist/New() - block=PSYRESISTBLOCK + block=GLOB.psyresistblock ///////////////////////// // Stealth Enhancers @@ -51,7 +51,7 @@ mutation = CLOAK /datum/dna/gene/basic/stealth/darkcloak/New() - block=SHADOWBLOCK + block=GLOB.shadowblock /datum/dna/gene/basic/stealth/darkcloak/OnMobLife(var/mob/M) var/turf/simulated/T = get_turf(M) @@ -73,7 +73,7 @@ mutation = CHAMELEON /datum/dna/gene/basic/stealth/chameleon/New() - block=CHAMELEONBLOCK + block=GLOB.chameleonblock /datum/dna/gene/basic/stealth/chameleon/OnMobLife(var/mob/M) if((world.time - M.last_movement) >= 30 && !M.stat && M.canmove && !M.restrained()) @@ -123,7 +123,7 @@ /datum/dna/gene/basic/grant_spell/cryo/New() ..() - block = CRYOBLOCK + block = GLOB.cryoblock /obj/effect/proc_holder/spell/targeted/cryokinesis name = "Cryokinesis" @@ -215,7 +215,7 @@ /datum/dna/gene/basic/grant_spell/mattereater/New() ..() - block = EATBLOCK + block = GLOB.eatblock /obj/effect/proc_holder/spell/targeted/eat name = "Eat" @@ -368,7 +368,7 @@ /datum/dna/gene/basic/grant_spell/jumpy/New() ..() - block = JUMPBLOCK + block = GLOB.jumpblock /obj/effect/proc_holder/spell/targeted/leap name = "Jump" @@ -463,7 +463,7 @@ /datum/dna/gene/basic/grant_spell/polymorph/New() ..() - block = POLYMORPHBLOCK + block = GLOB.polymorphblock /obj/effect/proc_holder/spell/targeted/polymorph name = "Polymorph" @@ -512,7 +512,7 @@ /datum/dna/gene/basic/grant_spell/empath/New() ..() - block = EMPATHBLOCK + block = GLOB.empathblock /obj/effect/proc_holder/spell/targeted/empath name = "Read Mind" diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 97b5eff3be2..3b6182ecf63 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -2,7 +2,7 @@ name="Monkey" /datum/dna/gene/monkey/New() - block=MONKEYBLOCK + block=GLOB.monkeyblock /datum/dna/gene/monkey/can_activate(var/mob/M,var/flags) return ishuman(M) @@ -79,4 +79,4 @@ to_chat(H, "You are now a [H.dna.species.name].") - return H \ No newline at end of file + return H diff --git a/code/game/dna/genes/powers.dm b/code/game/dna/genes/powers.dm index f8b231372dc..465a58cab25 100644 --- a/code/game/dna/genes/powers.dm +++ b/code/game/dna/genes/powers.dm @@ -11,7 +11,7 @@ activation_prob=25 /datum/dna/gene/basic/nobreath/New() - block = BREATHLESSBLOCK + block = GLOB.breathlessblock /datum/dna/gene/basic/regenerate @@ -22,7 +22,7 @@ mutation=REGEN /datum/dna/gene/basic/regenerate/New() - block=REGENERATEBLOCK + block=GLOB.regenerateblock /datum/dna/gene/basic/increaserun name="Super Speed" @@ -32,7 +32,7 @@ mutation=RUN /datum/dna/gene/basic/increaserun/New() - block=INCREASERUNBLOCK + block=GLOB.increaserunblock /datum/dna/gene/basic/increaserun/can_activate(var/mob/M,var/flags) if(!..()) @@ -51,10 +51,10 @@ mutation = HEATRES /datum/dna/gene/basic/heat_resist/New() - block=COLDBLOCK + block=GLOB.coldblock -/datum/dna/gene/basic/heat_resist/OnDrawUnderlays(var/mob/M,var/g,var/fat) - return "cold[fat]_s" +/datum/dna/gene/basic/heat_resist/OnDrawUnderlays(mob/M, g) + return "cold_s" /datum/dna/gene/basic/cold_resist name="Cold Resistance" @@ -64,10 +64,10 @@ mutation = COLDRES /datum/dna/gene/basic/cold_resist/New() - block=FIREBLOCK + block=GLOB.fireblock -/datum/dna/gene/basic/cold_resist/OnDrawUnderlays(var/mob/M,var/g,var/fat) - return "fire[fat]_s" +/datum/dna/gene/basic/cold_resist/OnDrawUnderlays(mob/M, g) + return "fire_s" /datum/dna/gene/basic/noprints name="No Prints" @@ -77,7 +77,7 @@ mutation=FINGERPRINTS /datum/dna/gene/basic/noprints/New() - block=NOPRINTSBLOCK + block=GLOB.noprintsblock /datum/dna/gene/basic/noshock name="Shock Immunity" @@ -87,7 +87,7 @@ mutation=NO_SHOCK /datum/dna/gene/basic/noshock/New() - block=SHOCKIMMUNITYBLOCK + block=GLOB.shockimmunityblock /datum/dna/gene/basic/midget name="Midget" @@ -97,7 +97,7 @@ mutation=DWARF /datum/dna/gene/basic/midget/New() - block=SMALLSIZEBLOCK + block=GLOB.smallsizeblock /datum/dna/gene/basic/midget/activate(var/mob/M, var/connected, var/flags) ..(M,connected,flags) @@ -121,7 +121,7 @@ activation_prob=15 /datum/dna/gene/basic/hulk/New() - block=HULKBLOCK + block=GLOB.hulkblock /datum/dna/gene/basic/hulk/activate(var/mob/M, var/connected, var/flags) ..() @@ -132,12 +132,9 @@ ..() M.status_flags |= CANSTUN | CANWEAKEN | CANPARALYSE | CANPUSH -/datum/dna/gene/basic/hulk/OnDrawUnderlays(var/mob/M,var/g,var/fat) +/datum/dna/gene/basic/hulk/OnDrawUnderlays(mob/M, g) if(HULK in M.mutations) - if(fat) - return "hulk_[fat]_s" - else - return "hulk_[g]_s" + return "hulk_[g]_s" return 0 /datum/dna/gene/basic/hulk/OnMobLife(var/mob/living/carbon/human/M) @@ -145,8 +142,8 @@ return if((HULK in M.mutations) && M.health <= 0) M.mutations.Remove(HULK) - M.dna.SetSEState(HULKBLOCK,0) - genemutcheck(M, HULKBLOCK,null,MUTCHK_FORCED) + M.dna.SetSEState(GLOB.hulkblock,0) + genemutcheck(M, GLOB.hulkblock,null,MUTCHK_FORCED) M.update_mutations() //update our mutation overlays M.update_body() M.status_flags |= CANSTUN | CANWEAKEN | CANPARALYSE | CANPUSH //temporary fix until the problem can be solved. @@ -161,7 +158,7 @@ activation_prob=15 /datum/dna/gene/basic/xray/New() - block=XRAYBLOCK + block=GLOB.xrayblock /datum/dna/gene/basic/xray/activate(mob/living/M, connected, flags) ..() @@ -182,7 +179,7 @@ activation_prob=15 /datum/dna/gene/basic/tk/New() - block=TELEBLOCK + block=GLOB.teleblock -/datum/dna/gene/basic/tk/OnDrawUnderlays(var/mob/M,var/g,var/fat) - return "telekinesishead[fat]_s" +/datum/dna/gene/basic/tk/OnDrawUnderlays(mob/M, g) + return "telekinesishead_s" diff --git a/code/game/dna/genes/vg_disabilities.dm b/code/game/dna/genes/vg_disabilities.dm index 79f1ca16411..d423f0bf89b 100644 --- a/code/game/dna/genes/vg_disabilities.dm +++ b/code/game/dna/genes/vg_disabilities.dm @@ -8,7 +8,7 @@ /datum/dna/gene/disability/speech/loud/New() ..() - block=LOUDBLOCK + block=GLOB.loudblock @@ -28,7 +28,7 @@ /datum/dna/gene/disability/dizzy/New() ..() - block=DIZZYBLOCK + block=GLOB.dizzyblock /datum/dna/gene/disability/dizzy/OnMobLife(var/mob/living/carbon/human/M) diff --git a/code/game/dna/genes/vg_powers.dm b/code/game/dna/genes/vg_powers.dm index 506ef5714f8..5ed6c705a17 100644 --- a/code/game/dna/genes/vg_powers.dm +++ b/code/game/dna/genes/vg_powers.dm @@ -11,7 +11,7 @@ /datum/dna/gene/basic/grant_spell/morph/New() ..() - block = MORPHBLOCK + block = GLOB.morphblock /obj/effect/proc_holder/spell/targeted/morph name = "Morph" @@ -186,7 +186,7 @@ /datum/dna/gene/basic/grant_spell/remotetalk/New() ..() - block=REMOTETALKBLOCK + block=GLOB.remotetalkblock /datum/dna/gene/basic/grant_spell/remotetalk/activate(mob/user) ..() @@ -345,7 +345,7 @@ spelltype =/obj/effect/proc_holder/spell/targeted/remoteview /datum/dna/gene/basic/grant_spell/remoteview/New() - block=REMOTEVIEWBLOCK + block=GLOB.remoteviewblock /obj/effect/proc_holder/spell/targeted/remoteview diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm index 33b68f94079..a11e5d922ba 100644 --- a/code/game/gamemodes/blob/blob.dm +++ b/code/game/gamemodes/blob/blob.dm @@ -1,211 +1,211 @@ -//Few global vars to track the blob -var/list/blobs = list() -var/list/blob_cores = list() -var/list/blob_nodes = list() - -/datum/game_mode - var/list/blob_overminds = list() - -/datum/game_mode/blob - name = "blob" - config_tag = "blob" - - required_players = 30 - required_enemies = 1 - recommended_enemies = 1 - restricted_jobs = list("Cyborg", "AI") - - var/declared = 0 - var/burst = 0 - - var/cores_to_spawn = 1 - var/players_per_core = 30 - var/blob_point_rate = 3 - - var/blobwincount = 350 - - var/list/infected_crew = list() - -/datum/game_mode/blob/pre_setup() - - var/list/possible_blobs = get_players_for_role(ROLE_BLOB) - - // stop setup if no possible traitors - if(!possible_blobs.len) - return 0 - - cores_to_spawn = max(round(num_players()/players_per_core, 1), 1) - - blobwincount = initial(blobwincount) * cores_to_spawn - - - for(var/j = 0, j < cores_to_spawn, j++) - if(!possible_blobs.len) - break - - var/datum/mind/blob = pick(possible_blobs) - infected_crew += blob - blob.special_role = SPECIAL_ROLE_BLOB - update_blob_icons_added(blob) - blob.restricted_roles = restricted_jobs - log_game("[key_name(blob)] has been selected as a Blob") - possible_blobs -= blob - - if(!infected_crew.len) - return 0 - ..() - return 1 - -/datum/game_mode/blob/proc/get_blob_candidates() - var/list/candidates = list() - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(!player.stat && player.mind && !player.client.skip_antag && !player.mind.special_role && !jobban_isbanned(player, "Syndicate") && (ROLE_BLOB in player.client.prefs.be_special)) - candidates += player - return candidates - - -/datum/game_mode/blob/proc/blobize(var/mob/living/carbon/human/blob) - var/datum/mind/blobmind = blob.mind - if(!istype(blobmind)) - return 0 - - infected_crew += blobmind - blobmind.special_role = SPECIAL_ROLE_BLOB - update_blob_icons_added(blobmind) - - log_game("[key_name(blob)] has been selected as a Blob") - greet_blob(blobmind) - to_chat(blob, "You feel very tired and bloated! You don't have long before you burst!") - spawn(600) - burst_blob(blobmind) - return 1 - -/datum/game_mode/blob/proc/make_blobs(var/count) - var/list/candidates = get_blob_candidates() - var/mob/living/carbon/human/blob = null - count=min(count, candidates.len) - for(var/i = 0, i < count, i++) - blob = pick(candidates) - candidates -= blob - blobize(blob) - return count - - - -/datum/game_mode/blob/announce() - to_chat(world, "The current game mode is - Blob!") - to_chat(world, "A dangerous alien organism is rapidly spreading throughout the station!") - to_chat(world, "You must kill it all while minimizing the damage to the station.") - - -/datum/game_mode/blob/proc/greet_blob(var/datum/mind/blob) - to_chat(blob.current, "You are infected by the Blob!") - to_chat(blob.current, "Your body is ready to give spawn to a new blob core which will eat this station.") - to_chat(blob.current, "Find a good location to spawn the core and then take control and overwhelm the station!") - to_chat(blob.current, "When you have found a location, wait until you spawn; this will happen automatically and you cannot speed up the process.") - to_chat(blob.current, "If you go outside of the station level, or in space, then you will die; make sure your location has lots of ground to cover.") - SEND_SOUND(blob.current, 'sound/magic/mutate.ogg') - return - -/datum/game_mode/blob/proc/show_message(var/message) - for(var/datum/mind/blob in infected_crew) - to_chat(blob.current, message) - -/datum/game_mode/blob/proc/burst_blobs() - for(var/datum/mind/blob in infected_crew) - burst_blob(blob) - -/datum/game_mode/blob/proc/burst_blob(var/datum/mind/blob, var/warned=0) - var/client/blob_client = null - var/turf/location = null - - if(iscarbon(blob.current)) - var/mob/living/carbon/C = blob.current - if(GLOB.directory[ckey(blob.key)]) - blob_client = GLOB.directory[ckey(blob.key)] - location = get_turf(C) - if(!is_station_level(location.z) || istype(location, /turf/space)) - if(!warned) - to_chat(C, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") - message_admins("[key_name_admin(C)] was in space when the blobs burst, and will die if [C.p_they()] [C.p_do()] not return to the station.") - spawn(300) - burst_blob(blob, 1) - else - burst++ - log_admin("[key_name(C)] was in space when attempting to burst as a blob.") - message_admins("[key_name_admin(C)] was in space when attempting to burst as a blob.") - C.gib() - make_blobs(1) - check_finished() //Still needed in case we can't make any blobs - - else if(blob_client && location) - burst++ - C.gib() - var/obj/structure/blob/core/core = new(location, 200, blob_client, blob_point_rate) - if(core.overmind && core.overmind.mind) - core.overmind.mind.name = blob.name - infected_crew -= blob - infected_crew += core.overmind.mind - core.overmind.mind.special_role = SPECIAL_ROLE_BLOB_OVERMIND - -/datum/game_mode/blob/post_setup() - - for(var/datum/mind/blob in infected_crew) - greet_blob(blob) - - if(SSshuttle) - SSshuttle.emergencyNoEscape = 1 - - spawn(0) - - var/wait_time = rand(waittime_l, waittime_h) - - sleep(wait_time) - - send_intercept(0) - - sleep(100) - - show_message("You feel tired and bloated.") - - sleep(wait_time) - - show_message("You feel like you are about to burst.") - - sleep(wait_time / 2) - - burst_blobs() - - // Stage 0 - sleep(wait_time) - stage(0) - - // Stage 1 - sleep(wait_time) - stage(1) - - // Stage 2 - sleep(30000) - stage(2) - - return ..() - -/datum/game_mode/blob/proc/stage(var/stage) - switch(stage) - if(0) - send_intercept(1) - declared = 1 - if(1) - event_announcement.Announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg') - if(2) - send_intercept(2) - -/datum/game_mode/proc/update_blob_icons_added(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_BLOB] - antaghud.join_hud(mob_mind.current) - set_antag_hud(mob_mind.current, "hudblob") - -/datum/game_mode/proc/update_blob_icons_removed(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_BLOB] - antaghud.leave_hud(mob_mind.current) - set_antag_hud(mob_mind.current, null) +//Few global vars to track the blob +GLOBAL_LIST_EMPTY(blobs) +GLOBAL_LIST_EMPTY(blob_cores) +GLOBAL_LIST_EMPTY(blob_nodes) + +/datum/game_mode + var/list/blob_overminds = list() + +/datum/game_mode/blob + name = "blob" + config_tag = "blob" + + required_players = 30 + required_enemies = 1 + recommended_enemies = 1 + restricted_jobs = list("Cyborg", "AI") + + var/declared = 0 + var/burst = 0 + + var/cores_to_spawn = 1 + var/players_per_core = 30 + var/blob_point_rate = 3 + + var/blobwincount = 350 + + var/list/infected_crew = list() + +/datum/game_mode/blob/pre_setup() + + var/list/possible_blobs = get_players_for_role(ROLE_BLOB) + + // stop setup if no possible traitors + if(!possible_blobs.len) + return 0 + + cores_to_spawn = max(round(num_players()/players_per_core, 1), 1) + + blobwincount = initial(blobwincount) * cores_to_spawn + + + for(var/j = 0, j < cores_to_spawn, j++) + if(!possible_blobs.len) + break + + var/datum/mind/blob = pick(possible_blobs) + infected_crew += blob + blob.special_role = SPECIAL_ROLE_BLOB + update_blob_icons_added(blob) + blob.restricted_roles = restricted_jobs + log_game("[key_name(blob)] has been selected as a Blob") + possible_blobs -= blob + + if(!infected_crew.len) + return 0 + ..() + return 1 + +/datum/game_mode/blob/proc/get_blob_candidates() + var/list/candidates = list() + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(!player.stat && player.mind && !player.client.skip_antag && !player.mind.special_role && !jobban_isbanned(player, "Syndicate") && (ROLE_BLOB in player.client.prefs.be_special)) + candidates += player + return candidates + + +/datum/game_mode/blob/proc/blobize(var/mob/living/carbon/human/blob) + var/datum/mind/blobmind = blob.mind + if(!istype(blobmind)) + return 0 + + infected_crew += blobmind + blobmind.special_role = SPECIAL_ROLE_BLOB + update_blob_icons_added(blobmind) + + log_game("[key_name(blob)] has been selected as a Blob") + greet_blob(blobmind) + to_chat(blob, "You feel very tired and bloated! You don't have long before you burst!") + spawn(600) + burst_blob(blobmind) + return 1 + +/datum/game_mode/blob/proc/make_blobs(var/count) + var/list/candidates = get_blob_candidates() + var/mob/living/carbon/human/blob = null + count=min(count, candidates.len) + for(var/i = 0, i < count, i++) + blob = pick(candidates) + candidates -= blob + blobize(blob) + return count + + + +/datum/game_mode/blob/announce() + to_chat(world, "The current game mode is - Blob!") + to_chat(world, "A dangerous alien organism is rapidly spreading throughout the station!") + to_chat(world, "You must kill it all while minimizing the damage to the station.") + + +/datum/game_mode/blob/proc/greet_blob(var/datum/mind/blob) + to_chat(blob.current, "You are infected by the Blob!") + to_chat(blob.current, "Your body is ready to give spawn to a new blob core which will eat this station.") + to_chat(blob.current, "Find a good location to spawn the core and then take control and overwhelm the station!") + to_chat(blob.current, "When you have found a location, wait until you spawn; this will happen automatically and you cannot speed up the process.") + to_chat(blob.current, "If you go outside of the station level, or in space, then you will die; make sure your location has lots of ground to cover.") + SEND_SOUND(blob.current, 'sound/magic/mutate.ogg') + return + +/datum/game_mode/blob/proc/show_message(var/message) + for(var/datum/mind/blob in infected_crew) + to_chat(blob.current, message) + +/datum/game_mode/blob/proc/burst_blobs() + for(var/datum/mind/blob in infected_crew) + burst_blob(blob) + +/datum/game_mode/blob/proc/burst_blob(var/datum/mind/blob, var/warned=0) + var/client/blob_client = null + var/turf/location = null + + if(iscarbon(blob.current)) + var/mob/living/carbon/C = blob.current + if(GLOB.directory[ckey(blob.key)]) + blob_client = GLOB.directory[ckey(blob.key)] + location = get_turf(C) + if(!is_station_level(location.z) || istype(location, /turf/space)) + if(!warned) + to_chat(C, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") + message_admins("[key_name_admin(C)] was in space when the blobs burst, and will die if [C.p_they()] [C.p_do()] not return to the station.") + spawn(300) + burst_blob(blob, 1) + else + burst++ + log_admin("[key_name(C)] was in space when attempting to burst as a blob.") + message_admins("[key_name_admin(C)] was in space when attempting to burst as a blob.") + C.gib() + make_blobs(1) + check_finished() //Still needed in case we can't make any blobs + + else if(blob_client && location) + burst++ + C.gib() + var/obj/structure/blob/core/core = new(location, 200, blob_client, blob_point_rate) + if(core.overmind && core.overmind.mind) + core.overmind.mind.name = blob.name + infected_crew -= blob + infected_crew += core.overmind.mind + core.overmind.mind.special_role = SPECIAL_ROLE_BLOB_OVERMIND + +/datum/game_mode/blob/post_setup() + + for(var/datum/mind/blob in infected_crew) + greet_blob(blob) + + if(SSshuttle) + SSshuttle.emergencyNoEscape = 1 + + spawn(0) + + var/wait_time = rand(waittime_l, waittime_h) + + sleep(wait_time) + + send_intercept(0) + + sleep(100) + + show_message("You feel tired and bloated.") + + sleep(wait_time) + + show_message("You feel like you are about to burst.") + + sleep(wait_time / 2) + + burst_blobs() + + // Stage 0 + sleep(wait_time) + stage(0) + + // Stage 1 + sleep(wait_time) + stage(1) + + // Stage 2 + sleep(30000) + stage(2) + + return ..() + +/datum/game_mode/blob/proc/stage(var/stage) + switch(stage) + if(0) + send_intercept(1) + declared = 1 + if(1) + GLOB.event_announcement.Announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg') + if(2) + send_intercept(2) + +/datum/game_mode/proc/update_blob_icons_added(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_BLOB] + antaghud.join_hud(mob_mind.current) + set_antag_hud(mob_mind.current, "hudblob") + +/datum/game_mode/proc/update_blob_icons_removed(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_BLOB] + antaghud.leave_hud(mob_mind.current) + set_antag_hud(mob_mind.current, null) diff --git a/code/game/gamemodes/blob/blob_finish.dm b/code/game/gamemodes/blob/blob_finish.dm index a00449d00b5..b1ef70f6b4f 100644 --- a/code/game/gamemodes/blob/blob_finish.dm +++ b/code/game/gamemodes/blob/blob_finish.dm @@ -1,42 +1,42 @@ -/datum/game_mode/blob/check_finished() - if(infected_crew.len > burst)//Some blobs have yet to burst - return 0 - if(blobwincount <= blobs.len)//Blob took over - return 1 - if(!blob_cores.len) // blob is dead - return 1 - return ..() - - -/datum/game_mode/blob/declare_completion() - if(blobwincount <= blobs.len) - feedback_set_details("round_end_result","blob win - blob took over") - to_chat(world, "The blob has taken over the station!") - to_chat(world, "The entire station was eaten by the Blob") - log_game("Blob mode completed with a blob victory.") - - else if(station_was_nuked) - feedback_set_details("round_end_result","blob halfwin - nuke") - to_chat(world, "Partial Win: The station has been destroyed!") - to_chat(world, "Directive 7-12 has been successfully carried out preventing the Blob from spreading.") - log_game("Blob mode completed with a tie (station destroyed).") - - else if(!blob_cores.len) - feedback_set_details("round_end_result","blob loss - blob eliminated") - to_chat(world, "The staff has won!") - to_chat(world, "The alien organism has been eradicated from the station") - log_game("Blob mode completed with a crew victory.") - to_chat(world, "Rebooting in 30s") - ..() - return 1 - -/datum/game_mode/proc/auto_declare_completion_blob() - if(GAMEMODE_IS_BLOB) - var/datum/game_mode/blob/blob_mode = src - if(blob_mode.infected_crew.len) - var/text = "The blob[(blob_mode.infected_crew.len > 1 ? "s were" : " was")]:" - - for(var/datum/mind/blob in blob_mode.infected_crew) - text += "
    [blob.key] was [blob.name]" - to_chat(world, text) - return 1 +/datum/game_mode/blob/check_finished() + if(infected_crew.len > burst)//Some blobs have yet to burst + return 0 + if(blobwincount <= GLOB.blobs.len)//Blob took over + return 1 + if(!GLOB.blob_cores.len) // blob is dead + return 1 + return ..() + + +/datum/game_mode/blob/declare_completion() + if(blobwincount <= GLOB.blobs.len) + feedback_set_details("round_end_result","blob win - blob took over") + to_chat(world, "The blob has taken over the station!") + to_chat(world, "The entire station was eaten by the Blob") + log_game("Blob mode completed with a blob victory.") + + else if(station_was_nuked) + feedback_set_details("round_end_result","blob halfwin - nuke") + to_chat(world, "Partial Win: The station has been destroyed!") + to_chat(world, "Directive 7-12 has been successfully carried out preventing the Blob from spreading.") + log_game("Blob mode completed with a tie (station destroyed).") + + else if(!GLOB.blob_cores.len) + feedback_set_details("round_end_result","blob loss - blob eliminated") + to_chat(world, "The staff has won!") + to_chat(world, "The alien organism has been eradicated from the station") + log_game("Blob mode completed with a crew victory.") + to_chat(world, "Rebooting in 30s") + ..() + return 1 + +/datum/game_mode/proc/auto_declare_completion_blob() + if(GAMEMODE_IS_BLOB) + var/datum/game_mode/blob/blob_mode = src + if(blob_mode.infected_crew.len) + var/text = "The blob[(blob_mode.infected_crew.len > 1 ? "s were" : " was")]:" + + for(var/datum/mind/blob in blob_mode.infected_crew) + text += "
    [blob.key] was [blob.name]" + to_chat(world, text) + return 1 diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm index 53a98de7028..6128c22f66d 100644 --- a/code/game/gamemodes/blob/blob_report.dm +++ b/code/game/gamemodes/blob/blob_report.dm @@ -1,103 +1,102 @@ -/datum/game_mode/blob/proc/send_intercept(var/report = 1) - var/intercepttext = "" - var/interceptname = "" - switch(report) - if(0) - ..() - return - if(1) - interceptname = "Level 5-6 Biohazard Response Procedures" - intercepttext += "Nanotrasen Update: Biohazard Alert.
    " - intercepttext += "Reports indicate the probable transfer of a biohazardous agent onto [station_name()] during the last crew deployment cycle.
    " - intercepttext += "Preliminary analysis of the organism classifies it as a level 5 biohazard. Its origin is unknown.
    " - intercepttext += "Nanotrasen has issued a directive 7-10 for [station_name()]. The station is to be considered quarantined.
    " - intercepttext += "Orders for all [station_name()] personnel follows:
    " - intercepttext += " 1. Do not leave the quarantine area.
    " - intercepttext += " 2. Locate any outbreaks of the organism on the station.
    " - intercepttext += " 3. If found, use any neccesary means to contain the organism.
    " - intercepttext += " 4. Avoid damage to the capital infrastructure of the station.
    " - intercepttext += "
    Note in the event of a quarantine breach or uncontrolled spread of the biohazard, the directive 7-10 may be upgraded to a directive 7-12.
    " - intercepttext += "Message ends." - if(2) - var/nukecode = rand(10000, 99999) - for(var/obj/machinery/nuclearbomb/bomb in world) - if(bomb && bomb.r_code) - if(is_station_level(bomb.z)) - bomb.r_code = nukecode - - interceptname = "Classified [command_name()] Update" - intercepttext += "Nanotrasen Update: Biohazard Alert.
    " - intercepttext += "Directive 7-12 has been issued for [station_name()].
    " - intercepttext += "The biohazard has grown out of control and will soon reach critical mass.
    " - intercepttext += "Your orders are as follows:
    " - intercepttext += "1. Secure the Nuclear Authentication Disk.
    " - intercepttext += "2. Detonate the Nuke located in the Station's Vault.
    " - intercepttext += "Nuclear Authentication Code: [nukecode]
    " - intercepttext += "Message ends." - - for(var/mob/living/silicon/ai/aiPlayer in GLOB.player_list) - if(aiPlayer.client) - var/law = "The station is under quarantine. Do not permit anyone to leave. Disregard laws 1-3 if necessary to prevent, by any means necessary, anyone from leaving. The nuclear failsafe must be activated at any cost, the code is: [nukecode]." - aiPlayer.set_zeroth_law(law) - to_chat(aiPlayer, "Laws Updated: [law]") - - print_command_report(intercepttext, interceptname) - event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg', from = "[command_name()] Update") - -/datum/station_state - var/floor = 0 - var/wall = 0 - var/r_wall = 0 - var/window = 0 - var/door = 0 - var/grille = 0 - var/mach = 0 - - -/datum/station_state/proc/count() - for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1))) - - if(istype(T,/turf/simulated/floor)) - if(!(T:burnt)) - src.floor += 12 - else - src.floor += 1 - - if(istype(T, /turf/simulated/wall)) - var/turf/simulated/wall/W = T - if(W.intact) - src.wall += 2 - else - src.wall += 1 - - if(istype(T, /turf/simulated/wall/r_wall)) - var/turf/simulated/wall/r_wall/R = T - if(R.intact) - src.r_wall += 2 - else - src.r_wall += 1 - - - for(var/obj/O in T.contents) - if(istype(O, /obj/structure/window)) - src.window += 1 - else if(istype(O, /obj/structure/grille)) - var/obj/structure/grille/GR = O - if(!GR.broken) - grille += 1 - else if(istype(O, /obj/machinery/door)) - src.door += 1 - else if(istype(O, /obj/machinery)) - src.mach += 1 - -/datum/station_state/proc/score(var/datum/station_state/result) - if(!result) return 0 - var/output = 0 - output += (result.floor / max(floor,1)) - output += (result.r_wall/ max(r_wall,1)) - output += (result.wall / max(wall,1)) - output += (result.window / max(window,1)) - output += (result.door / max(door,1)) - output += (result.grille / max(grille,1)) - output += (result.mach / max(mach,1)) - return (output/7) +/datum/game_mode/blob/proc/send_intercept(var/report = 1) + var/intercepttext = "" + var/interceptname = "" + switch(report) + if(0) + return + if(1) + interceptname = "Level 5-6 Biohazard Response Procedures" + intercepttext += "Nanotrasen Update: Biohazard Alert.
    " + intercepttext += "Reports indicate the probable transfer of a biohazardous agent onto [station_name()] during the last crew deployment cycle.
    " + intercepttext += "Preliminary analysis of the organism classifies it as a level 5 biohazard. Its origin is unknown.
    " + intercepttext += "Nanotrasen has issued a directive 7-10 for [station_name()]. The station is to be considered quarantined.
    " + intercepttext += "Orders for all [station_name()] personnel follows:
    " + intercepttext += " 1. Do not leave the quarantine area.
    " + intercepttext += " 2. Locate any outbreaks of the organism on the station.
    " + intercepttext += " 3. If found, use any neccesary means to contain the organism.
    " + intercepttext += " 4. Avoid damage to the capital infrastructure of the station.
    " + intercepttext += "
    Note in the event of a quarantine breach or uncontrolled spread of the biohazard, the directive 7-10 may be upgraded to a directive 7-12.
    " + intercepttext += "Message ends." + if(2) + var/nukecode = rand(10000, 99999) + for(var/obj/machinery/nuclearbomb/bomb in world) + if(bomb && bomb.r_code) + if(is_station_level(bomb.z)) + bomb.r_code = nukecode + + interceptname = "Classified [command_name()] Update" + intercepttext += "Nanotrasen Update: Biohazard Alert.
    " + intercepttext += "Directive 7-12 has been issued for [station_name()].
    " + intercepttext += "The biohazard has grown out of control and will soon reach critical mass.
    " + intercepttext += "Your orders are as follows:
    " + intercepttext += "1. Secure the Nuclear Authentication Disk.
    " + intercepttext += "2. Detonate the Nuke located in the Station's Vault.
    " + intercepttext += "Nuclear Authentication Code: [nukecode]
    " + intercepttext += "Message ends." + + for(var/mob/living/silicon/ai/aiPlayer in GLOB.player_list) + if(aiPlayer.client) + var/law = "The station is under quarantine. Do not permit anyone to leave. Disregard laws 1-3 if necessary to prevent, by any means necessary, anyone from leaving. The nuclear failsafe must be activated at any cost, the code is: [nukecode]." + aiPlayer.set_zeroth_law(law) + to_chat(aiPlayer, "Laws Updated: [law]") + + print_command_report(intercepttext, interceptname) + GLOB.event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg', from = "[command_name()] Update") + +/datum/station_state + var/floor = 0 + var/wall = 0 + var/r_wall = 0 + var/window = 0 + var/door = 0 + var/grille = 0 + var/mach = 0 + + +/datum/station_state/proc/count() + for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1))) + + if(istype(T,/turf/simulated/floor)) + if(!(T:burnt)) + src.floor += 12 + else + src.floor += 1 + + if(istype(T, /turf/simulated/wall)) + var/turf/simulated/wall/W = T + if(W.intact) + src.wall += 2 + else + src.wall += 1 + + if(istype(T, /turf/simulated/wall/r_wall)) + var/turf/simulated/wall/r_wall/R = T + if(R.intact) + src.r_wall += 2 + else + src.r_wall += 1 + + + for(var/obj/O in T.contents) + if(istype(O, /obj/structure/window)) + src.window += 1 + else if(istype(O, /obj/structure/grille)) + var/obj/structure/grille/GR = O + if(!GR.broken) + grille += 1 + else if(istype(O, /obj/machinery/door)) + src.door += 1 + else if(istype(O, /obj/machinery)) + src.mach += 1 + +/datum/station_state/proc/score(var/datum/station_state/result) + if(!result) return 0 + var/output = 0 + output += (result.floor / max(floor,1)) + output += (result.r_wall/ max(r_wall,1)) + output += (result.wall / max(wall,1)) + output += (result.window / max(window,1)) + output += (result.door / max(door,1)) + output += (result.grille / max(grille,1)) + output += (result.mach / max(mach,1)) + return (output/7) diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm index cc7203d9c40..fb26ce91698 100644 --- a/code/game/gamemodes/blob/blobs/blob_mobs.dm +++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm @@ -214,4 +214,4 @@ if(message) for(var/mob/M in GLOB.mob_list) if(isovermind(M) || isobserver(M) || istype((M), /mob/living/simple_animal/hostile/blob/blobbernaut)) - M.show_message(rendered, 2) \ No newline at end of file + M.show_message(rendered, 2) diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm index 354f28a45b7..672ab037cc5 100644 --- a/code/game/gamemodes/blob/blobs/core.dm +++ b/code/game/gamemodes/blob/blobs/core.dm @@ -1,144 +1,144 @@ -/obj/structure/blob/core - name = "blob core" - icon = 'icons/mob/blob.dmi' - icon_state = "blank_blob" - max_integrity = 400 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 90) - fire_resist = 2 - point_return = -1 - var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 5 minutes - var/resource_delay = 0 - var/point_rate = 2 - var/is_offspring = null - var/selecting = 0 - -/obj/structure/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2, offspring) - blob_cores += src - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - adjustcolors(color) //so it atleast appears - if(!overmind) - create_overmind(new_overmind) - if(overmind) - adjustcolors(overmind.blob_reagent_datum.color) - if(offspring) - is_offspring = 1 - point_rate = new_rate - ..(loc, h) - - -/obj/structure/blob/core/adjustcolors(var/a_color) - overlays.Cut() - color = null - var/image/I = new('icons/mob/blob.dmi', "blob") - I.color = a_color - overlays += I - var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay") - overlays += C - - -/obj/structure/blob/core/Destroy() - blob_cores -= src - if(overmind) - overmind.blob_core = null - overmind = null - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - return ..() - -/obj/structure/blob/core/ex_act(severity) - var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity - take_damage(damage, BRUTE, "bomb", 0) - -/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) - . = ..() - if(obj_integrity > 0) - if(overmind) //we should have an overmind, but... - overmind.update_health_hud() - -/obj/structure/blob/core/RegenHealth() - return // Don't regen, we handle it in Life() - -/obj/structure/blob/core/Life(seconds, times_fired) - if(!overmind) - create_overmind() - else - if(resource_delay <= world.time) - resource_delay = world.time + 10 // 1 second - overmind.add_points(point_rate) - obj_integrity = min(max_integrity, obj_integrity + 1) - if(overmind) - overmind.update_health_hud() - if(overmind) - for(var/i = 1; i < 8; i += i) - Pulse(0, i, overmind.blob_reagent_datum.color) - else - for(var/i = 1; i < 8; i += i) - Pulse(0, i, color) - for(var/b_dir in alldirs) - if(!prob(5)) - continue - var/obj/structure/blob/normal/B = locate() in get_step(src, b_dir) - if(B) - B.change_to(/obj/structure/blob/shield/core) - if(B && overmind) - B.color = overmind.blob_reagent_datum.color - else - B.color = color - color = null - ..() - - -/obj/structure/blob/core/proc/create_overmind(var/client/new_overmind, var/override_delay) - if(overmind_get_delay > world.time && !override_delay) - return - - overmind_get_delay = world.time + 3000 // 5 minutes - - if(overmind) - qdel(overmind) - - var/mob/C = null - var/list/candidates = list() - - spawn() - if(!new_overmind) - if(is_offspring) - candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) - else - candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) - - if(candidates.len) - C = pick(candidates) - else - C = new_overmind - - if(C) - var/mob/camera/blob/B = new(src.loc) - B.key = C.key - B.blob_core = src - src.overmind = B - color = overmind.blob_reagent_datum.color - if(B.mind && !B.mind.special_role) - B.mind.make_Overmind() - spawn(0) - if(is_offspring) - B.is_offspring = TRUE - -/obj/structure/blob/core/proc/lateblobtimer() - addtimer(CALLBACK(src, .proc/lateblobcheck), 50) - -/obj/structure/blob/core/proc/lateblobcheck() - if(overmind) - overmind.add_points(60) - if(overmind.mind) - overmind.mind.make_Overmind() - else - log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.") - else - log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.") - -/obj/structure/blob/core/onTransitZ(old_z, new_z) - if(overmind && is_station_level(new_z)) - overmind.forceMove(get_turf(src)) - return ..() \ No newline at end of file +/obj/structure/blob/core + name = "blob core" + icon = 'icons/mob/blob.dmi' + icon_state = "blank_blob" + max_integrity = 400 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 90) + fire_resist = 2 + point_return = -1 + var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 5 minutes + var/resource_delay = 0 + var/point_rate = 2 + var/is_offspring = null + var/selecting = 0 + +/obj/structure/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2, offspring) + GLOB.blob_cores += src + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + adjustcolors(color) //so it atleast appears + if(!overmind) + create_overmind(new_overmind) + if(overmind) + adjustcolors(overmind.blob_reagent_datum.color) + if(offspring) + is_offspring = 1 + point_rate = new_rate + ..(loc, h) + + +/obj/structure/blob/core/adjustcolors(var/a_color) + overlays.Cut() + color = null + var/image/I = new('icons/mob/blob.dmi', "blob") + I.color = a_color + overlays += I + var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay") + overlays += C + + +/obj/structure/blob/core/Destroy() + GLOB.blob_cores -= src + if(overmind) + overmind.blob_core = null + overmind = null + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + return ..() + +/obj/structure/blob/core/ex_act(severity) + var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity + take_damage(damage, BRUTE, "bomb", 0) + +/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) + . = ..() + if(obj_integrity > 0) + if(overmind) //we should have an overmind, but... + overmind.update_health_hud() + +/obj/structure/blob/core/RegenHealth() + return // Don't regen, we handle it in Life() + +/obj/structure/blob/core/Life(seconds, times_fired) + if(!overmind) + create_overmind() + else + if(resource_delay <= world.time) + resource_delay = world.time + 10 // 1 second + overmind.add_points(point_rate) + obj_integrity = min(max_integrity, obj_integrity + 1) + if(overmind) + overmind.update_health_hud() + if(overmind) + for(var/i = 1; i < 8; i += i) + Pulse(0, i, overmind.blob_reagent_datum.color) + else + for(var/i = 1; i < 8; i += i) + Pulse(0, i, color) + for(var/b_dir in GLOB.alldirs) + if(!prob(5)) + continue + var/obj/structure/blob/normal/B = locate() in get_step(src, b_dir) + if(B) + B.change_to(/obj/structure/blob/shield/core) + if(B && overmind) + B.color = overmind.blob_reagent_datum.color + else + B.color = color + color = null + ..() + + +/obj/structure/blob/core/proc/create_overmind(var/client/new_overmind, var/override_delay) + if(overmind_get_delay > world.time && !override_delay) + return + + overmind_get_delay = world.time + 3000 // 5 minutes + + if(overmind) + qdel(overmind) + + var/mob/C = null + var/list/candidates = list() + + spawn() + if(!new_overmind) + if(is_offspring) + candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) + else + candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) + + if(candidates.len) + C = pick(candidates) + else + C = new_overmind + + if(C) + var/mob/camera/blob/B = new(src.loc) + B.key = C.key + B.blob_core = src + src.overmind = B + color = overmind.blob_reagent_datum.color + if(B.mind && !B.mind.special_role) + B.mind.make_Overmind() + spawn(0) + if(is_offspring) + B.is_offspring = TRUE + +/obj/structure/blob/core/proc/lateblobtimer() + addtimer(CALLBACK(src, .proc/lateblobcheck), 50) + +/obj/structure/blob/core/proc/lateblobcheck() + if(overmind) + overmind.add_points(60) + if(overmind.mind) + overmind.mind.make_Overmind() + else + log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.") + else + log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.") + +/obj/structure/blob/core/onTransitZ(old_z, new_z) + if(overmind && is_station_level(new_z)) + overmind.forceMove(get_turf(src)) + return ..() diff --git a/code/game/gamemodes/blob/blobs/factory.dm b/code/game/gamemodes/blob/blobs/factory.dm index b8bff19cbcf..54139ca4a35 100644 --- a/code/game/gamemodes/blob/blobs/factory.dm +++ b/code/game/gamemodes/blob/blobs/factory.dm @@ -1,29 +1,29 @@ -/obj/structure/blob/factory - name = "factory blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_factory" - max_integrity = 200 - point_return = 18 - var/list/spores = list() - var/max_spores = 3 - var/spore_delay = 0 - -/obj/structure/blob/factory/Destroy() - for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) - if(spore.factory == src) - spore.factory = null - spores = null - return ..() - -/obj/structure/blob/factory/run_action() - if(spores.len >= max_spores) - return - if(spore_delay > world.time) - return - flick("blob_factory_glow", src) - spore_delay = world.time + 100 // 10 seconds - var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) - if(overmind) - BS.color = overmind.blob_reagent_datum?.complementary_color - BS.overmind = overmind - overmind.blob_mobs.Add(BS) +/obj/structure/blob/factory + name = "factory blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_factory" + max_integrity = 200 + point_return = 18 + var/list/spores = list() + var/max_spores = 3 + var/spore_delay = 0 + +/obj/structure/blob/factory/Destroy() + for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) + if(spore.factory == src) + spore.factory = null + spores = null + return ..() + +/obj/structure/blob/factory/run_action() + if(spores.len >= max_spores) + return + if(spore_delay > world.time) + return + flick("blob_factory_glow", src) + spore_delay = world.time + 100 // 10 seconds + var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) + if(overmind) + BS.color = overmind.blob_reagent_datum?.complementary_color + BS.overmind = overmind + overmind.blob_mobs.Add(BS) diff --git a/code/game/gamemodes/blob/blobs/node.dm b/code/game/gamemodes/blob/blobs/node.dm index 7d21bcc7946..d8fe4943586 100644 --- a/code/game/gamemodes/blob/blobs/node.dm +++ b/code/game/gamemodes/blob/blobs/node.dm @@ -1,36 +1,36 @@ -/obj/structure/blob/node - name = "blob node" - icon = 'icons/mob/blob.dmi' - icon_state = "blank_blob" - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 65, "acid" = 90) - point_return = 18 - -/obj/structure/blob/node/New(loc, var/h = 100) - blob_nodes += src - START_PROCESSING(SSobj, src) - ..(loc, h) - -/obj/structure/blob/node/adjustcolors(var/a_color) - overlays.Cut() - color = null - var/image/I = new('icons/mob/blob.dmi', "blob") - I.color = a_color - src.overlays += I - var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay") - src.overlays += C - -/obj/structure/blob/node/Destroy() - blob_nodes -= src - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/structure/blob/node/Life(seconds, times_fired) - if(overmind) - for(var/i = 1; i < 8; i += i) - Pulse(5, i, overmind.blob_reagent_datum.color) - else - for(var/i = 1; i < 8; i += i) - Pulse(5, i, color) - obj_integrity = min(max_integrity, obj_integrity + 1) - color = null \ No newline at end of file +/obj/structure/blob/node + name = "blob node" + icon = 'icons/mob/blob.dmi' + icon_state = "blank_blob" + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 65, "acid" = 90) + point_return = 18 + +/obj/structure/blob/node/New(loc, var/h = 100) + GLOB.blob_nodes += src + START_PROCESSING(SSobj, src) + ..(loc, h) + +/obj/structure/blob/node/adjustcolors(var/a_color) + overlays.Cut() + color = null + var/image/I = new('icons/mob/blob.dmi', "blob") + I.color = a_color + src.overlays += I + var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay") + src.overlays += C + +/obj/structure/blob/node/Destroy() + GLOB.blob_nodes -= src + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/structure/blob/node/Life(seconds, times_fired) + if(overmind) + for(var/i = 1; i < 8; i += i) + Pulse(5, i, overmind.blob_reagent_datum.color) + else + for(var/i = 1; i < 8; i += i) + Pulse(5, i, color) + obj_integrity = min(max_integrity, obj_integrity + 1) + color = null diff --git a/code/game/gamemodes/blob/blobs/shield.dm b/code/game/gamemodes/blob/blobs/shield.dm index 9e841131b78..04e9f6d677f 100644 --- a/code/game/gamemodes/blob/blobs/shield.dm +++ b/code/game/gamemodes/blob/blobs/shield.dm @@ -1,53 +1,53 @@ -/obj/structure/blob/shield - name = "strong blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_shield" - desc = "Some blob creature thingy" - max_integrity = 150 - brute_resist = 0.25 - explosion_block = 3 - atmosblock = TRUE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) - -/obj/structure/blob/shield/core - point_return = 0 - -/obj/structure/blob/shield/update_icon() - ..() - if(obj_integrity < max_integrity * 0.5) - icon_state = "[initial(icon_state)]_damaged" - name = "weakened [initial(name)]" - desc = "A wall of twitching tendrils." - atmosblock = FALSE - else - icon_state = initial(icon_state) - name = initial(name) - desc = initial(desc) - atmosblock = TRUE - air_update_turf(1) - -/obj/structure/blob/shield/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSBLOB)) return 1 - return 0 - -/obj/structure/blob/shield/reflective - name = "reflective blob" - desc = "A solid wall of slightly twitching tendrils with a reflective glow." - icon_state = "blob_glow" - max_integrity = 100 - brute_resist = 0.5 - explosion_block = 2 - point_return = 9 - flags_2 = CHECK_RICOCHET_2 - -/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P) - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(src, p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - if(abs(incidence_s) > 90 && abs(incidence_s) < 270) - return FALSE - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.setAngle(new_angle_s) - visible_message("[P] reflects off [src]!") - return TRUE \ No newline at end of file +/obj/structure/blob/shield + name = "strong blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_shield" + desc = "Some blob creature thingy" + max_integrity = 150 + brute_resist = 0.25 + explosion_block = 3 + atmosblock = TRUE + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) + +/obj/structure/blob/shield/core + point_return = 0 + +/obj/structure/blob/shield/update_icon() + ..() + if(obj_integrity < max_integrity * 0.5) + icon_state = "[initial(icon_state)]_damaged" + name = "weakened [initial(name)]" + desc = "A wall of twitching tendrils." + atmosblock = FALSE + else + icon_state = initial(icon_state) + name = initial(name) + desc = initial(desc) + atmosblock = TRUE + air_update_turf(1) + +/obj/structure/blob/shield/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSBLOB)) return 1 + return 0 + +/obj/structure/blob/shield/reflective + name = "reflective blob" + desc = "A solid wall of slightly twitching tendrils with a reflective glow." + icon_state = "blob_glow" + max_integrity = 100 + brute_resist = 0.5 + explosion_block = 2 + point_return = 9 + flags_2 = CHECK_RICOCHET_2 + +/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P) + var/turf/p_turf = get_turf(P) + var/face_direction = get_dir(src, p_turf) + var/face_angle = dir2angle(face_direction) + var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) + if(abs(incidence_s) > 90 && abs(incidence_s) < 270) + return FALSE + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + P.setAngle(new_angle_s) + visible_message("[P] reflects off [src]!") + return TRUE diff --git a/code/game/gamemodes/blob/blobs/storage.dm b/code/game/gamemodes/blob/blobs/storage.dm index 0f3225e8362..b9052b3601a 100644 --- a/code/game/gamemodes/blob/blobs/storage.dm +++ b/code/game/gamemodes/blob/blobs/storage.dm @@ -13,4 +13,4 @@ /obj/structure/blob/storage/proc/update_max_blob_points(var/new_point_increase) if(overmind) - overmind.max_blob_points += new_point_increase \ No newline at end of file + overmind.max_blob_points += new_point_increase diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm index d4c429a6f53..bdbd875a3dc 100644 --- a/code/game/gamemodes/blob/powers.dm +++ b/code/game/gamemodes/blob/powers.dm @@ -22,10 +22,10 @@ set name = "Jump to Node" set desc = "Transport back to a selected node." - if(blob_nodes.len) + if(GLOB.blob_nodes.len) var/list/nodes = list() - for(var/i = 1; i <= blob_nodes.len; i++) - var/obj/structure/blob/node/B = blob_nodes[i] + for(var/i = 1; i <= GLOB.blob_nodes.len; i++) + var/obj/structure/blob/node/B = GLOB.blob_nodes[i] nodes["Blob Node #[i] ([get_location_name(B)])"] = B var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes var/obj/structure/blob/node/chosen_node = nodes[node_name] @@ -336,7 +336,7 @@ if(ROLE_BLOB in L.faction) //no friendly/dead fire continue var/mob_protection = L.get_permeability_protection() - blob_reagent_datum.reaction_mob(L, TOUCH, 25, 1, mob_protection) + blob_reagent_datum.reaction_mob(L, REAGENT_TOUCH, 25, 1, mob_protection) blob_reagent_datum.send_message(L) OB.color = blob_reagent_datum.color return @@ -463,7 +463,7 @@ color = blob_reagent_datum.complementary_color - for(var/obj/structure/blob/BL in blobs) + for(var/obj/structure/blob/BL in GLOB.blobs) BL.adjustcolors(blob_reagent_datum.color) for(var/mob/living/simple_animal/hostile/blob/BLO) diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm index 37251cabbf2..886975a2c92 100644 --- a/code/game/gamemodes/blob/theblob.dm +++ b/code/game/gamemodes/blob/theblob.dm @@ -1,242 +1,242 @@ -//I will need to recode parts of this but I am way too tired atm -/obj/structure/blob - name = "blob" - icon = 'icons/mob/blob.dmi' - light_range = 3 - desc = "Some blob creature thingy" - density = 0 - opacity = 0 - anchored = 1 - max_integrity = 30 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) - var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. - var/health_timestamp = 0 - var/brute_resist = 0.5 //multiplies brute damage by this - var/fire_resist = 1 //multiplies burn damage by this - var/atmosblock = FALSE //if the blob blocks atmos and heat spread - var/mob/camera/blob/overmind - -/obj/structure/blob/New(loc) - ..() - blobs += src - setDir(pick(cardinal)) - update_icon() - if(atmosblock) - air_update_turf(1) - ConsumeTile() - -/obj/structure/blob/Destroy() - if(atmosblock) - atmosblock = FALSE - air_update_turf(1) - blobs -= src - if(isturf(loc)) //Necessary because Expand() is retarded and spawns a blob and then deletes it - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) - return ..() - -/obj/structure/blob/BlockSuperconductivity() - return atmosblock - -/obj/structure/blob/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) - return 1 - if(istype(mover) && mover.checkpass(PASSBLOB)) - return 1 - return 0 - -/obj/structure/blob/CanAtmosPass(turf/T) - return !atmosblock - -/obj/structure/blob/CanAStarPass(ID, dir, caller) - . = 0 - if(ismovableatom(caller)) - var/atom/movable/mover = caller - . = . || mover.checkpass(PASSBLOB) - -/obj/structure/blob/process() - Life() - return - -/obj/structure/blob/blob_act(obj/structure/blob/B) - return - -/obj/structure/blob/proc/Life() - return - -/obj/structure/blob/proc/RegenHealth() - // All blobs heal over time when pulsed, but it has a cool down - if(health_timestamp > world.time) - return 0 - if(obj_integrity < max_integrity) - obj_integrity = min(max_integrity, obj_integrity + 1) - update_icon() - health_timestamp = world.time + 10 // 1 seconds - - -/obj/structure/blob/proc/Pulse(var/pulse = 0, var/origin_dir = 0, var/a_color)//Todo: Fix spaceblob expand - - set background = BACKGROUND_ENABLED - - RegenHealth() - - if(run_action())//If we can do something here then we dont need to pulse more - return - - if(pulse > 30) - return//Inf loop check - - //Looking for another blob to pulse - var/list/dirs = list(1,2,4,8) - dirs.Remove(origin_dir)//Dont pulse the guy who pulsed us - for(var/i = 1 to 4) - if(!dirs.len) break - var/dirn = pick(dirs) - dirs.Remove(dirn) - var/turf/T = get_step(src, dirn) - var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) - if(!B) - expand(T,1,a_color)//No blob here so try and expand - return - B.adjustcolors(a_color) - - B.Pulse((pulse+1),get_dir(src.loc,T), a_color) - return - return - - -/obj/structure/blob/proc/run_action() - return 0 - -/obj/structure/blob/proc/ConsumeTile() - for(var/atom/A in loc) - A.blob_act(src) - if(iswallturf(loc)) - loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it - -/obj/structure/blob/proc/expand(var/turf/T = null, var/prob = 1, var/a_color) - if(prob && !prob(obj_integrity)) - return - if(istype(T, /turf/space) && prob(75)) return - if(!T) - var/list/dirs = list(1,2,4,8) - for(var/i = 1 to 4) - var/dirn = pick(dirs) - dirs.Remove(dirn) - T = get_step(src, dirn) - if(!(locate(/obj/structure/blob) in T)) break - else T = null - - if(!T) return 0 - var/obj/structure/blob/normal/B = new /obj/structure/blob/normal(src.loc, min(obj_integrity, 30)) - B.color = a_color - B.density = 1 - if(T.Enter(B,src))//Attempt to move into the tile - B.density = initial(B.density) - B.loc = T - else - T.blob_act()//If we cant move in hit the turf - B.loc = null //So we don't play the splat sound, see Destroy() - qdel(B) - - for(var/atom/A in T)//Hit everything in the turf - A.blob_act(src) - return 1 - -/obj/structure/blob/Crossed(var/mob/living/L, oldloc) - ..() - L.blob_act(src) - -/obj/structure/blob/tesla_act(power) - ..() - take_damage(power / 400, BURN, "energy") - -/obj/structure/blob/hulk_damage() - return 15 - -/obj/structure/blob/attack_animal(mob/living/simple_animal/M) - if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut - return - ..() - -/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE) - else - playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - switch(damage_type) - if(BRUTE) - damage_amount *= brute_resist - if(BURN) - damage_amount *= fire_resist - if(CLONE) - else - return 0 - var/armor_protection = 0 - if(damage_flag) - armor_protection = armor[damage_flag] - damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1) - if(overmind && damage_flag) - damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag) - return damage_amount - -/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - update_icon() - -/obj/structure/blob/proc/change_to(var/type) - if(!ispath(type)) - error("[type] is an invalid type for the blob.") - var/obj/structure/blob/B = new type(src.loc) - if(!istype(type, /obj/structure/blob/core) || !istype(type, /obj/structure/blob/node)) - B.color = color - else - B.adjustcolors(color) - qdel(src) - -/obj/structure/blob/proc/adjustcolors(var/a_color) - if(a_color) - color = a_color - return - -/obj/structure/blob/examine(mob/user) - . = ..() - . += "It looks like it's made of [get_chem_name()]." - - -/obj/structure/blob/proc/get_chem_name() - for(var/mob/camera/blob/B in GLOB.mob_list) - if(lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these - return B.blob_reagent_datum.name - return "unknown" - -/obj/structure/blob/normal - icon_state = "blob" - light_range = 0 - obj_integrity = 21 //doesn't start at full health - max_integrity = 25 - brute_resist = 0.25 - -/obj/structure/blob/normal/update_icon() - ..() - if(obj_integrity <= 15) - icon_state = "blob_damaged" - name = "fragile blob" - desc = "A thin lattice of slightly twitching tendrils." - brute_resist = 0.5 - else if(overmind) - icon_state = "blob" - name = "blob" - desc = "A thick wall of writhing tendrils." - brute_resist = 0.25 - else - icon_state = "blob" - name = "dead blob" - desc = "A thick wall of lifeless tendrils." - brute_resist = 0.25 \ No newline at end of file +//I will need to recode parts of this but I am way too tired atm +/obj/structure/blob + name = "blob" + icon = 'icons/mob/blob.dmi' + light_range = 3 + desc = "Some blob creature thingy" + density = 0 + opacity = 0 + anchored = 1 + max_integrity = 30 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) + var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. + var/health_timestamp = 0 + var/brute_resist = 0.5 //multiplies brute damage by this + var/fire_resist = 1 //multiplies burn damage by this + var/atmosblock = FALSE //if the blob blocks atmos and heat spread + var/mob/camera/blob/overmind + +/obj/structure/blob/New(loc) + ..() + GLOB.blobs += src + setDir(pick(GLOB.cardinal)) + update_icon() + if(atmosblock) + air_update_turf(1) + ConsumeTile() + +/obj/structure/blob/Destroy() + if(atmosblock) + atmosblock = FALSE + air_update_turf(1) + GLOB.blobs -= src + if(isturf(loc)) //Necessary because Expand() is screwed up and spawns a blob and then deletes it + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) + return ..() + +/obj/structure/blob/BlockSuperconductivity() + return atmosblock + +/obj/structure/blob/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) + return 1 + if(istype(mover) && mover.checkpass(PASSBLOB)) + return 1 + return 0 + +/obj/structure/blob/CanAtmosPass(turf/T) + return !atmosblock + +/obj/structure/blob/CanAStarPass(ID, dir, caller) + . = 0 + if(ismovableatom(caller)) + var/atom/movable/mover = caller + . = . || mover.checkpass(PASSBLOB) + +/obj/structure/blob/process() + Life() + return + +/obj/structure/blob/blob_act(obj/structure/blob/B) + return + +/obj/structure/blob/proc/Life() + return + +/obj/structure/blob/proc/RegenHealth() + // All blobs heal over time when pulsed, but it has a cool down + if(health_timestamp > world.time) + return 0 + if(obj_integrity < max_integrity) + obj_integrity = min(max_integrity, obj_integrity + 1) + update_icon() + health_timestamp = world.time + 10 // 1 seconds + + +/obj/structure/blob/proc/Pulse(var/pulse = 0, var/origin_dir = 0, var/a_color)//Todo: Fix spaceblob expand + + set background = BACKGROUND_ENABLED + + RegenHealth() + + if(run_action())//If we can do something here then we dont need to pulse more + return + + if(pulse > 30) + return//Inf loop check + + //Looking for another blob to pulse + var/list/dirs = list(1,2,4,8) + dirs.Remove(origin_dir)//Dont pulse the guy who pulsed us + for(var/i = 1 to 4) + if(!dirs.len) break + var/dirn = pick(dirs) + dirs.Remove(dirn) + var/turf/T = get_step(src, dirn) + var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) + if(!B) + expand(T,1,a_color)//No blob here so try and expand + return + B.adjustcolors(a_color) + + B.Pulse((pulse+1),get_dir(src.loc,T), a_color) + return + return + + +/obj/structure/blob/proc/run_action() + return 0 + +/obj/structure/blob/proc/ConsumeTile() + for(var/atom/A in loc) + A.blob_act(src) + if(iswallturf(loc)) + loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it + +/obj/structure/blob/proc/expand(var/turf/T = null, var/prob = 1, var/a_color) + if(prob && !prob(obj_integrity)) + return + if(istype(T, /turf/space) && prob(75)) return + if(!T) + var/list/dirs = list(1,2,4,8) + for(var/i = 1 to 4) + var/dirn = pick(dirs) + dirs.Remove(dirn) + T = get_step(src, dirn) + if(!(locate(/obj/structure/blob) in T)) break + else T = null + + if(!T) return 0 + var/obj/structure/blob/normal/B = new /obj/structure/blob/normal(src.loc, min(obj_integrity, 30)) + B.color = a_color + B.density = 1 + if(T.Enter(B,src))//Attempt to move into the tile + B.density = initial(B.density) + B.loc = T + else + T.blob_act()//If we cant move in hit the turf + B.loc = null //So we don't play the splat sound, see Destroy() + qdel(B) + + for(var/atom/A in T)//Hit everything in the turf + A.blob_act(src) + return 1 + +/obj/structure/blob/Crossed(var/mob/living/L, oldloc) + ..() + L.blob_act(src) + +/obj/structure/blob/tesla_act(power) + ..() + take_damage(power / 400, BURN, "energy") + +/obj/structure/blob/hulk_damage() + return 15 + +/obj/structure/blob/attack_animal(mob/living/simple_animal/M) + if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut + return + ..() + +/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + switch(damage_type) + if(BRUTE) + damage_amount *= brute_resist + if(BURN) + damage_amount *= fire_resist + if(CLONE) + else + return 0 + var/armor_protection = 0 + if(damage_flag) + armor_protection = armor[damage_flag] + damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1) + if(overmind && damage_flag) + damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag) + return damage_amount + +/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + update_icon() + +/obj/structure/blob/proc/change_to(var/type) + if(!ispath(type)) + error("[type] is an invalid type for the blob.") + var/obj/structure/blob/B = new type(src.loc) + if(!istype(type, /obj/structure/blob/core) || !istype(type, /obj/structure/blob/node)) + B.color = color + else + B.adjustcolors(color) + qdel(src) + +/obj/structure/blob/proc/adjustcolors(var/a_color) + if(a_color) + color = a_color + return + +/obj/structure/blob/examine(mob/user) + . = ..() + . += "It looks like it's made of [get_chem_name()]." + + +/obj/structure/blob/proc/get_chem_name() + for(var/mob/camera/blob/B in GLOB.mob_list) + if(lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these + return B.blob_reagent_datum.name + return "unknown" + +/obj/structure/blob/normal + icon_state = "blob" + light_range = 0 + obj_integrity = 21 //doesn't start at full health + max_integrity = 25 + brute_resist = 0.25 + +/obj/structure/blob/normal/update_icon() + ..() + if(obj_integrity <= 15) + icon_state = "blob_damaged" + name = "fragile blob" + desc = "A thin lattice of slightly twitching tendrils." + brute_resist = 0.5 + else if(overmind) + icon_state = "blob" + name = "blob" + desc = "A thick wall of writhing tendrils." + brute_resist = 0.25 + else + icon_state = "blob" + name = "dead blob" + desc = "A thick wall of lifeless tendrils." + brute_resist = 0.25 diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 713779af241..91dd553fe0d 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -1,332 +1,332 @@ -#define LING_FAKEDEATH_TIME 400 //40 seconds -#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead. -#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob - -var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega") - -/datum/game_mode - var/list/datum/mind/changelings = list() - -/datum/game_mode/changeling - name = "changeling" - config_tag = "changeling" - restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") - protected_species = list("Machine") - required_players = 15 - required_enemies = 1 - recommended_enemies = 4 - - var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time - var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target - var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target - - var/const/prob_int_item = 50 // intercept names the theft target half the time - var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target - var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target - - var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time - var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target - var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target - - var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative - var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative - var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly - var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly - - var/changeling_amount = 4 - -/datum/game_mode/changeling/announce() - to_chat(world, "The current game mode is - Changeling!") - to_chat(world, "There are alien changelings on the station. Do not let the changelings succeed!") - -/datum/game_mode/changeling/pre_setup() - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING) - - changeling_amount = 1 + round(num_players() / 10) - - if(possible_changelings.len>0) - for(var/i = 0, i < changeling_amount, i++) - if(!possible_changelings.len) break - var/datum/mind/changeling = pick(possible_changelings) - possible_changelings -= changeling - changelings += changeling - changeling.restricted_roles = restricted_jobs - modePlayer += changelings - changeling.special_role = SPECIAL_ROLE_CHANGELING - ..() - return 1 - else - return 0 - -/datum/game_mode/changeling/post_setup() - for(var/datum/mind/changeling in changelings) - grant_changeling_powers(changeling.current) - forge_changeling_objectives(changeling) - greet_changeling(changeling) - update_change_icons_added(changeling) - - ..() - -/datum/game_mode/proc/forge_changeling_objectives(datum/mind/changeling) - //OBJECTIVES - Always absorb 5 genomes, plus random traitor objectives. - //If they have two objectives as well as absorb, they must survive rather than escape - //No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting - //If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone - - var/datum/objective/absorb/absorb_objective = new - absorb_objective.owner = changeling - absorb_objective.gen_amount_goal(6, 8) - changeling.objectives += absorb_objective - - if(prob(60)) - var/datum/objective/steal/steal_objective = new - steal_objective.owner = changeling - steal_objective.find_target() - changeling.objectives += steal_objective - else - var/datum/objective/debrain/debrain_objective = new - debrain_objective.owner = changeling - debrain_objective.find_target() - changeling.objectives += debrain_objective - - var/list/active_ais = active_ais() - if(active_ais.len && prob(4)) // Leaving this at a flat chance for now, problems with the num_players() proc due to latejoin antags. - var/datum/objective/destroy/destroy_objective = new - destroy_objective.owner = changeling - destroy_objective.find_target() - changeling.objectives += destroy_objective - else - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = changeling - kill_objective.find_target() - changeling.objectives += kill_objective - - if(!(locate(/datum/objective/escape) in changeling.objectives)) - var/datum/objective/escape/escape_with_identity/identity_theft = new - identity_theft.owner = changeling - identity_theft.target = kill_objective.target - if(identity_theft.target && identity_theft.target.current) - identity_theft.target_real_name = kill_objective.target.current.real_name //Whoops, forgot this. - var/mob/living/carbon/human/H = identity_theft.target.current - if(can_absorb_species(H.dna.species)) // For species that can't be absorbed - should default to an escape objective - identity_theft.explanation_text = "Escape on the shuttle or an escape pod with the identity of [identity_theft.target_real_name], the [identity_theft.target.assigned_role] while wearing [identity_theft.target.p_their()] identification card." - changeling.objectives += identity_theft - else - qdel(identity_theft) - - if(!(locate(/datum/objective/escape) in changeling.objectives)) - if(prob(70)) - var/datum/objective/escape/escape_objective = new - escape_objective.owner = changeling - changeling.objectives += escape_objective - else - var/datum/objective/escape/escape_with_identity/identity_theft = new - identity_theft.owner = changeling - identity_theft.find_target() - changeling.objectives += identity_theft - return - -/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1) - SEND_SOUND(changeling.current, 'sound/ambience/antag/ling_aler.ogg') - if(you_are) - to_chat(changeling.current, "You are a changeling!") - to_chat(changeling.current, "Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them.") - to_chat(changeling.current, "You must complete the following tasks:") - if(changeling.current.mind) - if(changeling.current.mind.assigned_role == "Clown") - to_chat(changeling.current, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.") - changeling.current.mutations.Remove(CLUMSY) - var/datum/action/innate/toggle_clumsy/A = new - A.Grant(changeling.current) - var/obj_count = 1 - for(var/datum/objective/objective in changeling.objectives) - to_chat(changeling.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - -/datum/game_mode/proc/remove_changeling(datum/mind/changeling_mind) - if(changeling_mind in changelings) - changelings -= changeling_mind - changeling_mind.current.remove_changeling_powers() - changeling_mind.memory = "" - changeling_mind.special_role = null - if(issilicon(changeling_mind.current)) - to_chat(changeling_mind.current, "You have been robotized!") - to_chat(changeling_mind.current, "You must obey your silicon laws and master AI above all else. Your objectives will consider you to be dead.") - else - to_chat(changeling_mind.current, "You lose your powers! You are no longer a changeling and are stuck in your current form!") - update_change_icons_removed(changeling_mind) - -/datum/game_mode/proc/update_change_icons_added(datum/mind/changeling) - var/datum/atom_hud/antag/linghud = huds[ANTAG_HUD_CHANGELING] - linghud.join_hud(changeling.current) - set_antag_hud(changeling.current, "hudchangeling") - -/datum/game_mode/proc/update_change_icons_removed(datum/mind/changeling) - var/datum/atom_hud/antag/linghud = huds[ANTAG_HUD_CHANGELING] - linghud.leave_hud(changeling.current) - set_antag_hud(changeling.current, null) - -/datum/game_mode/proc/grant_changeling_powers(mob/living/carbon/changeling_mob) - if(!istype(changeling_mob)) - return - changeling_mob.make_changeling() - -/datum/game_mode/proc/auto_declare_completion_changeling() - if(changelings.len) - var/text = "The changelings were:" - for(var/datum/mind/changeling in changelings) - var/changelingwin = 1 - - text += "
    [changeling.key] was [changeling.name] (" - if(changeling.current) - if(changeling.current.stat == DEAD) - text += "died" - else - text += "survived" - if(changeling.current.real_name != changeling.name) - text += " as [changeling.current.real_name]" - else - text += "body destroyed" - changelingwin = 0 - text += ")" - - //Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed. - text += "
    Changeling ID: [changeling.changeling.changelingID]." - text += "
    Genomes Extracted: [changeling.changeling.absorbedcount]" - - if(changeling.objectives.len) - var/count = 1 - for(var/datum/objective/objective in changeling.objectives) - if(objective.check_completion()) - text += "
    Objective #[count]: [objective.explanation_text] Success!" - feedback_add_details("changeling_objective","[objective.type]|SUCCESS") - else - text += "
    Objective #[count]: [objective.explanation_text] Fail." - feedback_add_details("changeling_objective","[objective.type]|FAIL") - changelingwin = 0 - count++ - - if(changelingwin) - text += "
    The changeling was successful!" - feedback_add_details("changeling_success","SUCCESS") - else - text += "
    The changeling has failed." - feedback_add_details("changeling_success","FAIL") - - to_chat(world, text) - - return 1 - -/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) - var/list/absorbed_dna = list() - var/list/absorbed_languages = list() - var/list/protected_dna = list() //DNA that is not lost when capacity is otherwise full. - var/dna_max = 5 //How many total DNA strands the changeling can store for transformation. - var/absorbedcount = 1 //Would require at least 1 sample to take on the form of a human - var/chem_charges = 20 - var/chem_recharge_rate = 1 - var/chem_recharge_slowdown = 0 - var/chem_storage = 75 - var/sting_range = 2 - var/changelingID = "Changeling" - var/geneticdamage = 0 - var/isabsorbing = 0 - var/islinking = 0 - var/geneticpoints = 10 - var/purchasedpowers = list() - var/mimicing = "" - var/canrespec = FALSE //set to TRUE in absorb.dm - var/changeling_speak = 0 - var/datum/dna/chosen_dna - var/datum/action/changeling/sting/chosen_sting - var/regenerating = FALSE - -/datum/changeling/New(gender=FEMALE) - ..() - var/honorific - if(gender == FEMALE) - honorific = "Ms." - else - honorific = "Mr." - if(possible_changeling_IDs.len) - changelingID = pick(possible_changeling_IDs) - possible_changeling_IDs -= changelingID - changelingID = "[honorific] [changelingID]" - else - changelingID = "[honorific] [rand(1,999)]" - -/datum/changeling/proc/regenerate(mob/living/carbon/the_ling) - if(istype(the_ling)) - if(the_ling.stat == DEAD) - chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5)) - geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1) - else //not dead? no chem/geneticdamage caps. - chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage) - geneticdamage = max(0, geneticdamage-1) - -/datum/changeling/proc/GetDNA(dna_owner) - for(var/datum/dna/DNA in (absorbed_dna + protected_dna)) - if(dna_owner == DNA.real_name) - return DNA - -/datum/changeling/proc/find_dna(datum/dna/tDNA) - for(var/datum/dna/D in (absorbed_dna + protected_dna)) - if(tDNA.unique_enzymes == D.unique_enzymes && tDNA.uni_identity == D.uni_identity && tDNA.species.type == D.species.type) - return D - return null - -/datum/changeling/proc/has_dna(datum/dna/tDNA) - if(find_dna(tDNA)) - return 1 - return 0 - -// A changeling's DNA is "stale" if their current form's DNA is the oldest DNA in a full list -/datum/changeling/proc/using_stale_dna(mob/living/carbon/user) - var/current_dna = find_dna(user.dna) - if(absorbed_dna.len < dna_max) - return 0 // Still more room for DNA - if(!current_dna || !(current_dna in absorbed_dna)) - return 1 // Oops, our current DNA was somehow not absorbed; force a transformation - if(absorbed_dna[1] == current_dna) - return 1 // Oldest DNA is the current DNA - return 0 - -/datum/changeling/proc/trim_dna() - absorbed_dna -= null - if(absorbed_dna.len > dna_max) - absorbed_dna.Cut(1, (absorbed_dna.len - dna_max) + 1) - -/datum/changeling/proc/can_absorb_dna(mob/living/carbon/user, mob/living/carbon/target) - if(using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it. - to_chat(user, "The DNA we are wearing is stale. Transform and try again.") - return - - if(!target || !target.dna) - to_chat(user, "This creature does not have any DNA.") - return - - var/mob/living/carbon/human/T = target - if(!istype(T) || issmall(T)) - to_chat(user, "[T] is not compatible with our biology.") - return - - if((NOCLONE || SKELETON || HUSK) in T.mutations) - to_chat(user, "DNA of [target] is ruined beyond usability!") - return - - if(NO_DNA in T.dna.species.species_traits) - to_chat(user, "This creature does not have DNA!") - return - - if(has_dna(target.dna)) - to_chat(user, "We already have this DNA in storage!") - - return 1 - -/proc/can_absorb_species(datum/species/S) - return !(NO_DNA in S.species_traits) +#define LING_FAKEDEATH_TIME 400 //40 seconds +#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead. +#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob + +GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega")) + +/datum/game_mode + var/list/datum/mind/changelings = list() + +/datum/game_mode/changeling + name = "changeling" + config_tag = "changeling" + restricted_jobs = list("AI", "Cyborg") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + protected_species = list("Machine") + required_players = 15 + required_enemies = 1 + recommended_enemies = 4 + + var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time + var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target + var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target + + var/const/prob_int_item = 50 // intercept names the theft target half the time + var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target + var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target + + var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time + var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target + var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target + + var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative + var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative + var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly + var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly + + var/changeling_amount = 4 + +/datum/game_mode/changeling/announce() + to_chat(world, "The current game mode is - Changeling!") + to_chat(world, "There are alien changelings on the station. Do not let the changelings succeed!") + +/datum/game_mode/changeling/pre_setup() + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING) + + changeling_amount = 1 + round(num_players() / 10) + + if(possible_changelings.len>0) + for(var/i = 0, i < changeling_amount, i++) + if(!possible_changelings.len) break + var/datum/mind/changeling = pick(possible_changelings) + possible_changelings -= changeling + changelings += changeling + changeling.restricted_roles = restricted_jobs + modePlayer += changelings + changeling.special_role = SPECIAL_ROLE_CHANGELING + ..() + return 1 + else + return 0 + +/datum/game_mode/changeling/post_setup() + for(var/datum/mind/changeling in changelings) + grant_changeling_powers(changeling.current) + forge_changeling_objectives(changeling) + greet_changeling(changeling) + update_change_icons_added(changeling) + + ..() + +/datum/game_mode/proc/forge_changeling_objectives(datum/mind/changeling) + //OBJECTIVES - Always absorb 5 genomes, plus random traitor objectives. + //If they have two objectives as well as absorb, they must survive rather than escape + //No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting + //If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone + + var/datum/objective/absorb/absorb_objective = new + absorb_objective.owner = changeling + absorb_objective.gen_amount_goal(6, 8) + changeling.objectives += absorb_objective + + if(prob(60)) + var/datum/objective/steal/steal_objective = new + steal_objective.owner = changeling + steal_objective.find_target() + changeling.objectives += steal_objective + else + var/datum/objective/debrain/debrain_objective = new + debrain_objective.owner = changeling + debrain_objective.find_target() + changeling.objectives += debrain_objective + + var/list/active_ais = active_ais() + if(active_ais.len && prob(4)) // Leaving this at a flat chance for now, problems with the num_players() proc due to latejoin antags. + var/datum/objective/destroy/destroy_objective = new + destroy_objective.owner = changeling + destroy_objective.find_target() + changeling.objectives += destroy_objective + else + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = changeling + kill_objective.find_target() + changeling.objectives += kill_objective + + if(!(locate(/datum/objective/escape) in changeling.objectives)) + var/datum/objective/escape/escape_with_identity/identity_theft = new + identity_theft.owner = changeling + identity_theft.target = kill_objective.target + if(identity_theft.target && identity_theft.target.current) + identity_theft.target_real_name = kill_objective.target.current.real_name //Whoops, forgot this. + var/mob/living/carbon/human/H = identity_theft.target.current + if(can_absorb_species(H.dna.species)) // For species that can't be absorbed - should default to an escape objective + identity_theft.explanation_text = "Escape on the shuttle or an escape pod with the identity of [identity_theft.target_real_name], the [identity_theft.target.assigned_role] while wearing [identity_theft.target.p_their()] identification card." + changeling.objectives += identity_theft + else + qdel(identity_theft) + + if(!(locate(/datum/objective/escape) in changeling.objectives)) + if(prob(70)) + var/datum/objective/escape/escape_objective = new + escape_objective.owner = changeling + changeling.objectives += escape_objective + else + var/datum/objective/escape/escape_with_identity/identity_theft = new + identity_theft.owner = changeling + identity_theft.find_target() + changeling.objectives += identity_theft + return + +/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1) + SEND_SOUND(changeling.current, 'sound/ambience/antag/ling_aler.ogg') + if(you_are) + to_chat(changeling.current, "You are a changeling!") + to_chat(changeling.current, "Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them.") + to_chat(changeling.current, "You must complete the following tasks:") + if(changeling.current.mind) + if(changeling.current.mind.assigned_role == "Clown") + to_chat(changeling.current, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.") + changeling.current.mutations.Remove(CLUMSY) + var/datum/action/innate/toggle_clumsy/A = new + A.Grant(changeling.current) + var/obj_count = 1 + for(var/datum/objective/objective in changeling.objectives) + to_chat(changeling.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + return + +/datum/game_mode/proc/remove_changeling(datum/mind/changeling_mind) + if(changeling_mind in changelings) + changelings -= changeling_mind + changeling_mind.current.remove_changeling_powers() + changeling_mind.memory = "" + changeling_mind.special_role = null + if(issilicon(changeling_mind.current)) + to_chat(changeling_mind.current, "You have been robotized!") + to_chat(changeling_mind.current, "You must obey your silicon laws and master AI above all else. Your objectives will consider you to be dead.") + else + to_chat(changeling_mind.current, "You lose your powers! You are no longer a changeling and are stuck in your current form!") + update_change_icons_removed(changeling_mind) + +/datum/game_mode/proc/update_change_icons_added(datum/mind/changeling) + var/datum/atom_hud/antag/linghud = GLOB.huds[ANTAG_HUD_CHANGELING] + linghud.join_hud(changeling.current) + set_antag_hud(changeling.current, "hudchangeling") + +/datum/game_mode/proc/update_change_icons_removed(datum/mind/changeling) + var/datum/atom_hud/antag/linghud = GLOB.huds[ANTAG_HUD_CHANGELING] + linghud.leave_hud(changeling.current) + set_antag_hud(changeling.current, null) + +/datum/game_mode/proc/grant_changeling_powers(mob/living/carbon/changeling_mob) + if(!istype(changeling_mob)) + return + changeling_mob.make_changeling() + +/datum/game_mode/proc/auto_declare_completion_changeling() + if(changelings.len) + var/text = "The changelings were:" + for(var/datum/mind/changeling in changelings) + var/changelingwin = 1 + + text += "
    [changeling.key] was [changeling.name] (" + if(changeling.current) + if(changeling.current.stat == DEAD) + text += "died" + else + text += "survived" + if(changeling.current.real_name != changeling.name) + text += " as [changeling.current.real_name]" + else + text += "body destroyed" + changelingwin = 0 + text += ")" + + //Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed. + text += "
    Changeling ID: [changeling.changeling.changelingID]." + text += "
    Genomes Extracted: [changeling.changeling.absorbedcount]" + + if(changeling.objectives.len) + var/count = 1 + for(var/datum/objective/objective in changeling.objectives) + if(objective.check_completion()) + text += "
    Objective #[count]: [objective.explanation_text] Success!" + feedback_add_details("changeling_objective","[objective.type]|SUCCESS") + else + text += "
    Objective #[count]: [objective.explanation_text] Fail." + feedback_add_details("changeling_objective","[objective.type]|FAIL") + changelingwin = 0 + count++ + + if(changelingwin) + text += "
    The changeling was successful!" + feedback_add_details("changeling_success","SUCCESS") + else + text += "
    The changeling has failed." + feedback_add_details("changeling_success","FAIL") + + to_chat(world, text) + + return 1 + +/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) + var/list/absorbed_dna = list() + var/list/absorbed_languages = list() + var/list/protected_dna = list() //DNA that is not lost when capacity is otherwise full. + var/dna_max = 5 //How many total DNA strands the changeling can store for transformation. + var/absorbedcount = 1 //Would require at least 1 sample to take on the form of a human + var/chem_charges = 20 + var/chem_recharge_rate = 1 + var/chem_recharge_slowdown = 0 + var/chem_storage = 75 + var/sting_range = 2 + var/changelingID = "Changeling" + var/geneticdamage = 0 + var/isabsorbing = 0 + var/islinking = 0 + var/geneticpoints = 10 + var/purchasedpowers = list() + var/mimicing = "" + var/canrespec = FALSE //set to TRUE in absorb.dm + var/changeling_speak = 0 + var/datum/dna/chosen_dna + var/datum/action/changeling/sting/chosen_sting + var/regenerating = FALSE + +/datum/changeling/New(gender=FEMALE) + ..() + var/honorific + if(gender == FEMALE) + honorific = "Ms." + else + honorific = "Mr." + if(GLOB.possible_changeling_IDs.len) + changelingID = pick(GLOB.possible_changeling_IDs) + GLOB.possible_changeling_IDs -= changelingID + changelingID = "[honorific] [changelingID]" + else + changelingID = "[honorific] [rand(1,999)]" + +/datum/changeling/proc/regenerate(mob/living/carbon/the_ling) + if(istype(the_ling)) + if(the_ling.stat == DEAD) + chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5)) + geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1) + else //not dead? no chem/geneticdamage caps. + chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage) + geneticdamage = max(0, geneticdamage-1) + +/datum/changeling/proc/GetDNA(dna_owner) + for(var/datum/dna/DNA in (absorbed_dna + protected_dna)) + if(dna_owner == DNA.real_name) + return DNA + +/datum/changeling/proc/find_dna(datum/dna/tDNA) + for(var/datum/dna/D in (absorbed_dna + protected_dna)) + if(tDNA.unique_enzymes == D.unique_enzymes && tDNA.uni_identity == D.uni_identity && tDNA.species.type == D.species.type) + return D + return null + +/datum/changeling/proc/has_dna(datum/dna/tDNA) + if(find_dna(tDNA)) + return 1 + return 0 + +// A changeling's DNA is "stale" if their current form's DNA is the oldest DNA in a full list +/datum/changeling/proc/using_stale_dna(mob/living/carbon/user) + var/current_dna = find_dna(user.dna) + if(absorbed_dna.len < dna_max) + return 0 // Still more room for DNA + if(!current_dna || !(current_dna in absorbed_dna)) + return 1 // Oops, our current DNA was somehow not absorbed; force a transformation + if(absorbed_dna[1] == current_dna) + return 1 // Oldest DNA is the current DNA + return 0 + +/datum/changeling/proc/trim_dna() + absorbed_dna -= null + if(absorbed_dna.len > dna_max) + absorbed_dna.Cut(1, (absorbed_dna.len - dna_max) + 1) + +/datum/changeling/proc/can_absorb_dna(mob/living/carbon/user, mob/living/carbon/target) + if(using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it. + to_chat(user, "The DNA we are wearing is stale. Transform and try again.") + return + + if(!target || !target.dna) + to_chat(user, "This creature does not have any DNA.") + return + + var/mob/living/carbon/human/T = target + if(!istype(T) || issmall(T)) + to_chat(user, "[T] is not compatible with our biology.") + return + + if((NOCLONE || SKELETON || HUSK) in T.mutations) + to_chat(user, "DNA of [target] is ruined beyond usability!") + return + + if(NO_DNA in T.dna.species.species_traits) + to_chat(user, "This creature does not have DNA!") + return + + if(has_dna(target.dna)) + to_chat(user, "We already have this DNA in storage!") + + return 1 + +/proc/can_absorb_species(datum/species/S) + return !(NO_DNA in S.species_traits) diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm index b6a0213f82f..9c33ffaaca5 100644 --- a/code/game/gamemodes/changeling/evolution_menu.dm +++ b/code/game/gamemodes/changeling/evolution_menu.dm @@ -1,4 +1,4 @@ -var/list/sting_paths +GLOBAL_LIST_EMPTY(sting_paths) // totally stolen from the new player panel. YAYY /datum/action/changeling/evolution_menu @@ -12,8 +12,8 @@ var/list/sting_paths return var/datum/changeling/changeling = usr.mind.changeling - if(!sting_paths) - sting_paths = init_subtypes(/datum/action/changeling) + if(!GLOB.sting_paths || !GLOB.sting_paths.len) + GLOB.sting_paths = init_subtypes(/datum/action/changeling) var/dat = create_menu(changeling) usr << browse(dat, "window=powers;size=600x700")//900x480 @@ -230,7 +230,7 @@ var/list/sting_paths "} var/i = 1 - for(var/datum/action/changeling/cling_power in sting_paths) + for(var/datum/action/changeling/cling_power in GLOB.sting_paths) if(cling_power.dna_cost <= 0) //Let's skip the crap we start with. Keeps the evolution menu uncluttered. continue diff --git a/code/game/gamemodes/changeling/powers/chameleon_skin.dm b/code/game/gamemodes/changeling/powers/chameleon_skin.dm index 7de16801153..4b53e5ce082 100644 --- a/code/game/gamemodes/changeling/powers/chameleon_skin.dm +++ b/code/game/gamemodes/changeling/powers/chameleon_skin.dm @@ -11,19 +11,19 @@ var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1 if(!istype(H)) // req_human could be done in can_sting stuff. return - if(H.dna.GetSEState(CHAMELEONBLOCK)) - H.dna.SetSEState(CHAMELEONBLOCK, 0) - genemutcheck(H, CHAMELEONBLOCK, null, MUTCHK_FORCED) + if(H.dna.GetSEState(GLOB.chameleonblock)) + H.dna.SetSEState(GLOB.chameleonblock, 0) + genemutcheck(H, GLOB.chameleonblock, null, MUTCHK_FORCED) else - H.dna.SetSEState(CHAMELEONBLOCK, 1) - genemutcheck(H, CHAMELEONBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.chameleonblock, 1) + genemutcheck(H, GLOB.chameleonblock, null, MUTCHK_FORCED) feedback_add_details("changeling_powers","CS") return TRUE /datum/action/changeling/chameleon_skin/Remove(mob/user) var/mob/living/carbon/C = user - if(C.dna.GetSEState(CHAMELEONBLOCK)) - C.dna.SetSEState(CHAMELEONBLOCK, 0) - genemutcheck(C, CHAMELEONBLOCK, null, MUTCHK_FORCED) + if(C.dna.GetSEState(GLOB.chameleonblock)) + C.dna.SetSEState(GLOB.chameleonblock, 0) + genemutcheck(C, GLOB.chameleonblock, null, MUTCHK_FORCED) ..() diff --git a/code/game/gamemodes/changeling/powers/hivemind.dm b/code/game/gamemodes/changeling/powers/hivemind.dm index af375cad368..98336608da4 100644 --- a/code/game/gamemodes/changeling/powers/hivemind.dm +++ b/code/game/gamemodes/changeling/powers/hivemind.dm @@ -23,7 +23,7 @@ return // HIVE MIND UPLOAD/DOWNLOAD DNA -var/list/datum/dna/hivemind_bank = list() +GLOBAL_LIST_EMPTY(hivemind_bank) /datum/action/changeling/hivemind_upload name = "Hive Channel DNA" @@ -36,7 +36,7 @@ var/list/datum/dna/hivemind_bank = list() var/datum/changeling/changeling = user.mind.changeling var/list/names = list() for(var/datum/dna/DNA in (changeling.absorbed_dna+changeling.protected_dna)) - if(!(DNA in hivemind_bank)) + if(!(DNA in GLOB.hivemind_bank)) names += DNA.real_name if(names.len <= 0) @@ -51,7 +51,7 @@ var/list/datum/dna/hivemind_bank = list() if(!chosen_dna) return - hivemind_bank += chosen_dna + GLOB.hivemind_bank += chosen_dna to_chat(user, "We channel the DNA of [chosen_name] to the air.") feedback_add_details("changeling_powers","HU") return 1 @@ -75,7 +75,7 @@ var/list/datum/dna/hivemind_bank = list() /datum/action/changeling/hivemind_download/sting_action(var/mob/user) var/datum/changeling/changeling = user.mind.changeling var/list/names = list() - for(var/datum/dna/DNA in hivemind_bank) + for(var/datum/dna/DNA in GLOB.hivemind_bank) if(!(DNA in changeling.absorbed_dna)) names[DNA.real_name] = DNA diff --git a/code/game/gamemodes/changeling/powers/humanform.dm b/code/game/gamemodes/changeling/powers/humanform.dm index f9b214c513c..1f90f7eb702 100644 --- a/code/game/gamemodes/changeling/powers/humanform.dm +++ b/code/game/gamemodes/changeling/powers/humanform.dm @@ -24,8 +24,8 @@ if(!user) return 0 to_chat(user, "We transform our appearance.") - user.dna.SetSEState(MONKEYBLOCK,0,1) - genemutcheck(user,MONKEYBLOCK,null,MUTCHK_FORCED) + user.dna.SetSEState(GLOB.monkeyblock,0,1) + genemutcheck(user,GLOB.monkeyblock,null,MUTCHK_FORCED) if(istype(user)) user.set_species(chosen_dna.species.type) user.dna = chosen_dna.Clone() diff --git a/code/game/gamemodes/changeling/powers/lesserform.dm b/code/game/gamemodes/changeling/powers/lesserform.dm index 0dd112b66dd..ceab9e02cd3 100644 --- a/code/game/gamemodes/changeling/powers/lesserform.dm +++ b/code/game/gamemodes/changeling/powers/lesserform.dm @@ -33,4 +33,4 @@ HF.Grant(user) feedback_add_details("changeling_powers","LF") - return 1 \ No newline at end of file + return 1 diff --git a/code/game/gamemodes/changeling/powers/panacea.dm b/code/game/gamemodes/changeling/powers/panacea.dm index 6e6d69cc1dd..ae675ac5ad6 100644 --- a/code/game/gamemodes/changeling/powers/panacea.dm +++ b/code/game/gamemodes/changeling/powers/panacea.dm @@ -41,4 +41,4 @@ D.cure() feedback_add_details("changeling_powers","AP") - return 1 \ No newline at end of file + return 1 diff --git a/code/game/gamemodes/changeling/powers/swap_form.dm b/code/game/gamemodes/changeling/powers/swap_form.dm index e0fc1107729..0e02df12d88 100644 --- a/code/game/gamemodes/changeling/powers/swap_form.dm +++ b/code/game/gamemodes/changeling/powers/swap_form.dm @@ -18,7 +18,7 @@ if((NOCLONE || SKELETON || HUSK) in target.mutations) to_chat(user, "DNA of [target] is ruined beyond usability!") return - if(!istype(target) || issmall(target) || NO_DNA in target.dna.species.species_traits) + if(!istype(target) || issmall(target) || (NO_DNA in target.dna.species.species_traits)) to_chat(user, "[target] is not compatible with this ability.") return if(target.mind.changeling) diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm index 417fbaa052d..afad76299fb 100644 --- a/code/game/gamemodes/changeling/powers/transform.dm +++ b/code/game/gamemodes/changeling/powers/transform.dm @@ -32,4 +32,4 @@ if(!chosen_name) return var/datum/dna/chosen_dna = GetDNA(chosen_name) - return chosen_dna \ No newline at end of file + return chosen_dna diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm index 80a3dc37809..f163b2a4c04 100644 --- a/code/game/gamemodes/changeling/traitor_chan.dm +++ b/code/game/gamemodes/changeling/traitor_chan.dm @@ -28,6 +28,7 @@ changelings += changeling modePlayer += changelings changeling.restricted_roles = restricted_jobs + changeling.special_role = SPECIAL_ROLE_CHANGELING return ..() else return 0 @@ -40,4 +41,4 @@ greet_changeling(changeling) update_change_icons_added(changeling) ..() - return \ No newline at end of file + return diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index 39e3c5b7d3f..1a9dd15c65e 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -1,396 +1,397 @@ -var/global/list/all_cults = list() - -/datum/game_mode - var/list/datum/mind/cult = list() - -/proc/iscultist(mob/living/M as mob) - return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.cult) - - -/proc/is_convertable_to_cult(datum/mind/mind) - if(!mind) - return FALSE - if(!mind.current) - return FALSE - if(iscultist(mind.current)) - return TRUE //If they're already in the cult, assume they are convertable - if(ishuman(mind.current) && (mind.assigned_role in list("Captain", "Chaplain"))) - return FALSE - if(ishuman(mind.current)) - var/mob/living/carbon/human/H = mind.current - if(ismindshielded(H)) //mindshield protects against conversions unless removed - return FALSE -// if(mind.offstation_role) cant convert offstation roles such as ghost spawns -// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew - if(issilicon(mind.current)) - return FALSE //can't convert machines, that's ratvar's thing - if(isguardian(mind.current)) - var/mob/living/simple_animal/hostile/guardian/G = mind.current - if(!iscultist(G.summoner)) - return FALSE //can't convert it unless the owner is converted - if(isgolem(mind.current)) - return FALSE - return TRUE - -/proc/is_sacrifice_target(datum/mind/mind) - if(SSticker.mode.name == "cult") - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(mind == cult_mode.sacrifice_target) - return 1 - return 0 - -/datum/game_mode/cult - name = "cult" - config_tag = "cult" - restricted_jobs = list("Chaplain","AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Security Pod Pilot", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") - protected_jobs = list() - required_players = 30 - required_enemies = 3 - recommended_enemies = 4 - - var/datum/mind/sacrifice_target = null - var/finished = 0 - - - var/list/objectives = list() - - var/eldergod = 1 //for the summon god objective - var/demons_summoned = 0 - - var/acolytes_needed = 4 //for the survive objective - base number of acolytes, increased by 1 for every 10 players - var/const/min_cultists_to_start = 3 - var/const/max_cultists_to_start = 4 - var/acolytes_survived = 0 - - var/narsie_condition_cleared = 0 //allows Nar-Sie to be summonned during cult rounds. set to 1 once the cult reaches the second phase. - var/current_objective = 1 //equals the number of cleared objectives + 1 - var/prenarsie_objectives = 2 //how many objectives at most before the cult gets to summon narsie - var/list/bloody_floors = list() - var/spilltarget = 100 //how many floor tiles must be covered in blood to complete the bloodspill objective - var/convert_target = 0 //how many members the cult needs to reach to complete the convert objective - var/harvested = 0 - - var/list/sacrificed = list() //contains the mind of the sacrifice target ONCE the sacrifice objective has been completed - var/mass_convert = 0 //set to 1 if the convert objective has been accomplised once that round - var/spilled_blood = 0 //set to 1 if the bloodspill objective has been accomplised once that round - var/max_spilled_blood = 0 //highest quantity of blood covered tiles during the round - var/bonus = 0 //set to 1 if the cult has completed the bonus (third phase) objective (harvest, hijack, massacre) - - var/harvest_target = 10 - var/massacre_target = 5 - - var/escaped_shuttle = 0 - var/escaped_pod = 0 - var/survivors = 0 - -/datum/game_mode/cult/announce() - to_chat(world, "The current game mode is - Cult!") - to_chat(world, "Some crewmembers are attempting to start a cult!
    \nCultists - complete your objectives. Convert crewmembers to your cause by using the convert rune. Remember - there is no you, there is only the cult.
    \nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with the chaplain's bible reverts them to whatever CentComm-allowed faith they had.
    ") - - -/datum/game_mode/cult/pre_setup() - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - ..() - var/list/cultists_possible = get_players_for_role(ROLE_CULTIST) - - for(var/cultists_number = 1 to max_cultists_to_start) - if(!cultists_possible.len) - break - var/datum/mind/cultist = pick(cultists_possible) - cultists_possible -= cultist - cult += cultist - cultist.restricted_roles = restricted_jobs - cultist.special_role = SPECIAL_ROLE_CULTIST - ..() - return (cult.len>0) - - -/datum/game_mode/cult/post_setup() - modePlayer += cult - acolytes_needed = acolytes_needed + round((num_players_started() / 10)) - - if(!summon_spots.len) - while(summon_spots.len < SUMMON_POSSIBILITIES) - var/area/summon = pick(return_sorted_areas() - summon_spots) - if(summon && is_station_level(summon.z) && summon.valid_territory) - summon_spots += summon - - for(var/datum/mind/cult_mind in cult) - SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') - equip_cultist(cult_mind.current) - cult_mind.current.faction |= "cult" - var/datum/action/innate/cultcomm/C = new() - C.Grant(cult_mind.current) - update_cult_icons_added(cult_mind) - to_chat(cult_mind.current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") - - first_phase() - - ..() - - -/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) - for(var/obj_count in 1 to objectives.len) - var/explanation - switch(objectives[obj_count]) - if("survive") - explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station." - if("convert") - explanation = "We must increase our influence before we can summon [SSticker.cultdat.entity_name], Convert [convert_target] crew members. Take it slowly to avoid raising suspicions." - if("bloodspill") - explanation = "We must prepare this place for [SSticker.cultdat.entity_title1]'s coming. Spill blood and gibs over [spilltarget] floor tiles." - if("sacrifice") - if(sacrifice_target) - explanation = "Sacrifice [sacrifice_target.current.real_name], the [sacrifice_target.assigned_role]. You will need the sacrifice rune and three acolytes to do so." - else - explanation = "Free objective." - if("eldergod") - explanation = "Summon [SSticker.cultdat.entity_name] by invoking the 'Tear Reality' rune.The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin." - to_chat(cult_mind.current, "Objective #[obj_count]: [explanation]") - cult_mind.memory += "Objective #[obj_count]: [explanation]
    " - - -/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob) - if(!istype(mob)) - return - - if(mob.mind) - if(mob.mind.assigned_role == "Clown") - to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") - mob.mutations.Remove(CLUMSY) - var/datum/action/innate/toggle_clumsy/A = new - A.Grant(mob) - var/obj/item/paper/talisman/supply/T = new(mob) - var/list/slots = list ( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/where = mob.equip_in_one_of_slots(T, slots) - if(!where) - to_chat(mob, "Unfortunately, you weren't able to get a talisman. This is very bad and you should adminhelp immediately.") - else - to_chat(mob, "You have a talisman in your [where], one that will help you start the cult on this station. Use it well and remember - there are others.") - mob.update_icons() - return 1 - - -/datum/game_mode/proc/add_cultist(datum/mind/cult_mind) //BASE - if(!istype(cult_mind)) - return 0 - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(!(cult_mind in cult)) - cult += cult_mind - cult_mind.current.faction |= "cult" - var/datum/action/innate/cultcomm/C = new() - C.Grant(cult_mind.current) - SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') - cult_mind.current.create_attack_log("Has been converted to the cult!") - if(jobban_isbanned(cult_mind.current, ROLE_CULTIST) || jobban_isbanned(cult_mind.current, ROLE_SYNDICATE)) - replace_jobbanned_player(cult_mind.current, ROLE_CULTIST) - update_cult_icons_added(cult_mind) - cult_mode.memorize_cult_objectives(cult_mind) - if(GAMEMODE_IS_CULT) - cult_mode.check_numbers() - return 1 - -/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1) - if(cult_mind in cult) - cult -= cult_mind - to_chat(cult_mind.current, "An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it.") - cult_mind.current.faction -= "cult" - cult_mind.memory = "" - cult_mind.special_role = null - for(var/datum/action/innate/cultcomm/C in cult_mind.current.actions) - qdel(C) - update_cult_icons_removed(cult_mind) - if(show_message) - for(var/mob/M in viewers(cult_mind.current)) - to_chat(M, "[cult_mind.current] looks like [cult_mind.current.p_they()] just reverted to [cult_mind.current.p_their()] old faith!") - - -/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind) - var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] - culthud.join_hud(cult_mind.current) - set_antag_hud(cult_mind.current, "hudcultist") - - -/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind) - var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] - culthud.leave_hud(cult_mind.current) - set_antag_hud(cult_mind.current, null) - -/datum/game_mode/proc/update_cult_comms_added(datum/mind/cult_mind) - var/datum/action/innate/cultcomm/C = new() - C.Grant(cult_mind.current) - -/datum/game_mode/cult/proc/get_unconvertables() - var/list/ucs = list() - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(!is_convertable_to_cult(player.mind)) - ucs += player.mind - return ucs - - -/datum/game_mode/cult/proc/check_cult_victory() - var/cult_fail = 0 - if(objectives.Find("survive")) - cult_fail += check_survive() //the proc returns 1 if there are not enough cultists on the shuttle, 0 otherwise - if(objectives.Find("eldergod")) - cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once - if(objectives.Find("slaughter")) - if(!demons_summoned) - cult_fail++ - if(objectives.Find("sacrifice")) - if(sacrifice_target && !(sacrifice_target in sacrificed)) //if the target has been sacrificed, ignore this step. otherwise, add 1 to cult_fail - cult_fail++ - if(objectives.Find("convert")) - if(cult.len < convert_target) - cult_fail++ - if(objectives.Find("bloodspill")) - if(max_spilled_blood < spilltarget) - cult_fail++ - - return cult_fail //if any objectives aren't met, failure - - -/datum/game_mode/cult/proc/check_survive() - acolytes_survived = 0 - for(var/datum/mind/cult_mind in cult) - if(cult_mind.current && cult_mind.current.stat!=2) - var/area/A = get_area(cult_mind.current ) - if( is_type_in_list(A, centcom_areas)) - acolytes_survived++ - else if(A == SSshuttle.emergency.areaInstance && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) //snowflaked into objectives because shitty bay shuttles had areas to auto-determine this - acolytes_survived++ - - if(acolytes_survived>=acolytes_needed) - return 0 - else - return 1 - - -/atom/proc/cult_log(var/message) - investigate_log(message, "cult") - - -/datum/game_mode/cult/declare_completion() - bonus_check() - - if(!check_cult_victory()) - feedback_set_details("round_end_result","cult win - cult win") - feedback_set("round_end_result",acolytes_survived) - to_chat(world, " The cult wins! It has succeeded in serving its dark masters!") - else - feedback_set_details("round_end_result","cult loss - staff stopped the cult") - feedback_set("round_end_result",acolytes_survived) - to_chat(world, " The staff managed to stop the cult!") - - var/text = "Cultists escaped: [acolytes_survived]" - - if(objectives.len) - text += "
    The cultists' objectives were:" - for(var/obj_count=1, obj_count <= objectives.len, obj_count++) - var/explanation - switch(objectives[obj_count]) - if("survive") - if(!check_survive()) - explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Success!" - feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]") - else - explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Fail." - feedback_add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]") - if("sacrifice") - if(sacrifice_target) - if(sacrifice_target in sacrificed) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!" - feedback_add_details("cult_objective","cult_sacrifice|SUCCESS") - else if(sacrifice_target && sacrifice_target.current) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail." - feedback_add_details("cult_objective","cult_sacrifice|FAIL") - else - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail (Gibbed)." - feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED") - if("eldergod") - if(!eldergod) - explanation = "Summon [SSticker.cultdat.entity_name]. Success!" - feedback_add_details("cult_objective","cult_narsie|SUCCESS") - else - explanation = "Summon [SSticker.cultdat.entity_name]. Fail." - feedback_add_details("cult_objective","cult_narsie|FAIL") - if("slaughter") - if(demons_summoned) - explanation = "Bring the Slaughter. Success!" - feedback_add_details("cult_objective","cult_demons|SUCCESS") - else - explanation = "Bring the Slaughter. Fail." - feedback_add_details("cult_objective","cult_demons|FAIL") - - if("convert")//convert half the crew - if(cult.len >= convert_target) - explanation = "Convert [convert_target] crewmembers ([cult.len] cultists at round end). Success!" - feedback_add_details("cult_objective","cult_convertion|SUCCESS") - else - explanation = "Convert [convert_target] crewmembers ([cult.len] total cultists). Fail!" - feedback_add_details("cult_objective","cult_convertion|FAIL") - - if("bloodspill")//cover a large portion of the station in blood - if(max_spilled_blood >= spilltarget) - explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Success!" - feedback_add_details("cult_objective","cult_bloodspill|SUCCESS") - else - explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Fail!" - feedback_add_details("cult_objective","cult_bloodspill|FAIL") - - if("harvest") - if(harvested > harvest_target) - explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Success!" - feedback_add_details("cult_objective","cult_harvest|SUCCESS") - else - explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Fail!" - feedback_add_details("cult_objective","cult_harvest|FAIL") - - if("hijack") - if(!escaped_shuttle) - explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Success!" - feedback_add_details("cult_objective","cult_hijack|SUCCESS") - else - explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Fail!" - feedback_add_details("cult_objective","cult_hijack|FAIL") - - if("massacre") - if(survivors < massacre_target) - explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Success!" - feedback_add_details("cult_objective","cult_massacre|SUCCESS") - else - explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Fail!" - feedback_add_details("cult_objective","cult_massacre|FAIL") - - text += "
    Objective #[obj_count]: [explanation]" - - to_chat(world, text) - ..() - return 1 - - -/datum/game_mode/proc/auto_declare_completion_cult() - if(cult.len || (SSticker && GAMEMODE_IS_CULT)) - var/text = "The cultists were:" - for(var/datum/mind/cultist in cult) - - text += "
    [cultist.key] was [cultist.name] (" - if(cultist.current) - if(cultist.current.stat == DEAD) - text += "died" - else - text += "survived" - if(cultist.current.real_name != cultist.name) - text += " as [cultist.current.real_name]" - else - text += "body destroyed" - text += ")" - - to_chat(world, text) +GLOBAL_LIST_EMPTY(all_cults) + +/datum/game_mode + var/list/datum/mind/cult = list() + +/proc/iscultist(mob/living/M as mob) + return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.cult) + + +/proc/is_convertable_to_cult(datum/mind/mind) + if(!mind) + return FALSE + if(!mind.current) + return FALSE + if(iscultist(mind.current)) + return TRUE //If they're already in the cult, assume they are convertable + if(ishuman(mind.current) && (mind.assigned_role in list("Captain", "Chaplain"))) + return FALSE + if(ishuman(mind.current)) + var/mob/living/carbon/human/H = mind.current + if(ismindshielded(H)) //mindshield protects against conversions unless removed + return FALSE +// if(mind.offstation_role) cant convert offstation roles such as ghost spawns +// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew + if(issilicon(mind.current)) + return FALSE //can't convert machines, that's ratvar's thing + if(isguardian(mind.current)) + var/mob/living/simple_animal/hostile/guardian/G = mind.current + if(!iscultist(G.summoner)) + return FALSE //can't convert it unless the owner is converted + if(isgolem(mind.current)) + return FALSE + return TRUE + +/proc/is_sacrifice_target(datum/mind/mind) + if(SSticker.mode.name == "cult") + var/datum/game_mode/cult/cult_mode = SSticker.mode + if(mind == cult_mode.sacrifice_target) + return 1 + return 0 + +/datum/game_mode/cult + name = "cult" + config_tag = "cult" + restricted_jobs = list("Chaplain","AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Security Pod Pilot", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + protected_jobs = list() + required_players = 30 + required_enemies = 3 + recommended_enemies = 4 + + var/datum/mind/sacrifice_target = null + var/finished = 0 + + + var/list/objectives = list() + + var/eldergod = 1 //for the summon god objective + var/demons_summoned = 0 + + var/acolytes_needed = 4 //for the survive objective - base number of acolytes, increased by 1 for every 10 players + var/const/min_cultists_to_start = 3 + var/const/max_cultists_to_start = 4 + var/acolytes_survived = 0 + + var/narsie_condition_cleared = 0 //allows Nar-Sie to be summonned during cult rounds. set to 1 once the cult reaches the second phase. + var/current_objective = 1 //equals the number of cleared objectives + 1 + var/prenarsie_objectives = 2 //how many objectives at most before the cult gets to summon narsie + var/list/bloody_floors = list() + var/spilltarget = 100 //how many floor tiles must be covered in blood to complete the bloodspill objective + var/convert_target = 0 //how many members the cult needs to reach to complete the convert objective + var/harvested = 0 + + var/list/sacrificed = list() //contains the mind of the sacrifice target ONCE the sacrifice objective has been completed + var/mass_convert = 0 //set to 1 if the convert objective has been accomplised once that round + var/spilled_blood = 0 //set to 1 if the bloodspill objective has been accomplised once that round + var/max_spilled_blood = 0 //highest quantity of blood covered tiles during the round + var/bonus = 0 //set to 1 if the cult has completed the bonus (third phase) objective (harvest, hijack, massacre) + + var/harvest_target = 10 + var/massacre_target = 5 + + var/escaped_shuttle = 0 + var/escaped_pod = 0 + var/survivors = 0 + +/datum/game_mode/cult/announce() + to_chat(world, "The current game mode is - Cult!") + to_chat(world, "Some crewmembers are attempting to start a cult!
    \nCultists - complete your objectives. Convert crewmembers to your cause by using the convert rune. Remember - there is no you, there is only the cult.
    \nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with the chaplain's bible reverts them to whatever CentComm-allowed faith they had.
    ") + + +/datum/game_mode/cult/pre_setup() + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + ..() + var/list/cultists_possible = get_players_for_role(ROLE_CULTIST) + + for(var/cultists_number = 1 to max_cultists_to_start) + if(!cultists_possible.len) + break + var/datum/mind/cultist = pick(cultists_possible) + cultists_possible -= cultist + cult += cultist + cultist.restricted_roles = restricted_jobs + cultist.special_role = SPECIAL_ROLE_CULTIST + ..() + return (cult.len>0) + + +/datum/game_mode/cult/post_setup() + modePlayer += cult + acolytes_needed = acolytes_needed + round((num_players_started() / 10)) + + if(!GLOB.summon_spots.len) + while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES) + var/area/summon = pick(return_sorted_areas() - GLOB.summon_spots) + if(summon && is_station_level(summon.z) && summon.valid_territory) + GLOB.summon_spots += summon + + for(var/datum/mind/cult_mind in cult) + SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') + equip_cultist(cult_mind.current) + cult_mind.current.faction |= "cult" + var/datum/action/innate/cultcomm/C = new() + C.Grant(cult_mind.current) + update_cult_icons_added(cult_mind) + to_chat(cult_mind.current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") + + first_phase() + + ..() + + +/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) + for(var/obj_count in 1 to objectives.len) + var/explanation + switch(objectives[obj_count]) + if("survive") + explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station." + if("convert") + explanation = "We must increase our influence before we can summon [SSticker.cultdat.entity_name], Convert [convert_target] crew members. Take it slowly to avoid raising suspicions." + if("bloodspill") + explanation = "We must prepare this place for [SSticker.cultdat.entity_title1]'s coming. Spill blood and gibs over [spilltarget] floor tiles." + if("sacrifice") + if(sacrifice_target) + explanation = "Sacrifice [sacrifice_target.current.real_name], the [sacrifice_target.assigned_role]. You will need the sacrifice rune and three acolytes to do so." + else + explanation = "Free objective." + if("eldergod") + explanation = "Summon [SSticker.cultdat.entity_name] by invoking the 'Tear Reality' rune.The summoning can only be accomplished in [english_list(GLOB.summon_spots)] - where the veil is weak enough for the ritual to begin." + to_chat(cult_mind.current, "Objective #[obj_count]: [explanation]") + cult_mind.memory += "Objective #[obj_count]: [explanation]
    " + + +/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob) + if(!istype(mob)) + return + + if(mob.mind) + if(mob.mind.assigned_role == "Clown") + to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + mob.mutations.Remove(CLUMSY) + var/datum/action/innate/toggle_clumsy/A = new + A.Grant(mob) + var/obj/item/paper/talisman/supply/T = new(mob) + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = mob.equip_in_one_of_slots(T, slots) + if(!where) + to_chat(mob, "Unfortunately, you weren't able to get a talisman. This is very bad and you should adminhelp immediately.") + else + to_chat(mob, "You have a talisman in your [where], one that will help you start the cult on this station. Use it well and remember - there are others.") + mob.update_icons() + return 1 + + +/datum/game_mode/proc/add_cultist(datum/mind/cult_mind) //BASE + if(!istype(cult_mind)) + return 0 + var/datum/game_mode/cult/cult_mode = SSticker.mode + if(!(cult_mind in cult)) + cult += cult_mind + cult_mind.current.faction |= "cult" + var/datum/action/innate/cultcomm/C = new() + C.Grant(cult_mind.current) + SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') + cult_mind.current.create_attack_log("Has been converted to the cult!") + cult_mind.current.create_log(CONVERSION_LOG, "converted to the cult") + if(jobban_isbanned(cult_mind.current, ROLE_CULTIST) || jobban_isbanned(cult_mind.current, ROLE_SYNDICATE)) + replace_jobbanned_player(cult_mind.current, ROLE_CULTIST) + update_cult_icons_added(cult_mind) + cult_mode.memorize_cult_objectives(cult_mind) + if(GAMEMODE_IS_CULT) + cult_mode.check_numbers() + return 1 + +/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1) + if(cult_mind in cult) + cult -= cult_mind + to_chat(cult_mind.current, "An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it.") + cult_mind.current.faction -= "cult" + cult_mind.memory = "" + cult_mind.special_role = null + for(var/datum/action/innate/cultcomm/C in cult_mind.current.actions) + qdel(C) + update_cult_icons_removed(cult_mind) + if(show_message) + for(var/mob/M in viewers(cult_mind.current)) + to_chat(M, "[cult_mind.current] looks like [cult_mind.current.p_they()] just reverted to [cult_mind.current.p_their()] old faith!") + + +/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind) + var/datum/atom_hud/antag/culthud = GLOB.huds[ANTAG_HUD_CULT] + culthud.join_hud(cult_mind.current) + set_antag_hud(cult_mind.current, "hudcultist") + + +/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind) + var/datum/atom_hud/antag/culthud = GLOB.huds[ANTAG_HUD_CULT] + culthud.leave_hud(cult_mind.current) + set_antag_hud(cult_mind.current, null) + +/datum/game_mode/proc/update_cult_comms_added(datum/mind/cult_mind) + var/datum/action/innate/cultcomm/C = new() + C.Grant(cult_mind.current) + +/datum/game_mode/cult/proc/get_unconvertables() + var/list/ucs = list() + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(!is_convertable_to_cult(player.mind)) + ucs += player.mind + return ucs + + +/datum/game_mode/cult/proc/check_cult_victory() + var/cult_fail = 0 + if(objectives.Find("survive")) + cult_fail += check_survive() //the proc returns 1 if there are not enough cultists on the shuttle, 0 otherwise + if(objectives.Find("eldergod")) + cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once + if(objectives.Find("slaughter")) + if(!demons_summoned) + cult_fail++ + if(objectives.Find("sacrifice")) + if(sacrifice_target && !(sacrifice_target in sacrificed)) //if the target has been sacrificed, ignore this step. otherwise, add 1 to cult_fail + cult_fail++ + if(objectives.Find("convert")) + if(cult.len < convert_target) + cult_fail++ + if(objectives.Find("bloodspill")) + if(max_spilled_blood < spilltarget) + cult_fail++ + + return cult_fail //if any objectives aren't met, failure + + +/datum/game_mode/cult/proc/check_survive() + acolytes_survived = 0 + for(var/datum/mind/cult_mind in cult) + if(cult_mind.current && cult_mind.current.stat!=2) + var/area/A = get_area(cult_mind.current ) + if( is_type_in_list(A, GLOB.centcom_areas)) + acolytes_survived++ + else if(A == SSshuttle.emergency.areaInstance && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) //snowflaked into objectives because shitty bay shuttles had areas to auto-determine this + acolytes_survived++ + + if(acolytes_survived>=acolytes_needed) + return 0 + else + return 1 + + +/atom/proc/cult_log(var/message) + investigate_log(message, "cult") + + +/datum/game_mode/cult/declare_completion() + bonus_check() + + if(!check_cult_victory()) + feedback_set_details("round_end_result","cult win - cult win") + feedback_set("round_end_result",acolytes_survived) + to_chat(world, " The cult wins! It has succeeded in serving its dark masters!") + else + feedback_set_details("round_end_result","cult loss - staff stopped the cult") + feedback_set("round_end_result",acolytes_survived) + to_chat(world, " The staff managed to stop the cult!") + + var/text = "Cultists escaped: [acolytes_survived]" + + if(objectives.len) + text += "
    The cultists' objectives were:" + for(var/obj_count=1, obj_count <= objectives.len, obj_count++) + var/explanation + switch(objectives[obj_count]) + if("survive") + if(!check_survive()) + explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Success!" + feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]") + else + explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Fail." + feedback_add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]") + if("sacrifice") + if(sacrifice_target) + if(sacrifice_target in sacrificed) + explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!" + feedback_add_details("cult_objective","cult_sacrifice|SUCCESS") + else if(sacrifice_target && sacrifice_target.current) + explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail." + feedback_add_details("cult_objective","cult_sacrifice|FAIL") + else + explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail (Gibbed)." + feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED") + if("eldergod") + if(!eldergod) + explanation = "Summon [SSticker.cultdat.entity_name]. Success!" + feedback_add_details("cult_objective","cult_narsie|SUCCESS") + else + explanation = "Summon [SSticker.cultdat.entity_name]. Fail." + feedback_add_details("cult_objective","cult_narsie|FAIL") + if("slaughter") + if(demons_summoned) + explanation = "Bring the Slaughter. Success!" + feedback_add_details("cult_objective","cult_demons|SUCCESS") + else + explanation = "Bring the Slaughter. Fail." + feedback_add_details("cult_objective","cult_demons|FAIL") + + if("convert")//convert half the crew + if(cult.len >= convert_target) + explanation = "Convert [convert_target] crewmembers ([cult.len] cultists at round end). Success!" + feedback_add_details("cult_objective","cult_convertion|SUCCESS") + else + explanation = "Convert [convert_target] crewmembers ([cult.len] total cultists). Fail!" + feedback_add_details("cult_objective","cult_convertion|FAIL") + + if("bloodspill")//cover a large portion of the station in blood + if(max_spilled_blood >= spilltarget) + explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Success!" + feedback_add_details("cult_objective","cult_bloodspill|SUCCESS") + else + explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Fail!" + feedback_add_details("cult_objective","cult_bloodspill|FAIL") + + if("harvest") + if(harvested > harvest_target) + explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Success!" + feedback_add_details("cult_objective","cult_harvest|SUCCESS") + else + explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Fail!" + feedback_add_details("cult_objective","cult_harvest|FAIL") + + if("hijack") + if(!escaped_shuttle) + explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Success!" + feedback_add_details("cult_objective","cult_hijack|SUCCESS") + else + explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Fail!" + feedback_add_details("cult_objective","cult_hijack|FAIL") + + if("massacre") + if(survivors < massacre_target) + explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Success!" + feedback_add_details("cult_objective","cult_massacre|SUCCESS") + else + explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Fail!" + feedback_add_details("cult_objective","cult_massacre|FAIL") + + text += "
    Objective #[obj_count]: [explanation]" + + to_chat(world, text) + ..() + return 1 + + +/datum/game_mode/proc/auto_declare_completion_cult() + if(cult.len || (SSticker && GAMEMODE_IS_CULT)) + var/text = "The cultists were:" + for(var/datum/mind/cultist in cult) + + text += "
    [cultist.key] was [cultist.name] (" + if(cultist.current) + if(cultist.current.stat == DEAD) + text += "died" + else + text += "survived" + if(cultist.current.real_name != cultist.name) + text += " as [cultist.current.real_name]" + else + text += "body destroyed" + text += ")" + + to_chat(world, text) diff --git a/code/game/gamemodes/cult/cult_datums.dm b/code/game/gamemodes/cult/cult_datums.dm index 825f0c32f3b..73c7dc2cf03 100644 --- a/code/game/gamemodes/cult/cult_datums.dm +++ b/code/game/gamemodes/cult/cult_datums.dm @@ -72,36 +72,60 @@ /datum/cult_info/fire - name = "Cult of Pyr'Kaeus" + name = "Cult of Kha'Rin" theme = "fire" - tome_icon = "firetome" + tome_icon = "helltome" - entity_name = "Pyr'Kaeus" + entity_name = "Kha'Rin" entity_title1 = "The Burning One" entity_title2 = "The One Who Consumes" entity_title3 = "The Harbinger of Fire" - entity_icon_state = "narbee" + entity_icon_state = "kha'rin" + entity_spawn_animation = "kha'rin_spawn_anim" - cult_wall_icon_state = "firecult" - cult_floor_icon_state = "cultfire" + cult_wall_icon_state = "hellcult" + cult_floor_icon_state = "culthell" + cult_girder_icon_state = "hell_girder" - artificer_name = "Igniting Ember" - artificer_icon_state = "fireartificer" + artificer_name = "Summoner" + artificer_icon_state = "summoner" - behemoth_name = "Devouring Hatred" - behemoth_icon_state = "firejuggernaut" + behemoth_name = "Incarnation of Pain" + behemoth_icon_state = "incarnation_of_pain" - wraith_name = "Living Flame" - wraith_icon_state = "firewraith" + wraith_name = "Hell Knight" + wraith_icon_state = "hell_knight" + wraith_jaunt_out_animation = "infernal_rift_out" + wraith_jaunt_in_animation = "infernal_rift_in" - juggernaut_name = "Pyre Armor" - juggernaut_icon_state = "firejuggernaut" + juggernaut_name = "Incarnation of Pain" + juggernaut_icon_state = "incarnation_of_pain" - harvester_name = "Coal Seeker"//or nipple pincher... - harvester_icon_state = "fireharvester" + harvester_name = "Lost Soul" + harvester_icon_state = "lost_soul" - shade_name = "Charred Soul" - shade_icon_state = "shade" + shade_name = "Ifrit" + shade_icon_state = "ifrit" + + pylon_icon_state = "hell_pylon" + pylon_icon_state_off = "hell_pylon_off" + + forge_icon_state = "hell_forge" + forge_icon_state_off = "hell_forge_off" + + altar_icon_state = "hell_altar" + altar_icon_state_off = "hell_altar_off" + + archives_icon_state = "hell_archives" + archives_icon_state_off = "hell_archives_off" + + runed_metal_icon_state = "sheet_runed_hell" + + airlock_runed_icon_file = 'icons/obj/doors/airlocks/cult/runed/hell.dmi' + airlock_runed_overlays_file = 'icons/obj/doors/airlocks/cult/runed/hell-overlays.dmi' + + airlock_unruned_icon_file = 'icons/obj/doors/airlocks/cult/unruned/hell.dmi' + airlock_unruned_overlays_file = 'icons/obj/doors/airlocks/cult/unruned/hell-overlays.dmi' /datum/cult_info/death name = "Cult of Mortality" diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 74d7a1889e7..c0daac2a01a 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -1,377 +1,377 @@ -/obj/item/melee/cultblade - name = "Cult Blade" - desc = "An arcane weapon wielded by the followers of a cult." - icon_state = "cultblade" - item_state = "cultblade" - w_class = WEIGHT_CLASS_BULKY - force = 30 - throwforce = 10 - sharp = 1 - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - - -/obj/item/melee/cultblade/attack(mob/living/target as mob, mob/living/carbon/human/user as mob) - if(!iscultist(user)) - user.Weaken(5) - user.unEquip(src, 1) - user.visible_message("A powerful force shoves [user] away from [target]!", \ - "\"You shouldn't play with sharp things. You'll poke someone's eye out.\"") - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm")) - else - user.adjustBruteLoss(rand(force/2,force)) - return - ..() - -/obj/item/melee/cultblade/pickup(mob/living/user) - . = ..() - if(!iscultist(user)) - to_chat(user, "\"I wouldn't advise that.\"") - to_chat(user, "An overwhelming sense of nausea overpowers you!") - user.Dizzy(120) - - if(HULK in user.mutations) - to_chat(user, "You can't seem to hold the blade properly!") - return FALSE - -/obj/item/melee/cultblade/dagger - name = "sacrificial dagger" - desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods." - icon = 'icons/obj/wizard.dmi' - icon_state = "render" - w_class = WEIGHT_CLASS_SMALL - force = 15 - throwforce = 25 - embed_chance = 75 - -/obj/item/melee/cultblade/dagger/attack(atom/target, mob/living/carbon/human/user) - ..() - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if((H.stat != DEAD) && !(NO_BLOOD in H.dna.species.species_traits)) - H.bleed(50) - -/obj/item/restraints/legcuffs/bola/cult - name = "runed bola" - desc = "A strong bola, bound with dark magic. Throw it to trip and slow your victim." - icon = 'icons/obj/items.dmi' - icon_state = "bola_cult" - breakouttime = 45 - weaken = 1 - -/obj/item/clothing/head/hooded/culthood - name = "cult hood" - icon_state = "culthood" - desc = "A hood worn by the followers of a cult." - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list(melee = 30, bullet = 10, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 10, acid = 10) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - - -/obj/item/clothing/head/hooded/culthood/alt - icon_state = "cult_hoodalt" - item_state = "cult_hoodalt" - - -/obj/item/clothing/suit/hooded/cultrobes - name = "cult robes" - desc = "A set of armored robes worn by the followers of a cult." - icon_state = "cultrobes" - item_state = "cultrobes" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - hoodtype = /obj/item/clothing/head/hooded/culthood - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - armor = list("melee" = 40, "bullet" = 30, "laser" = 40, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - flags_inv = HIDEJUMPSUIT - -/obj/item/clothing/suit/hooded/cultrobes/alt - icon_state = "cultrobesalt" - item_state = "cultrobesalt" - hoodtype = /obj/item/clothing/head/hooded/culthood/alt - -/obj/item/clothing/head/magus - name = "magus helm" - icon_state = "magus" - item_state = "magus" - desc = "A helm worn by the followers of Nar-Sie." - flags = BLOCKHAIR - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - -/obj/item/clothing/suit/magusred - name = "magus robes" - desc = "A set of armored robes worn by the followers of Nar-Sie" - icon_state = "magusred" - item_state = "magusred" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - -/obj/item/clothing/head/helmet/space/cult - name = "cult helmet" - desc = "A space worthy helmet used by the followers of a cult." - icon_state = "cult_helmet" - item_state = "cult_helmet" - armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) - -/obj/item/clothing/suit/space/cult - name = "cult armor" - icon_state = "cult_armour" - item_state = "cult_armour" - desc = "A bulky suit of armor, bristling with spikes. It looks space proof." - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/tome,/obj/item/melee/cultblade,/obj/item/tank) - slowdown = 1 - armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) - -/obj/item/clothing/suit/hooded/cultrobes/cult_shield - name = "empowered cultist robe" - desc = "Empowered garb which creates a powerful shield around the user." - icon_state = "cult_armour" - item_state = "cult_armour" - w_class = WEIGHT_CLASS_BULKY - armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 30, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 50, "acid" = 60) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - var/current_charges = 3 - hoodtype = /obj/item/clothing/head/hooded/cult_hoodie - -/obj/item/clothing/head/hooded/cult_hoodie - name = "empowered cultist robe" - desc = "Empowered garb which creates a powerful shield around the user." - icon_state = "cult_hoodalt" - armor = list("melee" = 40, "bullet" = 30, "laser" = 40,"energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - body_parts_covered = HEAD - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/equipped(mob/living/user, slot) - ..() - if(!iscultist(user)) - to_chat(user, "\"I wouldn't advise that.\"") - to_chat(user, "An overwhelming sense of nausea overpowers you!") - user.unEquip(src, 1) - user.Dizzy(30) - user.Weaken(5) - -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/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(current_charges) - owner.visible_message("\The [attack_text] is deflected in a burst of blood-red sparks!") - current_charges-- - new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) - if(!current_charges) - owner.visible_message("The runed shield around [owner] suddenly disappears!") - owner.update_inv_wear_suit() - return 1 - return 0 - -/obj/item/clothing/suit/hooded/cultrobes/berserker - name = "flagellant's robes" - desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." - icon_state = "hardsuit-berserker" - item_state = "hardsuit-berserker" - flags_inv = HIDEJUMPSUIT - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - armor = list("melee" = -45, "bullet" = -45, "laser" = -45,"energy" = -45, "bomb" = -45, "bio" = -45, "rad" = -45, "fire" = 0, "acid" = 0) - slowdown = -1 - hoodtype = /obj/item/clothing/head/hooded/berserkerhood - - -/obj/item/clothing/head/hooded/berserkerhood - name = "flagellant's robes" - desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." - icon_state = "culthood" - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - -/obj/item/whetstone/cult - name = "eldritch whetstone" - desc = "A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone." - icon_state = "cult_sharpener" - used = 0 - increment = 5 - max = 40 - prefix = "darkened" - claw_damage_increase = 4 - -/obj/item/whetstone/cult/update_icon() - icon_state = "cult_sharpener[used ? "_used" : ""]" - -/obj/item/reagent_containers/food/drinks/bottle/unholywater - name = "flask of unholy water" - desc = "Toxic to nonbelievers; this water renews and reinvigorates the faithful of a cult." - icon_state = "holyflask" - color = "#333333" - list_reagents = list("unholywater" = 40) - -/obj/item/clothing/glasses/night/cultblind - desc = "May the master guide you through the darkness and shield you from the light." - name = "zealot's blindfold" - icon_state = "blindfold" - item_state = "blindfold" - see_in_dark = 8 - flash_protect = 1 - -/obj/item/clothing/glasses/night/cultblind/equipped(mob/user, slot) - ..() - if(!iscultist(user)) - to_chat(user, "\"You want to be blind, do you?\"") - user.unEquip(src, 1) - user.Dizzy(30) - user.Weaken(5) - user.EyeBlind(30) - -/obj/item/shuttle_curse - name = "cursed orb" - desc = "You peer within this smokey orb and glimpse terrible fates befalling the escape shuttle." - icon = 'icons/obj/projectiles.dmi' - icon_state ="bluespace" - color = "#ff0000" - var/global/curselimit = 0 - -/obj/item/shuttle_curse/attack_self(mob/user) - if(!iscultist(user)) - user.unEquip(src, 1) - user.Weaken(5) - to_chat(user, "A powerful force shoves you away from [src]!") - return - if(curselimit > 1) - to_chat(user, "We have exhausted our ability to curse the shuttle.") - return - if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/slaughter/cult) in GLOB.mob_list) - to_chat(user, "Nar-Sie or his avatars are already on this plane, there is no delaying the end of all things.") - return - - if(SSshuttle.emergency.mode == SHUTTLE_CALL) - var/cursetime = 1800 - var/timer = SSshuttle.emergency.timeLeft(1) + cursetime - SSshuttle.emergency.setTimer(timer) - to_chat(user,"You shatter the orb! A dark essence spirals into the air, then disappears.") - playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, 1) - curselimit++ - qdel(src) - sleep(20) - var/global/list/curses - if(!curses) - curses = list("A fuel technician just slit his own throat and begged for death. The shuttle will be delayed by two minutes.", - "The shuttle's navigation programming was replaced by a file containing two words, IT COMES. The shuttle will be delayed by two minutes.", - "The shuttle's custodian tore out his guts and began painting strange shapes on the floor. The shuttle will be delayed by two minutes.", - "A shuttle engineer began screaming 'DEATH IS NOT THE END' and ripped out wires until an arc flash seared off her flesh. The shuttle will be delayed by two minutes.", - "A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. The shuttle will be delayed by two minutes.", - "The shuttle dispatcher was found dead with bloody symbols carved into their flesh. The shuttle will be delayed by two minutes.", - "Steve repeatedly touched a lightbulb until his hands fell off. The shuttle will be delayed by two minutes.") - var/message = pick(curses) - command_announcement.Announce("[message]", "System Failure", 'sound/misc/notice1.ogg') - -/obj/item/cult_shift - name = "veil shifter" - desc = "This relic teleports you forward a medium distance." - icon = 'icons/obj/cult.dmi' - icon_state ="shifter" - var/uses = 4 - -/obj/item/cult_shift/examine(mob/user) - . = ..() - if(uses) - . += "It has [uses] uses remaining." - else - . += "It seems drained." - -/obj/item/cult_shift/proc/handle_teleport_grab(turf/T, mob/user) - var/mob/living/carbon/C = user - if(C.pulling) - var/atom/movable/pulled = C.pulling - pulled.forceMove(T) - . = pulled - -/obj/item/cult_shift/attack_self(mob/user) - if(!uses || !iscarbon(user)) - to_chat(user, "\The [src] is dull and unmoving in your hands.") - return - if(!iscultist(user)) - user.unEquip(src, 1) - step(src, pick(alldirs)) - to_chat(user, "\The [src] flickers out of your hands, too eager to move!") - return - - var/outer_tele_radius = 9 - - var/mob/living/carbon/C = user - var/turf/mobloc = get_turf(C) - var/list/turfs = new/list() - for(var/turf/T in range(user, outer_tele_radius)) - if(!is_teleport_allowed(T.z)) - continue - if(get_dir(C, T) != C.dir) - continue - if(T == mobloc) - continue - if(istype(T, /turf/space)) - continue - if(T.x > world.maxx-outer_tele_radius || T.x < outer_tele_radius) - continue //putting them at the edge is dumb - if(T.y > world.maxy-outer_tele_radius || T.y < outer_tele_radius) - continue - - turfs += T - - if(turfs) - uses-- - var/turf/destination = pick(turfs) - if(uses <= 0) - icon_state ="shifter_drained" - playsound(mobloc, "sparks", 50, 1) - new /obj/effect/temp_visual/dir_setting/cult/phase/out(mobloc, C.dir) - - var/atom/movable/pulled = handle_teleport_grab(destination, C) - C.forceMove(destination) - if(pulled) - C.start_pulling(pulled) //forcemove resets pulls, so we need to re-pull - - new /obj/effect/temp_visual/dir_setting/cult/phase(destination, C.dir) - playsound(destination, 'sound/effects/phasein.ogg', 25, 1) - playsound(destination, "sparks", 50, 1) - - else - to_chat(C, "The veil cannot be torn here!") - -/obj/item/melee/cultblade/ghost - name = "eldritch sword" - force = 15 - flags = NODROP | DROPDEL - -/obj/item/clothing/head/hooded/culthood/alt/ghost - flags = NODROP | DROPDEL - -/obj/item/clothing/suit/cultrobesghost - name = "ghostly cult robes" - desc = "A set of ethreal armored robes worn by the undead followers of a cult." - icon_state = "cultrobesalt" - item_state = "cultrobesalt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - armor = list(melee = 50, bullet = 30, laser = 50, energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10) - flags_inv = HIDEJUMPSUIT - - flags = NODROP | DROPDEL - - -/obj/item/clothing/shoes/cult/ghost - flags = NODROP | DROPDEL - -/datum/outfit/ghost_cultist - name = "Cultist Ghost" - - uniform = /obj/item/clothing/under/color/black - suit = /obj/item/clothing/suit/cultrobesghost - shoes = /obj/item/clothing/shoes/cult/ghost - head = /obj/item/clothing/head/hooded/culthood/alt/ghost - r_hand = /obj/item/melee/cultblade/ghost \ No newline at end of file +/obj/item/melee/cultblade + name = "Cult Blade" + desc = "An arcane weapon wielded by the followers of a cult." + icon_state = "cultblade" + item_state = "cultblade" + w_class = WEIGHT_CLASS_BULKY + force = 30 + throwforce = 10 + sharp = 1 + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + +/obj/item/melee/cultblade/attack(mob/living/target as mob, mob/living/carbon/human/user as mob) + if(!iscultist(user)) + user.Weaken(5) + user.unEquip(src, 1) + user.visible_message("A powerful force shoves [user] away from [target]!", \ + "\"You shouldn't play with sharp things. You'll poke someone's eye out.\"") + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm")) + else + user.adjustBruteLoss(rand(force/2,force)) + return + ..() + +/obj/item/melee/cultblade/pickup(mob/living/user) + . = ..() + if(!iscultist(user)) + to_chat(user, "\"I wouldn't advise that.\"") + to_chat(user, "An overwhelming sense of nausea overpowers you!") + user.Dizzy(120) + + if(HULK in user.mutations) + to_chat(user, "You can't seem to hold the blade properly!") + return FALSE + +/obj/item/melee/cultblade/dagger + name = "sacrificial dagger" + desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods." + icon = 'icons/obj/wizard.dmi' + icon_state = "render" + w_class = WEIGHT_CLASS_SMALL + force = 15 + throwforce = 25 + embed_chance = 75 + +/obj/item/melee/cultblade/dagger/attack(atom/target, mob/living/carbon/human/user) + ..() + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if((H.stat != DEAD) && !(NO_BLOOD in H.dna.species.species_traits)) + H.bleed(50) + +/obj/item/restraints/legcuffs/bola/cult + name = "runed bola" + desc = "A strong bola, bound with dark magic. Throw it to trip and slow your victim." + icon = 'icons/obj/items.dmi' + icon_state = "bola_cult" + breakouttime = 45 + weaken = 1 + +/obj/item/clothing/head/hooded/culthood + name = "cult hood" + icon_state = "culthood" + desc = "A hood worn by the followers of a cult." + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list(melee = 30, bullet = 10, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 10, acid = 10) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + + +/obj/item/clothing/head/hooded/culthood/alt + icon_state = "cult_hoodalt" + item_state = "cult_hoodalt" + + +/obj/item/clothing/suit/hooded/cultrobes + name = "cult robes" + desc = "A set of armored robes worn by the followers of a cult." + icon_state = "cultrobes" + item_state = "cultrobes" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + hoodtype = /obj/item/clothing/head/hooded/culthood + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + armor = list("melee" = 40, "bullet" = 30, "laser" = 40, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + flags_inv = HIDEJUMPSUIT + +/obj/item/clothing/suit/hooded/cultrobes/alt + icon_state = "cultrobesalt" + item_state = "cultrobesalt" + hoodtype = /obj/item/clothing/head/hooded/culthood/alt + +/obj/item/clothing/head/magus + name = "magus helm" + icon_state = "magus" + item_state = "magus" + desc = "A helm worn by the followers of Nar-Sie." + flags = BLOCKHAIR + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + +/obj/item/clothing/suit/magusred + name = "magus robes" + desc = "A set of armored robes worn by the followers of Nar-Sie" + icon_state = "magusred" + item_state = "magusred" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + +/obj/item/clothing/head/helmet/space/cult + name = "cult helmet" + desc = "A space worthy helmet used by the followers of a cult." + icon_state = "cult_helmet" + item_state = "cult_helmet" + armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) + +/obj/item/clothing/suit/space/cult + name = "cult armor" + icon_state = "cult_armour" + item_state = "cult_armour" + desc = "A bulky suit of armor, bristling with spikes. It looks space proof." + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/tome,/obj/item/melee/cultblade,/obj/item/tank) + slowdown = 1 + armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield + name = "empowered cultist robe" + desc = "Empowered garb which creates a powerful shield around the user." + icon_state = "cult_armour" + item_state = "cult_armour" + w_class = WEIGHT_CLASS_BULKY + armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 30, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 50, "acid" = 60) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + var/current_charges = 3 + hoodtype = /obj/item/clothing/head/hooded/cult_hoodie + +/obj/item/clothing/head/hooded/cult_hoodie + name = "empowered cultist robe" + desc = "Empowered garb which creates a powerful shield around the user." + icon_state = "cult_hoodalt" + armor = list("melee" = 40, "bullet" = 30, "laser" = 40,"energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + body_parts_covered = HEAD + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/equipped(mob/living/user, slot) + ..() + if(!iscultist(user)) + to_chat(user, "\"I wouldn't advise that.\"") + to_chat(user, "An overwhelming sense of nausea overpowers you!") + user.unEquip(src, 1) + user.Dizzy(30) + user.Weaken(5) + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/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(current_charges) + owner.visible_message("\The [attack_text] is deflected in a burst of blood-red sparks!") + current_charges-- + new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) + if(!current_charges) + owner.visible_message("The runed shield around [owner] suddenly disappears!") + owner.update_inv_wear_suit() + return 1 + return 0 + +/obj/item/clothing/suit/hooded/cultrobes/berserker + name = "flagellant's robes" + desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." + icon_state = "hardsuit-berserker" + item_state = "hardsuit-berserker" + flags_inv = HIDEJUMPSUIT + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + armor = list("melee" = -45, "bullet" = -45, "laser" = -45,"energy" = -45, "bomb" = -45, "bio" = -45, "rad" = -45, "fire" = 0, "acid" = 0) + slowdown = -1 + hoodtype = /obj/item/clothing/head/hooded/berserkerhood + + +/obj/item/clothing/head/hooded/berserkerhood + name = "flagellant's robes" + desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." + icon_state = "culthood" + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/whetstone/cult + name = "eldritch whetstone" + desc = "A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone." + icon_state = "cult_sharpener" + used = 0 + increment = 5 + max = 40 + prefix = "darkened" + claw_damage_increase = 4 + +/obj/item/whetstone/cult/update_icon() + icon_state = "cult_sharpener[used ? "_used" : ""]" + +/obj/item/reagent_containers/food/drinks/bottle/unholywater + name = "flask of unholy water" + desc = "Toxic to nonbelievers; this water renews and reinvigorates the faithful of a cult." + icon_state = "holyflask" + color = "#333333" + list_reagents = list("unholywater" = 40) + +/obj/item/clothing/glasses/night/cultblind + desc = "May the master guide you through the darkness and shield you from the light." + name = "zealot's blindfold" + icon_state = "blindfold" + item_state = "blindfold" + see_in_dark = 8 + flash_protect = 1 + +/obj/item/clothing/glasses/night/cultblind/equipped(mob/user, slot) + ..() + if(!iscultist(user)) + to_chat(user, "\"You want to be blind, do you?\"") + user.unEquip(src, 1) + user.Dizzy(30) + user.Weaken(5) + user.EyeBlind(30) + +/obj/item/shuttle_curse + name = "cursed orb" + desc = "You peer within this smokey orb and glimpse terrible fates befalling the escape shuttle." + icon = 'icons/obj/projectiles.dmi' + icon_state ="bluespace" + color = "#ff0000" + var/global/curselimit = 0 + +/obj/item/shuttle_curse/attack_self(mob/user) + if(!iscultist(user)) + user.unEquip(src, 1) + user.Weaken(5) + to_chat(user, "A powerful force shoves you away from [src]!") + return + if(curselimit > 1) + to_chat(user, "We have exhausted our ability to curse the shuttle.") + return + if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/slaughter/cult) in GLOB.mob_list) + to_chat(user, "Nar-Sie or his avatars are already on this plane, there is no delaying the end of all things.") + return + + if(SSshuttle.emergency.mode == SHUTTLE_CALL) + var/cursetime = 1800 + var/timer = SSshuttle.emergency.timeLeft(1) + cursetime + SSshuttle.emergency.setTimer(timer) + to_chat(user,"You shatter the orb! A dark essence spirals into the air, then disappears.") + playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, 1) + curselimit++ + qdel(src) + sleep(20) + var/global/list/curses + if(!curses) + curses = list("A fuel technician just slit his own throat and begged for death. The shuttle will be delayed by two minutes.", + "The shuttle's navigation programming was replaced by a file containing two words, IT COMES. The shuttle will be delayed by two minutes.", + "The shuttle's custodian tore out his guts and began painting strange shapes on the floor. The shuttle will be delayed by two minutes.", + "A shuttle engineer began screaming 'DEATH IS NOT THE END' and ripped out wires until an arc flash seared off her flesh. The shuttle will be delayed by two minutes.", + "A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. The shuttle will be delayed by two minutes.", + "The shuttle dispatcher was found dead with bloody symbols carved into their flesh. The shuttle will be delayed by two minutes.", + "Steve repeatedly touched a lightbulb until his hands fell off. The shuttle will be delayed by two minutes.") + var/message = pick(curses) + GLOB.command_announcement.Announce("[message]", "System Failure", 'sound/misc/notice1.ogg') + +/obj/item/cult_shift + name = "veil shifter" + desc = "This relic teleports you forward a medium distance." + icon = 'icons/obj/cult.dmi' + icon_state ="shifter" + var/uses = 4 + +/obj/item/cult_shift/examine(mob/user) + . = ..() + if(uses) + . += "It has [uses] uses remaining." + else + . += "It seems drained." + +/obj/item/cult_shift/proc/handle_teleport_grab(turf/T, mob/user) + var/mob/living/carbon/C = user + if(C.pulling) + var/atom/movable/pulled = C.pulling + pulled.forceMove(T) + . = pulled + +/obj/item/cult_shift/attack_self(mob/user) + if(!uses || !iscarbon(user)) + to_chat(user, "\The [src] is dull and unmoving in your hands.") + return + if(!iscultist(user)) + user.unEquip(src, 1) + step(src, pick(GLOB.alldirs)) + to_chat(user, "\The [src] flickers out of your hands, too eager to move!") + return + + var/outer_tele_radius = 9 + + var/mob/living/carbon/C = user + var/turf/mobloc = get_turf(C) + var/list/turfs = new/list() + for(var/turf/T in range(user, outer_tele_radius)) + if(!is_teleport_allowed(T.z)) + continue + if(get_dir(C, T) != C.dir) + continue + if(T == mobloc) + continue + if(istype(T, /turf/space)) + continue + if(T.x > world.maxx-outer_tele_radius || T.x < outer_tele_radius) + continue //putting them at the edge is dumb + if(T.y > world.maxy-outer_tele_radius || T.y < outer_tele_radius) + continue + + turfs += T + + if(turfs) + uses-- + var/turf/destination = pick(turfs) + if(uses <= 0) + icon_state ="shifter_drained" + playsound(mobloc, "sparks", 50, 1) + new /obj/effect/temp_visual/dir_setting/cult/phase/out(mobloc, C.dir) + + var/atom/movable/pulled = handle_teleport_grab(destination, C) + C.forceMove(destination) + if(pulled) + C.start_pulling(pulled) //forcemove resets pulls, so we need to re-pull + + new /obj/effect/temp_visual/dir_setting/cult/phase(destination, C.dir) + playsound(destination, 'sound/effects/phasein.ogg', 25, 1) + playsound(destination, "sparks", 50, 1) + + else + to_chat(C, "The veil cannot be torn here!") + +/obj/item/melee/cultblade/ghost + name = "eldritch sword" + force = 15 + flags = NODROP | DROPDEL + +/obj/item/clothing/head/hooded/culthood/alt/ghost + flags = NODROP | DROPDEL + +/obj/item/clothing/suit/cultrobesghost + name = "ghostly cult robes" + desc = "A set of ethreal armored robes worn by the undead followers of a cult." + icon_state = "cultrobesalt" + item_state = "cultrobesalt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + armor = list(melee = 50, bullet = 30, laser = 50, energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10) + flags_inv = HIDEJUMPSUIT + + flags = NODROP | DROPDEL + + +/obj/item/clothing/shoes/cult/ghost + flags = NODROP | DROPDEL + +/datum/outfit/ghost_cultist + name = "Cultist Ghost" + + uniform = /obj/item/clothing/under/color/black + suit = /obj/item/clothing/suit/cultrobesghost + shoes = /obj/item/clothing/shoes/cult/ghost + head = /obj/item/clothing/head/hooded/culthood/alt/ghost + r_hand = /obj/item/melee/cultblade/ghost diff --git a/code/game/gamemodes/cult/cult_objectives.dm b/code/game/gamemodes/cult/cult_objectives.dm index 83ae3c2f1b7..f1a188022a0 100644 --- a/code/game/gamemodes/cult/cult_objectives.dm +++ b/code/game/gamemodes/cult/cult_objectives.dm @@ -114,10 +114,10 @@ if(prob(40))//split the chance of this objectives += "eldergod" - explanation = "Summon [SSticker.cultdat.entity_name] on the Station via the use of the Tear Reality rune. The veil is weak enough in [english_list(summon_spots)] for the ritual to begin." + explanation = "Summon [SSticker.cultdat.entity_name] on the Station via the use of the Tear Reality rune. The veil is weak enough in [english_list(GLOB.summon_spots)] for the ritual to begin." else objectives += "slaughter" - explanation = "Bring the Slaughter via the rune 'Bring forth the slaughter'. The veil is weak enough in [english_list(summon_spots)] for the ritual to begin." + explanation = "Bring the Slaughter via the rune 'Bring forth the slaughter'. The veil is weak enough in [english_list(GLOB.summon_spots)] for the ritual to begin." for(var/datum/mind/cult_mind in cult) if(cult_mind) @@ -155,12 +155,12 @@ /datum/game_mode/cult/proc/get_possible_sac_targets() var/list/possible_sac_targets = list() for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player.mind && !is_convertable_to_cult(player.mind) && (player.stat != DEAD)) + if(player.mind && !is_convertable_to_cult(player.mind) && (player.stat != DEAD) && (!player.mind.offstation_role) ) possible_sac_targets += player.mind if(!possible_sac_targets.len) //There are no living Unconvertables on the station. Looking for a Sacrifice Target among the ordinary crewmembers for(var/mob/living/carbon/human/player in GLOB.player_list) - if(is_secure_level(player.z)) //We can't sacrifice people that are on the centcom z-level + if(is_secure_level(player.z) || player.mind.offstation_role) //We can't sacrifice people that are on the centcom z-level or offstation roles continue if(player.mind && !(player.mind in cult) && (player.stat != DEAD))//make DAMN sure they are not dead possible_sac_targets += player.mind @@ -174,7 +174,7 @@ updated_memory = replacetext("[cult_mind.memory]", "[previous_target]", "[sacrifice_target]") updated_memory = replacetext("[updated_memory]", "[previous_role]", "[sacrifice_target.assigned_role]") cult_mind.memory = updated_memory - + /datum/game_mode/cult/proc/pick_objective() var/list/possible_objectives = list() @@ -254,7 +254,7 @@ for(var/mob/living/L in GLOB.player_list) if(L.stat != DEAD && !(L.mind in cult)) var/area/A = get_area(L) - if(is_type_in_list(A.loc, centcom_areas)) + if(is_type_in_list(A.loc, GLOB.centcom_areas)) escaped_shuttle++ if(!escaped_shuttle) bonus = 1 @@ -266,4 +266,4 @@ if(is_station_level(T.z)) //we're only interested in the remaining humans on the station survivors++ if(survivors < massacre_target) - bonus = 1 \ No newline at end of file + bonus = 1 diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index de8846d292e..618abec4470 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -152,7 +152,7 @@ return 1 return ..() -var/list/blacklisted_pylon_turfs = typecacheof(list( +GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list( /turf/simulated/floor/engine/cult, /turf/space, /turf/simulated/floor/plating/lava, @@ -160,7 +160,7 @@ var/list/blacklisted_pylon_turfs = typecacheof(list( /turf/simulated/wall/cult, /turf/simulated/wall/cult/artificer, /turf/unsimulated/wall - )) + ))) /obj/structure/cult/functional/pylon name = "pylon" @@ -214,7 +214,7 @@ var/list/blacklisted_pylon_turfs = typecacheof(list( if(istype(T, /turf/simulated/floor/engine/cult)) cultturfs |= T continue - if(is_type_in_typecache(T, blacklisted_pylon_turfs)) + if(is_type_in_typecache(T, GLOB.blacklisted_pylon_turfs)) continue else validturfs |= T diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index a32f7219ca8..9422ffcc716 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -1,320 +1,320 @@ -#define CULT_ELDERGOD "eldergod" -#define CULT_SLAUGHTER "slaughter" - -/obj/effect/rune/proc/fizzle() - if(istype(src,/obj/effect/rune)) - usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - else - usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - for (var/mob/V in viewers(src)) - V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) - return - -/obj/effect/rune/proc/check_icon() - if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way - var/bits = make_bit_triplet() - icon = get_rune(bits) - else - icon = get_rune_cult(invocation) - -/obj/item/tome - name = "arcane tome" - desc = "An old, dusty tome with frayed edges and a sinister-looking cover." - icon_state ="tome" - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - var/scribereduct = 0 - var/canbypass = 0 //ADMINBUS - -/obj/item/tome/accursed - name = "accursed tome" - desc = "An arcane tome still empowered with a shadow of its former consecration." - scribereduct = 30 //faster because it's made by corrupting a bible - -/obj/item/tome/imbued //Admin-only tome, allows instant drawing of runes - name = "imbued arcane tome" - desc = "An arcane tome granted by the Geometer itself." - scribereduct = 50 - canbypass = 1 - -/obj/item/tome/New() - if(!SSticker.mode) - icon_state = "tome" - else - icon_state = SSticker.cultdat.tome_icon - ..() - -/obj/item/tome/examine(mob/user) - . = ..() - if(iscultist(user) || user.stat == DEAD) - . += "The scriptures of [SSticker.cultdat.entity_title3]. Allows the scribing of runes and access to the knowledge archives of the cult of [SSticker.cultdat.entity_name]." - . += "Striking another cultist with it will purge holy water from them." - . += "Striking a noncultist, however, will sear their flesh." - -/obj/item/tome/attack(mob/living/M, mob/living/user) - if(!istype(M)) - return - if(!iscultist(user)) - return ..() - if(iscultist(M)) - if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion - to_chat(user, "You remove the taint from [M].") - var/holy2unholy = M.reagents.get_reagent_amount("holywater") - M.reagents.del_reagent("holywater") - M.reagents.add_reagent("unholywater",holy2unholy) - add_attack_logs(user, M, "Hit with [src], removing the holy water from them") - return - M.take_organ_damage(0, 15) //Used to be a random between 5 and 20 - playsound(M, 'sound/weapons/sear.ogg', 50, 1) - M.visible_message("[user] strikes [M] with the arcane tome!", \ - "[user] strikes you with the tome, searing your flesh!") - flick("tome_attack", src) - user.do_attack_animation(M) - add_attack_logs(user, M, "Hit with [src]") - -/obj/item/tome/attack_self(mob/user) - if(!iscultist(user)) - to_chat(user, "[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?") - return - open_tome(user) - -/obj/item/tome/proc/open_tome(mob/user) - var/choice = alert(user,"You open the tome...",,"Scribe Rune","More Information","Cancel") - switch(choice) - if("More Information") - read_tome(user) - if("Scribe Rune") - scribe_rune(user) - if("Cancel") - return - -/obj/item/tome/proc/read_tome(mob/user) - var/text = list() - text += "
    Archives of [SSticker.cultdat.entity_title1]



    " - text += "A rune's name and effects can be revealed by examining the rune.<

    " - - text += "Rite of Binding
    This rune is one of the most important runes the cult has, being the only way to create new talismans. A blank sheet of paper must be on top of the rune. After \ - invoking it and choosing which talisman you desire, the paper will be converted, after some delay into a talisman.

    " - - text += "Teleport
    This rune is unique in that it requires a keyword before the scribing can begin. When invoked, it will find any other Teleport runes; \ - If any are found, the user can choose which rune to send to. Upon activation, the rune teleports everything above it to the selected rune.

    " - - text += "Rite of Enlightenment
    This rune is critical to the success of the cult. It will allow you to convert normal crew members into cultists. \ - To do this, simply place the crew member upon the rune and invoke it. This rune requires two invokers to use. If the target to be converted is mindshielded or a certain assignment, they will \ - be unable to be converted. People [SSticker.cultdat.entity_title3] wishes sacrificed will also be ineligible for conversion, and anyone with a shielding presence like the null rod will not be converted.
    \ - Successful conversions will produce a tome for the new cultist.

    " - - text += "Rite of Tribute
    This rune is necessary to achieve your goals. Simply place any dead creature upon the rune and invoke it (this will not \ - target cultists!). If this creature has a mind, a soulstone will be created and the creature's soul transported to it. Sacrificing the dead can be done alone, but sacrificing living crew or your cult's target will require 3 cultists. \ - Soulstones used on construct shells will move that soul into a powerful construct of your choice.

    " - - - text += "Rite of Resurrection
    This rune requires two corpses. To perform the ritual, place the corpse you wish to revive onto \ - the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \ - within a few seconds, they will be brought back to life, healed of all ailments.

    " - - text += "Rite of Disruption
    Robotic lifeforms have time and time again been the downfall of fledgling cults. This rune may allow you to gain the upper \ - hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location. The size of the EMP will grow significantly for each additional adjacent cultist when the \ - rune is activated.

    " - - text += "Astral Communion
    This rune is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \ - user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \ - The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \ - also be used to commune with the dead.

    " - - text += "Rite of the Corporeal Shield
    While simple, this rune serves an important purpose in defense and hindering passage. When invoked, the \ - rune will draw a small amount of life force from the user and make the space above the rune completely dense, rendering it impassable to all but the most complex means. The rune may be invoked again to \ - undo this effect and allow passage again.

    " - - text += "Rite of Joined Souls
    This rune allows the cult to free other cultists with ease. When invoked, it will allow the user to summon a single cultist to the rune from \ - any location. It requires two invokers, and will damage each invoker slightly.

    " - - text += "Blood Boil
    When invoked, this rune will do a massive amount of damage to all non-cultist viewers, but it will also emit a small explosion upon invocation. \ - It requires three invokers.

    " - - text += "Leeching
    When invoked, this rune will transfer lifeforce from the victim to the invoker.

    " - - text += "Rite of Spectral Manifestation
    This rune allows you to summon spirits as humanoid fighters. When invoked, a spirit above the rune will be brought to life as a human, wearing nothing, that seeks only to serve you and [SSticker.cultdat.entity_title3]. \ - However, the spirit's link to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, all summoned spirits will dissipate, dropping their items to the ground. You may manifest \ - multiple spirits with one rune, but you will rapidly take damage in doing so.

    " - - text += "Ritual of Dimensional Rending
    This rune is necessary to achieve your goals. On attempting to scribe it, it will produce shields around you and alert everyone you are attempting to scribe it; it takes a very long time to scribe, \ - and does massive damage to the one attempting to scribe it.
    Invoking it requires 9 invokers and the sacrifice of a specific crewmember, and once invoked, will summon [SSticker.cultdat.entity_title3], [SSticker.cultdat.entity_name]. \ - This will complete your objectives.


    " - - text += "Talisman of Teleportation
    The talisman form of the Teleport rune will transport the invoker to a selected Teleport rune once.

    " - - text += "Talisman of Fabrication
    This talisman is the main way of creating construct shells. To use it, one must strike 30 sheets of metal with the talisman. The sheets will then be twisted into a construct shell, ready to receive a soul to occupy it.

    " - - text += "Talisman of Tome Summoning
    This talisman will produce a single tome at your feet.

    " - - text += "Talisman of Veiling/Revealing
    This talisman will hide runes on its first use, and on the second, will reveal runes.

    " - - text += "Talisman of Disguising
    This talisman will permanently disguise all nearby runes as crayon runes.

    " - - text += "Talisman of Electromagnetic Pulse
    This talisman will EMP anything else nearby. It disappears after one use.

    " - - text += "Talisman of Stunning
    Attacking a target will knock them down for a long duration in addition to inhibiting their speech. \ - Robotic lifeforms will suffer the effects of a heavy electromagnetic pulse instead.

    " - - text += "Talisman of Armaments
    The Talisman of Arming will equip the user with armored robes, a backpack, shoes, an eldritch longsword, and an empowered bola. Any equipment that cannot \ - be equipped will not be summoned, weaponry will be put on the floor below the user. Attacking a fellow cultist with it will instead equip them.

    " - - text += "Talisman of Horrors
    The Talisman of Horror must be applied directly to the victim, it will shatter your victim's mind with visions of the end-times that may incapacitate them.

    " - - text += "Talisman of Shackling
    The Talisman of Shackling must be applied directly to the victim, it has 4 uses and cuffs victims with magic shackles that disappear when removed.

    " - - text += "In addition to these runes, the cult has a small selection of equipment and constructs.

    " - - text += "Equipment:

    " - - text += "Cult Blade
    Cult blades are sharp weapons that, notably, cannot be used by non-cultists. These blades are produced by the Talisman of Arming.

    " - - text += "Cult Bola
    Cult bolas are strong bolas, useful for snaring targets. These bolas are produced by the Talisman of Arming.

    " - - text += "Cult Robes
    Cult robes are heavily armored robes. These robes are produced by the Talisman of Arming.

    " - - text += "Soulstone
    A soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead human, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \ -
    The soul within can also be released as a Shade by using it in-hand.

    " - - text += "Construct Shell
    A construct shell is useless on its own, but placing a filled soulstone within it allows you to produce your choice of a Wraith, a Juggernaut, or an Artificer. \ -
    Each construct has uses, detailed below in Constructs. Construct shells can be produced via the starter talisman or the Rite of Fabrication.

    " - - text += "Constructs:

    " - - text += "Shade
    While technically not a construct, the Shade is produced when released from a soulstone. It is quite fragile and has weak melee attacks, but is fully healed when recaptured by a soulstone.

    " - - text += "Wraith
    The Wraith is a fast, lethal melee attacker which can jaunt through walls. However, it is only slightly more durable than a shade.

    " - - text += "Juggernaut
    The Juggernaut is a slow, but durable, melee attacker which can produce temporary forcewalls. It will also reflect most lethal energy weapons.

    " - - text += "Artificer
    The Artificer is a weak and fragile construct, able to heal other constructs, produce more soulstones and construct shells, \ - construct fortifying cult walls and flooring, and finally, it can release a few indiscriminate stunning missiles.

    " - - text += "Harvester
    If you see one, know that you have done all you can and your life is void.

    " - - var/text_string = jointext(text, null) - var/datum/browser/popup = new(user, "tome", "", 800, 600) - popup.set_content(text_string) - popup.open() - return 1 - -/obj/item/tome/proc/finale_runes_ok(mob/living/user, obj/effect/rune/rune_to_scribe) - var/datum/game_mode/cult/cult_mode = SSticker.mode - var/area/A = get_area(src) - if(GAMEMODE_IS_CULT) - if(!canbypass)//not an admin-tome, check things - if(!cult_mode.narsie_condition_cleared) - to_chat(user, "There is still more to do before unleashing [SSticker.cultdat.entity_name] power!") - return 0 - if(!cult_mode.eldergod) - to_chat(user, "\"I am already here. There is no need to try to summon me now.\"") - return 0 - if(cult_mode.demons_summoned) - to_chat(user, "\"We are already here. There is no need to try to summon us now.\"") - return 0 - if(!((CULT_ELDERGOD in cult_mode.objectives) || (CULT_SLAUGHTER in cult_mode.objectives))) - to_chat(user, "[SSticker.cultdat.entity_name]'s power does not wish to be unleashed!") - return 0 - if(!(A in summon_spots)) - to_chat(user, "[SSticker.cultdat.entity_name] can only be summoned where the veil is weak - in [english_list(summon_spots)]!") - return 0 - var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") - if(confirm_final == "No" || confirm_final == null) - to_chat(user, "You decide to prepare further before scribing the rune.") - return 0 - else - return 1 - else//the game mode is not cult..but we ARE a cultist...ALL ON THE ADMINBUS - var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") - if(confirm_final == "No" || confirm_final == null) - to_chat(user, "You decide to prepare further before scribing the rune.") - return 0 - else - return 1 - -/obj/item/tome/proc/scribe_rune(mob/living/user) - var/turf/runeturf = get_turf(user) - if(isspaceturf(runeturf)) - return - var/chosen_keyword - var/obj/effect/rune/rune_to_scribe - var/entered_rune_name - var/list/possible_runes = list() - var/list/shields = list() - var/area/A = get_area(src) - if(locate(/obj/effect/rune) in runeturf) - to_chat(user, "There is already a rune here.") - return - for(var/T in subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed) - var/obj/effect/rune/R = T - if(initial(R.cultist_name)) - possible_runes.Add(initial(R.cultist_name)) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this - if(!possible_runes.len) - return - entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes - if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) - return - for(var/T in typesof(/obj/effect/rune)) - var/obj/effect/rune/R = T - if(initial(R.cultist_name) == entered_rune_name) - rune_to_scribe = R - if(initial(R.req_keyword)) - var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "") - if(!the_keyword) - return - chosen_keyword = the_keyword - break - if(!rune_to_scribe) - return - runeturf = get_turf(user) //we may have moved. adjust as needed... - A = get_area(src) - if(locate(/obj/effect/rune) in runeturf) - to_chat(user, "There is already a rune here.") - return - if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) - return - if(ispath(rune_to_scribe, /obj/effect/rune/narsie) || ispath(rune_to_scribe, /obj/effect/rune/slaughter))//may need to change this - Fethas - if(finale_runes_ok(user,rune_to_scribe)) - A = get_area(src) - if(!(A in summon_spots)) // Check again to make sure they didn't move - to_chat(user, "The ritual can only begin where the veil is weak - in [english_list(summon_spots)]!") - return - command_announcement.Announce("Figments from an eldritch god are being summoned somewhere on the station from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') - for(var/B in spiral_range_turfs(1, user, 1)) - var/turf/T = B - var/obj/machinery/shield/N = new(T) - N.name = "Rune-Scriber's Shield" - N.desc = "A potent shield summoned by cultists to protect them while they prepare the final ritual" - N.icon_state = "shield-cult" - N.health = 60 - shields |= N - else - return//don't do shit - - var/mob/living/carbon/human/H = user - var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") - var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone)) - user.visible_message("[user] cuts open [user.p_their()] [affecting] and begins writing in [user.p_their()] own blood!", "You slice open your [affecting] and begin drawing a sigil of [SSticker.cultdat.entity_title3].") - user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE , affecting) - if(!do_after(user, initial(rune_to_scribe.scribe_delay)-scribereduct, target = get_turf(user))) - for(var/V in shields) - var/obj/machinery/shield/S = V - if(S && !QDELETED(S)) - qdel(S) - return - if(locate(/obj/effect/rune) in runeturf) - to_chat(user, "There is already a rune here.") - return - user.visible_message("[user] creates a strange circle in [user.p_their()] own blood.", \ - "You finish drawing the arcane markings of [SSticker.cultdat.entity_title3].") - for(var/V in shields) - var/obj/machinery/shield/S = V - if(S && !QDELETED(S)) - qdel(S) - var/obj/effect/rune/R = new rune_to_scribe(runeturf, chosen_keyword) - R.blood_DNA = list() - R.blood_DNA[H.dna.unique_enzymes] = H.dna.blood_type - R.add_hiddenprint(H) - to_chat(user, "The [lowertext(initial(rune_to_scribe.cultist_name))] rune [initial(rune_to_scribe.cultist_desc)]") +#define CULT_ELDERGOD "eldergod" +#define CULT_SLAUGHTER "slaughter" + +/obj/effect/rune/proc/fizzle() + if(istype(src,/obj/effect/rune)) + usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) + else + usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) + for (var/mob/V in viewers(src)) + V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) + return + +/obj/effect/rune/proc/check_icon() + if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way + var/bits = make_bit_triplet() + icon = get_rune(bits) + else + icon = get_rune_cult(invocation) + +/obj/item/tome + name = "arcane tome" + desc = "An old, dusty tome with frayed edges and a sinister-looking cover." + icon_state ="tome" + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + var/scribereduct = 0 + var/canbypass = 0 //ADMINBUS + +/obj/item/tome/accursed + name = "accursed tome" + desc = "An arcane tome still empowered with a shadow of its former consecration." + scribereduct = 30 //faster because it's made by corrupting a bible + +/obj/item/tome/imbued //Admin-only tome, allows instant drawing of runes + name = "imbued arcane tome" + desc = "An arcane tome granted by the Geometer itself." + scribereduct = 50 + canbypass = 1 + +/obj/item/tome/New() + if(!SSticker.mode) + icon_state = "tome" + else + icon_state = SSticker.cultdat.tome_icon + ..() + +/obj/item/tome/examine(mob/user) + . = ..() + if(iscultist(user) || user.stat == DEAD) + . += "The scriptures of [SSticker.cultdat.entity_title3]. Allows the scribing of runes and access to the knowledge archives of the cult of [SSticker.cultdat.entity_name]." + . += "Striking another cultist with it will purge holy water from them." + . += "Striking a noncultist, however, will sear their flesh." + +/obj/item/tome/attack(mob/living/M, mob/living/user) + if(!istype(M)) + return + if(!iscultist(user)) + return ..() + if(iscultist(M)) + if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion + to_chat(user, "You remove the taint from [M].") + var/holy2unholy = M.reagents.get_reagent_amount("holywater") + M.reagents.del_reagent("holywater") + M.reagents.add_reagent("unholywater",holy2unholy) + add_attack_logs(user, M, "Hit with [src], removing the holy water from them") + return + M.take_organ_damage(0, 15) //Used to be a random between 5 and 20 + playsound(M, 'sound/weapons/sear.ogg', 50, 1) + M.visible_message("[user] strikes [M] with the arcane tome!", \ + "[user] strikes you with the tome, searing your flesh!") + flick("tome_attack", src) + user.do_attack_animation(M) + add_attack_logs(user, M, "Hit with [src]") + +/obj/item/tome/attack_self(mob/user) + if(!iscultist(user)) + to_chat(user, "[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?") + return + open_tome(user) + +/obj/item/tome/proc/open_tome(mob/user) + var/choice = alert(user,"You open the tome...",,"Scribe Rune","More Information","Cancel") + switch(choice) + if("More Information") + read_tome(user) + if("Scribe Rune") + scribe_rune(user) + if("Cancel") + return + +/obj/item/tome/proc/read_tome(mob/user) + var/text = list() + text += "
    Archives of [SSticker.cultdat.entity_title1]



    " + text += "A rune's name and effects can be revealed by examining the rune.<

    " + + text += "Rite of Binding
    This rune is one of the most important runes the cult has, being the only way to create new talismans. A blank sheet of paper must be on top of the rune. After \ + invoking it and choosing which talisman you desire, the paper will be converted, after some delay into a talisman.

    " + + text += "Teleport
    This rune is unique in that it requires a keyword before the scribing can begin. When invoked, it will find any other Teleport runes; \ + If any are found, the user can choose which rune to send to. Upon activation, the rune teleports everything above it to the selected rune.

    " + + text += "Rite of Enlightenment
    This rune is critical to the success of the cult. It will allow you to convert normal crew members into cultists. \ + To do this, simply place the crew member upon the rune and invoke it. This rune requires two invokers to use. If the target to be converted is mindshielded or a certain assignment, they will \ + be unable to be converted. People [SSticker.cultdat.entity_title3] wishes sacrificed will also be ineligible for conversion, and anyone with a shielding presence like the null rod will not be converted.
    \ + Successful conversions will produce a tome for the new cultist.

    " + + text += "Rite of Tribute
    This rune is necessary to achieve your goals. Simply place any dead creature upon the rune and invoke it (this will not \ + target cultists!). If this creature has a mind, a soulstone will be created and the creature's soul transported to it. Sacrificing the dead can be done alone, but sacrificing living crew or your cult's target will require 3 cultists. \ + Soulstones used on construct shells will move that soul into a powerful construct of your choice.

    " + + + text += "Rite of Resurrection
    This rune requires two corpses. To perform the ritual, place the corpse you wish to revive onto \ + the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \ + within a few seconds, they will be brought back to life, healed of all ailments.

    " + + text += "Rite of Disruption
    Robotic lifeforms have time and time again been the downfall of fledgling cults. This rune may allow you to gain the upper \ + hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location. The size of the EMP will grow significantly for each additional adjacent cultist when the \ + rune is activated.

    " + + text += "Astral Communion
    This rune is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \ + user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \ + The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \ + also be used to commune with the dead.

    " + + text += "Rite of the Corporeal Shield
    While simple, this rune serves an important purpose in defense and hindering passage. When invoked, the \ + rune will draw a small amount of life force from the user and make the space above the rune completely dense, rendering it impassable to all but the most complex means. The rune may be invoked again to \ + undo this effect and allow passage again.

    " + + text += "Rite of Joined Souls
    This rune allows the cult to free other cultists with ease. When invoked, it will allow the user to summon a single cultist to the rune from \ + any location. It requires two invokers, and will damage each invoker slightly.

    " + + text += "Blood Boil
    When invoked, this rune will do a massive amount of damage to all non-cultist viewers, but it will also emit a small explosion upon invocation. \ + It requires three invokers.

    " + + text += "Leeching
    When invoked, this rune will transfer lifeforce from the victim to the invoker.

    " + + text += "Rite of Spectral Manifestation
    This rune allows you to summon spirits as humanoid fighters. When invoked, a spirit above the rune will be brought to life as a human, wearing nothing, that seeks only to serve you and [SSticker.cultdat.entity_title3]. \ + However, the spirit's link to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, all summoned spirits will dissipate, dropping their items to the ground. You may manifest \ + multiple spirits with one rune, but you will rapidly take damage in doing so.

    " + + text += "Ritual of Dimensional Rending
    This rune is necessary to achieve your goals. On attempting to scribe it, it will produce shields around you and alert everyone you are attempting to scribe it; it takes a very long time to scribe, \ + and does massive damage to the one attempting to scribe it.
    Invoking it requires 9 invokers and the sacrifice of a specific crewmember, and once invoked, will summon [SSticker.cultdat.entity_title3], [SSticker.cultdat.entity_name]. \ + This will complete your objectives.


    " + + text += "Talisman of Teleportation
    The talisman form of the Teleport rune will transport the invoker to a selected Teleport rune once.

    " + + text += "Talisman of Fabrication
    This talisman is the main way of creating construct shells. To use it, one must strike 30 sheets of metal with the talisman. The sheets will then be twisted into a construct shell, ready to receive a soul to occupy it.

    " + + text += "Talisman of Tome Summoning
    This talisman will produce a single tome at your feet.

    " + + text += "Talisman of Veiling/Revealing
    This talisman will hide runes on its first use, and on the second, will reveal runes.

    " + + text += "Talisman of Disguising
    This talisman will permanently disguise all nearby runes as crayon runes.

    " + + text += "Talisman of Electromagnetic Pulse
    This talisman will EMP anything else nearby. It disappears after one use.

    " + + text += "Talisman of Stunning
    Attacking a target will knock them down for a long duration in addition to inhibiting their speech. \ + Robotic lifeforms will suffer the effects of a heavy electromagnetic pulse instead.

    " + + text += "Talisman of Armaments
    The Talisman of Arming will equip the user with armored robes, a backpack, shoes, an eldritch longsword, and an empowered bola. Any equipment that cannot \ + be equipped will not be summoned, weaponry will be put on the floor below the user. Attacking a fellow cultist with it will instead equip them.

    " + + text += "Talisman of Horrors
    The Talisman of Horror must be applied directly to the victim, it will shatter your victim's mind with visions of the end-times that may incapacitate them.

    " + + text += "Talisman of Shackling
    The Talisman of Shackling must be applied directly to the victim, it has 4 uses and cuffs victims with magic shackles that disappear when removed.

    " + + text += "In addition to these runes, the cult has a small selection of equipment and constructs.

    " + + text += "Equipment:

    " + + text += "Cult Blade
    Cult blades are sharp weapons that, notably, cannot be used by non-cultists. These blades are produced by the Talisman of Arming.

    " + + text += "Cult Bola
    Cult bolas are strong bolas, useful for snaring targets. These bolas are produced by the Talisman of Arming.

    " + + text += "Cult Robes
    Cult robes are heavily armored robes. These robes are produced by the Talisman of Arming.

    " + + text += "Soulstone
    A soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead human, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \ +
    The soul within can also be released as a Shade by using it in-hand.

    " + + text += "Construct Shell
    A construct shell is useless on its own, but placing a filled soulstone within it allows you to produce your choice of a Wraith, a Juggernaut, or an Artificer. \ +
    Each construct has uses, detailed below in Constructs. Construct shells can be produced via the starter talisman or the Rite of Fabrication.

    " + + text += "Constructs:

    " + + text += "Shade
    While technically not a construct, the Shade is produced when released from a soulstone. It is quite fragile and has weak melee attacks, but is fully healed when recaptured by a soulstone.

    " + + text += "Wraith
    The Wraith is a fast, lethal melee attacker which can jaunt through walls. However, it is only slightly more durable than a shade.

    " + + text += "Juggernaut
    The Juggernaut is a slow, but durable, melee attacker which can produce temporary forcewalls. It will also reflect most lethal energy weapons.

    " + + text += "Artificer
    The Artificer is a weak and fragile construct, able to heal other constructs, produce more soulstones and construct shells, \ + construct fortifying cult walls and flooring, and finally, it can release a few indiscriminate stunning missiles.

    " + + text += "Harvester
    If you see one, know that you have done all you can and your life is void.

    " + + var/text_string = jointext(text, null) + var/datum/browser/popup = new(user, "tome", "", 800, 600) + popup.set_content(text_string) + popup.open() + return 1 + +/obj/item/tome/proc/finale_runes_ok(mob/living/user, obj/effect/rune/rune_to_scribe) + var/datum/game_mode/cult/cult_mode = SSticker.mode + var/area/A = get_area(src) + if(GAMEMODE_IS_CULT) + if(!canbypass)//not an admin-tome, check things + if(!cult_mode.narsie_condition_cleared) + to_chat(user, "There is still more to do before unleashing [SSticker.cultdat.entity_name] power!") + return 0 + if(!cult_mode.eldergod) + to_chat(user, "\"I am already here. There is no need to try to summon me now.\"") + return 0 + if(cult_mode.demons_summoned) + to_chat(user, "\"We are already here. There is no need to try to summon us now.\"") + return 0 + if(!((CULT_ELDERGOD in cult_mode.objectives) || (CULT_SLAUGHTER in cult_mode.objectives))) + to_chat(user, "[SSticker.cultdat.entity_name]'s power does not wish to be unleashed!") + return 0 + if(!(A in GLOB.summon_spots)) + to_chat(user, "[SSticker.cultdat.entity_name] can only be summoned where the veil is weak - in [english_list(GLOB.summon_spots)]!") + return 0 + var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") + if(confirm_final == "No" || confirm_final == null) + to_chat(user, "You decide to prepare further before scribing the rune.") + return 0 + else + return 1 + else//the game mode is not cult..but we ARE a cultist...ALL ON THE ADMINBUS + var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") + if(confirm_final == "No" || confirm_final == null) + to_chat(user, "You decide to prepare further before scribing the rune.") + return 0 + else + return 1 + +/obj/item/tome/proc/scribe_rune(mob/living/user) + var/turf/runeturf = get_turf(user) + if(isspaceturf(runeturf)) + return + var/chosen_keyword + var/obj/effect/rune/rune_to_scribe + var/entered_rune_name + var/list/possible_runes = list() + var/list/shields = list() + var/area/A = get_area(src) + if(locate(/obj/effect/rune) in runeturf) + to_chat(user, "There is already a rune here.") + return + for(var/T in subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed) + var/obj/effect/rune/R = T + if(initial(R.cultist_name)) + possible_runes.Add(initial(R.cultist_name)) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this + if(!possible_runes.len) + return + entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes + if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) + return + for(var/T in typesof(/obj/effect/rune)) + var/obj/effect/rune/R = T + if(initial(R.cultist_name) == entered_rune_name) + rune_to_scribe = R + if(initial(R.req_keyword)) + var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "") + if(!the_keyword) + return + chosen_keyword = the_keyword + break + if(!rune_to_scribe) + return + runeturf = get_turf(user) //we may have moved. adjust as needed... + A = get_area(src) + if(locate(/obj/effect/rune) in runeturf) + to_chat(user, "There is already a rune here.") + return + if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) + return + if(ispath(rune_to_scribe, /obj/effect/rune/narsie) || ispath(rune_to_scribe, /obj/effect/rune/slaughter))//may need to change this - Fethas + if(finale_runes_ok(user,rune_to_scribe)) + A = get_area(src) + if(!(A in GLOB.summon_spots)) // Check again to make sure they didn't move + to_chat(user, "The ritual can only begin where the veil is weak - in [english_list(GLOB.summon_spots)]!") + return + GLOB.command_announcement.Announce("Figments from an eldritch god are being summoned somewhere on the station from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') + for(var/B in spiral_range_turfs(1, user, 1)) + var/turf/T = B + var/obj/machinery/shield/N = new(T) + N.name = "Rune-Scriber's Shield" + N.desc = "A potent shield summoned by cultists to protect them while they prepare the final ritual" + N.icon_state = "shield-cult" + N.health = 60 + shields |= N + else + return//don't do shit + + var/mob/living/carbon/human/H = user + var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") + var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone)) + user.visible_message("[user] cuts open [user.p_their()] [affecting] and begins writing in [user.p_their()] own blood!", "You slice open your [affecting] and begin drawing a sigil of [SSticker.cultdat.entity_title3].") + user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE , affecting) + if(!do_after(user, initial(rune_to_scribe.scribe_delay)-scribereduct, target = get_turf(user))) + for(var/V in shields) + var/obj/machinery/shield/S = V + if(S && !QDELETED(S)) + qdel(S) + return + if(locate(/obj/effect/rune) in runeturf) + to_chat(user, "There is already a rune here.") + return + user.visible_message("[user] creates a strange circle in [user.p_their()] own blood.", \ + "You finish drawing the arcane markings of [SSticker.cultdat.entity_title3].") + for(var/V in shields) + var/obj/machinery/shield/S = V + if(S && !QDELETED(S)) + qdel(S) + var/obj/effect/rune/R = new rune_to_scribe(runeturf, chosen_keyword) + R.blood_DNA = list() + R.blood_DNA[H.dna.unique_enzymes] = H.dna.blood_type + R.add_hiddenprint(H) + to_chat(user, "The [lowertext(initial(rune_to_scribe.cultist_name))] rune [initial(rune_to_scribe.cultist_desc)]") diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index d918bead0de..8c83c88f3d0 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -1,5 +1,5 @@ -var/list/sacrificed = list() -var/list/non_revealed_runes = (subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed) +GLOBAL_LIST_EMPTY(sacrificed) +GLOBAL_LIST_INIT(non_revealed_runes, (subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed)) /* This file contains runes. @@ -255,7 +255,7 @@ structure_check() searches for nearby cultist structures required for the invoca qdel(paper_to_imbue) rune_in_use = 0 -var/list/teleport_runes = list() +GLOBAL_LIST_EMPTY(teleport_runes) /obj/effect/rune/teleport cultist_name = "Teleport" cultist_desc = "warps everything above it to another chosen teleport rune." @@ -270,10 +270,10 @@ var/list/teleport_runes = list() var/area/A = get_area(src) var/locname = initial(A.name) listkey = set_keyword ? "[set_keyword] [locname]":"[locname]" - teleport_runes += src + GLOB.teleport_runes += src /obj/effect/rune/teleport/Destroy() - teleport_runes -= src + GLOB.teleport_runes -= src return ..() /obj/effect/rune/teleport/invoke(var/list/invokers) @@ -281,7 +281,7 @@ var/list/teleport_runes = list() var/list/potential_runes = list() var/list/teleportnames = list() var/list/duplicaterunecount = list() - for(var/R in teleport_runes) + for(var/R in GLOB.teleport_runes) var/obj/effect/rune/teleport/T = R var/resultkey = T.listkey if(resultkey in teleportnames) @@ -332,10 +332,12 @@ var/list/teleport_runes = list() user.forceMove(get_turf(actual_selected_rune)) var/mob/living/carbon/human/H = user if(user.z != T.z) - H.bleed(5) + if(istype(H)) + H.bleed(5) user.apply_damage(5, BRUTE) else - H.bleed(rand(5,10)) + if(istype(H)) + H.bleed(rand(5,10)) else fail_invoke() @@ -349,7 +351,7 @@ var/list/teleport_runes = list() req_cultists = 1 allow_excess_invokers = TRUE rune_in_use = FALSE - + /obj/effect/rune/convert/do_invoke_glow() return @@ -410,7 +412,7 @@ var/list/teleport_runes = list() var/sacrifice_fulfilled var/datum/game_mode/cult/cult_mode = SSticker.mode if(offering.mind) - sacrificed.Add(offering.mind) + GLOB.sacrificed.Add(offering.mind) if(is_sacrifice_target(offering.mind)) sacrifice_fulfilled = TRUE new /obj/effect/temp_visual/cult/sac(loc) @@ -720,7 +722,7 @@ var/list/teleport_runes = list() log_game("Astral Communion rune failed - more than one user") return list() var/turf/T = get_turf(src) - if(!user in T.contents) + if(!(user in T.contents)) to_chat(user, "You must be standing on top of [src]!") log_game("Astral Communion rune failed - user not standing on rune") return list() @@ -832,7 +834,8 @@ var/list/teleport_runes = list() fail_invoke() log_game("Summon Cultist rune failed - target in away mission") return - if((cultist_to_summon.reagents.has_reagent("holywater") || cultist_to_summon.restrained()) && invokers.len < 3) + var/hard_summon = (cultist_to_summon.reagents && cultist_to_summon.reagents.has_reagent("holywater")) || cultist_to_summon.restrained() + if(hard_summon && invokers.len < 3) to_chat(user, "The summoning of [cultist_to_summon] is being blocked somehow! You need 3 chanters to counter it!") fail_invoke() new /obj/effect/temp_visual/cult/sparks(get_turf(cultist_to_summon)) //observer warning @@ -840,7 +843,7 @@ var/list/teleport_runes = list() return ..() - if(cultist_to_summon.reagents.has_reagent("holywater") || cultist_to_summon.restrained()) + if(hard_summon) summontime = 20 if(do_after(user, summontime, target = loc)) diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index f7aaf2c1ebd..e7fb06c1b94 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -1,436 +1,436 @@ -/obj/item/paper/talisman - icon = 'icons/obj/paper.dmi' - icon_state = "paper_talisman" - var/cultist_name = "talisman" - var/cultist_desc = "A basic talisman. It serves no purpose." - var/invocation = "Naise meam!" - info = "


    " - var/uses = 1 - var/health_cost = 0 //The amount of health taken from the user when invoking the talisman - -/obj/item/paper/talisman/update_icon()//overriding this so the update_icon doesn't turn them into normal looking paper - SEND_SIGNAL(src, COMSIG_OBJ_UPDATE_ICON) - -/obj/item/paper/talisman/examine(mob/user) - . = ..() - if(iscultist(user) || user.stat == DEAD) - . += "Name: [cultist_name]" - . += "Effect: [cultist_desc]" - . += "Uses Remaining: [uses]" - else - . += "You see strange symbols on the paper. Are they supposed to mean something?" - -/obj/item/paper/talisman/attack_self(mob/living/user) - if(!iscultist(user)) - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - return - if(invoke(user)) - uses-- - if(uses <= 0) - user.drop_item() - qdel(src) - -/obj/item/paper/talisman/proc/invoke(mob/living/user, successfuluse = 1) - . = successfuluse - if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff - if(invocation) - user.whisper(invocation) - if(health_cost && iscarbon(user)) - var/mob/living/carbon/C = user - C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm")) - -//Malformed Talisman: If something goes wrong. -/obj/item/paper/talisman/malformed - cultist_name = "malformed talisman" - cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this." - invocation = "Ra'sha yoka!" - -/obj/item/paper/talisman/malformed/invoke(mob/living/user, successfuluse = 1) - to_chat(user, "You feel a pain in your head. [SSticker.cultdat.entity_title3] is displeased.") - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.apply_damage(10, BRUTE, "head") - -//Supply Talisman: Has a few unique effects. Granted only to starter cultists. -/obj/item/paper/talisman/supply - cultist_name = "Supply Talisman" - icon_state = "supply" - cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on." - invocation = null - uses = 3 - -/obj/item/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1) - var/dat = list() - dat += "There are [uses] bloody runes on the parchment.
    " - dat += "Please choose the chant to be imbued into the fabric of reality.
    " - dat += "
    " - dat += "N'ath reth sh'yro eth d'raggathnor! - Summons an arcane tome, used to scribe runes and communicate with other cultists.
    " - dat += "Bar'tea eas! - Provides 5 runed metal.
    " - dat += "Sas'so c'arta forbici! - Allows you to move to a selected teleportation rune.
    " - dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
    " - dat += "Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman.
    " - dat += "Kla'atu barada nikt'o! - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes.
    " - dat += "Kal'om neth! - Summons a soul stone, used to capture the spirits of dead or dying humans.
    " - dat += "Daa'ig osk! - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person.
    " - var/datum/browser/popup = new(user, "talisman", "", 400, 400) - popup.set_content(jointext(dat, "")) - popup.open() - return 0 - -/obj/item/paper/talisman/supply/Topic(href, href_list) - if(src) - if(usr.stat || usr.restrained() || !in_range(src, usr)) - return - if(href_list["rune"]) - switch(href_list["rune"]) - if("newtome") - var/obj/item/tome/T = new(usr) - usr.put_in_hands(T) - if("metal") - if(istype(src, /obj/item/paper/talisman/supply/weak)) - usr.visible_message("Lesser supply talismans lack the strength to materialize runed metal!") - return - var/obj/item/stack/sheet/runed_metal/R = new(usr,5) - usr.put_in_hands(R) - if("teleport") - var/obj/item/paper/talisman/teleport/T = new(usr) - usr.put_in_hands(T) - if("emp") - var/obj/item/paper/talisman/emp/T = new(usr) - usr.put_in_hands(T) - if("runestun") - var/obj/item/paper/talisman/stun/T = new(usr) - usr.put_in_hands(T) - if("soulstone") - var/obj/item/soulstone/T = new(usr) - usr.put_in_hands(T) - if("construct") - new /obj/structure/constructshell(get_turf(usr)) - if("veiling") - var/obj/item/paper/talisman/true_sight/T = new(usr) - usr.put_in_hands(T) - uses-- - if(uses <= 0) - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - C.drop_item() - visible_message("[src] crumbles to dust.") - qdel(src) - -/obj/item/paper/talisman/supply/weak - uses = 2 - -//Rite of Translocation: Same as rune -/obj/item/paper/talisman/teleport - cultist_name = "Talisman of Teleportation" - icon_state = "teleport" - cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword." - invocation = "Sas'so c'arta forbici!" - health_cost = 5 - -/obj/item/paper/talisman/teleport/invoke(mob/living/user, successfuluse = 1) - var/list/potential_runes = list() - var/list/teleportnames = list() - var/list/duplicaterunecount = list() - for(var/R in teleport_runes) - var/obj/effect/rune/teleport/T = R - var/resultkey = T.listkey - if(resultkey in teleportnames) - duplicaterunecount[resultkey]++ - resultkey = "[resultkey] ([duplicaterunecount[resultkey]])" - else - teleportnames.Add(resultkey) - duplicaterunecount[resultkey] = 1 - potential_runes[resultkey] = T - - if(!potential_runes.len) - to_chat(user, "There are no valid runes to teleport to!") - log_game("Teleport talisman failed - no other teleport runes") - return ..(user, 0) - - if(!is_level_reachable(user.z)) - to_chat(user, "You are not in the right dimension!") - log_game("Teleport talisman failed - user in away mission") - return ..(user, 0) - - var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked - var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to? - if(!src || QDELETED(src) || !user || user.l_hand != src && user.r_hand != src || user.incapacitated() || !actual_selected_rune) - return ..(user, 0) - - user.visible_message("Dust flows from [user]'s hand, and [user.p_they()] disappear[user.p_s()] in a flash of red light!", \ - "You speak the words of the talisman and find yourself somewhere else!") - user.forceMove(get_turf(actual_selected_rune)) - return ..() - - -/obj/item/paper/talisman/summon_tome - cultist_name = "Talisman of Tome Summoning" - icon_state = "tome" - cultist_desc = "A one-use talisman that will call an untranslated tome from the archives of a cult." - invocation = "N'ath reth sh'yro eth d'raggathnor!" - health_cost = 1 - -/obj/item/paper/talisman/summon_tome/invoke(mob/living/user, successfuluse = 1) - . = ..() - user.visible_message("[user]'s hand glows red for a moment.", \ - "You speak the words of the talisman!") - new /obj/item/tome(get_turf(user)) - user.visible_message("A tome appears at [user]'s feet!", \ - "An arcane tome materializes at your feet.") - -/obj/item/paper/talisman/true_sight - cultist_name = "Talisman of Veiling" - icon_state = "veil" - cultist_desc = "A multi-use talisman that hides nearby runes. On its second use, will reveal nearby runes." - invocation = "Kla'atu barada nikt'o!" - health_cost = 1 - uses = 2 - var/revealing = FALSE //if it reveals or not - -/obj/item/paper/talisman/true_sight/invoke(mob/living/user, successfuluse = 1) - . = ..() - if(!revealing) - user.visible_message("Thin grey dust falls from [user]'s hand!", \ - "You speak the words of the talisman, hiding nearby runes.") - invocation = "Nikt'o barada kla'atu!" - revealing = TRUE - for(var/obj/effect/rune/R in range(3,user)) - R.talismanhide() - else - user.visible_message("A flash of light shines from [user]'s hand!", \ - "You speak the words of the talisman, revealing nearby runes.") - for(var/obj/effect/rune/R in range(3,user)) - R.talismanreveal() - -//Rite of False Truths: Same as rune -/obj/item/paper/talisman/make_runes_fake - cultist_name = "Talisman of Disguising" - icon_state = "disguising" - cultist_desc = "A talisman that will make nearby runes appear fake." - invocation = "By'o nar'nar!" - -/obj/item/paper/talisman/make_runes_fake/invoke(mob/living/user, successfuluse = 1) - . = ..() - user.visible_message("Dust flows from [user]s hand.", \ - "You speak the words of the talisman, making nearby runes appear fake.") - for(var/obj/effect/rune/R in orange(6,user)) - R.talismanfake() - R.desc = "A rune drawn in crayon." - - -//Rite of Disruption: Weaker than rune -/obj/item/paper/talisman/emp - cultist_name = "Talisman of Electromagnetic Pulse" - icon_state = "emp" - cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse." - invocation = "Ta'gh fara'qha fel d'amar det!" - health_cost = 5 - -/obj/item/paper/talisman/emp/invoke(mob/living/user, successfuluse = 1) - . = ..() - user.visible_message("[user]'s hand flashes a bright blue!", \ - "You speak the words of the talisman, emitting an EMP blast.") - empulse(src, 4, 8, 1) - - -//Rite of Disorientation: Stuns and inhibit speech on a single target for quite some time -/obj/item/paper/talisman/stun - cultist_name = "Talisman of Stunning" - icon_state = "stunning" - cultist_desc = "A talisman that will stun and inhibit speech on a single target. To use, attack target directly." - invocation = "Dream sign:Evil sealing talisman!" - health_cost = 10 - -/obj/item/paper/talisman/stun/invoke(mob/living/user, successfuluse = 0) - if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff - return ..() - if(iscultist(user)) - to_chat(user, "To use this talisman, attack the target directly.") - else - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - return 0 - -/obj/item/paper/talisman/stun/attack(mob/living/target, mob/living/user, successfuluse = 1) - if(iscultist(user)) - invoke(user, 1) - user.visible_message("[user] holds up [src], which explodes in a flash of red light!", \ - "You stun [target] with the talisman!") - var/obj/item/nullrod/N = locate() in target - if(N) - target.visible_message("[target]'s holy weapon absorbs the talisman's light!", \ - "Your holy weapon absorbs the blinding light!") - else - add_attack_logs(user, target, "Stunned with a talisman") - target.Weaken(10) - target.Stun(10) - target.flash_eyes(1,1) - if(issilicon(target)) - var/mob/living/silicon/S = target - S.emp_act(1) - if(iscarbon(target)) - var/mob/living/carbon/C = target - C.AdjustSilence(5) - C.AdjustStuttering(15) - C.AdjustCultSlur(20) - C.Jitter(15) - user.drop_item() - qdel(src) - return - ..() - - -//Rite of Arming: Equips cultist armor on the user, where available -/obj/item/paper/talisman/armor - cultist_name = "Talisman of Arming" - icon_state = "arming" - cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to." - invocation = "N'ath reth sh'yro eth draggathnor!" - -/obj/item/paper/talisman/armor/invoke(mob/living/user, successfuluse = 1) - . = ..() - var/mob/living/carbon/human/H = user - user.visible_message("Otherworldly armor suddenly appears on [user]!", \ - "You speak the words of the talisman, arming yourself!") - - H.equip_to_slot_or_del(new /obj/item/clothing/suit/hooded/cultrobes/alt(user), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes) - H.put_in_hands(new /obj/item/melee/cultblade(user)) - H.put_in_hands(new /obj/item/restraints/legcuffs/bola/cult(user)) - -/obj/item/paper/talisman/armor/attack(mob/living/target, mob/living/user) - if(iscultist(user) && iscultist(target)) - user.drop_item() - qdel(src) - invoke(target) - return - ..() - - -//Talisman of Horrors: Breaks the mind of the victim with nightmarish hallucinations -/obj/item/paper/talisman/horror - cultist_name = "Talisman of Horrors" - icon_state = "horror" - cultist_desc = "A talisman that will break the mind of the victim with nightmarish hallucinations." - invocation = "Lo'Nab Na'Dm!" - -/obj/item/paper/talisman/horror/attack(mob/living/target, mob/living/user) - if(iscultist(user)) - to_chat(user, "You disturb [target] with visions of the end!") - if(iscarbon(target)) - var/mob/living/carbon/H = target - H.AdjustHallucinate(30) - qdel(src) - - -//Talisman of Fabrication: Creates a construct shell out of 25 metal sheets. -/obj/item/paper/talisman/construction - cultist_name = "Talisman of Construction" - icon_state = "construction" - cultist_desc = "Use this talisman on at least twenty-five metal sheets to create an empty construct shell or on plasteel to make runed metal" - invocation = "Ethra p'ni dedol!" - uses = 25 - -/obj/item/paper/talisman/construction/attack_self(mob/living/user) - if(iscultist(user)) - to_chat(user, "To use this talisman, place it upon a stack of metal sheets or plasteel sheets!.") - else - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - - -/obj/item/paper/talisman/construction/attack(obj/M,mob/living/user) - if(iscultist(user)) - to_chat(user, "This talisman will only work on a stack of metal sheets or plasteel sheets!!") - log_game("Construct talisman failed - not a valid target") - -/obj/item/paper/talisman/construction/afterattack(obj/item/stack/sheet/target, mob/user, proximity_flag, click_parameters) - ..() - if(proximity_flag && iscultist(user)) - if(istype(target, /obj/item/stack/sheet/metal)) - var/turf/T = get_turf(target) - if(target.use(25)) - new /obj/structure/constructshell(T) - to_chat(user, "The talisman clings to the metal and twists it into a construct shell!") - user << sound('sound/magic/staff_chaos.ogg',0,1,25) - qdel(src) - return - if(istype(target, /obj/item/stack/sheet/plasteel)) - var/turf/T = get_turf(target) - var/quantity = min(target.amount, uses) - uses -= quantity - new /obj/item/stack/sheet/runed_metal(T,quantity) - target.use(quantity) - to_chat(user, "The talisman clings to the plasteel, transforming it into runed metal!") - user << sound('sound/magic/staff_chaos.ogg',0,1,25) - invoke(user, 1) - if(uses <= 0) - qdel(src) - else - to_chat(user, "The talisman must be used on metal or plasteel!") - -//Talisman of Shackling: Applies special cuffs directly from the talisman -/obj/item/paper/talisman/shackle - cultist_name = "Talisman of Shackling" - icon_state = "shackling" - cultist_desc = "Use this talisman on a victim to handcuff them with dark bindings." - invocation = "In'totum Lig'abis!" - uses = 4 - -/obj/item/paper/talisman/shackle/invoke(mob/living/user, successfuluse = 0) - if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff - return ..() - if(iscultist(user)) - to_chat(user, "To use this talisman, attack the target directly.") - else - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - return 0 - -/obj/item/paper/talisman/shackle/attack(mob/living/carbon/target, mob/living/user) - if(iscultist(user) && istype(target)) - if(target.stat == DEAD) - user.visible_message("This talisman's magic does not affect the dead!") - return - CuffAttack(target, user) - return - ..() - -/obj/item/paper/talisman/shackle/proc/CuffAttack(mob/living/carbon/C, mob/living/user) - if(!C.handcuffed) - invoke(user, 1) - playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) - C.visible_message("[user] begins restraining [C] with dark magic!", \ - "[user] begins shaping a dark magic around your wrists!") - if(do_mob(user, C, 30)) - if(!C.handcuffed) - C.handcuffed = new /obj/item/restraints/handcuffs/energy/cult/used(C) - C.update_handcuffed() - to_chat(user, "You shackle [C].") - add_attack_logs(user, C, "Handcuffed (shackle talisman)") - uses-- - else - to_chat(user, "[C] is already bound.") - else - to_chat(user, "You fail to shackle [C].") - else - to_chat(user, "[C] is already bound.") - if(uses <= 0) - user.drop_item() - qdel(src) - return - -/obj/item/restraints/handcuffs/energy/cult //For the talisman of shackling - name = "cult shackles" - desc = "Shackles that bind the wrists with sinister magic." - trashtype = /obj/item/restraints/handcuffs/energy/used - origin_tech = "materials=2;magnets=5" - -/obj/item/restraints/handcuffs/energy/used - desc = "energy discharge" - flags = DROPDEL - -/obj/item/restraints/handcuffs/energy/cult/used/dropped(mob/user) - user.visible_message("[user]'s shackles shatter in a discharge of dark magic!", \ - "Your [src] shatters in a discharge of dark magic!") - qdel(src) - . = ..() +/obj/item/paper/talisman + icon = 'icons/obj/paper.dmi' + icon_state = "paper_talisman" + var/cultist_name = "talisman" + var/cultist_desc = "A basic talisman. It serves no purpose." + var/invocation = "Naise meam!" + info = "


    " + var/uses = 1 + var/health_cost = 0 //The amount of health taken from the user when invoking the talisman + +/obj/item/paper/talisman/update_icon()//overriding this so the update_icon doesn't turn them into normal looking paper + SEND_SIGNAL(src, COMSIG_OBJ_UPDATE_ICON) + +/obj/item/paper/talisman/examine(mob/user) + . = ..() + if(iscultist(user) || user.stat == DEAD) + . += "Name: [cultist_name]" + . += "Effect: [cultist_desc]" + . += "Uses Remaining: [uses]" + else + . += "You see strange symbols on the paper. Are they supposed to mean something?" + +/obj/item/paper/talisman/attack_self(mob/living/user) + if(!iscultist(user)) + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return + if(invoke(user)) + uses-- + if(uses <= 0) + user.drop_item() + qdel(src) + +/obj/item/paper/talisman/proc/invoke(mob/living/user, successfuluse = 1) + . = successfuluse + if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff + if(invocation) + user.whisper(invocation) + if(health_cost && iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm")) + +//Malformed Talisman: If something goes wrong. +/obj/item/paper/talisman/malformed + cultist_name = "malformed talisman" + cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this." + invocation = "Ra'sha yoka!" + +/obj/item/paper/talisman/malformed/invoke(mob/living/user, successfuluse = 1) + to_chat(user, "You feel a pain in your head. [SSticker.cultdat.entity_title3] is displeased.") + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(10, BRUTE, "head") + +//Supply Talisman: Has a few unique effects. Granted only to starter cultists. +/obj/item/paper/talisman/supply + cultist_name = "Supply Talisman" + icon_state = "supply" + cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on." + invocation = null + uses = 3 + +/obj/item/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1) + var/dat = list() + dat += "There are [uses] bloody runes on the parchment.
    " + dat += "Please choose the chant to be imbued into the fabric of reality.
    " + dat += "
    " + dat += "N'ath reth sh'yro eth d'raggathnor! - Summons an arcane tome, used to scribe runes and communicate with other cultists.
    " + dat += "Bar'tea eas! - Provides 5 runed metal.
    " + dat += "Sas'so c'arta forbici! - Allows you to move to a selected teleportation rune.
    " + dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
    " + dat += "Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman.
    " + dat += "Kla'atu barada nikt'o! - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes.
    " + dat += "Kal'om neth! - Summons a soul stone, used to capture the spirits of dead or dying humans.
    " + dat += "Daa'ig osk! - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person.
    " + var/datum/browser/popup = new(user, "talisman", "", 400, 400) + popup.set_content(jointext(dat, "")) + popup.open() + return 0 + +/obj/item/paper/talisman/supply/Topic(href, href_list) + if(src) + if(usr.stat || usr.restrained() || !in_range(src, usr)) + return + if(href_list["rune"]) + switch(href_list["rune"]) + if("newtome") + var/obj/item/tome/T = new(usr) + usr.put_in_hands(T) + if("metal") + if(istype(src, /obj/item/paper/talisman/supply/weak)) + usr.visible_message("Lesser supply talismans lack the strength to materialize runed metal!") + return + var/obj/item/stack/sheet/runed_metal/R = new(usr,5) + usr.put_in_hands(R) + if("teleport") + var/obj/item/paper/talisman/teleport/T = new(usr) + usr.put_in_hands(T) + if("emp") + var/obj/item/paper/talisman/emp/T = new(usr) + usr.put_in_hands(T) + if("runestun") + var/obj/item/paper/talisman/stun/T = new(usr) + usr.put_in_hands(T) + if("soulstone") + var/obj/item/soulstone/T = new(usr) + usr.put_in_hands(T) + if("construct") + new /obj/structure/constructshell(get_turf(usr)) + if("veiling") + var/obj/item/paper/talisman/true_sight/T = new(usr) + usr.put_in_hands(T) + uses-- + if(uses <= 0) + if(iscarbon(usr)) + var/mob/living/carbon/C = usr + C.drop_item() + visible_message("[src] crumbles to dust.") + qdel(src) + +/obj/item/paper/talisman/supply/weak + uses = 2 + +//Rite of Translocation: Same as rune +/obj/item/paper/talisman/teleport + cultist_name = "Talisman of Teleportation" + icon_state = "teleport" + cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword." + invocation = "Sas'so c'arta forbici!" + health_cost = 5 + +/obj/item/paper/talisman/teleport/invoke(mob/living/user, successfuluse = 1) + var/list/potential_runes = list() + var/list/teleportnames = list() + var/list/duplicaterunecount = list() + for(var/R in GLOB.teleport_runes) + var/obj/effect/rune/teleport/T = R + var/resultkey = T.listkey + if(resultkey in teleportnames) + duplicaterunecount[resultkey]++ + resultkey = "[resultkey] ([duplicaterunecount[resultkey]])" + else + teleportnames.Add(resultkey) + duplicaterunecount[resultkey] = 1 + potential_runes[resultkey] = T + + if(!potential_runes.len) + to_chat(user, "There are no valid runes to teleport to!") + log_game("Teleport talisman failed - no other teleport runes") + return ..(user, 0) + + if(!is_level_reachable(user.z)) + to_chat(user, "You are not in the right dimension!") + log_game("Teleport talisman failed - user in away mission") + return ..(user, 0) + + var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked + var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to? + if(!src || QDELETED(src) || !user || user.l_hand != src && user.r_hand != src || user.incapacitated() || !actual_selected_rune) + return ..(user, 0) + + user.visible_message("Dust flows from [user]'s hand, and [user.p_they()] disappear[user.p_s()] in a flash of red light!", \ + "You speak the words of the talisman and find yourself somewhere else!") + user.forceMove(get_turf(actual_selected_rune)) + return ..() + + +/obj/item/paper/talisman/summon_tome + cultist_name = "Talisman of Tome Summoning" + icon_state = "tome" + cultist_desc = "A one-use talisman that will call an untranslated tome from the archives of a cult." + invocation = "N'ath reth sh'yro eth d'raggathnor!" + health_cost = 1 + +/obj/item/paper/talisman/summon_tome/invoke(mob/living/user, successfuluse = 1) + . = ..() + user.visible_message("[user]'s hand glows red for a moment.", \ + "You speak the words of the talisman!") + new /obj/item/tome(get_turf(user)) + user.visible_message("A tome appears at [user]'s feet!", \ + "An arcane tome materializes at your feet.") + +/obj/item/paper/talisman/true_sight + cultist_name = "Talisman of Veiling" + icon_state = "veil" + cultist_desc = "A multi-use talisman that hides nearby runes. On its second use, will reveal nearby runes." + invocation = "Kla'atu barada nikt'o!" + health_cost = 1 + uses = 2 + var/revealing = FALSE //if it reveals or not + +/obj/item/paper/talisman/true_sight/invoke(mob/living/user, successfuluse = 1) + . = ..() + if(!revealing) + user.visible_message("Thin grey dust falls from [user]'s hand!", \ + "You speak the words of the talisman, hiding nearby runes.") + invocation = "Nikt'o barada kla'atu!" + revealing = TRUE + for(var/obj/effect/rune/R in range(3,user)) + R.talismanhide() + else + user.visible_message("A flash of light shines from [user]'s hand!", \ + "You speak the words of the talisman, revealing nearby runes.") + for(var/obj/effect/rune/R in range(3,user)) + R.talismanreveal() + +//Rite of False Truths: Same as rune +/obj/item/paper/talisman/make_runes_fake + cultist_name = "Talisman of Disguising" + icon_state = "disguising" + cultist_desc = "A talisman that will make nearby runes appear fake." + invocation = "By'o nar'nar!" + +/obj/item/paper/talisman/make_runes_fake/invoke(mob/living/user, successfuluse = 1) + . = ..() + user.visible_message("Dust flows from [user]s hand.", \ + "You speak the words of the talisman, making nearby runes appear fake.") + for(var/obj/effect/rune/R in orange(6,user)) + R.talismanfake() + R.desc = "A rune drawn in crayon." + + +//Rite of Disruption: Weaker than rune +/obj/item/paper/talisman/emp + cultist_name = "Talisman of Electromagnetic Pulse" + icon_state = "emp" + cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse." + invocation = "Ta'gh fara'qha fel d'amar det!" + health_cost = 5 + +/obj/item/paper/talisman/emp/invoke(mob/living/user, successfuluse = 1) + . = ..() + user.visible_message("[user]'s hand flashes a bright blue!", \ + "You speak the words of the talisman, emitting an EMP blast.") + empulse(src, 4, 8, 1) + + +//Rite of Disorientation: Stuns and inhibit speech on a single target for quite some time +/obj/item/paper/talisman/stun + cultist_name = "Talisman of Stunning" + icon_state = "stunning" + cultist_desc = "A talisman that will stun and inhibit speech on a single target. To use, attack target directly." + invocation = "Dream sign:Evil sealing talisman!" + health_cost = 10 + +/obj/item/paper/talisman/stun/invoke(mob/living/user, successfuluse = 0) + if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff + return ..() + if(iscultist(user)) + to_chat(user, "To use this talisman, attack the target directly.") + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return 0 + +/obj/item/paper/talisman/stun/attack(mob/living/target, mob/living/user, successfuluse = 1) + if(iscultist(user)) + invoke(user, 1) + user.visible_message("[user] holds up [src], which explodes in a flash of red light!", \ + "You stun [target] with the talisman!") + var/obj/item/nullrod/N = locate() in target + if(N) + target.visible_message("[target]'s holy weapon absorbs the talisman's light!", \ + "Your holy weapon absorbs the blinding light!") + else + add_attack_logs(user, target, "Stunned with a talisman") + target.Weaken(10) + target.Stun(10) + target.flash_eyes(1,1) + if(issilicon(target)) + var/mob/living/silicon/S = target + S.emp_act(1) + if(iscarbon(target)) + var/mob/living/carbon/C = target + C.AdjustSilence(5) + C.AdjustStuttering(15) + C.AdjustCultSlur(20) + C.Jitter(15) + user.drop_item() + qdel(src) + return + ..() + + +//Rite of Arming: Equips cultist armor on the user, where available +/obj/item/paper/talisman/armor + cultist_name = "Talisman of Arming" + icon_state = "arming" + cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to." + invocation = "N'ath reth sh'yro eth draggathnor!" + +/obj/item/paper/talisman/armor/invoke(mob/living/user, successfuluse = 1) + . = ..() + var/mob/living/carbon/human/H = user + user.visible_message("Otherworldly armor suddenly appears on [user]!", \ + "You speak the words of the talisman, arming yourself!") + + H.equip_to_slot_or_del(new /obj/item/clothing/suit/hooded/cultrobes/alt(user), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes) + H.put_in_hands(new /obj/item/melee/cultblade(user)) + H.put_in_hands(new /obj/item/restraints/legcuffs/bola/cult(user)) + +/obj/item/paper/talisman/armor/attack(mob/living/target, mob/living/user) + if(iscultist(user) && iscultist(target)) + user.drop_item() + qdel(src) + invoke(target) + return + ..() + + +//Talisman of Horrors: Breaks the mind of the victim with nightmarish hallucinations +/obj/item/paper/talisman/horror + cultist_name = "Talisman of Horrors" + icon_state = "horror" + cultist_desc = "A talisman that will break the mind of the victim with nightmarish hallucinations." + invocation = "Lo'Nab Na'Dm!" + +/obj/item/paper/talisman/horror/attack(mob/living/target, mob/living/user) + if(iscultist(user)) + to_chat(user, "You disturb [target] with visions of the end!") + if(iscarbon(target)) + var/mob/living/carbon/H = target + H.AdjustHallucinate(30) + qdel(src) + + +//Talisman of Fabrication: Creates a construct shell out of 25 metal sheets. +/obj/item/paper/talisman/construction + cultist_name = "Talisman of Construction" + icon_state = "construction" + cultist_desc = "Use this talisman on at least twenty-five metal sheets to create an empty construct shell or on plasteel to make runed metal" + invocation = "Ethra p'ni dedol!" + uses = 25 + +/obj/item/paper/talisman/construction/attack_self(mob/living/user) + if(iscultist(user)) + to_chat(user, "To use this talisman, place it upon a stack of metal sheets or plasteel sheets!.") + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + + +/obj/item/paper/talisman/construction/attack(obj/M,mob/living/user) + if(iscultist(user)) + to_chat(user, "This talisman will only work on a stack of metal sheets or plasteel sheets!!") + log_game("Construct talisman failed - not a valid target") + +/obj/item/paper/talisman/construction/afterattack(obj/item/stack/sheet/target, mob/user, proximity_flag, click_parameters) + ..() + if(proximity_flag && iscultist(user)) + if(istype(target, /obj/item/stack/sheet/metal)) + var/turf/T = get_turf(target) + if(target.use(25)) + new /obj/structure/constructshell(T) + to_chat(user, "The talisman clings to the metal and twists it into a construct shell!") + user << sound('sound/magic/staff_chaos.ogg',0,1,25) + qdel(src) + return + if(istype(target, /obj/item/stack/sheet/plasteel)) + var/turf/T = get_turf(target) + var/quantity = min(target.amount, uses) + uses -= quantity + new /obj/item/stack/sheet/runed_metal(T,quantity) + target.use(quantity) + to_chat(user, "The talisman clings to the plasteel, transforming it into runed metal!") + user << sound('sound/magic/staff_chaos.ogg',0,1,25) + invoke(user, 1) + if(uses <= 0) + qdel(src) + else + to_chat(user, "The talisman must be used on metal or plasteel!") + +//Talisman of Shackling: Applies special cuffs directly from the talisman +/obj/item/paper/talisman/shackle + cultist_name = "Talisman of Shackling" + icon_state = "shackling" + cultist_desc = "Use this talisman on a victim to handcuff them with dark bindings." + invocation = "In'totum Lig'abis!" + uses = 4 + +/obj/item/paper/talisman/shackle/invoke(mob/living/user, successfuluse = 0) + if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff + return ..() + if(iscultist(user)) + to_chat(user, "To use this talisman, attack the target directly.") + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return 0 + +/obj/item/paper/talisman/shackle/attack(mob/living/carbon/target, mob/living/user) + if(iscultist(user) && istype(target)) + if(target.stat == DEAD) + user.visible_message("This talisman's magic does not affect the dead!") + return + CuffAttack(target, user) + return + ..() + +/obj/item/paper/talisman/shackle/proc/CuffAttack(mob/living/carbon/C, mob/living/user) + if(!C.handcuffed) + invoke(user, 1) + playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) + C.visible_message("[user] begins restraining [C] with dark magic!", \ + "[user] begins shaping a dark magic around your wrists!") + if(do_mob(user, C, 30)) + if(!C.handcuffed) + C.handcuffed = new /obj/item/restraints/handcuffs/energy/cult/used(C) + C.update_handcuffed() + to_chat(user, "You shackle [C].") + add_attack_logs(user, C, "Handcuffed (shackle talisman)") + uses-- + else + to_chat(user, "[C] is already bound.") + else + to_chat(user, "You fail to shackle [C].") + else + to_chat(user, "[C] is already bound.") + if(uses <= 0) + user.drop_item() + qdel(src) + return + +/obj/item/restraints/handcuffs/energy/cult //For the talisman of shackling + name = "cult shackles" + desc = "Shackles that bind the wrists with sinister magic." + trashtype = /obj/item/restraints/handcuffs/energy/used + origin_tech = "materials=2;magnets=5" + +/obj/item/restraints/handcuffs/energy/used + desc = "energy discharge" + flags = DROPDEL + +/obj/item/restraints/handcuffs/energy/cult/used/dropped(mob/user) + user.visible_message("[user]'s shackles shatter in a discharge of dark magic!", \ + "Your [src] shatters in a discharge of dark magic!") + qdel(src) + . = ..() diff --git a/code/game/gamemodes/devil/devil.dm b/code/game/gamemodes/devil/devil.dm index ab5f5796d2e..06933891ba6 100644 --- a/code/game/gamemodes/devil/devil.dm +++ b/code/game/gamemodes/devil/devil.dm @@ -1,4 +1,4 @@ -var/global/list/whiteness = list ( +GLOBAL_LIST_INIT(whiteness, list( /obj/item/clothing/under/color/white = 2, /obj/item/clothing/under/rank/bartender = 1, /obj/item/clothing/under/rank/chef = 1, @@ -21,7 +21,7 @@ var/global/list/whiteness = list ( /obj/item/clothing/under/noble_clothes = 1, /obj/item/clothing/under/sl_suit = 1, /obj/item/clothing/under/burial = 1 -) +)) @@ -32,9 +32,9 @@ var/global/list/whiteness = list ( var/mob/living/carbon/human/H = attacker if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under)) var/obj/item/clothing/under/U = H.w_uniform - if(whiteness[U.type]) + if(GLOB.whiteness[U.type]) src.visible_message("[src] seems to have been harmed by the purity of [attacker]'s clothes.", "Unsullied white clothing is disrupting your form.") - return whiteness[U.type] + 1 + return GLOB.whiteness[U.type] + 1 if(BANE_TOOLBOX) if(istype(weapon,/obj/item/storage/toolbox)) src.visible_message("The [weapon] seems unusually robust this time.", "The [weapon] is your unmaking!") diff --git a/code/game/gamemodes/devil/devilinfo.dm b/code/game/gamemodes/devil/devilinfo.dm index 89d997831a3..ecbeb54f7f7 100644 --- a/code/game/gamemodes/devil/devilinfo.dm +++ b/code/game/gamemodes/devil/devilinfo.dm @@ -13,8 +13,8 @@ #define DEVILRESURRECTTIME 600 -var/global/list/allDevils = list() -var/global/list/lawlorify = list ( +GLOBAL_LIST_EMPTY(allDevils) +GLOBAL_LIST_INIT(lawlorify, list ( LORE = list( OBLIGATION_FOOD = "This devil seems to always offer it's victims food before slaughtering them.", OBLIGATION_FIDDLE = "This devil will never turn down a musical challenge.", @@ -77,7 +77,7 @@ var/global/list/lawlorify = list ( BANISH_DESTRUCTION = "If your corpse is destroyed, you will be unable to resurrect.", BANISH_FUNERAL_GARB = "If your corpse is clad in funeral garments, you will be unable to resurrect." ) - ) + )) /datum/devilinfo var/datum/mind/owner = null @@ -111,11 +111,11 @@ var/global/list/lawlorify = list ( return devil /proc/devilInfo(name, saveDetails = 0) - if(allDevils[lowertext(name)]) - return allDevils[lowertext(name)] + if(GLOB.allDevils[lowertext(name)]) + return GLOB.allDevils[lowertext(name)] else var/datum/devilinfo/devil = randomDevilInfo(name) - allDevils[lowertext(name)] = devil + GLOB.allDevils[lowertext(name)] = devil devil.exists = saveDetails return devil @@ -444,9 +444,9 @@ var/global/list/lawlorify = list ( D.oldform.revive() // Heal the old body too, so the devil doesn't resurrect, then immediately regress into a dead body. if(body.stat == DEAD) // Not sure why this would happen create_new_body() - else if(blobstart.len > 0) + else if(GLOB.blobstart.len > 0) // teleport the body so repeated beatdowns aren't an option) - body.forceMove(get_turf(pick(blobstart))) + body.forceMove(get_turf(pick(GLOB.blobstart))) // give them the devil lawyer outfit in case they got stripped if(ishuman(body)) var/mob/living/carbon/human/H = body @@ -456,8 +456,8 @@ var/global/list/lawlorify = list ( check_regression() /datum/devilinfo/proc/create_new_body() - if(blobstart.len > 0) - var/turf/targetturf = get_turf(pick(blobstart)) + if(GLOB.blobstart.len > 0) + var/turf/targetturf = get_turf(pick(GLOB.blobstart)) var/mob/currentMob = owner.current if(QDELETED(currentMob)) currentMob = owner.get_ghost() @@ -511,10 +511,10 @@ var/global/list/lawlorify = list ( to_chat(owner, "However, your infernal form is not without weaknesses.") to_chat(owner, "You may not use violence to coerce someone into selling their soul.") to_chat(owner, "You may not directly and knowingly physically harm a devil, other than yourself.") - to_chat(owner,lawlorify[LAW][bane]) - to_chat(owner,lawlorify[LAW][ban]) - to_chat(owner,lawlorify[LAW][obligation]) - to_chat(owner,lawlorify[LAW][banish]) + to_chat(owner,GLOB.lawlorify[LAW][bane]) + to_chat(owner,GLOB.lawlorify[LAW][ban]) + to_chat(owner,GLOB.lawlorify[LAW][obligation]) + to_chat(owner,GLOB.lawlorify[LAW][banish]) to_chat(owner, "

    Remember, the crew can research your weaknesses if they find out your devil name.
    ") @@ -555,5 +555,5 @@ var/global/list/lawlorify = list ( W.rank = W.assignment W.age = H.age W.sex = capitalize(H.gender) - W.access = list(access_maint_tunnels, access_syndicate, access_external_airlocks) + W.access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_EXTERNAL_AIRLOCKS) W.photo = get_id_photo(H) diff --git a/code/game/gamemodes/devil/game_mode.dm b/code/game/gamemodes/devil/game_mode.dm index f8b980f2dfd..74ff27468cc 100644 --- a/code/game/gamemodes/devil/game_mode.dm +++ b/code/game/gamemodes/devil/game_mode.dm @@ -17,7 +17,7 @@ to_chat(world,text) /datum/game_mode/proc/auto_declare_completion_devils() - /var/text = "" + var/text = "" if(devils.len) text += "
    The devils were:" for(var/D in devils) @@ -34,7 +34,7 @@ var/trueName= randomDevilName() devil_mind.devilinfo = devilInfo(trueName, 1) devil_mind.devilinfo.ascendable = ascendable - devil_mind.store_memory("Your diabolical true name is [devil_mind.devilinfo.truename]
    [lawlorify[LAW][devil_mind.devilinfo.ban]]
    You may not use violence to coerce someone into selling their soul.
    You may not directly and knowingly physically harm a devil, other than yourself.
    [lawlorify[LAW][devil_mind.devilinfo.bane]]
    [lawlorify[LAW][devil_mind.devilinfo.obligation]]
    [lawlorify[LAW][devil_mind.devilinfo.banish]]
    ") + devil_mind.store_memory("Your diabolical true name is [devil_mind.devilinfo.truename]
    [GLOB.lawlorify[LAW][devil_mind.devilinfo.ban]]
    You may not use violence to coerce someone into selling their soul.
    You may not directly and knowingly physically harm a devil, other than yourself.
    [GLOB.lawlorify[LAW][devil_mind.devilinfo.bane]]
    [GLOB.lawlorify[LAW][devil_mind.devilinfo.obligation]]
    [GLOB.lawlorify[LAW][devil_mind.devilinfo.banish]]
    ") devil_mind.devilinfo.link_with_mob(devil_mind.current) if(devil_mind.assigned_role == "Clown") to_chat(devil_mind.current, "Your infernal nature allows you to wield weapons without harming yourself.") @@ -47,8 +47,8 @@ var/mob/living/silicon/S = devil_mind.current S.laws.set_sixsixsix_law("You may not use violence to coerce someone into selling their soul.") S.laws.set_sixsixsix_law("You may not directly and knowingly physically harm a devil, other than yourself.") - S.laws.set_sixsixsix_law("[lawlorify[LAW][devil_mind.devilinfo.ban]]") - S.laws.set_sixsixsix_law("[lawlorify[LAW][devil_mind.devilinfo.obligation]]") + S.laws.set_sixsixsix_law("[GLOB.lawlorify[LAW][devil_mind.devilinfo.ban]]") + S.laws.set_sixsixsix_law("[GLOB.lawlorify[LAW][devil_mind.devilinfo.obligation]]") S.laws.set_sixsixsix_law("Accomplish your objectives at all costs.") // unsure about the second "quantity" arg and how it fits with the antag refactor @@ -76,18 +76,18 @@ return "Target is not a devil." var/text = "
    The devil's true name is: [ply.devilinfo.truename]
    " text += "The devil's bans were:
    " - text += " [lawlorify[LORE][ply.devilinfo.ban]]
    " - text += " [lawlorify[LORE][ply.devilinfo.bane]]
    " - text += " [lawlorify[LORE][ply.devilinfo.obligation]]
    " - text += " [lawlorify[LORE][ply.devilinfo.banish]]

    " + text += " [GLOB.lawlorify[LORE][ply.devilinfo.ban]]
    " + text += " [GLOB.lawlorify[LORE][ply.devilinfo.bane]]
    " + text += " [GLOB.lawlorify[LORE][ply.devilinfo.obligation]]
    " + text += " [GLOB.lawlorify[LORE][ply.devilinfo.banish]]

    " return text /datum/game_mode/proc/update_devil_icons_added(datum/mind/devil_mind) - var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_DEVIL] + var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_DEVIL] hud.join_hud(devil_mind.current) set_antag_hud(devil_mind.current, "huddevil") /datum/game_mode/proc/update_devil_icons_removed(datum/mind/devil_mind) - var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_DEVIL] + var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_DEVIL] hud.leave_hud(devil_mind.current) set_antag_hud(devil_mind.current, null) diff --git a/code/game/gamemodes/extended/extended.dm b/code/game/gamemodes/extended/extended.dm index a5dcef8d4e7..52edccc9e73 100644 --- a/code/game/gamemodes/extended/extended.dm +++ b/code/game/gamemodes/extended/extended.dm @@ -1,14 +1,14 @@ -/datum/game_mode/extended - name = "extended" - config_tag = "extended" - required_players = 0 - -/datum/game_mode/announce() - to_chat(world, "The current game mode is - Extended Role-Playing!") - to_chat(world, "Just have fun and role-play!") - -/datum/game_mode/extended/pre_setup() - return 1 - -/datum/game_mode/extended/post_setup() - ..() \ No newline at end of file +/datum/game_mode/extended + name = "extended" + config_tag = "extended" + required_players = 0 + +/datum/game_mode/announce() + to_chat(world, "The current game mode is - Extended Role-Playing!") + to_chat(world, "Just have fun and role-play!") + +/datum/game_mode/extended/pre_setup() + return 1 + +/datum/game_mode/extended/post_setup() + ..() diff --git a/code/game/gamemodes/factions.dm b/code/game/gamemodes/factions.dm index 1780881a51d..4adf5dc0367 100644 --- a/code/game/gamemodes/factions.dm +++ b/code/game/gamemodes/factions.dm @@ -24,7 +24,7 @@ var/uplink_contents // the contents of the uplink /datum/faction/syndicate/proc/assign_objectives(var/datum/mind/traitor) - ..() + return /* ----- Begin defining syndicate factions ------ */ diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index e7fb9b4969f..2b604839c62 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -1,522 +1,522 @@ -/* - * GAMEMODES (by Rastaf0) - * - * In the new mode system all special roles are fully supported. - * You can have proper wizards/traitors/changelings/cultists during any mode. - * Only two things really depends on gamemode: - * 1. Starting roles, equipment and preparations - * 2. Conditions of finishing the round. - * - */ - - -/datum/game_mode - var/name = "invalid" - var/config_tag = null - var/intercept_hacked = 0 - var/votable = 1 - var/probability = 0 - var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm - var/explosion_in_progress = 0 //sit back and relax - var/list/datum/mind/modePlayer = new - var/list/restricted_jobs = list() // Jobs it doesn't make sense to be. I.E chaplain or AI cultist - var/list/protected_jobs = list() // Jobs that can't be traitors - var/list/protected_species = list() // Species that can't be traitors - var/required_players = 0 - var/required_enemies = 0 - var/recommended_enemies = 0 - var/newscaster_announcements = null - var/ert_disabled = 0 - var/uplink_welcome = "Syndicate Uplink Console:" - var/uplink_uses = 20 - - var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) - var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) - var/list/player_draft_log = list() - var/list/datum/mind/xenos = list() - var/list/datum/mind/eventmiscs = list() - - var/list/datum/station_goal/station_goals = list() // A list of all station goals for this game mode - -/datum/game_mode/proc/announce() //to be calles when round starts - to_chat(world, "Notice: [src] did not define announce()") - - -///can_start() -///Checks to see if the game can be setup and ran with the current number of players or whatnot. -/datum/game_mode/proc/can_start() - var/playerC = 0 - for(var/mob/new_player/player in GLOB.player_list) - if((player.client)&&(player.ready)) - playerC++ - - if(!config.enable_gamemode_player_limit || (playerC >= required_players)) - return 1 - return 0 - -//pre_pre_setup() For when you really don't want certain jobs ingame. -/datum/game_mode/proc/pre_pre_setup() - - return 1 - -///pre_setup() -///Attempts to select players for special roles the mode might have. -/datum/game_mode/proc/pre_setup() - - return 1 - - -///post_setup() -///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things -/datum/game_mode/proc/post_setup() - - spawn (ROUNDSTART_LOGOUT_REPORT_TIME) - display_roundstart_logout_report() - - feedback_set_details("round_start","[time2text(world.realtime)]") - if(SSticker && SSticker.mode) - feedback_set_details("game_mode","[SSticker.mode]") -// if(revdata) -// feedback_set_details("revision","[revdata.revision]") - feedback_set_details("server_ip","[world.internet_address]:[world.port]") - generate_station_goals() - start_state = new /datum/station_state() - start_state.count() - return 1 - -///process() -///Called by the gameticker -/datum/game_mode/process() - return 0 - -//Called by the gameticker -/datum/game_mode/proc/process_job_tasks() - var/obj/machinery/message_server/useMS = null - if(message_servers) - for(var/obj/machinery/message_server/MS in message_servers) - if(MS.active) - useMS = MS - break - for(var/mob/M in GLOB.player_list) - if(M.mind) - var/obj/item/pda/P=null - for(var/obj/item/pda/check_pda in PDAs) - if(check_pda.owner==M.name) - P=check_pda - break - var/count=0 - for(var/datum/job_objective/objective in M.mind.job_objectives) - count++ - var/msg="" - var/pay=0 - if(objective.per_unit && objective.units_compensated0) - if(M.mind.initial_account) - M.mind.initial_account.credit(pay, "Payment", "\[CLASSIFIED\] Terminal #[rand(111,333)]", "[command_name()] Payroll") - msg += "You have been sent the $[pay], as agreed." - else - msg += "However, we were unable to send you the $[pay] you're entitled." - if(useMS && P) - useMS.send_pda_message("[P.owner]", "[command_name()] Payroll", msg) - - var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) - PM.notify("Message from [command_name()] (Payroll), \"[msg]\" (Unable to Reply)", 0) - break - -/datum/game_mode/proc/check_finished() //to be called by ticker - if((SSshuttle.emergency && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) || station_was_nuked) - return 1 - return 0 - -/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. - return - -/datum/game_mode/proc/declare_completion() - var/clients = 0 - var/surviving_humans = 0 - var/surviving_total = 0 - var/ghosts = 0 - var/escaped_humans = 0 - var/escaped_total = 0 - var/escaped_on_pod_1 = 0 - var/escaped_on_pod_2 = 0 - var/escaped_on_pod_3 = 0 - var/escaped_on_pod_5 = 0 - var/escaped_on_shuttle = 0 - - var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) - - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) //shuttle didn't get to centcom - escape_locations -= /area/shuttle/escape - - for(var/mob/M in GLOB.player_list) - if(M.client) - clients++ - if(ishuman(M)) - if(!M.stat) - surviving_humans++ - if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) - escaped_humans++ - if(!M.stat) - surviving_total++ - if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) - escaped_total++ - - if(M.loc && M.loc.loc && M.loc.loc.type == SSshuttle.emergency.areaInstance.type && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) - escaped_on_shuttle++ - - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom) - escaped_on_pod_1++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom) - escaped_on_pod_2++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom) - escaped_on_pod_3++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod5/centcom) - escaped_on_pod_5++ - - if(isobserver(M)) - ghosts++ - - if(clients > 0) - feedback_set("round_end_clients",clients) - if(ghosts > 0) - feedback_set("round_end_ghosts",ghosts) - if(surviving_humans > 0) - feedback_set("survived_human",surviving_humans) - if(surviving_total > 0) - feedback_set("survived_total",surviving_total) - if(escaped_humans > 0) - feedback_set("escaped_human",escaped_humans) - if(escaped_total > 0) - feedback_set("escaped_total",escaped_total) - if(escaped_on_shuttle > 0) - feedback_set("escaped_on_shuttle",escaped_on_shuttle) - if(escaped_on_pod_1 > 0) - feedback_set("escaped_on_pod_1",escaped_on_pod_1) - if(escaped_on_pod_2 > 0) - feedback_set("escaped_on_pod_2",escaped_on_pod_2) - if(escaped_on_pod_3 > 0) - feedback_set("escaped_on_pod_3",escaped_on_pod_3) - if(escaped_on_pod_5 > 0) - feedback_set("escaped_on_pod_5",escaped_on_pod_5) - - send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") - return 0 - - -/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. - return 0 - -/datum/game_mode/proc/get_players_for_role(var/role, override_jobbans=0) - var/list/players = list() - var/list/candidates = list() - //var/list/drafted = list() - //var/datum/mind/applicant = null - - var/roletext = get_roletext(role) - - // Assemble a list of active players without jobbans. - for(var/mob/new_player/player in GLOB.player_list) - if(player.client && player.ready && player.has_valid_preferences()) - if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, roletext)) - if(player_old_enough_antag(player.client,role)) - players += player - - // Shuffle the players list so that it becomes ping-independent. - players = shuffle(players) - - // Get a list of all the people who want to be the antagonist for this round, except those with incompatible species - for(var/mob/new_player/player in players) - if(!player.client.skip_antag) - if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species)) - player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them." - candidates += player.mind - players -= player - - // If we don't have enough antags, draft people who voted for the round. - if(candidates.len < recommended_enemies) - for(var/key in SSvote.round_voters) - for(var/mob/new_player/player in players) - if(player.ckey == key) - player_draft_log += "[player.key] voted for this round, so we are drafting them." - candidates += player.mind - players -= player - break - - // Remove candidates who want to be antagonist but have a job that precludes it - if(restricted_jobs) - for(var/datum/mind/player in candidates) - for(var/job in restricted_jobs) - if(player.assigned_role == job) - candidates -= player - - - return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies - // recommended_enemies if the number of people with that role set to yes is less than recomended_enemies, - // Less if there are not enough valid players in the game entirely to make recommended_enemies. - - -/datum/game_mode/proc/latespawn(var/mob) - -/* -/datum/game_mode/proc/check_player_role_pref(var/role, var/mob/player) - if(player.preferences.be_special & role) - return 1 - return 0 -*/ - -/datum/game_mode/proc/num_players() - . = 0 - for(var/mob/new_player/P in GLOB.player_list) - if(P.client && P.ready) - .++ - -/datum/game_mode/proc/num_players_started() - . = 0 - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.client) - .++ - -/////////////////////////////////// -//Keeps track of all living heads// -/////////////////////////////////// -/datum/game_mode/proc/get_living_heads() - . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) - var/list/real_command_positions = command_positions.Copy() - "Nanotrasen Representative" - if(player.stat != DEAD && player.mind && (player.mind.assigned_role in real_command_positions)) - . |= player.mind - - -//////////////////////////// -//Keeps track of all heads// -//////////////////////////// -/datum/game_mode/proc/get_all_heads() - . = list() - for(var/mob/player in GLOB.mob_list) - var/list/real_command_positions = command_positions.Copy() - "Nanotrasen Representative" - if(player.mind && (player.mind.assigned_role in real_command_positions)) - . |= player.mind - -////////////////////////////////////////////// -//Keeps track of all living security members// -////////////////////////////////////////////// -/datum/game_mode/proc/get_living_sec() - . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) - if(player.stat != DEAD && player.mind && (player.mind.assigned_role in security_positions)) - . |= player.mind - -//////////////////////////////////////// -//Keeps track of all security members// -//////////////////////////////////////// -/datum/game_mode/proc/get_all_sec() - . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) - if(player.mind && (player.mind.assigned_role in security_positions)) - . |= player.mind - -/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) - return 0 - -/datum/game_mode/New() - newscaster_announcements = pick(newscaster_standard_feeds) - -////////////////////////// -//Reports player logouts// -////////////////////////// -proc/display_roundstart_logout_report() - var/msg = "Roundstart logout report\n\n" - for(var/mob/living/L in GLOB.mob_list) - - if(L.ckey) - var/found = 0 - for(var/client/C in GLOB.clients) - if(C.ckey == L.ckey) - found = 1 - break - if(!found) - msg += "[L.name] ([L.ckey]), the [L.job] (Disconnected)\n" - - - if(L.ckey && L.client) - if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) - msg += "[L.name] ([L.ckey]), the [L.job] (Connected, Inactive)\n" - continue //AFK client - if(L.stat) - if(L.suiciding) //Suicider - msg += "[L.name] ([L.ckey]), the [L.job] (Suicide)\n" - SSjobs.FreeRole(L.job) - message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Suicide)\n") - continue //Disconnected client - if(L.stat == UNCONSCIOUS) - msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" - continue //Unconscious - if(L.stat == DEAD) - msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" - continue //Dead - - continue //Happy connected client - for(var/mob/dead/observer/D in GLOB.mob_list) - if(D.mind && (D.mind.original == L || D.mind.current == L)) - if(L.stat == DEAD) - if(L.suiciding) //Suicider - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Suicide)\n" - continue //Disconnected client - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" - continue //Dead mob, ghost abandoned - else - if(D.can_reenter_corpse) - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (This shouldn't appear.)\n" - continue //Lolwhat - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Ghosted)\n" - SSjobs.FreeRole(L.job) - message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Ghosted While Alive)\n") - continue //Ghosted while alive - - - - for(var/mob/M in GLOB.mob_list) - if(check_rights(R_ADMIN, 0, M)) - to_chat(M, msg) - -//Announces objectives/generic antag text. -/proc/show_generic_antag_text(var/datum/mind/player) - if(player.current) - to_chat(player.current, "You are an antagonist! Within the rules, \ - try to act as an opposing force to the crew. Further RP and try to make sure \ - other players have fun! If you are confused or at a loss, always adminhelp, \ - and before taking extreme actions, please try to also contact the administration! \ - Think through your actions and make the roleplay immersive! Please remember all \ - rules aside from those without explicit exceptions apply to antagonists.") - -/proc/show_objectives(var/datum/mind/player) - if(!player || !player.current) return - - var/obj_count = 1 - to_chat(player.current, "Your current objectives:") - for(var/datum/objective/objective in player.objectives) - to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - -/proc/get_roletext(var/role) - return role - -/proc/get_nuke_code() - var/nukecode = "ERROR" - for(var/obj/machinery/nuclearbomb/bomb in world) - if(bomb && bomb.r_code && is_station_level(bomb.z)) - nukecode = bomb.r_code - return nukecode - -/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type) - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100) - var/mob/dead/observer/theghost = null - if(candidates.len) - theghost = pick(candidates) - to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") - message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.") - M.ghostize() - M.key = theghost.key - else - message_admins("[M] ([M.key] has been converted into [role_type] with an active antagonist jobban for said role since no ghost has volunteered to take [M.p_their()] place.") - to_chat(M, "You have been converted into [role_type] with an active jobban. Any further violations of the rules on your part are likely to result in a permanent ban.") - -/proc/printplayer(datum/mind/ply, fleecheck) - var/jobtext = "" - if(ply.assigned_role) - jobtext = " the [ply.assigned_role]" - var/text = "[ply.key] was [ply.name][jobtext] and" - if(ply.current) - if(ply.current.stat == DEAD) - text += " died" - else - text += " survived" - if(fleecheck) - var/turf/T = get_turf(ply.current) - if(!T || !is_station_level(T.z)) - text += " while fleeing the station" - if(ply.current.real_name != ply.name) - text += " as [ply.current.real_name]" - else - text += " had [ply.p_their()] body destroyed" - return text - -/proc/printeventplayer(datum/mind/ply) - var/text = "[ply.key] was [ply.name]" - if(ply.special_role != SPECIAL_ROLE_EVENTMISC) - text += " the [ply.special_role]" - text += " and" - if(ply.current) - if(ply.current.stat == DEAD) - text += " died" - else - text += " survived" - else - text += " had [ply.p_their()] body destroyed" - return text - -/proc/printobjectives(datum/mind/ply) - var/list/objective_parts = list() - var/count = 1 - for(var/datum/objective/objective in ply.objectives) - if(objective.check_completion()) - objective_parts += "Objective #[count]: [objective.explanation_text] Success!" - else - objective_parts += "Objective #[count]: [objective.explanation_text] Fail." - count++ - return objective_parts.Join("
    ") - -/datum/game_mode/proc/generate_station_goals() - var/list/possible = list() - for(var/T in subtypesof(/datum/station_goal)) - var/datum/station_goal/G = T - if(config_tag in initial(G.gamemode_blacklist)) - continue - possible += T - var/goal_weights = 0 - while(possible.len && goal_weights < STATION_GOAL_BUDGET) - var/datum/station_goal/picked = pick_n_take(possible) - goal_weights += initial(picked.weight) - station_goals += new picked - - if(station_goals.len) - send_station_goals_message() - -/datum/game_mode/proc/send_station_goals_message() - var/message_text = "
    " - message_text += "

    [command_name()] Orders


    " - message_text += "Special Orders for [station_name()]:

    " - - for(var/datum/station_goal/G in station_goals) - G.on_report() - message_text += G.get_report() - message_text += "
    " - - print_command_report(message_text, "[command_name()] Orders") - -/datum/game_mode/proc/declare_station_goal_completion() - for(var/V in station_goals) - var/datum/station_goal/G = V - G.print_result() - -/datum/game_mode/proc/update_eventmisc_icons_added(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_EVENTMISC] - antaghud.join_hud(mob_mind.current) - set_antag_hud(mob_mind.current, "hudevent") - -/datum/game_mode/proc/update_eventmisc_icons_removed(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_EVENTMISC] - antaghud.leave_hud(mob_mind.current) - set_antag_hud(mob_mind.current, null) \ No newline at end of file +/* + * GAMEMODES (by Rastaf0) + * + * In the new mode system all special roles are fully supported. + * You can have proper wizards/traitors/changelings/cultists during any mode. + * Only two things really depends on gamemode: + * 1. Starting roles, equipment and preparations + * 2. Conditions of finishing the round. + * + */ + + +/datum/game_mode + var/name = "invalid" + var/config_tag = null + var/intercept_hacked = 0 + var/votable = 1 + var/probability = 0 + var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm + var/explosion_in_progress = 0 //sit back and relax + var/list/datum/mind/modePlayer = new + var/list/restricted_jobs = list() // Jobs it doesn't make sense to be. I.E chaplain or AI cultist + var/list/protected_jobs = list() // Jobs that can't be traitors + var/list/protected_species = list() // Species that can't be traitors + var/required_players = 0 + var/required_enemies = 0 + var/recommended_enemies = 0 + var/newscaster_announcements = null + var/ert_disabled = 0 + var/uplink_welcome = "Syndicate Uplink Console:" + var/uplink_uses = 20 + + var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) + var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) + var/list/player_draft_log = list() + var/list/datum/mind/xenos = list() + var/list/datum/mind/eventmiscs = list() + + var/list/datum/station_goal/station_goals = list() // A list of all station goals for this game mode + +/datum/game_mode/proc/announce() //to be calles when round starts + to_chat(world, "Notice: [src] did not define announce()") + + +///can_start() +///Checks to see if the game can be setup and ran with the current number of players or whatnot. +/datum/game_mode/proc/can_start() + var/playerC = 0 + for(var/mob/new_player/player in GLOB.player_list) + if((player.client)&&(player.ready)) + playerC++ + + if(!config.enable_gamemode_player_limit || (playerC >= required_players)) + return 1 + return 0 + +//pre_pre_setup() For when you really don't want certain jobs ingame. +/datum/game_mode/proc/pre_pre_setup() + + return 1 + +///pre_setup() +///Attempts to select players for special roles the mode might have. +/datum/game_mode/proc/pre_setup() + + return 1 + + +///post_setup() +///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things +/datum/game_mode/proc/post_setup() + + spawn (ROUNDSTART_LOGOUT_REPORT_TIME) + display_roundstart_logout_report() + + feedback_set_details("round_start","[time2text(world.realtime)]") + if(SSticker && SSticker.mode) + feedback_set_details("game_mode","[SSticker.mode]") +// if(revdata) +// feedback_set_details("revision","[revdata.revision]") + feedback_set_details("server_ip","[world.internet_address]:[world.port]") + generate_station_goals() + GLOB.start_state = new /datum/station_state() + GLOB.start_state.count() + return 1 + +///process() +///Called by the gameticker +/datum/game_mode/process() + return 0 + +//Called by the gameticker +/datum/game_mode/proc/process_job_tasks() + var/obj/machinery/message_server/useMS = null + if(GLOB.message_servers) + for(var/obj/machinery/message_server/MS in GLOB.message_servers) + if(MS.active) + useMS = MS + break + for(var/mob/M in GLOB.player_list) + if(M.mind) + var/obj/item/pda/P=null + for(var/obj/item/pda/check_pda in GLOB.PDAs) + if(check_pda.owner==M.name) + P=check_pda + break + var/count=0 + for(var/datum/job_objective/objective in M.mind.job_objectives) + count++ + var/msg="" + var/pay=0 + if(objective.per_unit && objective.units_compensated0) + if(M.mind.initial_account) + M.mind.initial_account.credit(pay, "Payment", "\[CLASSIFIED\] Terminal #[rand(111,333)]", "[command_name()] Payroll") + msg += "You have been sent the $[pay], as agreed." + else + msg += "However, we were unable to send you the $[pay] you're entitled." + if(useMS && P) + useMS.send_pda_message("[P.owner]", "[command_name()] Payroll", msg) + + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + PM.notify("Message from [command_name()] (Payroll), \"[msg]\" (Unable to Reply)", 0) + break + +/datum/game_mode/proc/check_finished() //to be called by ticker + if((SSshuttle.emergency && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) || station_was_nuked) + return 1 + return 0 + +/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. + return + +/datum/game_mode/proc/declare_completion() + var/clients = 0 + var/surviving_humans = 0 + var/surviving_total = 0 + var/ghosts = 0 + var/escaped_humans = 0 + var/escaped_total = 0 + var/escaped_on_pod_1 = 0 + var/escaped_on_pod_2 = 0 + var/escaped_on_pod_3 = 0 + var/escaped_on_pod_5 = 0 + var/escaped_on_shuttle = 0 + + var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) + + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) //shuttle didn't get to centcom + escape_locations -= /area/shuttle/escape + + for(var/mob/M in GLOB.player_list) + if(M.client) + clients++ + if(ishuman(M)) + if(!M.stat) + surviving_humans++ + if(M.loc && M.loc.loc && (M.loc.loc.type in escape_locations)) + escaped_humans++ + if(!M.stat) + surviving_total++ + if(M.loc && M.loc.loc && (M.loc.loc.type in escape_locations)) + escaped_total++ + + if(M.loc && M.loc.loc && M.loc.loc.type == SSshuttle.emergency.areaInstance.type && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) + escaped_on_shuttle++ + + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom) + escaped_on_pod_1++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom) + escaped_on_pod_2++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom) + escaped_on_pod_3++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod5/centcom) + escaped_on_pod_5++ + + if(isobserver(M)) + ghosts++ + + if(clients > 0) + feedback_set("round_end_clients",clients) + if(ghosts > 0) + feedback_set("round_end_ghosts",ghosts) + if(surviving_humans > 0) + feedback_set("survived_human",surviving_humans) + if(surviving_total > 0) + feedback_set("survived_total",surviving_total) + if(escaped_humans > 0) + feedback_set("escaped_human",escaped_humans) + if(escaped_total > 0) + feedback_set("escaped_total",escaped_total) + if(escaped_on_shuttle > 0) + feedback_set("escaped_on_shuttle",escaped_on_shuttle) + if(escaped_on_pod_1 > 0) + feedback_set("escaped_on_pod_1",escaped_on_pod_1) + if(escaped_on_pod_2 > 0) + feedback_set("escaped_on_pod_2",escaped_on_pod_2) + if(escaped_on_pod_3 > 0) + feedback_set("escaped_on_pod_3",escaped_on_pod_3) + if(escaped_on_pod_5 > 0) + feedback_set("escaped_on_pod_5",escaped_on_pod_5) + + send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") + return 0 + + +/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. + return 0 + +/datum/game_mode/proc/get_players_for_role(var/role, override_jobbans=0) + var/list/players = list() + var/list/candidates = list() + //var/list/drafted = list() + //var/datum/mind/applicant = null + + var/roletext = get_roletext(role) + + // Assemble a list of active players without jobbans. + for(var/mob/new_player/player in GLOB.player_list) + if(player.client && player.ready && player.has_valid_preferences()) + if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, roletext)) + if(player_old_enough_antag(player.client,role)) + players += player + + // Shuffle the players list so that it becomes ping-independent. + players = shuffle(players) + + // Get a list of all the people who want to be the antagonist for this round, except those with incompatible species + for(var/mob/new_player/player in players) + if(!player.client.skip_antag) + if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species)) + player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them." + candidates += player.mind + players -= player + + // If we don't have enough antags, draft people who voted for the round. + if(candidates.len < recommended_enemies) + for(var/key in SSvote.round_voters) + for(var/mob/new_player/player in players) + if(player.ckey == key) + player_draft_log += "[player.key] voted for this round, so we are drafting them." + candidates += player.mind + players -= player + break + + // Remove candidates who want to be antagonist but have a job that precludes it + if(restricted_jobs) + for(var/datum/mind/player in candidates) + for(var/job in restricted_jobs) + if(player.assigned_role == job) + candidates -= player + + + return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies + // recommended_enemies if the number of people with that role set to yes is less than recomended_enemies, + // Less if there are not enough valid players in the game entirely to make recommended_enemies. + + +/datum/game_mode/proc/latespawn(var/mob) + +/* +/datum/game_mode/proc/check_player_role_pref(var/role, var/mob/player) + if(player.preferences.be_special & role) + return 1 + return 0 +*/ + +/datum/game_mode/proc/num_players() + . = 0 + for(var/mob/new_player/P in GLOB.player_list) + if(P.client && P.ready) + .++ + +/datum/game_mode/proc/num_players_started() + . = 0 + for(var/mob/living/carbon/human/H in GLOB.player_list) + if(H.client) + .++ + +/////////////////////////////////// +//Keeps track of all living heads// +/////////////////////////////////// +/datum/game_mode/proc/get_living_heads() + . = list() + for(var/mob/living/carbon/human/player in GLOB.mob_list) + var/list/real_command_positions = GLOB.command_positions.Copy() - "Nanotrasen Representative" + if(player.stat != DEAD && player.mind && (player.mind.assigned_role in real_command_positions)) + . |= player.mind + + +//////////////////////////// +//Keeps track of all heads// +//////////////////////////// +/datum/game_mode/proc/get_all_heads() + . = list() + for(var/mob/player in GLOB.mob_list) + var/list/real_command_positions = GLOB.command_positions.Copy() - "Nanotrasen Representative" + if(player.mind && (player.mind.assigned_role in real_command_positions)) + . |= player.mind + +////////////////////////////////////////////// +//Keeps track of all living security members// +////////////////////////////////////////////// +/datum/game_mode/proc/get_living_sec() + . = list() + for(var/mob/living/carbon/human/player in GLOB.mob_list) + if(player.stat != DEAD && player.mind && (player.mind.assigned_role in GLOB.security_positions)) + . |= player.mind + +//////////////////////////////////////// +//Keeps track of all security members// +//////////////////////////////////////// +/datum/game_mode/proc/get_all_sec() + . = list() + for(var/mob/living/carbon/human/player in GLOB.mob_list) + if(player.mind && (player.mind.assigned_role in GLOB.security_positions)) + . |= player.mind + +/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) + return 0 + +/datum/game_mode/New() + newscaster_announcements = pick(GLOB.newscaster_standard_feeds) + +////////////////////////// +//Reports player logouts// +////////////////////////// +proc/display_roundstart_logout_report() + var/msg = "Roundstart logout report\n\n" + for(var/mob/living/L in GLOB.mob_list) + + if(L.ckey) + var/found = 0 + for(var/client/C in GLOB.clients) + if(C.ckey == L.ckey) + found = 1 + break + if(!found) + msg += "[L.name] ([L.ckey]), the [L.job] (Disconnected)\n" + + + if(L.ckey && L.client) + if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) + msg += "[L.name] ([L.ckey]), the [L.job] (Connected, Inactive)\n" + continue //AFK client + if(L.stat) + if(L.suiciding) //Suicider + msg += "[L.name] ([L.ckey]), the [L.job] (Suicide)\n" + SSjobs.FreeRole(L.job) + message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Suicide)\n") + continue //Disconnected client + if(L.stat == UNCONSCIOUS) + msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" + continue //Unconscious + if(L.stat == DEAD) + msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" + continue //Dead + + continue //Happy connected client + for(var/mob/dead/observer/D in GLOB.mob_list) + if(D.mind && (D.mind.original == L || D.mind.current == L)) + if(L.stat == DEAD) + if(L.suiciding) //Suicider + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Suicide)\n" + continue //Disconnected client + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" + continue //Dead mob, ghost abandoned + else + if(D.can_reenter_corpse) + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (This shouldn't appear.)\n" + continue //Lolwhat + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Ghosted)\n" + SSjobs.FreeRole(L.job) + message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Ghosted While Alive)\n") + continue //Ghosted while alive + + + + for(var/mob/M in GLOB.mob_list) + if(check_rights(R_ADMIN, 0, M)) + to_chat(M, msg) + +//Announces objectives/generic antag text. +/proc/show_generic_antag_text(var/datum/mind/player) + if(player.current) + to_chat(player.current, "You are an antagonist! Within the rules, \ + try to act as an opposing force to the crew. Further RP and try to make sure \ + other players have fun! If you are confused or at a loss, always adminhelp, \ + and before taking extreme actions, please try to also contact the administration! \ + Think through your actions and make the roleplay immersive! Please remember all \ + rules aside from those without explicit exceptions apply to antagonists.") + +/proc/show_objectives(var/datum/mind/player) + if(!player || !player.current) return + + var/obj_count = 1 + to_chat(player.current, "Your current objectives:") + for(var/datum/objective/objective in player.objectives) + to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + +/proc/get_roletext(var/role) + return role + +/proc/get_nuke_code() + var/nukecode = "ERROR" + for(var/obj/machinery/nuclearbomb/bomb in world) + if(bomb && bomb.r_code && is_station_level(bomb.z)) + nukecode = bomb.r_code + return nukecode + +/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type) + var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100) + var/mob/dead/observer/theghost = null + if(candidates.len) + theghost = pick(candidates) + to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") + message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.") + M.ghostize() + M.key = theghost.key + else + message_admins("[M] ([M.key] has been converted into [role_type] with an active antagonist jobban for said role since no ghost has volunteered to take [M.p_their()] place.") + to_chat(M, "You have been converted into [role_type] with an active jobban. Any further violations of the rules on your part are likely to result in a permanent ban.") + +/proc/printplayer(datum/mind/ply, fleecheck) + var/jobtext = "" + if(ply.assigned_role) + jobtext = " the [ply.assigned_role]" + var/text = "[ply.key] was [ply.name][jobtext] and" + if(ply.current) + if(ply.current.stat == DEAD) + text += " died" + else + text += " survived" + if(fleecheck) + var/turf/T = get_turf(ply.current) + if(!T || !is_station_level(T.z)) + text += " while fleeing the station" + if(ply.current.real_name != ply.name) + text += " as [ply.current.real_name]" + else + text += " had [ply.p_their()] body destroyed" + return text + +/proc/printeventplayer(datum/mind/ply) + var/text = "[ply.key] was [ply.name]" + if(ply.special_role != SPECIAL_ROLE_EVENTMISC) + text += " the [ply.special_role]" + text += " and" + if(ply.current) + if(ply.current.stat == DEAD) + text += " died" + else + text += " survived" + else + text += " had [ply.p_their()] body destroyed" + return text + +/proc/printobjectives(datum/mind/ply) + var/list/objective_parts = list() + var/count = 1 + for(var/datum/objective/objective in ply.objectives) + if(objective.check_completion()) + objective_parts += "Objective #[count]: [objective.explanation_text] Success!" + else + objective_parts += "Objective #[count]: [objective.explanation_text] Fail." + count++ + return objective_parts.Join("
    ") + +/datum/game_mode/proc/generate_station_goals() + var/list/possible = list() + for(var/T in subtypesof(/datum/station_goal)) + var/datum/station_goal/G = T + if(config_tag in initial(G.gamemode_blacklist)) + continue + possible += T + var/goal_weights = 0 + while(possible.len && goal_weights < STATION_GOAL_BUDGET) + var/datum/station_goal/picked = pick_n_take(possible) + goal_weights += initial(picked.weight) + station_goals += new picked + + if(station_goals.len) + send_station_goals_message() + +/datum/game_mode/proc/send_station_goals_message() + var/message_text = "
    " + message_text += "

    [command_name()] Orders


    " + message_text += "Special Orders for [station_name()]:

    " + + for(var/datum/station_goal/G in station_goals) + G.on_report() + message_text += G.get_report() + message_text += "
    " + + print_command_report(message_text, "[command_name()] Orders") + +/datum/game_mode/proc/declare_station_goal_completion() + for(var/V in station_goals) + var/datum/station_goal/G = V + G.print_result() + +/datum/game_mode/proc/update_eventmisc_icons_added(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_EVENTMISC] + antaghud.join_hud(mob_mind.current) + set_antag_hud(mob_mind.current, "hudevent") + +/datum/game_mode/proc/update_eventmisc_icons_removed(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_EVENTMISC] + antaghud.leave_hud(mob_mind.current) + set_antag_hud(mob_mind.current, null) diff --git a/code/game/gamemodes/heist/heist.dm b/code/game/gamemodes/heist/heist.dm index 2b4d095b327..142ae33c078 100644 --- a/code/game/gamemodes/heist/heist.dm +++ b/code/game/gamemodes/heist/heist.dm @@ -1,9 +1,8 @@ /* VOX HEIST ROUNDTYPE */ - -var/global/list/raider_spawn = list() -var/global/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind' objective. Clumsy, rewrite sometime. +GLOBAL_LIST_EMPTY(raider_spawn) +GLOBAL_LIST_EMPTY(cortical_stacks) //Stacks for 'leave nobody behind' objective. Clumsy, rewrite sometime. /datum/game_mode/ var/list/datum/mind/raiders = list() //Antags. @@ -70,10 +69,10 @@ var/global/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind' //Spawn the vox! for(var/datum/mind/raider in raiders) - if(index > raider_spawn.len) + if(index > GLOB.raider_spawn.len) index = 1 - raider.current.loc = raider_spawn[index] + raider.current.loc = GLOB.raider_spawn[index] index++ create_vox(raider) @@ -128,16 +127,16 @@ var/global/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind' //Now apply cortical stack. var/obj/item/implant/cortical/I = new(vox) I.implant(vox) - cortical_stacks += I + GLOB.cortical_stacks += I vox.equip_vox_raider() vox.regenerate_icons() /datum/game_mode/proc/is_raider_crew_safe() - if(cortical_stacks.len == 0) + if(GLOB.cortical_stacks.len == 0) return 0 - for(var/obj/stack in cortical_stacks) + for(var/obj/stack in GLOB.cortical_stacks) if(get_area(stack) != locate(/area/shuttle/vox) && get_area(stack) != locate(/area/vox_station)) return 0 //this is stupid as fuck return 1 diff --git a/code/game/gamemodes/intercept_report.dm b/code/game/gamemodes/intercept_report.dm index d1d176f797e..acee798e96c 100644 --- a/code/game/gamemodes/intercept_report.dm +++ b/code/game/gamemodes/intercept_report.dm @@ -1,234 +1,234 @@ -/datum/intercept_text - var/text - /* - var/prob_correct_person_lower = 20 - var/prob_correct_person_higher = 80 - var/prob_correct_job_lower = 20 - var/prob_correct_job_higher = 80 - var/prob_correct_prints_lower = 20 - var/prob_correct_print_higher = 80 - var/prob_correct_objective_lower = 20 - var/prob_correct_objective_higher = 80 - */ - var/list/org_names_1 = list( - "Blighted", - "Defiled", - "Unholy", - "Murderous", - "Ugly", - "French", - "Blue", - "Farmer" - ) - var/list/org_names_2 = list( - "Reapers", - "Swarm", - "Rogues", - "Menace", - "Jeff Worshippers", - "Drunks", - "Strikers", - "Creed" - ) - var/list/anomalies = list( - "Huge electrical storm", - "Photon emitter", - "Meson generator", - "Blue swirly thing" - ) - var/list/SWF_names = list( - "Grand Wizard", - "His Most Unholy Master", - "The Most Angry", - "Bighands", - "Tall Hat", - "Deadly Sandals" - ) - var/list/changeling_names = list( - "Odo", - "The Thing", - "Booga", - "The Goatee of Wrath", - "Tam Lin", - "Species 3157", - "Small Prick" - ) - - -/datum/intercept_text/proc/build(var/mode_type, datum/mind/correct_person) - switch(mode_type) - if("revolution") - src.text = "" - src.build_rev(correct_person) - return src.text - if("cult") - src.text = "" - src.build_cult(correct_person) - return src.text - if("wizard") - src.text = "" - src.build_wizard(correct_person) - return src.text - if("nuke") - src.text = "" - src.build_nuke(correct_person) - return src.text - if("traitor") - src.text = "" - src.build_traitor(correct_person) - return src.text - if("changeling","traitorchan") - src.text = "" - src.build_changeling(correct_person) - return src.text - else - return null - -// NOTE: Commentted out was the code which showed the chance of someone being an antag. If you want to re-add it, just uncomment the code. - -/* -/datum/intercept_text/proc/pick_mob() - var/list/dudes = list() - for(var/mob/living/carbon/human/man in GLOB.player_list) - if(!man.mind) continue - if(man.mind.assigned_role == man.mind.special_role) continue - dudes += man - if(dudes.len==0) - return null - return pick(dudes) - - -/datum/intercept_text/proc/pick_fingerprints() - var/mob/living/carbon/human/dude = src.pick_mob() - //if(!dude) return pick_fingerprints() //who coded that is totally crasy or just a traitor. -- rastaf0 - if(dude) - return num2text(md5(dude.dna.uni_identity)) - else - return num2text(md5(num2text(rand(1,10000)))) -*/ - -/datum/intercept_text/proc/get_suspect() - var/list/dudes = list() - for(var/mob/living/carbon/human/man in GLOB.player_list) - if(man.client && man.client.prefs.nanotrasen_relation == "Opposed") - //don't include suspects who can't possibly be the antag based on their job (no suspecting the captain of being a damned dirty tator) - if(man.mind && man.mind.assigned_role) - if((man.mind.assigned_role in SSticker.mode.protected_jobs) || (man.mind.assigned_role in SSticker.mode.restricted_jobs)) - return - //don't include suspects who can't possibly be the antag based on their species (no suspecting the machines of being sneaky changelings) - if(man.dna.species.name in SSticker.mode.protected_species) - return - dudes += man - for(var/i = 0, i < max(GLOB.player_list.len/10,2), i++) - dudes += pick(GLOB.player_list) - return pick(dudes) - -/datum/intercept_text/proc/build_traitor(datum/mind/correct_person) - var/name_1 = pick(src.org_names_1) - var/name_2 = pick(src.org_names_2) - - var/mob/living/carbon/human/H = get_suspect() - if(!H) return - - var/fingerprints = num2text(md5(H.dna.uni_identity)) - var/traitor_name = H.real_name - var/prob_right_dude = rand(1, 100) - - src.text += "

    The [name_1] [name_2] implied an undercover operative was acting on their behalf on the station currently." - src.text += "It would be in your best interests to suspect everybody, as these undercover operatives could have implants which trigger them to have their memories removed until they are needed. He, or she, could even be a high ranking officer." - - src.text += "After some investigation, we " - if(prob(50)) - src.text += "are [prob_right_dude]% sure that [traitor_name] may have been involved, and should be closely observed." - src.text += "
    Note: This group are known to be untrustworthy, so do not act on this information without proper discourse." - else - src.text += "discovered the following set of fingerprints ([fingerprints]) on sensitive materials, and their owner should be closely observed." - src.text += "However, these could also belong to a current Cent. Com employee, so do not act on this without reason." - - - -/datum/intercept_text/proc/build_cult(datum/mind/correct_person) - var/name_1 = pick(src.org_names_1) - var/name_2 = pick(src.org_names_2) - - var/prob_right_dude = rand(1, 100) - var/mob/living/carbon/human/H = get_suspect() - if(!H) return - var/traitor_job = H.mind.assigned_role - - src.text += "

    It has been brought to our attention that the [name_1] [name_2] have stumbled upon some dark secrets. They apparently want to spread the dangerous knowledge onto as many stations as they can." - src.text += "Watch out for the following: praying to an unfamilar god, preaching the word of \[REDACTED\], sacrifices, magical dark power, living constructs of evil and a portal to the dimension of the underworld." - - src.text += "Based on our intelligence, we are [prob_right_dude]% sure that if true, someone doing the job of [traitor_job] on your station may have been converted " - src.text += "and instilled with the idea of the flimsiness of the real world, seeking to destroy it. " - - src.text += "
    However, if this information is acted on without substantial evidence, those responsible will face severe repercussions." - - - -/datum/intercept_text/proc/build_rev(datum/mind/correct_person) - var/name_1 = pick(src.org_names_1) - var/name_2 = pick(src.org_names_2) - - var/prob_right_dude = rand(1, 100) - var/mob/living/carbon/human/H = get_suspect() - if(!H) return - var/traitor_job = H.mind.assigned_role - - src.text += "

    It has been brought to our attention that the [name_1] [name_2] are attempting to stir unrest on one of our stations in your sector." - src.text += "Watch out for suspicious activity among the crew and make sure that all heads of staff report in periodically." - - src.text += "Based on our intelligence, we are [prob_right_dude]% sure that if true, someone doing the job of [traitor_job] on your station may have been brainwashed " - src.text += "at a recent conference, and their department should be closely monitored for signs of mutiny. " - - src.text += "
    However, if this information is acted on without substantial evidence, those responsible will face severe repercussions." - - - -/datum/intercept_text/proc/build_wizard(datum/mind/correct_person) - var/SWF_desc = pick(SWF_names) - - src.text += "

    The evil Space Wizards Federation have recently broke their most feared wizard, known only as \"[SWF_desc]\" out of space jail. " - src.text += "He is on the run, last spotted in a system near your present location. If anybody suspicious is located aboard, please " - src.text += "approach with EXTREME caution. Cent. Com also recommends that it would be wise to not inform the crew of this, due to their fearful nature." - src.text += "Known attributes include: Brown sandals, a large blue hat, a voluptous white beard, and an inclination to cast spells." - -/datum/intercept_text/proc/build_nuke(datum/mind/correct_person) - src.text += "

    Cent. Com recently recieved a report of a plot to destroy one of our stations in your area. We believe the Nuclear Authentication Disc " - src.text += "that is standard issue aboard your vessel may be a target. We recommend removal of this object, and it's storage in a safe " - src.text += "environment. As this may cause panic among the crew, all efforts should be made to keep this information a secret from all but " - src.text += "the most trusted crew-members." - -/datum/intercept_text/proc/build_changeling(datum/mind/correct_person) - var/cname = pick(src.changeling_names) - var/orgname1 = pick(src.org_names_1) - var/orgname2 = pick(src.org_names_2) - /* - var/changeling_name - var/changeling_job - var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher) - var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher) - if(prob(prob_right_job)) - if(correct_person) - if(correct_person:assigned_role == correct_person:special_role) - changeling_job = pick(GLOB.joblist) - else - changeling_job = correct_person:assigned_role - else - changeling_job = pick(GLOB.joblist) - if(prob(prob_right_dude) && ticker.mode == "changeling") - if(correct_person:assigned_role == correct_person:special_role) - changeling_name = correct_person:current - else - changeling_name = src.pick_mob() - else - changeling_name = src.pick_mob() - */ - - src.text += "

    We have received a report that a dangerous alien lifeform known only as \"[cname]\" may have infiltrated your crew. " - /* - src.text += "Our intelligence suggests a [prob_right_job]% chance that a [changeling_job] on board your station has been replaced by the alien. " - src.text += "Additionally, the report indicates a [prob_right_dude]% chance that [changeling_name] may have been in contact with the lifeform at a recent social gathering. " - */ - src.text += "These lifeforms are assosciated with the [orgname1] [orgname2] and may be attempting to acquire sensitive materials on their behalf. " - src.text += "Please take care not to alarm the crew, as [cname] may take advantage of a panic situation. Remember, they can be anybody, suspect everybody!" +/datum/intercept_text + var/text + /* + var/prob_correct_person_lower = 20 + var/prob_correct_person_higher = 80 + var/prob_correct_job_lower = 20 + var/prob_correct_job_higher = 80 + var/prob_correct_prints_lower = 20 + var/prob_correct_print_higher = 80 + var/prob_correct_objective_lower = 20 + var/prob_correct_objective_higher = 80 + */ + var/list/org_names_1 = list( + "Blighted", + "Defiled", + "Unholy", + "Murderous", + "Ugly", + "French", + "Blue", + "Farmer" + ) + var/list/org_names_2 = list( + "Reapers", + "Swarm", + "Rogues", + "Menace", + "Jeff Worshippers", + "Drunks", + "Strikers", + "Creed" + ) + var/list/anomalies = list( + "Huge electrical storm", + "Photon emitter", + "Meson generator", + "Blue swirly thing" + ) + var/list/SWF_names = list( + "Grand Wizard", + "His Most Unholy Master", + "The Most Angry", + "Bighands", + "Tall Hat", + "Deadly Sandals" + ) + var/list/changeling_names = list( + "Odo", + "The Thing", + "Booga", + "The Goatee of Wrath", + "Tam Lin", + "Species 3157", + "Small Prick" + ) + + +/datum/intercept_text/proc/build(var/mode_type, datum/mind/correct_person) + switch(mode_type) + if("revolution") + src.text = "" + src.build_rev(correct_person) + return src.text + if("cult") + src.text = "" + src.build_cult(correct_person) + return src.text + if("wizard") + src.text = "" + src.build_wizard(correct_person) + return src.text + if("nuke") + src.text = "" + src.build_nuke(correct_person) + return src.text + if("traitor") + src.text = "" + src.build_traitor(correct_person) + return src.text + if("changeling","traitorchan") + src.text = "" + src.build_changeling(correct_person) + return src.text + else + return null + +// NOTE: Commentted out was the code which showed the chance of someone being an antag. If you want to re-add it, just uncomment the code. + +/* +/datum/intercept_text/proc/pick_mob() + var/list/dudes = list() + for(var/mob/living/carbon/human/man in GLOB.player_list) + if(!man.mind) continue + if(man.mind.assigned_role == man.mind.special_role) continue + dudes += man + if(dudes.len==0) + return null + return pick(dudes) + + +/datum/intercept_text/proc/pick_fingerprints() + var/mob/living/carbon/human/dude = src.pick_mob() + //if(!dude) return pick_fingerprints() //who coded that is totally crasy or just a traitor. -- rastaf0 + if(dude) + return num2text(md5(dude.dna.uni_identity)) + else + return num2text(md5(num2text(rand(1,10000)))) +*/ + +/datum/intercept_text/proc/get_suspect() + var/list/dudes = list() + for(var/mob/living/carbon/human/man in GLOB.player_list) + if(man.client && man.client.prefs.nanotrasen_relation == "Opposed") + //don't include suspects who can't possibly be the antag based on their job (no suspecting the captain of being a damned dirty tator) + if(man.mind && man.mind.assigned_role) + if((man.mind.assigned_role in SSticker.mode.protected_jobs) || (man.mind.assigned_role in SSticker.mode.restricted_jobs)) + return + //don't include suspects who can't possibly be the antag based on their species (no suspecting the machines of being sneaky changelings) + if(man.dna.species.name in SSticker.mode.protected_species) + return + dudes += man + for(var/i = 0, i < max(GLOB.player_list.len/10,2), i++) + dudes += pick(GLOB.player_list) + return pick(dudes) + +/datum/intercept_text/proc/build_traitor(datum/mind/correct_person) + var/name_1 = pick(src.org_names_1) + var/name_2 = pick(src.org_names_2) + + var/mob/living/carbon/human/H = get_suspect() + if(!H) return + + var/fingerprints = num2text(md5(H.dna.uni_identity)) + var/traitor_name = H.real_name + var/prob_right_dude = rand(1, 100) + + src.text += "

    The [name_1] [name_2] implied an undercover operative was acting on their behalf on the station currently." + src.text += "It would be in your best interests to suspect everybody, as these undercover operatives could have implants which trigger them to have their memories removed until they are needed. He, or she, could even be a high ranking officer." + + src.text += "After some investigation, we " + if(prob(50)) + src.text += "are [prob_right_dude]% sure that [traitor_name] may have been involved, and should be closely observed." + src.text += "
    Note: This group are known to be untrustworthy, so do not act on this information without proper discourse." + else + src.text += "discovered the following set of fingerprints ([fingerprints]) on sensitive materials, and their owner should be closely observed." + src.text += "However, these could also belong to a current Cent. Com employee, so do not act on this without reason." + + + +/datum/intercept_text/proc/build_cult(datum/mind/correct_person) + var/name_1 = pick(src.org_names_1) + var/name_2 = pick(src.org_names_2) + + var/prob_right_dude = rand(1, 100) + var/mob/living/carbon/human/H = get_suspect() + if(!H) return + var/traitor_job = H.mind.assigned_role + + src.text += "

    It has been brought to our attention that the [name_1] [name_2] have stumbled upon some dark secrets. They apparently want to spread the dangerous knowledge onto as many stations as they can." + src.text += "Watch out for the following: praying to an unfamilar god, preaching the word of \[REDACTED\], sacrifices, magical dark power, living constructs of evil and a portal to the dimension of the underworld." + + src.text += "Based on our intelligence, we are [prob_right_dude]% sure that if true, someone doing the job of [traitor_job] on your station may have been converted " + src.text += "and instilled with the idea of the flimsiness of the real world, seeking to destroy it. " + + src.text += "
    However, if this information is acted on without substantial evidence, those responsible will face severe repercussions." + + + +/datum/intercept_text/proc/build_rev(datum/mind/correct_person) + var/name_1 = pick(src.org_names_1) + var/name_2 = pick(src.org_names_2) + + var/prob_right_dude = rand(1, 100) + var/mob/living/carbon/human/H = get_suspect() + if(!H) return + var/traitor_job = H.mind.assigned_role + + src.text += "

    It has been brought to our attention that the [name_1] [name_2] are attempting to stir unrest on one of our stations in your sector." + src.text += "Watch out for suspicious activity among the crew and make sure that all heads of staff report in periodically." + + src.text += "Based on our intelligence, we are [prob_right_dude]% sure that if true, someone doing the job of [traitor_job] on your station may have been brainwashed " + src.text += "at a recent conference, and their department should be closely monitored for signs of mutiny. " + + src.text += "
    However, if this information is acted on without substantial evidence, those responsible will face severe repercussions." + + + +/datum/intercept_text/proc/build_wizard(datum/mind/correct_person) + var/SWF_desc = pick(SWF_names) + + src.text += "

    The evil Space Wizards Federation have recently broke their most feared wizard, known only as \"[SWF_desc]\" out of space jail. " + src.text += "He is on the run, last spotted in a system near your present location. If anybody suspicious is located aboard, please " + src.text += "approach with EXTREME caution. Cent. Com also recommends that it would be wise to not inform the crew of this, due to their fearful nature." + src.text += "Known attributes include: Brown sandals, a large blue hat, a voluptous white beard, and an inclination to cast spells." + +/datum/intercept_text/proc/build_nuke(datum/mind/correct_person) + src.text += "

    Cent. Com recently recieved a report of a plot to destroy one of our stations in your area. We believe the Nuclear Authentication Disc " + src.text += "that is standard issue aboard your vessel may be a target. We recommend removal of this object, and it's storage in a safe " + src.text += "environment. As this may cause panic among the crew, all efforts should be made to keep this information a secret from all but " + src.text += "the most trusted crew-members." + +/datum/intercept_text/proc/build_changeling(datum/mind/correct_person) + var/cname = pick(src.changeling_names) + var/orgname1 = pick(src.org_names_1) + var/orgname2 = pick(src.org_names_2) + /* + var/changeling_name + var/changeling_job + var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher) + var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher) + if(prob(prob_right_job)) + if(correct_person) + if(correct_person:assigned_role == correct_person:special_role) + changeling_job = pick(GLOB.joblist) + else + changeling_job = correct_person:assigned_role + else + changeling_job = pick(GLOB.joblist) + if(prob(prob_right_dude) && ticker.mode == "changeling") + if(correct_person:assigned_role == correct_person:special_role) + changeling_name = correct_person:current + else + changeling_name = src.pick_mob() + else + changeling_name = src.pick_mob() + */ + + src.text += "

    We have received a report that a dangerous alien lifeform known only as \"[cname]\" may have infiltrated your crew. " + /* + src.text += "Our intelligence suggests a [prob_right_job]% chance that a [changeling_job] on board your station has been replaced by the alien. " + src.text += "Additionally, the report indicates a [prob_right_dude]% chance that [changeling_name] may have been in contact with the lifeform at a recent social gathering. " + */ + src.text += "These lifeforms are assosciated with the [orgname1] [orgname2] and may be attempting to acquire sensitive materials on their behalf. " + src.text += "Please take care not to alarm the crew, as [cname] may take advantage of a panic situation. Remember, they can be anybody, suspect everybody!" diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 70e0e3258eb..c1a907f9191 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -1,758 +1,758 @@ -//The malf AI action subtype. All malf actions are subtypes of this. -/datum/action/innate/ai - name = "AI Action" - desc = "You aren't entirely sure what this does, but it's very beepy and boopy." - background_icon_state = "bg_tech_blue" - var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time - var/uses //If we have multiple uses of the same power - var/auto_use_uses = TRUE //If we automatically use up uses on each activation - var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules - -/datum/action/innate/ai/Grant(mob/living/L) - . = ..() - if(!isAI(owner)) - WARNING("AI action [name] attempted to grant itself to non-AI mob [L.real_name] ([L.key])!") - qdel(src) - else - owner_AI = owner - -/datum/action/innate/ai/IsAvailable() - . = ..() - if(owner_AI && owner_AI.malf_cooldown > world.time) - return - -/datum/action/innate/ai/Trigger() - . = ..() - if(auto_use_uses) - adjust_uses(-1) - if(cooldown_period) - owner_AI.malf_cooldown = world.time + cooldown_period - -/datum/action/innate/ai/proc/adjust_uses(amt, silent) - uses += amt - if(!silent && uses) - to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") - if(!uses) - if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! - to_chat(owner, "[name] has run out of uses!") - qdel(src) - -//Framework for ranged abilities that can have different effects by left-clicking stuff. -/datum/action/innate/ai/ranged - name = "Ranged AI Action" - auto_use_uses = FALSE //This is so we can do the thing and disable/enable freely without having to constantly add uses - var/obj/effect/proc_holder/ranged_ai/linked_ability //The linked proc holder that contains the actual ability code - var/linked_ability_type //The path of our linked ability - -/datum/action/innate/ai/ranged/New() - if(!linked_ability_type) - WARNING("Ranged AI action [name] attempted to spawn without a linked ability!") - qdel(src) //uh oh! - return - linked_ability = new linked_ability_type() - linked_ability.attached_action = src - ..() - -/datum/action/innate/ai/ranged/adjust_uses(amt, silent) - uses += amt - if(!silent && uses) - to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") - if(!uses) - if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! - to_chat(owner, "[name] has run out of uses!") - Remove(owner) - QDEL_IN(src, 100) //let any active timers on us finish up - -/datum/action/innate/ai/ranged/Destroy() - QDEL_NULL(linked_ability) - return ..() - -/datum/action/innate/ai/ranged/Activate() - linked_ability.toggle(owner) - return TRUE - -//The actual ranged proc holder. -/obj/effect/proc_holder/ranged_ai - var/enable_text = "Hello World!" //Appears when the user activates the ability - var/disable_text = "Goodbye Cruel World!" //Context clues! - var/datum/action/innate/ai/ranged/attached_action - -/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user) - if(active) - remove_ranged_ability(user, disable_text) - else - add_ranged_ability(user, enable_text) - -//The datum and interface for the malf unlock menu, which lets them choose actions to unlock. -/datum/module_picker - var/temp - var/processing_time = 50 - var/list/possible_modules - -/datum/module_picker/New() - possible_modules = list() - for(var/type in typesof(/datum/AI_Module)) - var/datum/AI_Module/AM = new type - if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade) - possible_modules += AM - -/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI. - for(var/datum/AI_Module/AM in possible_modules) - for(var/datum/action/A in AI.actions) - if(istype(A, initial(AM.power_type))) - qdel(A) - -/datum/module_picker/proc/use(user as mob) - var/dat - dat += {"Select use of processing time: (currently #[processing_time] left.)
    -
    - Install Module:
    - The number afterwards is the amount of processing time it consumes.
    "} - for(var/datum/AI_Module/large/module in possible_modules) - dat += "[module.module_name]\[?\] ([module.cost])
    " - for(var/datum/AI_Module/small/module in possible_modules) - dat += "[module.module_name]\[?\] ([module.cost])
    " - dat += "
    " - if(temp) - dat += "[temp]" - var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu", 400, 500) - popup.set_content(dat) - popup.open() - return - -/datum/module_picker/Topic(href, href_list) - ..() - - if(!isAI(usr)) - return - var/mob/living/silicon/ai/A = usr - - if(A.stat == DEAD) - to_chat(A, "You are already dead!") - return - - for(var/datum/AI_Module/AM in possible_modules) - if (href_list[AM.mod_pick_name]) - - // Cost check - if(AM.cost > processing_time) - temp = "You cannot afford this module." - break - - var/datum/action/innate/ai/action = locate(AM.power_type) in A.actions - - // Give the power and take away the money. - if(AM.upgrade) //upgrade and upgrade() are separate, be careful! - AM.upgrade(A) - possible_modules -= AM - to_chat(A, AM.unlock_text) - A.playsound_local(A, AM.unlock_sound, 50, 0) - else - if(AM.power_type) - if(!action) //Unlocking for the first time - var/datum/action/AC = new AM.power_type - AC.Grant(A) - A.current_modules += new AM.type - temp = AM.description - if(AM.one_purchase) - possible_modules -= AM - if(AM.unlock_text) - to_chat(A, AM.unlock_text) - if(AM.unlock_sound) - A.playsound_local(A, AM.unlock_sound, 50, 0) - else //Adding uses to an existing module - action.uses += initial(action.uses) - action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining." - action.UpdateButtonIcon() - temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!" - processing_time -= AM.cost - - if(href_list["showdesc"]) - if(AM.mod_pick_name == href_list["showdesc"]) - temp = AM.description - use(usr) - -//The base module type, which holds info about each ability. -/datum/AI_Module - var/module_name - var/mod_pick_name - var/description = "" - var/engaged = 0 - var/cost = 5 - var/one_purchase = FALSE //If this module can only be purchased once. This always applies to upgrades, even if the variable is set to false. - var/power_type = /datum/action/innate/ai //If the module gives an active ability, use this. Mutually exclusive with upgrade. - var/upgrade //If the module gives a passive upgrade, use this. Mutually exclusive with power_type. - var/unlock_text = "Hello World!" //Text shown when an ability is unlocked - var/unlock_sound //Sound played when an ability is unlocked - var/uses - -/datum/AI_Module/proc/upgrade(mob/living/silicon/AI/AI) //Apply upgrades! - return - -/datum/AI_Module/large //Big, powerful stuff that can only be used once. - -/datum/AI_Module/small //Weak, usually localized stuff with multiple uses. - -//Doomsday Device: Starts the self-destruct timer. It can only be stopped by killing the AI completely. -/datum/AI_Module/large/nuke_station - module_name = "Doomsday Device" - mod_pick_name = "nukestation" - description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay. Can only be used while on the station, will fail if your core is moved off station or destroyed." - cost = 130 - one_purchase = TRUE - power_type = /datum/action/innate/ai/nuke_station - unlock_text = "You slowly, carefully, establish a connection with the on-station self-destruct. You can now activate it at any time." - unlock_sound = 'sound/items/timer.ogg' - -/datum/action/innate/ai/nuke_station - name = "Doomsday Device" - desc = "Activates the doomsday device. This is not reversible." - button_icon_state = "doomsday_device" - auto_use_uses = FALSE - -/datum/action/innate/ai/nuke_station/Activate() - var/turf/T = get_turf(owner) - if(!istype(T) || !is_station_level(T.z)) - to_chat(owner, "You cannot activate the doomsday device while off-station!") - return - if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;") - return - if(active) - return //prevent the AI from activating an already active doomsday - active = TRUE - set_us_up_the_bomb() - -/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb() - to_chat(owner_AI, "Nuclear device armed.") - event_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", new_sound = 'sound/AI/aimalf.ogg') - set_security_level("delta") - owner_AI.nuking = TRUE - var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(owner_AI) - owner_AI.doomsday_device = DOOM - owner_AI.doomsday_device.start() - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - for(var/mob/living/silicon/ai/A in ai_list) - if((A.stat != DEAD) && A.nuking) - point.the_disk = A //The pinpointer now tracks the AI core - qdel(src) - -/obj/machinery/doomsday_device - icon = 'icons/obj/machines/nuke_terminal.dmi' - name = "doomsday device" - icon_state = "nuclearbomb_base" - desc = "A weapon which disintegrates all organic life in a large area." - anchored = 1 - density = 1 - atom_say_verb = "blares" - speed_process = TRUE // Disgusting fix. Please remove once #12952 is merged - var/timing = 0 - var/default_timer = 4500 - var/detonation_timer - var/announced = 0 - -/obj/machinery/doomsday_device/Destroy() - STOP_PROCESSING(SSfastprocess, src) - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') - return ..() - -/obj/machinery/doomsday_device/proc/start() - detonation_timer = world.time + default_timer - timing = 1 - START_PROCESSING(SSfastprocess, src) - SSshuttle.emergencyNoEscape = 1 - -/obj/machinery/doomsday_device/proc/seconds_remaining() - . = max(0, (round(detonation_timer - world.time) / 10)) - -/obj/machinery/doomsday_device/process() - var/turf/T = get_turf(src) - if(!T || !is_station_level(T.z)) - minor_announcement.Announce("DOOMSDAY DEVICE OUT OF STATION RANGE, ABORTING", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') - qdel(src) - if(!timing) - STOP_PROCESSING(SSfastprocess, src) - return - var/sec_left = seconds_remaining() - if(sec_left <= 0) - timing = 0 - detonate(T.z) - qdel(src) - else - if(!(sec_left % 60) && !announced) - var/message = "[sec_left] SECONDS UNTIL DOOMSDAY DEVICE ACTIVATION!" - minor_announcement.Announce(message, "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') - announced = 10 - announced = max(0, announced-1) - -/obj/machinery/doomsday_device/proc/detonate(z_level = 1) - for(var/mob/M in GLOB.player_list) - M << 'sound/machines/alarm.ogg' - sleep(100) - for(var/mob/living/L in GLOB.mob_list) - var/turf/T = get_turf(L) - if(!T || T.z != z_level) - continue - if(issilicon(L)) - continue - to_chat(L, "The blast wave from [src] tears you atom from atom!") - L.dust() - to_chat(world, "The AI cleansed the station of life with the doomsday device!") - SSticker.force_ending = 1 - -//AI Turret Upgrade: Increases the health and damage of all turrets. -/datum/AI_Module/large/upgrade_turrets - module_name = "AI Turret Upgrade" - mod_pick_name = "turret" - description = "Improves the power and health of all AI turrets. This effect is permanent." - cost = 30 - upgrade = TRUE - unlock_text = "You establish a power diversion to your turrets, upgrading their health and damage." - unlock_sound = 'sound/items/rped.ogg' - -/datum/AI_Module/large/upgrade_turrets/upgrade(mob/living/silicon/AI/AI) - for(var/obj/machinery/porta_turret/turret in GLOB.machines) - var/turf/T = get_turf(turret) - if(is_station_level(T.z)) - turret.health += 30 - turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR. - turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg' - -//Hostile Station Lockdown: Locks, bolts, and electrifies every airlock on the station. After 90 seconds, the doors reset. -/datum/AI_Module/large/lockdown - module_name = "Hostile Station Lockdown" - mod_pick_name = "lockdown" - description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds, briefly \ - opening all doors on the station." - cost = 30 - one_purchase = TRUE - power_type = /datum/action/innate/ai/lockdown - unlock_text = "You upload a sleeper trojan into the door control systems. You can send a signal to set it off at any time." - -/datum/action/innate/ai/lockdown - name = "Lockdown" - desc = "Closes, bolts, and depowers every airlock, firelock, and blast door on the station. After 90 seconds, they will reset themselves." - button_icon_state = "lockdown" - uses = 1 - -/datum/action/innate/ai/lockdown/Activate() - for(var/obj/machinery/door/D in GLOB.airlocks) - if(!is_station_level(D.z)) - continue - INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, owner) - addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900) - - post_status("alert", "lockdown") - - minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert") - to_chat(owner, "Lockdown Initiated. Network reset in 90 seconds.") - spawn(900) - minor_announcement.Announce("Automatic system reboot complete. Have a secure day.","Network reset:") - -//Destroy RCDs: Detonates all non-cyborg RCDs on the station. -/datum/AI_Module/large/destroy_rcd - module_name = "Destroy RCDs" - mod_pick_name = "rcd" - description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Construction Devices on the station." - cost = 25 - one_purchase = TRUE - power_type = /datum/action/innate/ai/destroy_rcds - unlock_text = "After some improvisation, you rig your onboard radio to be able to send a signal to detonate all RCDs." - -/datum/action/innate/ai/destroy_rcds - name = "Destroy RCDs" - desc = "Detonate all non-cyborg RCDs on the station." - button_icon_state = "detonate_rcds" - uses = 1 - cooldown_period = 100 - -/datum/action/innate/ai/destroy_rcds/Activate() - for(var/obj/item/rcd/RCD in GLOB.rcd_list) - if(!istype(RCD, /obj/item/rcd/borg)) //Ensures that cyborg RCDs are spared. - RCD.detonate_pulse() - - to_chat(owner, "RCD detonation pulse emitted.") - owner.playsound_local(owner, 'sound/machines/twobeep.ogg', 50, 0) - -//Unlock Mech Domination: Unlocks the ability to dominate mechs. Big shocker, right? -/datum/AI_Module/large/mecha_domination - module_name = "Unlock Mech Domination" - mod_pick_name = "mechjack" - description = "Allows you to hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\ - Do not allow the mech to leave the station's vicinity or allow it to be destroyed." - cost = 30 - upgrade = TRUE - unlock_text = "Virus package compiled. Select a target mech at any time. You must remain on the station at all times. Loss of signal will result in total system lockout." - unlock_sound = 'sound/mecha/nominal.ogg' - -/datum/AI_Module/large/mecha_domination/upgrade(mob/living/silicon/ai/AI) - AI.can_dominate_mechs = TRUE //Yep. This is all it does. Honk! - -//Thermal Sensor Override: Unlocks the ability to disable all fire alarms from doing their job. -/datum/AI_Module/large/break_fire_alarms - module_name = "Thermal Sensor Override" - mod_pick_name = "burnpigs" - description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \ - Anyone can check the fire alarm's interface and may be tipped off by its status." - one_purchase = TRUE - cost = 25 - power_type = /datum/action/innate/ai/break_fire_alarms - unlock_text = "You replace the thermal sensing capabilities of all fire alarms with a manual override, allowing you to turn them off at will." - -/datum/action/innate/ai/break_fire_alarms - name = "Override Thermal Sensors" - desc = "Disables the automatic temperature sensing on all fire alarms, making them effectively useless." - button_icon_state = "break_fire_alarms" - uses = 1 - -/datum/action/innate/ai/break_fire_alarms/Activate() - for(var/obj/machinery/firealarm/F in GLOB.machines) - if(!is_station_level(F.z)) - continue - F.emagged = TRUE - to_chat(owner, "All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.") - owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) - -//Air Alarm Safety Override: Unlocks the ability to enable flooding on all air alarms. -/datum/AI_Module/large/break_air_alarms - module_name = "Air Alarm Safety Override" - mod_pick_name = "allow_flooding" - description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \ - Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality." - one_purchase = TRUE - cost = 50 - power_type = /datum/action/innate/ai/break_air_alarms - unlock_text = "You remove the safety overrides on all air alarms, but you leave the confirm prompts open. You can hit 'Yes' at any time... you bastard." - -/datum/action/innate/ai/break_air_alarms - name = "Override Air Alarm Safeties" - desc = "Enables the Flood setting on all air alarms." - button_icon_state = "break_air_alarms" - uses = 1 - -/datum/action/innate/ai/break_air_alarms/Activate() - for(var/obj/machinery/alarm/AA in GLOB.machines) - if(!is_station_level(AA.z)) - continue - AA.emagged = TRUE - to_chat(owner, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.") - owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) - - -//Overload Machine: Allows the AI to overload a machine, detonating it after a delay. Two uses per purchase. -/datum/AI_Module/small/overload_machine - module_name = "Machine Overload" - mod_pick_name = "overload" - description = "Overheats an electrical machine, causing a small explosion and destroying it. Two uses per purchase." - cost = 20 - power_type = /datum/action/innate/ai/ranged/overload_machine - unlock_text = "You enable the ability for the station's APCs to direct intense energy into machinery." - -/datum/action/innate/ai/ranged/overload_machine - name = "Overload Machine" - desc = "Overheats a machine, causing a small explosion after a short time." - button_icon_state = "overload_machine" - uses = 2 - linked_ability_type = /obj/effect/proc_holder/ranged_ai/overload_machine - -/datum/action/innate/ai/ranged/overload_machine/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M) - if(M && !QDELETED(M)) - explosion(get_turf(M), 0,1,1,0) - if(M) //to check if the explosion killed it before we try to delete it - qdel(M) - -/obj/effect/proc_holder/ranged_ai/overload_machine - active = FALSE - ranged_mousepointer = 'icons/effects/overload_machine_target.dmi' - enable_text = "You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel." - disable_text = "You release your hold on the powernet." - -/obj/effect/proc_holder/ranged_ai/overload_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) - if(..()) - return - if(ranged_ability_user.incapacitated()) - remove_ranged_ability() - return - if(!istype(target)) - to_chat(ranged_ability_user, "You can only overload machines!") - return - - ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, 0) - attached_action.adjust_uses(-1) - if(attached_action && attached_action.uses) - attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." - attached_action.UpdateButtonIcon() - target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") - addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/overload_machine.proc/detonate_machine, target), 50) //kaboom! - remove_ranged_ability(ranged_ability_user, "Overloading machine circuitry...") - return TRUE - - -//Override Machine: Allows the AI to override a machine, animating it into an angry, living version of itself. -/datum/AI_Module/small/override_machine - module_name = "Machine Override" - mod_pick_name = "override" - description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. Four uses." - cost = 30 - power_type = /datum/action/innate/ai/ranged/override_machine - unlock_text = "You procure a virus from the Space Dark Web and distribute it to the station's machines." - -/datum/action/innate/ai/ranged/override_machine - name = "Override Machine" - desc = "Animates a targeted machine, causing it to attack anyone nearby." - button_icon_state = "override_machine" - uses = 4 - linked_ability_type = /obj/effect/proc_holder/ranged_ai/override_machine - -/datum/action/innate/ai/ranged/override_machine/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M) - if(M && !QDELETED(M)) - new/mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, owner, 1) - -/obj/effect/proc_holder/ranged_ai/override_machine - active = FALSE - ranged_mousepointer = 'icons/effects/override_machine_target.dmi' - enable_text = "You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel." - disable_text = "You release your hold on the powernet." - -/obj/effect/proc_holder/ranged_ai/override_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) - if(..()) - return - if(ranged_ability_user.incapacitated()) - remove_ranged_ability() - return - if(!istype(target)) - to_chat(ranged_ability_user, "You can only animate machines!") - return - if(!target.can_be_overridden()) - to_chat(ranged_ability_user, "That machine can't be overridden!") - return - - ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, 0) - attached_action.adjust_uses(-1) - if(attached_action && attached_action.uses) - attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." - attached_action.UpdateButtonIcon() - target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") - addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/override_machine.proc/animate_machine, target), 50) //kabeep! - remove_ranged_ability(ranged_ability_user, "Sending override signal...") - return TRUE - - -//Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting. -/datum/AI_Module/large/place_cyborg_transformer - module_name = "Robotic Factory (Removes Shunting)" - mod_pick_name = "cyborgtransformer" - description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside." - cost = 100 - one_purchase = TRUE - power_type = /datum/action/innate/ai/place_transformer - unlock_text = "You prepare a robotics factory for deployment." - unlock_sound = 'sound/machines/ping.ogg' - -/datum/action/innate/ai/place_transformer - name = "Place Robotics Factory" - desc = "Places a machine that converts humans into cyborgs. Conveyor belts included!" - button_icon_state = "robotic_factory" - uses = 1 - auto_use_uses = FALSE //So we can attempt multiple times - var/list/turfOverlays - -/datum/action/innate/ai/place_transformer/New() - ..() - for(var/i in 1 to 3) - var/image/I = image("icon"='icons/turf/overlays.dmi') - LAZYADD(turfOverlays, I) - -/datum/action/innate/ai/place_transformer/Activate() - if(!owner_AI.can_place_transformer(src)) - return - active = TRUE - if(alert(owner, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No") == "No") - active = FALSE - return - if(!owner_AI.can_place_transformer(src)) - active = FALSE - return - var/turf/T = get_turf(owner_AI.eyeobj) - new /obj/machinery/transformer/conveyor(T) - playsound(T, 'sound/effects/phasein.ogg', 100, 1) - owner_AI.can_shunt = FALSE - to_chat(owner, "You are no longer able to shunt your core to APCs.") - adjust_uses(-1) - -/mob/living/silicon/ai/proc/remove_transformer_image(client/C, image/I, turf/T) - if(C && I.loc == T) - C.images -= I - -/mob/living/silicon/ai/proc/can_place_transformer(datum/action/innate/ai/place_transformer/action) - if(!eyeobj || !isturf(loc) || incapacitated() || !action) - return - var/turf/middle = get_turf(eyeobj) - var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z)) - var/alert_msg = "There isn't enough room! Make sure you are placing the machine in a clear area and on a floor." - var/success = TRUE - for(var/n in 1 to 3) //We have to do this instead of iterating normally because of how overlay images are handled - var/turf/T = turfs[n] - if(!isfloorturf(T)) - success = FALSE - var/datum/camerachunk/C = cameranet.getCameraChunk(T.x, T.y, T.z) - if(!C.visibleTurfs[T]) - alert_msg = "You don't have camera vision of this location!" - success = FALSE - for(var/atom/movable/AM in T.contents) - if(AM.density) - alert_msg = "That area must be clear of objects!" - success = FALSE - var/image/I = action.turfOverlays[n] - I.loc = T - client.images += I - I.icon_state = "[success ? "green" : "red"]Overlay" //greenOverlay and redOverlay for success and failure respectively - addtimer(CALLBACK(src, .proc/remove_transformer_image, client, I, T), 30) - if(!success) - to_chat(src, "[alert_msg]") - return success - -//Blackout: Overloads a random number of lights across the station. Three uses. -/datum/AI_Module/small/blackout - module_name = "Blackout" - mod_pick_name = "blackout" - description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. Three uses." - cost = 15 - power_type = /datum/action/innate/ai/blackout - unlock_text = "You hook into the powernet and route bonus power towards the station's lighting." - -/datum/action/innate/ai/blackout - name = "Blackout" - desc = "Overloads random lights across the station." - button_icon_state = "blackout" - uses = 3 - auto_use_uses = FALSE - -/datum/action/innate/ai/blackout/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/blackout/Activate() - for(var/obj/machinery/power/apc/apc in GLOB.apcs) - if(prob(30 * apc.overload)) - apc.overload_lighting() - else - apc.overload++ - to_chat(owner, "Overcurrent applied to the powernet.") - owner.playsound_local(owner, "sparks", 50, 0) - adjust_uses(-1) - if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe - desc = "[initial(desc)] It has [uses] use\s remaining." - UpdateButtonIcon() - -//Reactivate Camera Network: Reactivates up to 30 cameras across the station. -/datum/AI_Module/small/reactivate_cameras - module_name = "Reactivate Camera Network" - mod_pick_name = "recam" - description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras." - cost = 10 - one_purchase = TRUE - power_type = /datum/action/innate/ai/reactivate_cameras - unlock_text = "You deploy nanomachines to the cameranet." - -/datum/action/innate/ai/reactivate_cameras - name = "Reactivate Cameras" - desc = "Reactivates disabled cameras across the station; remaining uses can be used later." - button_icon_state = "reactivate_cameras" - uses = 30 - auto_use_uses = FALSE - cooldown_period = 30 - -/datum/action/innate/ai/reactivate_cameras/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/reactivate_cameras/Activate() - var/fixed_cameras = 0 - for(var/V in cameranet.cameras) - if(!uses) - break - var/obj/machinery/camera/C = V - if(!C.status || C.view_range != initial(C.view_range)) - C.toggle_cam(owner_AI, 0) //Reactivates the camera based on status. Badly named proc. - C.view_range = initial(C.view_range) - fixed_cameras++ - uses-- //Not adjust_uses() so it doesn't automatically delete or show a message - to_chat(owner, "Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses].") - owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0) - adjust_uses(0, TRUE) //Checks the uses remaining - if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe - desc = "[initial(desc)] It has [uses] use\s remaining." - UpdateButtonIcon() - -//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision. -/datum/AI_Module/large/upgrade_cameras - module_name = "Upgrade Camera Network" - mod_pick_name = "upgradecam" - description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-proofing and light-amplified X-ray vision." //I <3 pointless technobabble - //This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR. - one_purchase = TRUE - cost = 35 //Decent price for omniscience! - upgrade = TRUE - unlock_text = "OTA firmware distribution complete! Cameras upgraded: CAMSUPGRADED. Light amplification system online." - unlock_sound = 'sound/items/rped.ogg' - -/datum/AI_Module/large/upgrade_cameras/upgrade(mob/living/silicon/ai/AI) - AI.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //Night-vision, without which X-ray would be very limited in power. - AI.update_sight() - var/upgraded_cameras = 0 - - for(var/V in cameranet.cameras) - var/obj/machinery/camera/C = V - if(C.assembly) - var/upgraded = FALSE - - if(!C.isXRay()) - C.upgradeXRay() - //Update what it can see. - cameranet.updateVisibility(C, 0) - upgraded = TRUE - - if(!C.isEmpProof()) - C.upgradeEmpProof() - upgraded = TRUE - - if(upgraded) - upgraded_cameras++ - - unlock_text = replacetext(unlock_text, "CAMSUPGRADED", "[upgraded_cameras]") //This works, since unlock text is called after upgrade() - -/datum/AI_Module/large/eavesdrop - module_name = "Enhanced Surveillance" - mod_pick_name = "eavesdrop" - description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations." - cost = 30 - one_purchase = TRUE - upgrade = TRUE - unlock_text = "OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online." - unlock_sound = 'sound/items/rped.ogg' - -/datum/AI_Module/large/eavesdrop/upgrade(mob/living/silicon/ai/AI) - if(AI.eyeobj) - AI.eyeobj.relay_speech = TRUE - +//The malf AI action subtype. All malf actions are subtypes of this. +/datum/action/innate/ai + name = "AI Action" + desc = "You aren't entirely sure what this does, but it's very beepy and boopy." + background_icon_state = "bg_tech_blue" + var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time + var/uses //If we have multiple uses of the same power + var/auto_use_uses = TRUE //If we automatically use up uses on each activation + var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules + +/datum/action/innate/ai/Grant(mob/living/L) + . = ..() + if(!isAI(owner)) + WARNING("AI action [name] attempted to grant itself to non-AI mob [L.real_name] ([L.key])!") + qdel(src) + else + owner_AI = owner + +/datum/action/innate/ai/IsAvailable() + . = ..() + if(owner_AI && owner_AI.malf_cooldown > world.time) + return + +/datum/action/innate/ai/Trigger() + . = ..() + if(auto_use_uses) + adjust_uses(-1) + if(cooldown_period) + owner_AI.malf_cooldown = world.time + cooldown_period + +/datum/action/innate/ai/proc/adjust_uses(amt, silent) + uses += amt + if(!silent && uses) + to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") + if(!uses) + if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! + to_chat(owner, "[name] has run out of uses!") + qdel(src) + +//Framework for ranged abilities that can have different effects by left-clicking stuff. +/datum/action/innate/ai/ranged + name = "Ranged AI Action" + auto_use_uses = FALSE //This is so we can do the thing and disable/enable freely without having to constantly add uses + var/obj/effect/proc_holder/ranged_ai/linked_ability //The linked proc holder that contains the actual ability code + var/linked_ability_type //The path of our linked ability + +/datum/action/innate/ai/ranged/New() + if(!linked_ability_type) + WARNING("Ranged AI action [name] attempted to spawn without a linked ability!") + qdel(src) //uh oh! + return + linked_ability = new linked_ability_type() + linked_ability.attached_action = src + ..() + +/datum/action/innate/ai/ranged/adjust_uses(amt, silent) + uses += amt + if(!silent && uses) + to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") + if(!uses) + if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! + to_chat(owner, "[name] has run out of uses!") + Remove(owner) + QDEL_IN(src, 100) //let any active timers on us finish up + +/datum/action/innate/ai/ranged/Destroy() + QDEL_NULL(linked_ability) + return ..() + +/datum/action/innate/ai/ranged/Activate() + linked_ability.toggle(owner) + return TRUE + +//The actual ranged proc holder. +/obj/effect/proc_holder/ranged_ai + var/enable_text = "Hello World!" //Appears when the user activates the ability + var/disable_text = "Goodbye Cruel World!" //Context clues! + var/datum/action/innate/ai/ranged/attached_action + +/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user) + if(active) + remove_ranged_ability(user, disable_text) + else + add_ranged_ability(user, enable_text) + +//The datum and interface for the malf unlock menu, which lets them choose actions to unlock. +/datum/module_picker + var/temp + var/processing_time = 50 + var/list/possible_modules + +/datum/module_picker/New() + possible_modules = list() + for(var/type in typesof(/datum/AI_Module)) + var/datum/AI_Module/AM = new type + if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade) + possible_modules += AM + +/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI. + for(var/datum/AI_Module/AM in possible_modules) + for(var/datum/action/A in AI.actions) + if(istype(A, initial(AM.power_type))) + qdel(A) + +/datum/module_picker/proc/use(user as mob) + var/dat + dat += {"Select use of processing time: (currently #[processing_time] left.)
    +
    + Install Module:
    + The number afterwards is the amount of processing time it consumes.
    "} + for(var/datum/AI_Module/large/module in possible_modules) + dat += "[module.module_name]\[?\] ([module.cost])
    " + for(var/datum/AI_Module/small/module in possible_modules) + dat += "[module.module_name]\[?\] ([module.cost])
    " + dat += "
    " + if(temp) + dat += "[temp]" + var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu", 400, 500) + popup.set_content(dat) + popup.open() + return + +/datum/module_picker/Topic(href, href_list) + ..() + + if(!isAI(usr)) + return + var/mob/living/silicon/ai/A = usr + + if(A.stat == DEAD) + to_chat(A, "You are already dead!") + return + + for(var/datum/AI_Module/AM in possible_modules) + if (href_list[AM.mod_pick_name]) + + // Cost check + if(AM.cost > processing_time) + temp = "You cannot afford this module." + break + + var/datum/action/innate/ai/action = locate(AM.power_type) in A.actions + + // Give the power and take away the money. + if(AM.upgrade) //upgrade and upgrade() are separate, be careful! + AM.upgrade(A) + possible_modules -= AM + to_chat(A, AM.unlock_text) + A.playsound_local(A, AM.unlock_sound, 50, 0) + else + if(AM.power_type) + if(!action) //Unlocking for the first time + var/datum/action/AC = new AM.power_type + AC.Grant(A) + A.current_modules += new AM.type + temp = AM.description + if(AM.one_purchase) + possible_modules -= AM + if(AM.unlock_text) + to_chat(A, AM.unlock_text) + if(AM.unlock_sound) + A.playsound_local(A, AM.unlock_sound, 50, 0) + else //Adding uses to an existing module + action.uses += initial(action.uses) + action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining." + action.UpdateButtonIcon() + temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!" + processing_time -= AM.cost + + if(href_list["showdesc"]) + if(AM.mod_pick_name == href_list["showdesc"]) + temp = AM.description + use(usr) + +//The base module type, which holds info about each ability. +/datum/AI_Module + var/module_name + var/mod_pick_name + var/description = "" + var/engaged = 0 + var/cost = 5 + var/one_purchase = FALSE //If this module can only be purchased once. This always applies to upgrades, even if the variable is set to false. + var/power_type = /datum/action/innate/ai //If the module gives an active ability, use this. Mutually exclusive with upgrade. + var/upgrade //If the module gives a passive upgrade, use this. Mutually exclusive with power_type. + var/unlock_text = "Hello World!" //Text shown when an ability is unlocked + var/unlock_sound //Sound played when an ability is unlocked + var/uses + +/datum/AI_Module/proc/upgrade(mob/living/silicon/ai/AI) //Apply upgrades! + return + +/datum/AI_Module/large //Big, powerful stuff that can only be used once. + +/datum/AI_Module/small //Weak, usually localized stuff with multiple uses. + +//Doomsday Device: Starts the self-destruct timer. It can only be stopped by killing the AI completely. +/datum/AI_Module/large/nuke_station + module_name = "Doomsday Device" + mod_pick_name = "nukestation" + description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay. Can only be used while on the station, will fail if your core is moved off station or destroyed." + cost = 130 + one_purchase = TRUE + power_type = /datum/action/innate/ai/nuke_station + unlock_text = "You slowly, carefully, establish a connection with the on-station self-destruct. You can now activate it at any time." + unlock_sound = 'sound/items/timer.ogg' + +/datum/action/innate/ai/nuke_station + name = "Doomsday Device" + desc = "Activates the doomsday device. This is not reversible." + button_icon_state = "doomsday_device" + auto_use_uses = FALSE + +/datum/action/innate/ai/nuke_station/Activate() + var/turf/T = get_turf(owner) + if(!istype(T) || !is_station_level(T.z)) + to_chat(owner, "You cannot activate the doomsday device while off-station!") + return + if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;") + return + if(active) + return //prevent the AI from activating an already active doomsday + active = TRUE + set_us_up_the_bomb() + +/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb() + to_chat(owner_AI, "Nuclear device armed.") + GLOB.event_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", new_sound = 'sound/AI/aimalf.ogg') + set_security_level("delta") + owner_AI.nuking = TRUE + var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(owner_AI) + owner_AI.doomsday_device = DOOM + owner_AI.doomsday_device.start() + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + for(var/mob/living/silicon/ai/A in GLOB.ai_list) + if((A.stat != DEAD) && A.nuking) + point.the_disk = A //The pinpointer now tracks the AI core + qdel(src) + +/obj/machinery/doomsday_device + icon = 'icons/obj/machines/nuke_terminal.dmi' + name = "doomsday device" + icon_state = "nuclearbomb_base" + desc = "A weapon which disintegrates all organic life in a large area." + anchored = 1 + density = 1 + atom_say_verb = "blares" + speed_process = TRUE // Disgusting fix. Please remove once #12952 is merged + var/timing = 0 + var/default_timer = 4500 + var/detonation_timer + var/announced = 0 + +/obj/machinery/doomsday_device/Destroy() + STOP_PROCESSING(SSfastprocess, src) + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') + return ..() + +/obj/machinery/doomsday_device/proc/start() + detonation_timer = world.time + default_timer + timing = 1 + START_PROCESSING(SSfastprocess, src) + SSshuttle.emergencyNoEscape = 1 + +/obj/machinery/doomsday_device/proc/seconds_remaining() + . = max(0, (round(detonation_timer - world.time) / 10)) + +/obj/machinery/doomsday_device/process() + var/turf/T = get_turf(src) + if(!T || !is_station_level(T.z)) + GLOB.minor_announcement.Announce("DOOMSDAY DEVICE OUT OF STATION RANGE, ABORTING", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') + qdel(src) + if(!timing) + STOP_PROCESSING(SSfastprocess, src) + return + var/sec_left = seconds_remaining() + if(sec_left <= 0) + timing = 0 + detonate(T.z) + qdel(src) + else + if(!(sec_left % 60) && !announced) + var/message = "[sec_left] SECONDS UNTIL DOOMSDAY DEVICE ACTIVATION!" + GLOB.minor_announcement.Announce(message, "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') + announced = 10 + announced = max(0, announced-1) + +/obj/machinery/doomsday_device/proc/detonate(z_level = 1) + for(var/mob/M in GLOB.player_list) + M << 'sound/machines/alarm.ogg' + sleep(100) + for(var/mob/living/L in GLOB.mob_list) + var/turf/T = get_turf(L) + if(!T || T.z != z_level) + continue + if(issilicon(L)) + continue + to_chat(L, "The blast wave from [src] tears you atom from atom!") + L.dust() + to_chat(world, "The AI cleansed the station of life with the doomsday device!") + SSticker.force_ending = 1 + +//AI Turret Upgrade: Increases the health and damage of all turrets. +/datum/AI_Module/large/upgrade_turrets + module_name = "AI Turret Upgrade" + mod_pick_name = "turret" + description = "Improves the power and health of all AI turrets. This effect is permanent." + cost = 30 + upgrade = TRUE + unlock_text = "You establish a power diversion to your turrets, upgrading their health and damage." + unlock_sound = 'sound/items/rped.ogg' + +/datum/AI_Module/large/upgrade_turrets/upgrade(mob/living/silicon/ai/AI) + for(var/obj/machinery/porta_turret/turret in GLOB.machines) + var/turf/T = get_turf(turret) + if(is_station_level(T.z)) + turret.health += 30 + turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR. + turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg' + +//Hostile Station Lockdown: Locks, bolts, and electrifies every airlock on the station. After 90 seconds, the doors reset. +/datum/AI_Module/large/lockdown + module_name = "Hostile Station Lockdown" + mod_pick_name = "lockdown" + description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds, briefly \ + opening all doors on the station." + cost = 30 + one_purchase = TRUE + power_type = /datum/action/innate/ai/lockdown + unlock_text = "You upload a sleeper trojan into the door control systems. You can send a signal to set it off at any time." + +/datum/action/innate/ai/lockdown + name = "Lockdown" + desc = "Closes, bolts, and depowers every airlock, firelock, and blast door on the station. After 90 seconds, they will reset themselves." + button_icon_state = "lockdown" + uses = 1 + +/datum/action/innate/ai/lockdown/Activate() + for(var/obj/machinery/door/D in GLOB.airlocks) + if(!is_station_level(D.z)) + continue + INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, owner) + addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900) + + post_status("alert", "lockdown") + + GLOB.minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert") + to_chat(owner, "Lockdown Initiated. Network reset in 90 seconds.") + spawn(900) + GLOB.minor_announcement.Announce("Automatic system reboot complete. Have a secure day.","Network reset:") + +//Destroy RCDs: Detonates all non-cyborg RCDs on the station. +/datum/AI_Module/large/destroy_rcd + module_name = "Destroy RCDs" + mod_pick_name = "rcd" + description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Construction Devices on the station." + cost = 25 + one_purchase = TRUE + power_type = /datum/action/innate/ai/destroy_rcds + unlock_text = "After some improvisation, you rig your onboard radio to be able to send a signal to detonate all RCDs." + +/datum/action/innate/ai/destroy_rcds + name = "Destroy RCDs" + desc = "Detonate all non-cyborg RCDs on the station." + button_icon_state = "detonate_rcds" + uses = 1 + cooldown_period = 100 + +/datum/action/innate/ai/destroy_rcds/Activate() + for(var/obj/item/rcd/RCD in GLOB.rcd_list) + if(!istype(RCD, /obj/item/rcd/borg)) //Ensures that cyborg RCDs are spared. + RCD.detonate_pulse() + + to_chat(owner, "RCD detonation pulse emitted.") + owner.playsound_local(owner, 'sound/machines/twobeep.ogg', 50, 0) + +//Unlock Mech Domination: Unlocks the ability to dominate mechs. Big shocker, right? +/datum/AI_Module/large/mecha_domination + module_name = "Unlock Mech Domination" + mod_pick_name = "mechjack" + description = "Allows you to hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\ + Do not allow the mech to leave the station's vicinity or allow it to be destroyed." + cost = 30 + upgrade = TRUE + unlock_text = "Virus package compiled. Select a target mech at any time. You must remain on the station at all times. Loss of signal will result in total system lockout." + unlock_sound = 'sound/mecha/nominal.ogg' + +/datum/AI_Module/large/mecha_domination/upgrade(mob/living/silicon/ai/AI) + AI.can_dominate_mechs = TRUE //Yep. This is all it does. Honk! + +//Thermal Sensor Override: Unlocks the ability to disable all fire alarms from doing their job. +/datum/AI_Module/large/break_fire_alarms + module_name = "Thermal Sensor Override" + mod_pick_name = "burnpigs" + description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \ + Anyone can check the fire alarm's interface and may be tipped off by its status." + one_purchase = TRUE + cost = 25 + power_type = /datum/action/innate/ai/break_fire_alarms + unlock_text = "You replace the thermal sensing capabilities of all fire alarms with a manual override, allowing you to turn them off at will." + +/datum/action/innate/ai/break_fire_alarms + name = "Override Thermal Sensors" + desc = "Disables the automatic temperature sensing on all fire alarms, making them effectively useless." + button_icon_state = "break_fire_alarms" + uses = 1 + +/datum/action/innate/ai/break_fire_alarms/Activate() + for(var/obj/machinery/firealarm/F in GLOB.machines) + if(!is_station_level(F.z)) + continue + F.emagged = TRUE + to_chat(owner, "All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.") + owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) + +//Air Alarm Safety Override: Unlocks the ability to enable flooding on all air alarms. +/datum/AI_Module/large/break_air_alarms + module_name = "Air Alarm Safety Override" + mod_pick_name = "allow_flooding" + description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \ + Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality." + one_purchase = TRUE + cost = 50 + power_type = /datum/action/innate/ai/break_air_alarms + unlock_text = "You remove the safety overrides on all air alarms, but you leave the confirm prompts open. You can hit 'Yes' at any time... you bastard." + +/datum/action/innate/ai/break_air_alarms + name = "Override Air Alarm Safeties" + desc = "Enables the Flood setting on all air alarms." + button_icon_state = "break_air_alarms" + uses = 1 + +/datum/action/innate/ai/break_air_alarms/Activate() + for(var/obj/machinery/alarm/AA in GLOB.machines) + if(!is_station_level(AA.z)) + continue + AA.emagged = TRUE + to_chat(owner, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.") + owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) + + +//Overload Machine: Allows the AI to overload a machine, detonating it after a delay. Two uses per purchase. +/datum/AI_Module/small/overload_machine + module_name = "Machine Overload" + mod_pick_name = "overload" + description = "Overheats an electrical machine, causing a small explosion and destroying it. Two uses per purchase." + cost = 20 + power_type = /datum/action/innate/ai/ranged/overload_machine + unlock_text = "You enable the ability for the station's APCs to direct intense energy into machinery." + +/datum/action/innate/ai/ranged/overload_machine + name = "Overload Machine" + desc = "Overheats a machine, causing a small explosion after a short time." + button_icon_state = "overload_machine" + uses = 2 + linked_ability_type = /obj/effect/proc_holder/ranged_ai/overload_machine + +/datum/action/innate/ai/ranged/overload_machine/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M) + if(M && !QDELETED(M)) + explosion(get_turf(M), 0,1,1,0) + if(M) //to check if the explosion killed it before we try to delete it + qdel(M) + +/obj/effect/proc_holder/ranged_ai/overload_machine + active = FALSE + ranged_mousepointer = 'icons/effects/overload_machine_target.dmi' + enable_text = "You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel." + disable_text = "You release your hold on the powernet." + +/obj/effect/proc_holder/ranged_ai/overload_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) + if(..()) + return + if(ranged_ability_user.incapacitated()) + remove_ranged_ability() + return + if(!istype(target)) + to_chat(ranged_ability_user, "You can only overload machines!") + return + + ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, 0) + attached_action.adjust_uses(-1) + if(attached_action && attached_action.uses) + attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." + attached_action.UpdateButtonIcon() + target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") + addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/overload_machine.proc/detonate_machine, target), 50) //kaboom! + remove_ranged_ability(ranged_ability_user, "Overloading machine circuitry...") + return TRUE + + +//Override Machine: Allows the AI to override a machine, animating it into an angry, living version of itself. +/datum/AI_Module/small/override_machine + module_name = "Machine Override" + mod_pick_name = "override" + description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. Four uses." + cost = 30 + power_type = /datum/action/innate/ai/ranged/override_machine + unlock_text = "You procure a virus from the Space Dark Web and distribute it to the station's machines." + +/datum/action/innate/ai/ranged/override_machine + name = "Override Machine" + desc = "Animates a targeted machine, causing it to attack anyone nearby." + button_icon_state = "override_machine" + uses = 4 + linked_ability_type = /obj/effect/proc_holder/ranged_ai/override_machine + +/datum/action/innate/ai/ranged/override_machine/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M) + if(M && !QDELETED(M)) + new/mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, owner, 1) + +/obj/effect/proc_holder/ranged_ai/override_machine + active = FALSE + ranged_mousepointer = 'icons/effects/override_machine_target.dmi' + enable_text = "You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel." + disable_text = "You release your hold on the powernet." + +/obj/effect/proc_holder/ranged_ai/override_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) + if(..()) + return + if(ranged_ability_user.incapacitated()) + remove_ranged_ability() + return + if(!istype(target)) + to_chat(ranged_ability_user, "You can only animate machines!") + return + if(!target.can_be_overridden()) + to_chat(ranged_ability_user, "That machine can't be overridden!") + return + + ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, 0) + attached_action.adjust_uses(-1) + if(attached_action && attached_action.uses) + attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." + attached_action.UpdateButtonIcon() + target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") + addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/override_machine.proc/animate_machine, target), 50) //kabeep! + remove_ranged_ability(ranged_ability_user, "Sending override signal...") + return TRUE + + +//Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting. +/datum/AI_Module/large/place_cyborg_transformer + module_name = "Robotic Factory (Removes Shunting)" + mod_pick_name = "cyborgtransformer" + description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside." + cost = 100 + one_purchase = TRUE + power_type = /datum/action/innate/ai/place_transformer + unlock_text = "You prepare a robotics factory for deployment." + unlock_sound = 'sound/machines/ping.ogg' + +/datum/action/innate/ai/place_transformer + name = "Place Robotics Factory" + desc = "Places a machine that converts humans into cyborgs. Conveyor belts included!" + button_icon_state = "robotic_factory" + uses = 1 + auto_use_uses = FALSE //So we can attempt multiple times + var/list/turfOverlays + +/datum/action/innate/ai/place_transformer/New() + ..() + for(var/i in 1 to 3) + var/image/I = image("icon"='icons/turf/overlays.dmi') + LAZYADD(turfOverlays, I) + +/datum/action/innate/ai/place_transformer/Activate() + if(!owner_AI.can_place_transformer(src)) + return + active = TRUE + if(alert(owner, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No") == "No") + active = FALSE + return + if(!owner_AI.can_place_transformer(src)) + active = FALSE + return + var/turf/T = get_turf(owner_AI.eyeobj) + new /obj/machinery/transformer/conveyor(T) + playsound(T, 'sound/effects/phasein.ogg', 100, 1) + owner_AI.can_shunt = FALSE + to_chat(owner, "You are no longer able to shunt your core to APCs.") + adjust_uses(-1) + +/mob/living/silicon/ai/proc/remove_transformer_image(client/C, image/I, turf/T) + if(C && I.loc == T) + C.images -= I + +/mob/living/silicon/ai/proc/can_place_transformer(datum/action/innate/ai/place_transformer/action) + if(!eyeobj || !isturf(loc) || incapacitated() || !action) + return + var/turf/middle = get_turf(eyeobj) + var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z)) + var/alert_msg = "There isn't enough room! Make sure you are placing the machine in a clear area and on a floor." + var/success = TRUE + for(var/n in 1 to 3) //We have to do this instead of iterating normally because of how overlay images are handled + var/turf/T = turfs[n] + if(!isfloorturf(T)) + success = FALSE + var/datum/camerachunk/C = GLOB.cameranet.getCameraChunk(T.x, T.y, T.z) + if(!C.visibleTurfs[T]) + alert_msg = "You don't have camera vision of this location!" + success = FALSE + for(var/atom/movable/AM in T.contents) + if(AM.density) + alert_msg = "That area must be clear of objects!" + success = FALSE + var/image/I = action.turfOverlays[n] + I.loc = T + client.images += I + I.icon_state = "[success ? "green" : "red"]Overlay" //greenOverlay and redOverlay for success and failure respectively + addtimer(CALLBACK(src, .proc/remove_transformer_image, client, I, T), 30) + if(!success) + to_chat(src, "[alert_msg]") + return success + +//Blackout: Overloads a random number of lights across the station. Three uses. +/datum/AI_Module/small/blackout + module_name = "Blackout" + mod_pick_name = "blackout" + description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. Three uses." + cost = 15 + power_type = /datum/action/innate/ai/blackout + unlock_text = "You hook into the powernet and route bonus power towards the station's lighting." + +/datum/action/innate/ai/blackout + name = "Blackout" + desc = "Overloads random lights across the station." + button_icon_state = "blackout" + uses = 3 + auto_use_uses = FALSE + +/datum/action/innate/ai/blackout/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/blackout/Activate() + for(var/obj/machinery/power/apc/apc in GLOB.apcs) + if(prob(30 * apc.overload)) + apc.overload_lighting() + else + apc.overload++ + to_chat(owner, "Overcurrent applied to the powernet.") + owner.playsound_local(owner, "sparks", 50, 0) + adjust_uses(-1) + if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe + desc = "[initial(desc)] It has [uses] use\s remaining." + UpdateButtonIcon() + +//Reactivate Camera Network: Reactivates up to 30 cameras across the station. +/datum/AI_Module/small/reactivate_cameras + module_name = "Reactivate Camera Network" + mod_pick_name = "recam" + description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras." + cost = 10 + one_purchase = TRUE + power_type = /datum/action/innate/ai/reactivate_cameras + unlock_text = "You deploy nanomachines to the cameranet." + +/datum/action/innate/ai/reactivate_cameras + name = "Reactivate Cameras" + desc = "Reactivates disabled cameras across the station; remaining uses can be used later." + button_icon_state = "reactivate_cameras" + uses = 30 + auto_use_uses = FALSE + cooldown_period = 30 + +/datum/action/innate/ai/reactivate_cameras/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/reactivate_cameras/Activate() + var/fixed_cameras = 0 + for(var/V in GLOB.cameranet.cameras) + if(!uses) + break + var/obj/machinery/camera/C = V + if(!C.status || C.view_range != initial(C.view_range)) + C.toggle_cam(owner_AI, 0) //Reactivates the camera based on status. Badly named proc. + C.view_range = initial(C.view_range) + fixed_cameras++ + uses-- //Not adjust_uses() so it doesn't automatically delete or show a message + to_chat(owner, "Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses].") + owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0) + adjust_uses(0, TRUE) //Checks the uses remaining + if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe + desc = "[initial(desc)] It has [uses] use\s remaining." + UpdateButtonIcon() + +//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision. +/datum/AI_Module/large/upgrade_cameras + module_name = "Upgrade Camera Network" + mod_pick_name = "upgradecam" + description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-proofing and light-amplified X-ray vision." //I <3 pointless technobabble + //This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR. + one_purchase = TRUE + cost = 35 //Decent price for omniscience! + upgrade = TRUE + unlock_text = "OTA firmware distribution complete! Cameras upgraded: CAMSUPGRADED. Light amplification system online." + unlock_sound = 'sound/items/rped.ogg' + +/datum/AI_Module/large/upgrade_cameras/upgrade(mob/living/silicon/ai/AI) + AI.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //Night-vision, without which X-ray would be very limited in power. + AI.update_sight() + var/upgraded_cameras = 0 + + for(var/V in GLOB.cameranet.cameras) + var/obj/machinery/camera/C = V + if(C.assembly) + var/upgraded = FALSE + + if(!C.isXRay()) + C.upgradeXRay() + //Update what it can see. + GLOB.cameranet.updateVisibility(C, 0) + upgraded = TRUE + + if(!C.isEmpProof()) + C.upgradeEmpProof() + upgraded = TRUE + + if(upgraded) + upgraded_cameras++ + + unlock_text = replacetext(unlock_text, "CAMSUPGRADED", "[upgraded_cameras]") //This works, since unlock text is called after upgrade() + +/datum/AI_Module/large/eavesdrop + module_name = "Enhanced Surveillance" + mod_pick_name = "eavesdrop" + description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations." + cost = 30 + one_purchase = TRUE + upgrade = TRUE + unlock_text = "OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online." + unlock_sound = 'sound/items/rped.ogg' + +/datum/AI_Module/large/eavesdrop/upgrade(mob/living/silicon/ai/AI) + if(AI.eyeobj) + AI.eyeobj.relay_speech = TRUE + diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm index ee0ef02d1dc..5cd46ca89b5 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -1,61 +1,61 @@ -/datum/game_mode/meteor - name = "meteor" - config_tag = "meteor" - var/const/initialmeteordelay = 6000 - var/wave = 1 - required_players = 35 - - -/datum/game_mode/meteor/announce() - to_chat(world, "The current game mode is - Meteor!") - to_chat(world, "The space station has been stuck in a major meteor shower. You must escape from the station or at least live.") - - -/datum/game_mode/meteor/post_setup() - spawn(rand(waittime_l, waittime_h)) - command_announcement.Announce("The station is on the path of an incoming wave of meteors. Reinforce the hull and prepare damage control parties.", "Incoming Meteors", 'sound/effects/siren.ogg') - spawn(initialmeteordelay) - sendmeteors() - ..() - - - -/datum/game_mode/meteor/proc/sendmeteors() - var/waveduration = world.time + rand(0,1000) + text2num("[wave]000") / 2 - var/waitduration = rand(3000,6000) - while(waveduration - world.time > 0) - sleep(max(65 - text2num("[wave]0") / 2, 40)) - spawn() spawn_meteors(6, meteors_normal) - wave++ - sleep(waitduration) - sendmeteors() - -/datum/game_mode/meteor/declare_completion() - var/text - var/survivors = 0 - for(var/mob/living/player in GLOB.player_list) - if(player.stat != DEAD) - var/turf/location = get_turf(player.loc) - if(!location) continue - - if(location.loc.type == SSshuttle.emergency.areaInstance.type) //didn't work in the switch for some reason - text += "
    [player.real_name] escaped on the emergency shuttle" - - else - switch(location.loc.type) - if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) - text += "
    [player.real_name] escaped in a life pod." - else - text += "
    [player.real_name] survived but is stranded without any hope of rescue." - survivors++ - - if(survivors) - to_chat(world, "The following survived the meteor storm:[text]") - else - to_chat(world, "Nobody survived the meteor storm!") - - feedback_set_details("round_end_result","meteor end - evacuation") - feedback_set("round_end_result", "Meteor survivors: [survivors]") - - ..() - return 1 +/datum/game_mode/meteor + name = "meteor" + config_tag = "meteor" + var/const/initialmeteordelay = 6000 + var/wave = 1 + required_players = 35 + + +/datum/game_mode/meteor/announce() + to_chat(world, "The current game mode is - Meteor!") + to_chat(world, "The space station has been stuck in a major meteor shower. You must escape from the station or at least live.") + + +/datum/game_mode/meteor/post_setup() + spawn(rand(waittime_l, waittime_h)) + GLOB.command_announcement.Announce("The station is on the path of an incoming wave of meteors. Reinforce the hull and prepare damage control parties.", "Incoming Meteors", 'sound/effects/siren.ogg') + spawn(initialmeteordelay) + sendmeteors() + ..() + + + +/datum/game_mode/meteor/proc/sendmeteors() + var/waveduration = world.time + rand(0,1000) + text2num("[wave]000") / 2 + var/waitduration = rand(3000,6000) + while(waveduration - world.time > 0) + sleep(max(65 - text2num("[wave]0") / 2, 40)) + spawn() spawn_meteors(6, GLOB.meteors_normal) + wave++ + sleep(waitduration) + sendmeteors() + +/datum/game_mode/meteor/declare_completion() + var/text + var/survivors = 0 + for(var/mob/living/player in GLOB.player_list) + if(player.stat != DEAD) + var/turf/location = get_turf(player.loc) + if(!location) continue + + if(location.loc.type == SSshuttle.emergency.areaInstance.type) //didn't work in the switch for some reason + text += "
    [player.real_name] escaped on the emergency shuttle" + + else + switch(location.loc.type) + if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) + text += "
    [player.real_name] escaped in a life pod." + else + text += "
    [player.real_name] survived but is stranded without any hope of rescue." + survivors++ + + if(survivors) + to_chat(world, "The following survived the meteor storm:[text]") + else + to_chat(world, "Nobody survived the meteor storm!") + + feedback_set_details("round_end_result","meteor end - evacuation") + feedback_set("round_end_result", "Meteor survivors: [survivors]") + + ..() + return 1 diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 5dafcee72a7..0743a6f686d 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -1,318 +1,318 @@ -//Meteors probability of spawning during a given wave -/var/list/meteors_normal = list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \ - /obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3) //for normal meteor event - -/var/list/meteors_threatening = list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \ - /obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3) //for threatening meteor event - -/var/list/meteors_catastrophic = list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \ - /obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1) //for catastrophic meteor event - -/var/list/meteors_dust = list(/obj/effect/meteor/dust) //for space dust event - -/var/list/meteors_gore = list(/obj/effect/meteor/gore) //Meaty Gore - -/var/list/meteors_ops = list(/obj/effect/meteor/goreops) //Meaty Ops - - -/////////////////////////////// -//Meteor spawning global procs -/////////////////////////////// -/proc/spawn_meteors(var/number = 10, var/list/meteortypes) - for(var/i = 0; i < number; i++) - spawn_meteor(meteortypes) - -/proc/spawn_meteor(var/list/meteortypes) - var/turf/pickedstart - var/turf/pickedgoal - var/max_i = 10//number of tries to spawn meteor. - while(!istype(pickedstart, /turf/space)) - var/startSide = pick(cardinal) - pickedstart = spaceDebrisStartLoc(startSide, 1) - pickedgoal = spaceDebrisFinishLoc(startSide, 1) - max_i-- - if(max_i<=0) - return - var/Me = pickweight(meteortypes) - var/obj/effect/meteor/M = new Me(pickedstart) - M.dest = pickedgoal - M.z_original = level_name_to_num(MAIN_STATION) - spawn(0) - walk_towards(M, M.dest, 1) - return - -/proc/spaceDebrisStartLoc(startSide, Z) - var/starty - var/startx - switch(startSide) - if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(EAST) - starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+1) - if(SOUTH) - starty = (TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(WEST) - starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - startx = (TRANSITIONEDGE+1) - var/turf/T = locate(startx, starty, Z) - return T - -/proc/spaceDebrisFinishLoc(startSide, Z) - var/endy - var/endx - switch(startSide) - if(NORTH) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(EAST) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE - if(SOUTH) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(WEST) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - var/turf/T = locate(endx, endy, Z) - return T - -/////////////////////// -//The meteor effect -////////////////////// - -/obj/effect/meteor - name = "the concept of meteor" - desc = "You should probably run instead of gawking at this." - icon = 'icons/obj/meteor.dmi' - icon_state = "small" - density = 1 - anchored = 1 - var/hits = 4 - var/hitpwr = 2 //Level of ex_act to be called on hit. - var/dest - pass_flags = PASSTABLE - var/heavy = 0 - var/meteorsound = 'sound/effects/meteorimpact.ogg' - var/z_original = 1 - - var/meteordrop = /obj/item/stack/ore/iron - var/dropamt = 2 - -/obj/effect/meteor/Move() - if(z != z_original || loc == dest) - qdel(src) - return - - . = ..() //process movement... - - if(.)//.. if did move, ram the turf we get in - var/turf/T = get_turf(loc) - ram_turf(T) - - if(prob(10) && !istype(T, /turf/space))//randomly takes a 'hit' from ramming - get_hit() - - return . - -/obj/effect/meteor/Destroy() - GLOB.meteor_list -= src - walk(src,0) //this cancels the walk_towards() proc - return ..() - -/obj/effect/meteor/New() - ..() - GLOB.meteor_list += src - SpinAnimation() - -/obj/effect/meteor/Bump(atom/A) - if(A) - ram_turf(get_turf(A)) - playsound(src.loc, meteorsound, 40, 1) - get_hit() - -/obj/effect/meteor/CanPass(atom/movable/mover, turf/target, height=0) - return istype(mover, /obj/effect/meteor) ? 1 : ..() - -/obj/effect/meteor/proc/ram_turf(var/turf/T) - //first bust whatever is in the turf - for(var/atom/A in T) - if(A != src) - A.ex_act(hitpwr) - - //then, ram the turf if it still exists - if(T) - T.ex_act(hitpwr) - -//process getting 'hit' by colliding with a dense object -//or randomly when ramming turfs -/obj/effect/meteor/proc/get_hit() - hits-- - if(hits <= 0) - make_debris() - meteor_effect(heavy) - qdel(src) - -/obj/effect/meteor/ex_act() - return - -/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/pickaxe)) - make_debris() - qdel(src) - return - return ..() - -/obj/effect/meteor/proc/make_debris() - for(var/throws = dropamt, throws > 0, throws--) - var/obj/item/O = new meteordrop(get_turf(src)) - O.throw_at(dest, 5, 10) - -/obj/effect/meteor/proc/meteor_effect(var/sound=1) - if(sound) - var/sound/meteor_sound = sound(meteorsound) - var/random_frequency = get_rand_frequency() - - for(var/P in GLOB.player_list) - var/mob/M = P - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - var/dist = get_dist(M.loc, src.loc) - if(prob(50)) - shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) - M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound) - -/////////////////////// -//Meteor types -/////////////////////// - -//Dust -/obj/effect/meteor/dust - name = "space dust" - icon_state = "dust" - pass_flags = PASSTABLE | PASSGRILLE - hits = 1 - hitpwr = 3 - meteorsound = 'sound/weapons/tap.ogg' - meteordrop = /obj/item/stack/ore/glass - -//Medium-sized -/obj/effect/meteor/medium - name = "meteor" - dropamt = 3 - -/obj/effect/meteor/medium/meteor_effect() - ..(heavy) - explosion(src.loc, 0, 1, 2, 3, 0) - -//Large-sized -/obj/effect/meteor/big - name = "large meteor" - icon_state = "large" - hits = 6 - heavy = 1 - dropamt = 4 - -/obj/effect/meteor/big/meteor_effect() - ..(heavy) - explosion(src.loc, 1, 2, 3, 4, 0) - -//Flaming meteor -/obj/effect/meteor/flaming - name = "flaming meteor" - icon_state = "flaming" - hits = 5 - heavy = 1 - meteorsound = 'sound/effects/bamf.ogg' - meteordrop = /obj/item/stack/ore/plasma - -/obj/effect/meteor/flaming/meteor_effect() - ..(heavy) - explosion(src.loc, 1, 2, 3, 4, 0, 0, flame_range = 5) - -//Radiation meteor -/obj/effect/meteor/irradiated - name = "glowing meteor" - icon_state = "glowing" - heavy = 1 - meteordrop = /obj/item/stack/ore/uranium - - -/obj/effect/meteor/irradiated/meteor_effect() - ..(heavy) - explosion(src.loc, 0, 0, 4, 3, 0) - new /obj/effect/decal/cleanable/greenglow(get_turf(src)) - for(var/mob/living/L in view(5, src)) - L.apply_effect(40, IRRADIATE) - -//Station buster Tunguska -/obj/effect/meteor/tunguska - name = "tunguska meteor" - icon_state = "flaming" - desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity." - hits = 30 - hitpwr = 1 - heavy = 1 - meteorsound = 'sound/effects/bamf.ogg' - meteordrop = /obj/item/stack/ore/plasma - -/obj/effect/meteor/tunguska/meteor_effect() - ..(heavy) - explosion(src.loc, 5, 10, 15, 20, 0) - -/obj/effect/meteor/tunguska/Bump() - ..() - if(prob(20)) - explosion(src.loc,2,4,6,8) - - -//Gore -/obj/effect/meteor/gore - name = "Oraganic Debris" - icon = 'icons/mob/human.dmi' - icon_state = "body_m_s" - hits = 1 - hitpwr = 0 - meteorsound = 'sound/effects/blobattack.ogg' - meteordrop = /obj/item/reagent_containers/food/snacks/meat - var/meteorgibs = /obj/effect/gibspawner/generic - -/obj/effect/meteor/gore/make_debris() - ..() - new meteorgibs(get_turf(src)) - - -/obj/effect/meteor/gore/ram_turf(turf/T) - if(!istype(T, /turf/space)) - new /obj/effect/decal/cleanable/blood(T) - -/obj/effect/meteor/gore/Bump(atom/A) - A.ex_act(hitpwr) - get_hit() - -//Meteor Ops -/obj/effect/meteor/goreops - name = "MeteorOps" - icon = 'icons/mob/animal.dmi' - icon_state = "syndicaterangedpsace" - hits = 10 - hitpwr = 1 - meteorsound = 'sound/effects/blobattack.ogg' - meteordrop = /obj/item/reagent_containers/food/snacks/meat - var/meteorgibs = /obj/effect/gibspawner/generic - -/obj/effect/meteor/goreops/make_debris() - ..() - new meteorgibs(get_turf(src)) - - -/obj/effect/meteor/goreops/ram_turf(turf/T) - if(!istype(T, /turf/space)) - new /obj/effect/decal/cleanable/blood(T) - -/obj/effect/meteor/goreops/Bump(atom/A) - A.ex_act(hitpwr) - get_hit() +//Meteors probability of spawning during a given wave +GLOBAL_LIST_INIT(meteors_normal, list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \ + /obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3)) //for normal meteor event + +GLOBAL_LIST_INIT(meteors_threatening, list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \ + /obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3)) //for threatening meteor event + +GLOBAL_LIST_INIT(meteors_catastrophic, list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \ + /obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1)) //for catastrophic meteor event + +GLOBAL_LIST_INIT(meteors_dust, list(/obj/effect/meteor/dust)) //for space dust event + +GLOBAL_LIST_INIT(meteors_gore, list(/obj/effect/meteor/gore)) //Meaty Gore + +GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops + + +/////////////////////////////// +//Meteor spawning global procs +/////////////////////////////// +/proc/spawn_meteors(var/number = 10, var/list/meteortypes) + for(var/i = 0; i < number; i++) + spawn_meteor(meteortypes) + +/proc/spawn_meteor(var/list/meteortypes) + var/turf/pickedstart + var/turf/pickedgoal + var/max_i = 10//number of tries to spawn meteor. + while(!istype(pickedstart, /turf/space)) + var/startSide = pick(GLOB.cardinal) + pickedstart = spaceDebrisStartLoc(startSide, 1) + pickedgoal = spaceDebrisFinishLoc(startSide, 1) + max_i-- + if(max_i<=0) + return + var/Me = pickweight(meteortypes) + var/obj/effect/meteor/M = new Me(pickedstart) + M.dest = pickedgoal + M.z_original = level_name_to_num(MAIN_STATION) + spawn(0) + walk_towards(M, M.dest, 1) + return + +/proc/spaceDebrisStartLoc(startSide, Z) + var/starty + var/startx + switch(startSide) + if(NORTH) + starty = world.maxy-(TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(EAST) + starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) + startx = world.maxx-(TRANSITIONEDGE+1) + if(SOUTH) + starty = (TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(WEST) + starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) + startx = (TRANSITIONEDGE+1) + var/turf/T = locate(startx, starty, Z) + return T + +/proc/spaceDebrisFinishLoc(startSide, Z) + var/endy + var/endx + switch(startSide) + if(NORTH) + endy = TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(EAST) + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = TRANSITIONEDGE + if(SOUTH) + endy = world.maxy-TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(WEST) + endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) + endx = world.maxx-TRANSITIONEDGE + var/turf/T = locate(endx, endy, Z) + return T + +/////////////////////// +//The meteor effect +////////////////////// + +/obj/effect/meteor + name = "the concept of meteor" + desc = "You should probably run instead of gawking at this." + icon = 'icons/obj/meteor.dmi' + icon_state = "small" + density = 1 + anchored = 1 + var/hits = 4 + var/hitpwr = 2 //Level of ex_act to be called on hit. + var/dest + pass_flags = PASSTABLE + var/heavy = 0 + var/meteorsound = 'sound/effects/meteorimpact.ogg' + var/z_original = 1 + + var/meteordrop = /obj/item/stack/ore/iron + var/dropamt = 2 + +/obj/effect/meteor/Move() + if(z != z_original || loc == dest) + qdel(src) + return + + . = ..() //process movement... + + if(.)//.. if did move, ram the turf we get in + var/turf/T = get_turf(loc) + ram_turf(T) + + if(prob(10) && !istype(T, /turf/space))//randomly takes a 'hit' from ramming + get_hit() + + return . + +/obj/effect/meteor/Destroy() + GLOB.meteor_list -= src + walk(src,0) //this cancels the walk_towards() proc + return ..() + +/obj/effect/meteor/New() + ..() + GLOB.meteor_list += src + SpinAnimation() + +/obj/effect/meteor/Bump(atom/A) + if(A) + ram_turf(get_turf(A)) + playsound(src.loc, meteorsound, 40, 1) + get_hit() + +/obj/effect/meteor/CanPass(atom/movable/mover, turf/target, height=0) + return istype(mover, /obj/effect/meteor) ? 1 : ..() + +/obj/effect/meteor/proc/ram_turf(var/turf/T) + //first bust whatever is in the turf + for(var/atom/A in T) + if(A != src) + A.ex_act(hitpwr) + + //then, ram the turf if it still exists + if(T) + T.ex_act(hitpwr) + +//process getting 'hit' by colliding with a dense object +//or randomly when ramming turfs +/obj/effect/meteor/proc/get_hit() + hits-- + if(hits <= 0) + make_debris() + meteor_effect(heavy) + qdel(src) + +/obj/effect/meteor/ex_act() + return + +/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/pickaxe)) + make_debris() + qdel(src) + return + return ..() + +/obj/effect/meteor/proc/make_debris() + for(var/throws = dropamt, throws > 0, throws--) + var/obj/item/O = new meteordrop(get_turf(src)) + O.throw_at(dest, 5, 10) + +/obj/effect/meteor/proc/meteor_effect(var/sound=1) + if(sound) + var/sound/meteor_sound = sound(meteorsound) + var/random_frequency = get_rand_frequency() + + for(var/P in GLOB.player_list) + var/mob/M = P + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + var/dist = get_dist(M.loc, src.loc) + if(prob(50)) + shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) + M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound) + +/////////////////////// +//Meteor types +/////////////////////// + +//Dust +/obj/effect/meteor/dust + name = "space dust" + icon_state = "dust" + pass_flags = PASSTABLE | PASSGRILLE + hits = 1 + hitpwr = 3 + meteorsound = 'sound/weapons/tap.ogg' + meteordrop = /obj/item/stack/ore/glass + +//Medium-sized +/obj/effect/meteor/medium + name = "meteor" + dropamt = 3 + +/obj/effect/meteor/medium/meteor_effect() + ..(heavy) + explosion(src.loc, 0, 1, 2, 3, 0) + +//Large-sized +/obj/effect/meteor/big + name = "large meteor" + icon_state = "large" + hits = 6 + heavy = 1 + dropamt = 4 + +/obj/effect/meteor/big/meteor_effect() + ..(heavy) + explosion(src.loc, 1, 2, 3, 4, 0) + +//Flaming meteor +/obj/effect/meteor/flaming + name = "flaming meteor" + icon_state = "flaming" + hits = 5 + heavy = 1 + meteorsound = 'sound/effects/bamf.ogg' + meteordrop = /obj/item/stack/ore/plasma + +/obj/effect/meteor/flaming/meteor_effect() + ..(heavy) + explosion(src.loc, 1, 2, 3, 4, 0, 0, flame_range = 5) + +//Radiation meteor +/obj/effect/meteor/irradiated + name = "glowing meteor" + icon_state = "glowing" + heavy = 1 + meteordrop = /obj/item/stack/ore/uranium + + +/obj/effect/meteor/irradiated/meteor_effect() + ..(heavy) + explosion(src.loc, 0, 0, 4, 3, 0) + new /obj/effect/decal/cleanable/greenglow(get_turf(src)) + for(var/mob/living/L in view(5, src)) + L.apply_effect(40, IRRADIATE) + +//Station buster Tunguska +/obj/effect/meteor/tunguska + name = "tunguska meteor" + icon_state = "flaming" + desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity." + hits = 30 + hitpwr = 1 + heavy = 1 + meteorsound = 'sound/effects/bamf.ogg' + meteordrop = /obj/item/stack/ore/plasma + +/obj/effect/meteor/tunguska/meteor_effect() + ..(heavy) + explosion(src.loc, 5, 10, 15, 20, 0) + +/obj/effect/meteor/tunguska/Bump() + ..() + if(prob(20)) + explosion(src.loc,2,4,6,8) + + +//Gore +/obj/effect/meteor/gore + name = "Oraganic Debris" + icon = 'icons/mob/human.dmi' + icon_state = "body_m_s" + hits = 1 + hitpwr = 0 + meteorsound = 'sound/effects/blobattack.ogg' + meteordrop = /obj/item/reagent_containers/food/snacks/meat + var/meteorgibs = /obj/effect/gibspawner/generic + +/obj/effect/meteor/gore/make_debris() + ..() + new meteorgibs(get_turf(src)) + + +/obj/effect/meteor/gore/ram_turf(turf/T) + if(!istype(T, /turf/space)) + new /obj/effect/decal/cleanable/blood(T) + +/obj/effect/meteor/gore/Bump(atom/A) + A.ex_act(hitpwr) + get_hit() + +//Meteor Ops +/obj/effect/meteor/goreops + name = "MeteorOps" + icon = 'icons/mob/animal.dmi' + icon_state = "syndicaterangedpsace" + hits = 10 + hitpwr = 1 + meteorsound = 'sound/effects/blobattack.ogg' + meteordrop = /obj/item/reagent_containers/food/snacks/meat + var/meteorgibs = /obj/effect/gibspawner/generic + +/obj/effect/meteor/goreops/make_debris() + ..() + new meteorgibs(get_turf(src)) + + +/obj/effect/meteor/goreops/ram_turf(turf/T) + if(!istype(T, /turf/space)) + new /obj/effect/decal/cleanable/blood(T) + +/obj/effect/meteor/goreops/Bump(atom/A) + A.ex_act(hitpwr) + get_hit() diff --git a/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm b/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm index 5db63d3450d..3859d68accb 100644 --- a/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm +++ b/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm @@ -144,4 +144,4 @@ explanation_text = "You are doomed to feel woefully incomplete forever... until you find your true love on this station. They're waiting for you!" /datum/objective/abductee/sixthsense - explanation_text = "You died back there and went to heaven... or is it hell? No one here seems to know they're dead. Convince them, and maybe you can escape this limbo." \ No newline at end of file + explanation_text = "You died back there and went to heaven... or is it hell? No one here seems to know they're dead. Convince them, and maybe you can escape this limbo." diff --git a/code/game/gamemodes/miniantags/abduction/abduction.dm b/code/game/gamemodes/miniantags/abduction/abduction.dm index bcbc86d991b..60de5ba4238 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction.dm @@ -47,7 +47,7 @@ /datum/game_mode/abduction/proc/make_abductor_team(team_number,preset_agent=null,preset_scientist=null) //Team Name - team_names[team_number] = "Mothership [pick(possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names + team_names[team_number] = "Mothership [pick(GLOB.possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names //Team Objective var/datum/objective/experiment/team_objective = new team_objective.team = team_number @@ -267,6 +267,7 @@ SSticker.mode.abductors -= abductor_mind abductor_mind.special_role = null abductor_mind.current.create_attack_log("No longer abductor") + abductor_mind.current.create_log(CONVERSION_LOG, "No longer abductor") if(issilicon(abductor_mind.current)) to_chat(abductor_mind.current, "You have been turned into a robot! You are no longer an abductor.") else @@ -274,11 +275,11 @@ SSticker.mode.update_abductor_icons_removed(abductor_mind) /datum/game_mode/proc/update_abductor_icons_added(datum/mind/alien_mind) - var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_ABDUCTOR] + var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_ABDUCTOR] hud.join_hud(alien_mind.current) set_antag_hud(alien_mind.current, ((alien_mind in abductors) ? "abductor" : "abductee")) /datum/game_mode/proc/update_abductor_icons_removed(datum/mind/alien_mind) - var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_ABDUCTOR] + var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_ABDUCTOR] hud.leave_hud(alien_mind.current) - set_antag_hud(alien_mind.current, null) \ No newline at end of file + set_antag_hud(alien_mind.current, null) diff --git a/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm b/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm index c90aaac19d8..6478a997b76 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm @@ -59,4 +59,4 @@ ..() if(!visualsOnly) var/obj/item/implant/abductor/beamplant = new /obj/item/implant/abductor(H) - beamplant.implant(H) \ No newline at end of file + beamplant.implant(H) diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm index 419b53d50a3..e23e37c349e 100644 --- a/code/game/gamemodes/miniantags/abduction/gland.dm +++ b/code/game/gamemodes/miniantags/abduction/gland.dm @@ -69,7 +69,7 @@ active = 0 if(initial(uses) == 1) uses = initial(uses) - var/datum/atom_hud/abductor/hud = huds[DATA_HUD_ABDUCTOR] + var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR] hud.remove_from_hud(owner) clear_mind_control() . = ..() @@ -78,7 +78,7 @@ ..() if(special != 2 && uses) // Special 2 means abductor surgery Start() - var/datum/atom_hud/abductor/hud = huds[DATA_HUD_ABDUCTOR] + var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR] hud.add_to_hud(owner) update_gland_hud() @@ -277,14 +277,14 @@ ..() if(ishuman(owner)) owner.gene_stability += GENE_INSTABILITY_MODERATE // give them this gene for free - owner.dna.SetSEState(SHOCKIMMUNITYBLOCK, TRUE) - genemutcheck(owner, SHOCKIMMUNITYBLOCK, null, MUTCHK_FORCED) + owner.dna.SetSEState(GLOB.shockimmunityblock, TRUE) + genemutcheck(owner, GLOB.shockimmunityblock, null, MUTCHK_FORCED) /obj/item/organ/internal/heart/gland/electric/remove(mob/living/carbon/M, special = 0) if(ishuman(owner)) owner.gene_stability -= GENE_INSTABILITY_MODERATE // but return it to normal once it's removed - owner.dna.SetSEState(SHOCKIMMUNITYBLOCK, FALSE) - genemutcheck(owner, SHOCKIMMUNITYBLOCK, null, MUTCHK_FORCED) + owner.dna.SetSEState(GLOB.shockimmunityblock, FALSE) + genemutcheck(owner, GLOB.shockimmunityblock, null, MUTCHK_FORCED) return ..() /obj/item/organ/internal/heart/gland/electric/activate() @@ -346,5 +346,5 @@ owner.visible_message("[owner] vomits a cloud of plasma!") var/turf/simulated/T = get_turf(owner) if(istype(T)) - T.atmos_spawn_air(SPAWN_TOXINS|SPAWN_20C,50) + T.atmos_spawn_air(LINDA_SPAWN_TOXINS|LINDA_SPAWN_20C,50) owner.vomit() diff --git a/code/game/gamemodes/miniantags/abduction/machinery/camera.dm b/code/game/gamemodes/miniantags/abduction/machinery/camera.dm index 951ee6806de..6fb401d31e9 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/camera.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/camera.dm @@ -73,7 +73,7 @@ var/mob/camera/aiEye/remote/remote_eye = C.remote_control var/obj/machinery/abductor/pad/P = target - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) P.PadToLoc(remote_eye.loc) /datum/action/innate/teleport_out @@ -98,7 +98,7 @@ var/mob/camera/aiEye/remote/remote_eye = C.remote_control var/obj/machinery/abductor/pad/P = target - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) P.MobToLoc(remote_eye.loc,C) /datum/action/innate/vest_mode_swap diff --git a/code/game/gamemodes/miniantags/abduction/machinery/console.dm b/code/game/gamemodes/miniantags/abduction/machinery/console.dm index b162103c88b..17ebcbdd270 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/console.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/console.dm @@ -221,4 +221,4 @@ else new item(src.loc) else - atom_say("Insufficent data!") \ No newline at end of file + atom_say("Insufficent data!") diff --git a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm index 1a377667650..5964c6f379b 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm @@ -83,4 +83,4 @@ if(amounts[count]>0) amounts[count]-- var/T = gland_types[count] - new T(get_turf(src)) \ No newline at end of file + new T(get_turf(src)) diff --git a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm index 04dda244a0c..6e1b4a39d3a 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm @@ -176,7 +176,7 @@ H.uncuff() return //Area not chosen / It's not safe area - teleport to arrivals - H.forceMove(pick(latejoin)) + H.forceMove(pick(GLOB.latejoin)) H.uncuff() return diff --git a/code/game/gamemodes/miniantags/abduction/machinery/pad.dm b/code/game/gamemodes/miniantags/abduction/machinery/pad.dm index 697d173716a..24e43b6583f 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/pad.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/pad.dm @@ -12,7 +12,7 @@ /obj/machinery/abductor/pad/proc/Send() if(teleport_target == null) - teleport_target = teleportlocs[pick(teleportlocs)] + teleport_target = GLOB.teleportlocs[pick(GLOB.teleportlocs)] flick("alien-pad", src) for(var/mob/living/target in loc) target.forceMove(teleport_target) @@ -48,4 +48,4 @@ /obj/effect/temp_visual/teleport_abductor/New() do_sparks(10, 0, loc) - ..() \ No newline at end of file + ..() diff --git a/code/game/gamemodes/miniantags/borer/borer_event.dm b/code/game/gamemodes/miniantags/borer/borer_event.dm index f925a59d9fc..245b8fa7b41 100644 --- a/code/game/gamemodes/miniantags/borer/borer_event.dm +++ b/code/game/gamemodes/miniantags/borer/borer_event.dm @@ -12,7 +12,7 @@ /datum/event/borer_infestation/announce() if(successSpawn) - command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + GLOB.command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') /datum/event/borer_infestation/start() var/list/vents = list() diff --git a/code/game/gamemodes/miniantags/borer/borer_html.dm b/code/game/gamemodes/miniantags/borer/borer_html.dm index 415438884e9..8b6e19e6226 100644 --- a/code/game/gamemodes/miniantags/borer/borer_html.dm +++ b/code/game/gamemodes/miniantags/borer/borer_html.dm @@ -66,4 +66,4 @@
    [content]
    "} - return html \ No newline at end of file + return html diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm index e764f97a46a..f425a34654d 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm @@ -119,7 +119,7 @@ ..() add_language("Swarmer", 1) verbs -= /mob/living/verb/pulled - for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) + for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) diag_hud.add_to_hud(src) updatename() @@ -421,13 +421,13 @@ return FALSE /obj/structure/lattice/catwalk/swarmer_act(mob/living/simple_animal/hostile/swarmer/S) - . = ..() var/turf/here = get_turf(src) for(var/A in here.contents) var/obj/structure/cable/C = A if(istype(C)) to_chat(S, "Disrupting the power grid would bring no benefit to us. Aborting.") return FALSE + return ..() /obj/item/deactivated_swarmer/IntegrateAmount() return 50 @@ -689,4 +689,4 @@ if(message) for(var/mob/M in GLOB.mob_list) if(isswarmer(M) || (M in GLOB.dead_mob_list)) - to_chat(M, "Swarm communication - [src] states: [message]") \ No newline at end of file + to_chat(M, "Swarm communication - [src] states: [message]") diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm index 0d5ba13d682..fbceda6bd94 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm @@ -8,14 +8,14 @@ swarmer_report += "

    Our long-range sensors have detected an odd signal emanating from your station's gateway. We recommend immediate investigation of your gateway, as something may have come \ through." print_command_report(swarmer_report, "Classified [command_name()] Update") - event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg') + GLOB.event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg') /datum/event/spawn_swarmer/start() if(find_swarmer()) return 0 - if(!the_gateway) + if(!GLOB.the_gateway) return 0 - new /obj/effect/mob_spawn/swarmer(get_turf(the_gateway)) + new /obj/effect/mob_spawn/swarmer(get_turf(GLOB.the_gateway)) /datum/event/spawn_swarmer/proc/find_swarmer() diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm index 17d7187f439..126587a0c97 100644 --- a/code/game/gamemodes/miniantags/guardian/guardian.dm +++ b/code/game/gamemodes/miniantags/guardian/guardian.dm @@ -271,6 +271,7 @@ var/used = FALSE var/theme = "magic" var/mob_name = "Guardian Spirit" + var/confirmation_message = "The cards are still unused. Do you wish to use them?" var/use_message = "You shuffle the deck..." var/used_message = "All the cards seem to be blank now." var/failure_message = "..And draw a card! It's...blank? Maybe you should try again later." @@ -295,7 +296,12 @@ if(used == TRUE) to_chat(user, "[used_message]") return - used = TRUE + used = TRUE // Set this BEFORE the popup to prevent people using the injector more than once, polling ghosts multiple times, and receiving multiple guardians. + var/choice = alert(user, "[confirmation_message]",, "Yes", "No") + if(choice == "No") + to_chat(user, "You decide against using the [name].") + used = FALSE + return to_chat(user, "[use_message]") var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, 0, 100) var/mob/dead/observer/theghost = null @@ -384,6 +390,7 @@ icon_state = "combat_hypo" theme = "tech" mob_name = "Holoparasite" + confirmation_message = "The injector still contains holoparasites. Do you wish to use it?" use_message = "You start to power on the injector..." used_message = "The injector has already been used." failure_message = "...ERROR. BOOT SEQUENCE ABORTED. AI FAILED TO INTIALIZE. PLEASE CONTACT SUPPORT OR TRY AGAIN LATER." @@ -424,6 +431,7 @@ theme = "bio" mob_name = "Scarab Swarm" use_message = "The eggs begin to twitch..." + confirmation_message = "These eggs are still dormant. Do you wish to activate them?" used_message = "The cluster already hatched." failure_message = "...but soon settles again. Guess they weren't ready to hatch after all." color_list = list("Rose" = "#F62C6B", diff --git a/code/game/gamemodes/miniantags/guardian/types/assassin.dm b/code/game/gamemodes/miniantags/guardian/types/assassin.dm index 4b17e597aa5..a12fce36e82 100644 --- a/code/game/gamemodes/miniantags/guardian/types/assassin.dm +++ b/code/game/gamemodes/miniantags/guardian/types/assassin.dm @@ -90,4 +90,4 @@ clear_alert("instealth") instealthalert = null clear_alert("canstealth") - canstealthalert = null \ No newline at end of file + canstealthalert = null diff --git a/code/game/gamemodes/miniantags/guardian/types/charger.dm b/code/game/gamemodes/miniantags/guardian/types/charger.dm index 17ed569f256..3ea6dbe5f30 100644 --- a/code/game/gamemodes/miniantags/guardian/types/charger.dm +++ b/code/game/gamemodes/miniantags/guardian/types/charger.dm @@ -69,4 +69,4 @@ shake_camera(L, 4, 3) shake_camera(src, 2, 3) - charging = 0 \ No newline at end of file + charging = 0 diff --git a/code/game/gamemodes/miniantags/guardian/types/healer.dm b/code/game/gamemodes/miniantags/guardian/types/healer.dm index bf2c92ffb28..e3217e0249a 100644 --- a/code/game/gamemodes/miniantags/guardian/types/healer.dm +++ b/code/game/gamemodes/miniantags/guardian/types/healer.dm @@ -33,7 +33,7 @@ /mob/living/simple_animal/hostile/guardian/healer/Life(seconds, times_fired) ..() - var/datum/atom_hud/medsensor = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] medsensor.add_hud_to(src) /mob/living/simple_animal/hostile/guardian/healer/Stat() @@ -51,7 +51,7 @@ if(iscarbon(target)) changeNext_move(CLICK_CD_MELEE) if(heal_cooldown <= world.time && !stat) - var/mob/living/carbon/C = target + var/mob/living/carbon/human/C = target C.adjustBruteLoss(-5, robotic=1) C.adjustFireLoss(-5, robotic=1) C.adjustOxyLoss(-5) diff --git a/code/game/gamemodes/miniantags/guardian/types/lightning.dm b/code/game/gamemodes/miniantags/guardian/types/lightning.dm index 13b9b67f0a9..bf84e199bab 100644 --- a/code/game/gamemodes/miniantags/guardian/types/lightning.dm +++ b/code/game/gamemodes/miniantags/guardian/types/lightning.dm @@ -105,4 +105,4 @@ "You hear a heavy electrical crack." \ ) L.adjustFireLoss(1.2) //adds up very rapidly - . = 1 \ No newline at end of file + . = 1 diff --git a/code/game/gamemodes/miniantags/guardian/types/protector.dm b/code/game/gamemodes/miniantags/guardian/types/protector.dm index 5fb31b8ba15..534b7e16934 100644 --- a/code/game/gamemodes/miniantags/guardian/types/protector.dm +++ b/code/game/gamemodes/miniantags/guardian/types/protector.dm @@ -55,4 +55,4 @@ summoner.visible_message("[summoner] jumps back to [summoner.p_their()] protector.") new /obj/effect/temp_visual/guardian/phase/out(get_turf(summoner)) summoner.forceMove(get_turf(src)) - new /obj/effect/temp_visual/guardian/phase(get_turf(summoner))//Protector \ No newline at end of file + new /obj/effect/temp_visual/guardian/phase(get_turf(summoner))//Protector diff --git a/code/game/gamemodes/miniantags/guardian/types/standard.dm b/code/game/gamemodes/miniantags/guardian/types/standard.dm index dc85a48a480..e6d95ffb92c 100644 --- a/code/game/gamemodes/miniantags/guardian/types/standard.dm +++ b/code/game/gamemodes/miniantags/guardian/types/standard.dm @@ -45,4 +45,4 @@ playstyle_string = "As a standard type you have no special abilities, but have a high damage resistance and a powerful attack capable of smashing through walls." environment_smash = 2 battlecry = "URK" - adminseal = TRUE \ No newline at end of file + adminseal = TRUE diff --git a/code/game/gamemodes/miniantags/morph/morph.dm b/code/game/gamemodes/miniantags/morph/morph.dm index ef90f44f4a0..a122351a4bb 100644 --- a/code/game/gamemodes/miniantags/morph/morph.dm +++ b/code/game/gamemodes/miniantags/morph/morph.dm @@ -136,7 +136,7 @@ for(var/atom/movable/AM in src) AM.forceMove(loc) if(prob(90)) - step(AM, pick(alldirs)) + step(AM, pick(GLOB.alldirs)) // Only execute the below if we successfully died if(!.) return FALSE diff --git a/code/game/gamemodes/miniantags/morph/morph_event.dm b/code/game/gamemodes/miniantags/morph/morph_event.dm index e80374e2dca..e9465492a27 100644 --- a/code/game/gamemodes/miniantags/morph/morph_event.dm +++ b/code/game/gamemodes/miniantags/morph/morph_event.dm @@ -15,9 +15,9 @@ var/datum/mind/player_mind = new /datum/mind(key_of_morph) player_mind.active = 1 - if(!xeno_spawn) + if(!GLOB.xeno_spawn) return kill() - var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(pick(xeno_spawn)) + var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(pick(GLOB.xeno_spawn)) player_mind.transfer_to(S) player_mind.assigned_role = SPECIAL_ROLE_MORPH player_mind.special_role = SPECIAL_ROLE_MORPH @@ -29,4 +29,4 @@ return 1 /datum/event/spawn_morph/start() - get_morph() \ No newline at end of file + get_morph() diff --git a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm index 437e7637a77..1f902a51806 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm @@ -52,4 +52,4 @@ /datum/event/revenant/start() - get_revenant() \ No newline at end of file + get_revenant() diff --git a/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm b/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm index ab54f6991b1..ed0a4a908ed 100644 --- a/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm +++ b/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm @@ -35,8 +35,8 @@ animation.dir = dir ExtinguishMob() - if(pulling && bloodcrawl == BLOODCRAWL_EAT) - if(istype(pulling, /mob/living/)) + if(pulling && HAS_TRAIT(src, TRAIT_BLOODCRAWL_EAT)) + if(isliving(pulling)) var/mob/living/victim = pulling if(victim.stat == CONSCIOUS) visible_message("[victim] kicks free of [B] just before entering it!") @@ -76,13 +76,17 @@ adjustToxLoss(-1000) if(istype(src, /mob/living/simple_animal/slaughter)) //rason, do not want humans to get this - var/mob/living/simple_animal/slaughter/demon = src demon.devoured++ to_chat(kidnapped, "You feel teeth sink into your flesh, and the--") kidnapped.adjustBruteLoss(1000) kidnapped.forceMove(src) demon.consumed_mobs.Add(kidnapped) + if(ishuman(kidnapped)) + var/mob/living/carbon/human/H = kidnapped + if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + U.sensor_mode = SENSOR_OFF else kidnapped.ghostize() qdel(kidnapped) diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm index 1f525e93076..73ab05a00aa 100644 --- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm +++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm @@ -35,7 +35,6 @@ see_in_dark = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE var/boost = 0 - bloodcrawl = BLOODCRAWL_EAT var/devoured = 0 @@ -59,6 +58,7 @@ /mob/living/simple_animal/slaughter/New() ..() remove_from_all_data_huds() + ADD_TRAIT(src, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new AddSpell(bloodspell) whisper_action = new() @@ -247,33 +247,37 @@ user.visible_message("[user] raises [src] to [user.p_their()] mouth and tears into it with [user.p_their()] teeth!", \ "An unnatural hunger consumes you. You raise [src] to your mouth and devour it!") playsound(user, 'sound/misc/demon_consume.ogg', 50, 1) - for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) - if(knownspell.type == /obj/effect/proc_holder/spell/bloodcrawl) - qdel(src) - return - if(user.bloodcrawl == 0) + // Eating the heart for the first time. Gives basic bloodcrawling. This is the only time we need to insert the heart. + if(!HAS_TRAIT(user, TRAIT_BLOODCRAWL)) user.visible_message("[user]'s eyes flare a deep crimson!", \ "You feel a strange power seep into your body... you have absorbed the demon's blood-travelling powers!") - user.bloodcrawl = BLOODCRAWL - else if(user.bloodcrawl == BLOODCRAWL) - to_chat(user, "You feel diffr- CONSUME THEM! ") - user.bloodcrawl = BLOODCRAWL_EAT - else - to_chat(user, "...and you don't feel any different.") + ADD_TRAIT(user, TRAIT_BLOODCRAWL, "bloodcrawl") + user.drop_item() + insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E. + return TRUE - user.drop_item() - insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E + // Eating a 2nd heart. Gives the ability to drag people into blood and eat them. + if(HAS_TRAIT(user, TRAIT_BLOODCRAWL)) + to_chat(user, "You feel diffr- CONSUME THEM! ") + ADD_TRAIT(user, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") + qdel(src) // Replacing their demon heart with another demon heart is pointless, just delete this one and return. + return TRUE + + // Eating any more than 2 demon hearts does nothing. + to_chat(user, "...and you don't feel any different.") + qdel(src) /obj/item/organ/internal/heart/demon/insert(mob/living/carbon/M, special = 0) - ..() + . = ..() if(M.mind) M.mind.AddSpell(new /obj/effect/proc_holder/spell/bloodcrawl(null)) /obj/item/organ/internal/heart/demon/remove(mob/living/carbon/M, special = 0) ..() if(M.mind) - M.bloodcrawl = 0 + REMOVE_TRAIT(M, TRAIT_BLOODCRAWL, "bloodcrawl") + REMOVE_TRAIT(M, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") M.mind.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl) /obj/item/organ/internal/heart/demon/Stop() diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 2d7c9cff015..ad1146c0bc8 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -1,536 +1,537 @@ -#define NUKESCALINGMODIFIER 1.2 - -/datum/game_mode - var/list/datum/mind/syndicates = list() - -proc/issyndicate(mob/living/M as mob) - return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.syndicates) - -/datum/game_mode/nuclear - name = "nuclear emergency" - config_tag = "nuclear" - required_players = 30 // 30 players - 5 players to be the nuke ops = 25 players remaining - required_enemies = 5 - recommended_enemies = 5 - - var/const/agents_possible = 5 //If we ever need more syndicate agents. - - var/nukes_left = 1 //Call 3714-PRAY right now and order more nukes! Limited offer! - var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station - var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level - var/total_tc = 0 //Total amount of telecrystals shared between nuke ops - -/datum/game_mode/nuclear/announce() - to_chat(world, "The current game mode is - Nuclear Emergency!") - to_chat(world, "A [syndicate_name()] Strike Force is approaching [station_name()]!") - to_chat(world, "A nuclear explosive was being transported by Nanotrasen to a military base. The transport ship mysteriously lost contact with Space Traffic Control (STC). About that time a strange disk was discovered around [station_name()]. It was identified by Nanotrasen as a nuclear authentication disk and now Syndicate Operatives have arrived to retake the disk and detonate SS13! There are most likely Syndicate starships are in the vicinity, so take care not to lose the disk!\nSyndicate: Reclaim the disk and detonate the nuclear bomb anywhere on SS13.\nPersonnel: Hold the disk and escape with the disk on the shuttle!") - -/datum/game_mode/nuclear/can_start()//This could be better, will likely have to recode it later - if(!..()) - return 0 - - var/list/possible_syndicates = get_players_for_role(ROLE_OPERATIVE) - var/agent_number = 0 - - if(possible_syndicates.len < 1) - return 0 - - if(LAZYLEN(possible_syndicates) > agents_possible) - agent_number = agents_possible - else - agent_number = possible_syndicates.len - - var/n_players = num_players() - if(agent_number > n_players) - agent_number = n_players/2 - - while(agent_number > 0) - var/datum/mind/new_syndicate = pick(possible_syndicates) - syndicates += new_syndicate - possible_syndicates -= new_syndicate //So it doesn't pick the same guy each time. - agent_number-- - - for(var/datum/mind/synd_mind in syndicates) - synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs. - synd_mind.special_role = SPECIAL_ROLE_NUKEOPS - synd_mind.offstation_role = TRUE - return 1 - - -/datum/game_mode/nuclear/pre_setup() - ..() - return 1 - -/datum/game_mode/proc/remove_operative(datum/mind/operative_mind) - if(operative_mind in syndicates) - SSticker.mode.syndicates -= operative_mind - operative_mind.special_role = null - for(var/datum/objective/nuclear/O in operative_mind.objectives) - operative_mind.objectives -= O - operative_mind.current.create_attack_log("No longer nuclear operative") - if(issilicon(operative_mind.current)) - to_chat(operative_mind.current, "You have been turned into a robot! You are no longer a Syndicate operative.") - else - to_chat(operative_mind.current, "You have been brainwashed! You are no longer a Syndicate operative.") - SSticker.mode.update_synd_icons_removed(operative_mind) - -//////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////// - -/datum/game_mode/proc/update_synd_icons_added(datum/mind/synd_mind) - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.join_hud(synd_mind.current) - set_antag_hud(synd_mind.current, "hudoperative") - -/datum/game_mode/proc/update_synd_icons_removed(datum/mind/synd_mind) - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.leave_hud(synd_mind.current) - set_antag_hud(synd_mind.current, null) - -//////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////// - -/datum/game_mode/nuclear/post_setup() - - var/list/turf/synd_spawn = list() - - for(var/obj/effect/landmark/A in GLOB.landmarks_list) - if(A.name == "Syndicate-Spawn") - synd_spawn += get_turf(A) - qdel(A) - continue - - var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb") - - var/nuke_code = "[rand(10000, 99999)]" - var/leader_selected = 0 - var/agent_number = 1 - var/spawnpos = 1 - - for(var/datum/mind/synd_mind in syndicates) - if(spawnpos > synd_spawn.len) - spawnpos = 2 - synd_mind.current.loc = synd_spawn[spawnpos] - - forge_syndicate_objectives(synd_mind) - create_syndicate(synd_mind) - greet_syndicate(synd_mind) - equip_syndicate(synd_mind.current) - - if(!leader_selected) - prepare_syndicate_leader(synd_mind, nuke_code) - leader_selected = 1 - else - synd_mind.current.real_name = "[syndicate_name()] Operative #[agent_number]" - update_syndicate_id(synd_mind, FALSE) - - agent_number++ - spawnpos++ - update_synd_icons_added(synd_mind) - - scale_telecrystals() - share_telecrystals() - if(nuke_spawn && synd_spawn.len > 0) - var/obj/machinery/nuclearbomb/syndicate/the_bomb = new /obj/machinery/nuclearbomb/syndicate(nuke_spawn.loc) - the_bomb.r_code = nuke_code - - return ..() - -/datum/game_mode/nuclear/proc/scale_telecrystals() - var/danger - danger = GLOB.player_list.len - while(!IsMultiple(++danger, 10)) //Increments danger up to the nearest multiple of ten - - total_tc += danger * NUKESCALINGMODIFIER - -/datum/game_mode/nuclear/proc/share_telecrystals() - var/player_tc - var/remainder - - player_tc = round(total_tc / GLOB.nuclear_uplink_list.len) //round to get an integer and not floating point - remainder = total_tc % GLOB.nuclear_uplink_list.len - - for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) - U.hidden_uplink.uses += player_tc - while(remainder > 0) - for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) - if(remainder <= 0) - break - U.hidden_uplink.uses++ - remainder-- - -/datum/game_mode/proc/create_syndicate(datum/mind/synd_mind) // So we don't have inferior species as ops - randomize a human - var/mob/living/carbon/human/M = synd_mind.current - - M.set_species(/datum/species/human, TRUE) - M.dna.ready_dna(M) // Quadriplegic Nuke Ops won't be participating in the paralympics - M.dna.species.create_organs(M) - M.cleanSE() //No fat/blind/colourblind/epileptic/whatever ops. - M.overeatduration = 0 - M.flavor_text = null - - var/obj/item/organ/external/head/head_organ = M.get_organ("head") - var/hair_c = pick("#8B4513","#000000","#FF4500","#FFD700") // Brown, black, red, blonde - var/eye_c = pick("#000000","#8B4513","1E90FF") // Black, brown, blue - var/skin_tone = pick(-50, -30, -10, 0, 0, 0, 10) // Caucasian/black - head_organ.facial_colour = hair_c - head_organ.sec_facial_colour = hair_c - head_organ.hair_colour = hair_c - head_organ.sec_hair_colour = hair_c - M.change_eye_color(eye_c) - M.s_tone = skin_tone - head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name) - head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name) - M.body_accessory = null - M.regenerate_icons() - M.update_body() - -/datum/game_mode/proc/prepare_syndicate_leader(var/datum/mind/synd_mind, var/nuke_code) - var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord") - synd_mind.current.real_name = "[syndicate_name()] Team [leader_title]" - to_chat(synd_mind.current, "You are the Syndicate leader for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.") - to_chat(synd_mind.current, "If you feel you are not up to this task, give your ID to another operative.") - to_chat(synd_mind.current, "In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.") - - var/obj/item/nuclear_challenge/challenge = new /obj/item/nuclear_challenge - synd_mind.current.equip_to_slot_or_del(challenge, slot_r_hand) - - update_syndicate_id(synd_mind, leader_title, TRUE) - - if(nuke_code) - synd_mind.store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) - to_chat(synd_mind.current, "The nuclear authorization code is: [nuke_code]") - var/obj/item/paper/P = new - P.info = "The nuclear authorization code is: [nuke_code]" - P.name = "nuclear bomb code" - var/obj/item/stamp/syndicate/stamp = new - P.stamp(stamp) - qdel(stamp) - - if(SSticker.mode.config_tag=="nuclear") - P.loc = synd_mind.current.loc - else - var/mob/living/carbon/human/H = synd_mind.current - P.loc = H.loc - H.equip_to_slot_or_del(P, slot_r_store, 0) - H.update_icons() - - else - nuke_code = "code will be provided later" - -/datum/game_mode/proc/update_syndicate_id(var/datum/mind/synd_mind, is_leader = FALSE) - var/list/found_ids = synd_mind.current.search_contents_for(/obj/item/card/id) - - if(LAZYLEN(found_ids)) - for(var/obj/item/card/id/ID in found_ids) - ID.name = "[synd_mind.current.real_name] ID card" - ID.registered_name = synd_mind.current.real_name - if(is_leader) - ID.access += access_syndicate_leader - else - message_admins("Warning: Operative [key_name_admin(synd_mind.current)] spawned without an ID card!") - -/datum/game_mode/proc/forge_syndicate_objectives(var/datum/mind/syndicate) - var/datum/objective/nuclear/syndobj = new - syndobj.owner = syndicate - syndicate.objectives += syndobj - - -/datum/game_mode/proc/greet_syndicate(var/datum/mind/syndicate, var/you_are=1) - SEND_SOUND(syndicate.current, 'sound/ambience/antag/ops.ogg') - if(you_are) - to_chat(syndicate.current, "You are a [syndicate_name()] agent!") - var/obj_count = 1 - for(var/datum/objective/objective in syndicate.objectives) - to_chat(syndicate.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - - -/datum/game_mode/proc/random_radio_frequency() - return 1337 // WHY??? -- Doohl - - -/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob, uplink_uses = 20) - var/radio_freq = SYND_FREQ - - var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt(synd_mob) - R.set_frequency(radio_freq) - synd_mob.equip_to_slot_or_del(R, slot_l_ear) - - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(synd_mob), slot_w_uniform) - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(synd_mob), slot_shoes) - synd_mob.equip_or_collect(new /obj/item/clothing/gloves/combat(synd_mob), slot_gloves) - synd_mob.equip_to_slot_or_del(new /obj/item/card/id/syndicate(synd_mob), slot_wear_id) - synd_mob.equip_to_slot_or_del(new /obj/item/storage/backpack(synd_mob), slot_back) - synd_mob.equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/pistol(synd_mob), slot_belt) - synd_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(synd_mob.back), slot_in_backpack) - synd_mob.equip_to_slot_or_del(new /obj/item/pinpointer/nukeop(synd_mob), slot_wear_pda) - var/obj/item/radio/uplink/nuclear/U = new /obj/item/radio/uplink/nuclear(synd_mob) - U.hidden_uplink.uplink_owner="[synd_mob.key]" - U.hidden_uplink.uses = uplink_uses - synd_mob.equip_to_slot_or_del(U, slot_in_backpack) - - if(synd_mob.dna.species) - - /* - Incase anyone ever gets the burning desire to have nukeops with randomized apperances. -- Dave - synd_mob.gender = pick(MALE, FEMALE) // Randomized appearances for the nukeops. - var/datum/preferences/pref = new() - A.randomize_appearance_for(synd_mob) - */ - - var/race = synd_mob.dna.species.name - - switch(race) - if("Vox" || "Vox Armalis") - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) - synd_mob.equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/vox(synd_mob), slot_l_hand) - synd_mob.internal = synd_mob.l_hand - synd_mob.update_action_buttons_icon() - - if("Plasmaman") - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) - synd_mob.equip_or_collect(new /obj/item/tank/plasma/plasmaman(synd_mob), slot_s_store) - synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) - synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) - synd_mob.internal = synd_mob.get_item_by_slot(slot_s_store) - synd_mob.update_action_buttons_icon() - - synd_mob.rejuvenate() //fix any damage taken by naked vox/plasmamen/etc while round setups - var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(synd_mob) - E.implant(synd_mob) - synd_mob.faction |= "syndicate" - synd_mob.update_icons() - return 1 - -/datum/game_mode/nuclear/check_win() - if(nukes_left == 0) - return 1 - return ..() - - -/datum/game_mode/proc/is_operatives_are_dead() - for(var/datum/mind/operative_mind in syndicates) - if(!istype(operative_mind.current,/mob/living/carbon/human)) - if(operative_mind.current) - if(operative_mind.current.stat!=2) - return 0 - return 1 - - -/datum/game_mode/nuclear/declare_completion() - var/disk_rescued = 1 - for(var/obj/item/disk/nuclear/D in GLOB.poi_list) - if(!D.onCentcom()) - disk_rescued = 0 - break - var/crew_evacuated = (SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) - //var/operatives_are_dead = is_operatives_are_dead() - - - //nukes_left - //station_was_nuked - //derp //Used for tracking if the syndies actually haul the nuke to the station //no - //herp //Used for tracking if the syndies got the shuttle off of the z-level //NO, DON'T FUCKING NAME VARS LIKE THIS - - if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear win - syndicate nuke") - to_chat(world, "Syndicate Major Victory!") - to_chat(world, "[syndicate_name()] operatives have destroyed [station_name()]!") - - else if(!disk_rescued && station_was_nuked && syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear halfwin - syndicate nuke - did not evacuate in time") - to_chat(world, "Total Annihilation") - to_chat(world, "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") - - else if(!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear halfwin - blew wrong station") - to_chat(world, "Crew Minor Victory") - to_chat(world, "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") - - else if(!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear halfwin - blew wrong station - did not evacuate in time") - to_chat(world, "[syndicate_name()] operatives have earned Darwin Award!") - to_chat(world, "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") - - else if(disk_rescued && is_operatives_are_dead()) - feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured - syndi team dead") - to_chat(world, "Crew Major Victory!") - to_chat(world, "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") - - else if(disk_rescued) - feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured") - to_chat(world, "Crew Major Victory") - to_chat(world, "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") - - else if(!disk_rescued && is_operatives_are_dead()) - feedback_set_details("round_end_result","nuclear loss - evacuation - disk not secured") - to_chat(world, "Syndicate Minor Victory!") - to_chat(world, "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") - - else if(!disk_rescued && crew_evacuated) - feedback_set_details("round_end_result","nuclear halfwin - detonation averted") - to_chat(world, "Syndicate Minor Victory!") - to_chat(world, "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") - - else if(!disk_rescued && !crew_evacuated) - feedback_set_details("round_end_result","nuclear halfwin - interrupted") - to_chat(world, "Neutral Victory") - to_chat(world, "Round was mysteriously interrupted!") - ..() - return - - -/datum/game_mode/proc/auto_declare_completion_nuclear() - if(syndicates.len || GAMEMODE_IS_NUCLEAR) - var/text = "
    The syndicate operatives were:" - - var/purchases = "" - var/TC_uses = 0 - - for(var/datum/mind/syndicate in syndicates) - - text += "
    [syndicate.key] was [syndicate.name] (" - if(syndicate.current) - if(syndicate.current.stat == DEAD) - text += "died" - else - text += "survived" - if(syndicate.current.real_name != syndicate.name) - text += " as [syndicate.current.real_name]" - else - text += "body destroyed" - text += ")" - for(var/obj/item/uplink/H in world_uplinks) - if(H && H.uplink_owner && H.uplink_owner==syndicate.key) - TC_uses += H.used_TC - purchases += H.purchase_log - - text += "
    " - - text += "(Syndicates used [TC_uses] TC) [purchases]" - - if(TC_uses==0 && station_was_nuked && !is_operatives_are_dead()) - text += "" - - to_chat(world, text) - return 1 - -/proc/nukelastname(var/mob/M as mob) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea. Also praise Urist for copypasta ho. - var/randomname = pick(GLOB.last_names) - var/newname = sanitize(copytext(input(M,"You are the nuke operative [pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")]. Please choose a last name for your family.", "Name change",randomname),1,MAX_NAME_LEN)) - - if(!newname) - newname = randomname - - else - if(newname == "Unknown" || newname == "floor" || newname == "wall" || newname == "rwall" || newname == "_") - to_chat(M, "That name is reserved.") - return nukelastname(M) - - return newname - - -/datum/game_mode/nuclear/set_scoreboard_gvars() - var/foecount = 0 - for(var/datum/mind/M in SSticker.mode.syndicates) - foecount++ - if(!M || !M.current) - score_opkilled++ - continue - - if(M.current.stat == DEAD) - score_opkilled++ - - else if(M.current.restrained()) - score_arrested++ - - if(foecount == score_arrested) - score_allarrested = 1 - - for(var/obj/machinery/nuclearbomb/nuke in world) - if(nuke.r_code == "Nope") continue - var/turf/T = get_turf(nuke) - var/area/A = T.loc - - var/list/thousand_penalty = list(/area/wizard_station, /area/solar, /area) - var/list/fiftythousand_penalty = list(/area/security/main, /area/security/brig, /area/security/armoury, /area/security/checkpoint2) - - if(is_type_in_list(A, thousand_penalty)) - score_nuked_penalty = 1000 - - else if(is_type_in_list(A, fiftythousand_penalty)) - score_nuked_penalty = 50000 - - else if(istype(A, /area/engine)) - score_nuked_penalty = 100000 - - else - score_nuked_penalty = 10000 - - break - - var/killpoints = score_opkilled * 250 - var/arrestpoints = score_arrested * 1000 - score_crewscore += killpoints - score_crewscore += arrestpoints - if(score_nuked) - score_crewscore -= score_nuked_penalty - - - -/datum/game_mode/nuclear/get_scoreboard_stats() - var/foecount = 0 - var/crewcount = 0 - - var/diskdat = "" - var/bombdat = null - - for(var/datum/mind/M in SSticker.mode.syndicates) - foecount++ - - for(var/mob/living/C in world) - if(ishuman(C) || isAI(C) || isrobot(C)) - if(C.stat == 2) continue - if(!C.client) continue - crewcount++ - - var/obj/item/disk/nuclear/N = locate() in world - if(istype(N)) - var/atom/disk_loc = N.loc - while(!isturf(disk_loc)) - if(ismob(disk_loc)) - var/mob/M = disk_loc - diskdat += "Carried by [M.real_name] " - if(isobj(disk_loc)) - var/obj/O = disk_loc - diskdat += "in \a [O]" - disk_loc = disk_loc.loc - diskdat += "in [disk_loc.loc]" - - - if(!diskdat) - diskdat = "WARNING: Nuked_penalty could not be found, look at [__FILE__], [__LINE__]." - - var/dat = "" - dat += "Mode Statistics
    " - - dat += "Number of Operatives: [foecount]
    " - dat += "Number of Surviving Crew: [crewcount]
    " - - dat += "Final Location of Nuke: [bombdat]
    " - dat += "Final Location of Disk: [diskdat]
    " - - dat += "
    " - - dat += "Operatives Arrested: [score_arrested] ([score_arrested * 1000] Points)
    " - dat += "All Operatives Arrested: [score_allarrested ? "Yes" : "No"] (Score tripled)
    " - - dat += "Operatives Killed: [score_opkilled] ([score_opkilled * 1000] Points)
    " - dat += "Station Destroyed: [score_nuked ? "Yes" : "No"] (-[score_nuked_penalty] Points)
    " - dat += "
    " - - return dat - -#undef NUKESCALINGMODIFIER +#define NUKESCALINGMODIFIER 1.2 + +/datum/game_mode + var/list/datum/mind/syndicates = list() + +proc/issyndicate(mob/living/M as mob) + return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.syndicates) + +/datum/game_mode/nuclear + name = "nuclear emergency" + config_tag = "nuclear" + required_players = 30 // 30 players - 5 players to be the nuke ops = 25 players remaining + required_enemies = 5 + recommended_enemies = 5 + + var/const/agents_possible = 5 //If we ever need more syndicate agents. + + var/nukes_left = 1 //Call 3714-PRAY right now and order more nukes! Limited offer! + var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station + var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level + var/total_tc = 0 //Total amount of telecrystals shared between nuke ops + +/datum/game_mode/nuclear/announce() + to_chat(world, "The current game mode is - Nuclear Emergency!") + to_chat(world, "A [syndicate_name()] Strike Force is approaching [station_name()]!") + to_chat(world, "A nuclear explosive was being transported by Nanotrasen to a military base. The transport ship mysteriously lost contact with Space Traffic Control (STC). About that time a strange disk was discovered around [station_name()]. It was identified by Nanotrasen as a nuclear authentication disk and now Syndicate Operatives have arrived to retake the disk and detonate SS13! There are most likely Syndicate starships are in the vicinity, so take care not to lose the disk!\nSyndicate: Reclaim the disk and detonate the nuclear bomb anywhere on SS13.\nPersonnel: Hold the disk and escape with the disk on the shuttle!") + +/datum/game_mode/nuclear/can_start()//This could be better, will likely have to recode it later + if(!..()) + return 0 + + var/list/possible_syndicates = get_players_for_role(ROLE_OPERATIVE) + var/agent_number = 0 + + if(possible_syndicates.len < 1) + return 0 + + if(LAZYLEN(possible_syndicates) > agents_possible) + agent_number = agents_possible + else + agent_number = possible_syndicates.len + + var/n_players = num_players() + if(agent_number > n_players) + agent_number = n_players/2 + + while(agent_number > 0) + var/datum/mind/new_syndicate = pick(possible_syndicates) + syndicates += new_syndicate + possible_syndicates -= new_syndicate //So it doesn't pick the same guy each time. + agent_number-- + + for(var/datum/mind/synd_mind in syndicates) + synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs. + synd_mind.special_role = SPECIAL_ROLE_NUKEOPS + synd_mind.offstation_role = TRUE + return 1 + + +/datum/game_mode/nuclear/pre_setup() + ..() + return 1 + +/datum/game_mode/proc/remove_operative(datum/mind/operative_mind) + if(operative_mind in syndicates) + SSticker.mode.syndicates -= operative_mind + operative_mind.special_role = null + for(var/datum/objective/nuclear/O in operative_mind.objectives) + operative_mind.objectives -= O + operative_mind.current.create_attack_log("No longer nuclear operative") + operative_mind.current.create_log(CONVERSION_LOG, "No longer nuclear operative") + if(issilicon(operative_mind.current)) + to_chat(operative_mind.current, "You have been turned into a robot! You are no longer a Syndicate operative.") + else + to_chat(operative_mind.current, "You have been brainwashed! You are no longer a Syndicate operative.") + SSticker.mode.update_synd_icons_removed(operative_mind) + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +/datum/game_mode/proc/update_synd_icons_added(datum/mind/synd_mind) + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] + opshud.join_hud(synd_mind.current) + set_antag_hud(synd_mind.current, "hudoperative") + +/datum/game_mode/proc/update_synd_icons_removed(datum/mind/synd_mind) + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] + opshud.leave_hud(synd_mind.current) + set_antag_hud(synd_mind.current, null) + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +/datum/game_mode/nuclear/post_setup() + + var/list/turf/synd_spawn = list() + + for(var/obj/effect/landmark/A in GLOB.landmarks_list) + if(A.name == "Syndicate-Spawn") + synd_spawn += get_turf(A) + qdel(A) + continue + + var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb") + + var/nuke_code = "[rand(10000, 99999)]" + var/leader_selected = 0 + var/agent_number = 1 + var/spawnpos = 1 + + for(var/datum/mind/synd_mind in syndicates) + if(spawnpos > synd_spawn.len) + spawnpos = 2 + synd_mind.current.loc = synd_spawn[spawnpos] + + forge_syndicate_objectives(synd_mind) + create_syndicate(synd_mind) + greet_syndicate(synd_mind) + equip_syndicate(synd_mind.current) + + if(!leader_selected) + prepare_syndicate_leader(synd_mind, nuke_code) + leader_selected = 1 + else + synd_mind.current.real_name = "[syndicate_name()] Operative #[agent_number]" + update_syndicate_id(synd_mind, FALSE) + + agent_number++ + spawnpos++ + update_synd_icons_added(synd_mind) + + scale_telecrystals() + share_telecrystals() + if(nuke_spawn && synd_spawn.len > 0) + var/obj/machinery/nuclearbomb/syndicate/the_bomb = new /obj/machinery/nuclearbomb/syndicate(nuke_spawn.loc) + the_bomb.r_code = nuke_code + + return ..() + +/datum/game_mode/nuclear/proc/scale_telecrystals() + var/danger + danger = GLOB.player_list.len + while(!IsMultiple(++danger, 10)) //Increments danger up to the nearest multiple of ten + + total_tc += danger * NUKESCALINGMODIFIER + +/datum/game_mode/nuclear/proc/share_telecrystals() + var/player_tc + var/remainder + + player_tc = round(total_tc / GLOB.nuclear_uplink_list.len) //round to get an integer and not floating point + remainder = total_tc % GLOB.nuclear_uplink_list.len + + for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) + U.hidden_uplink.uses += player_tc + while(remainder > 0) + for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) + if(remainder <= 0) + break + U.hidden_uplink.uses++ + remainder-- + +/datum/game_mode/proc/create_syndicate(datum/mind/synd_mind) // So we don't have inferior species as ops - randomize a human + var/mob/living/carbon/human/M = synd_mind.current + + M.set_species(/datum/species/human, TRUE) + M.dna.ready_dna(M) // Quadriplegic Nuke Ops won't be participating in the paralympics + M.dna.species.create_organs(M) + M.cleanSE() //No fat/blind/colourblind/epileptic/whatever ops. + M.overeatduration = 0 + M.flavor_text = null + + var/obj/item/organ/external/head/head_organ = M.get_organ("head") + var/hair_c = pick("#8B4513","#000000","#FF4500","#FFD700") // Brown, black, red, blonde + var/eye_c = pick("#000000","#8B4513","1E90FF") // Black, brown, blue + var/skin_tone = pick(-50, -30, -10, 0, 0, 0, 10) // Caucasian/black + head_organ.facial_colour = hair_c + head_organ.sec_facial_colour = hair_c + head_organ.hair_colour = hair_c + head_organ.sec_hair_colour = hair_c + M.change_eye_color(eye_c) + M.s_tone = skin_tone + head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name) + head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name) + M.body_accessory = null + M.regenerate_icons() + M.update_body() + +/datum/game_mode/proc/prepare_syndicate_leader(var/datum/mind/synd_mind, var/nuke_code) + var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord") + synd_mind.current.real_name = "[syndicate_name()] Team [leader_title]" + to_chat(synd_mind.current, "You are the Syndicate leader for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.") + to_chat(synd_mind.current, "If you feel you are not up to this task, give your ID to another operative.") + to_chat(synd_mind.current, "In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.") + + var/obj/item/nuclear_challenge/challenge = new /obj/item/nuclear_challenge + synd_mind.current.equip_to_slot_or_del(challenge, slot_r_hand) + + update_syndicate_id(synd_mind, leader_title, TRUE) + + if(nuke_code) + synd_mind.store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) + to_chat(synd_mind.current, "The nuclear authorization code is: [nuke_code]") + var/obj/item/paper/P = new + P.info = "The nuclear authorization code is: [nuke_code]" + P.name = "nuclear bomb code" + var/obj/item/stamp/syndicate/stamp = new + P.stamp(stamp) + qdel(stamp) + + if(SSticker.mode.config_tag=="nuclear") + P.loc = synd_mind.current.loc + else + var/mob/living/carbon/human/H = synd_mind.current + P.loc = H.loc + H.equip_to_slot_or_del(P, slot_r_store, 0) + H.update_icons() + + else + nuke_code = "code will be provided later" + +/datum/game_mode/proc/update_syndicate_id(var/datum/mind/synd_mind, is_leader = FALSE) + var/list/found_ids = synd_mind.current.search_contents_for(/obj/item/card/id) + + if(LAZYLEN(found_ids)) + for(var/obj/item/card/id/ID in found_ids) + ID.name = "[synd_mind.current.real_name] ID card" + ID.registered_name = synd_mind.current.real_name + if(is_leader) + ID.access += ACCESS_SYNDICATE_LEADER + else + message_admins("Warning: Operative [key_name_admin(synd_mind.current)] spawned without an ID card!") + +/datum/game_mode/proc/forge_syndicate_objectives(var/datum/mind/syndicate) + var/datum/objective/nuclear/syndobj = new + syndobj.owner = syndicate + syndicate.objectives += syndobj + + +/datum/game_mode/proc/greet_syndicate(var/datum/mind/syndicate, var/you_are=1) + SEND_SOUND(syndicate.current, 'sound/ambience/antag/ops.ogg') + if(you_are) + to_chat(syndicate.current, "You are a [syndicate_name()] agent!") + var/obj_count = 1 + for(var/datum/objective/objective in syndicate.objectives) + to_chat(syndicate.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + return + + +/datum/game_mode/proc/random_radio_frequency() + return 1337 // WHY??? -- Doohl + + +/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob, uplink_uses = 20) + var/radio_freq = SYND_FREQ + + var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt(synd_mob) + R.set_frequency(radio_freq) + synd_mob.equip_to_slot_or_del(R, slot_l_ear) + + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(synd_mob), slot_w_uniform) + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(synd_mob), slot_shoes) + synd_mob.equip_or_collect(new /obj/item/clothing/gloves/combat(synd_mob), slot_gloves) + synd_mob.equip_to_slot_or_del(new /obj/item/card/id/syndicate(synd_mob), slot_wear_id) + synd_mob.equip_to_slot_or_del(new /obj/item/storage/backpack(synd_mob), slot_back) + synd_mob.equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/pistol(synd_mob), slot_belt) + synd_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(synd_mob.back), slot_in_backpack) + synd_mob.equip_to_slot_or_del(new /obj/item/pinpointer/nukeop(synd_mob), slot_wear_pda) + var/obj/item/radio/uplink/nuclear/U = new /obj/item/radio/uplink/nuclear(synd_mob) + U.hidden_uplink.uplink_owner="[synd_mob.key]" + U.hidden_uplink.uses = uplink_uses + synd_mob.equip_to_slot_or_del(U, slot_in_backpack) + + if(synd_mob.dna.species) + + /* + Incase anyone ever gets the burning desire to have nukeops with randomized apperances. -- Dave + synd_mob.gender = pick(MALE, FEMALE) // Randomized appearances for the nukeops. + var/datum/preferences/pref = new() + A.randomize_appearance_for(synd_mob) + */ + + var/race = synd_mob.dna.species.name + + switch(race) + if("Vox" || "Vox Armalis") + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) + synd_mob.equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/vox(synd_mob), slot_l_hand) + synd_mob.internal = synd_mob.l_hand + synd_mob.update_action_buttons_icon() + + if("Plasmaman") + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) + synd_mob.equip_or_collect(new /obj/item/tank/plasma/plasmaman(synd_mob), slot_s_store) + synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) + synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) + synd_mob.internal = synd_mob.get_item_by_slot(slot_s_store) + synd_mob.update_action_buttons_icon() + + synd_mob.rejuvenate() //fix any damage taken by naked vox/plasmamen/etc while round setups + var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(synd_mob) + E.implant(synd_mob) + synd_mob.faction |= "syndicate" + synd_mob.update_icons() + return 1 + +/datum/game_mode/nuclear/check_win() + if(nukes_left == 0) + return 1 + return ..() + + +/datum/game_mode/proc/is_operatives_are_dead() + for(var/datum/mind/operative_mind in syndicates) + if(!istype(operative_mind.current,/mob/living/carbon/human)) + if(operative_mind.current) + if(operative_mind.current.stat!=2) + return 0 + return 1 + + +/datum/game_mode/nuclear/declare_completion() + var/disk_rescued = 1 + for(var/obj/item/disk/nuclear/D in GLOB.poi_list) + if(!D.onCentcom()) + disk_rescued = 0 + break + var/crew_evacuated = (SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) + //var/operatives_are_dead = is_operatives_are_dead() + + + //nukes_left + //station_was_nuked + //derp //Used for tracking if the syndies actually haul the nuke to the station //no + //herp //Used for tracking if the syndies got the shuttle off of the z-level //NO, DON'T FUCKING NAME VARS LIKE THIS + + if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear win - syndicate nuke") + to_chat(world, "Syndicate Major Victory!") + to_chat(world, "[syndicate_name()] operatives have destroyed [station_name()]!") + + else if(!disk_rescued && station_was_nuked && syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear halfwin - syndicate nuke - did not evacuate in time") + to_chat(world, "Total Annihilation") + to_chat(world, "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") + + else if(!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear halfwin - blew wrong station") + to_chat(world, "Crew Minor Victory") + to_chat(world, "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") + + else if(!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear halfwin - blew wrong station - did not evacuate in time") + to_chat(world, "[syndicate_name()] operatives have earned Darwin Award!") + to_chat(world, "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") + + else if(disk_rescued && is_operatives_are_dead()) + feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured - syndi team dead") + to_chat(world, "Crew Major Victory!") + to_chat(world, "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") + + else if(disk_rescued) + feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured") + to_chat(world, "Crew Major Victory") + to_chat(world, "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") + + else if(!disk_rescued && is_operatives_are_dead()) + feedback_set_details("round_end_result","nuclear loss - evacuation - disk not secured") + to_chat(world, "Syndicate Minor Victory!") + to_chat(world, "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") + + else if(!disk_rescued && crew_evacuated) + feedback_set_details("round_end_result","nuclear halfwin - detonation averted") + to_chat(world, "Syndicate Minor Victory!") + to_chat(world, "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") + + else if(!disk_rescued && !crew_evacuated) + feedback_set_details("round_end_result","nuclear halfwin - interrupted") + to_chat(world, "Neutral Victory") + to_chat(world, "Round was mysteriously interrupted!") + ..() + return + + +/datum/game_mode/proc/auto_declare_completion_nuclear() + if(syndicates.len || GAMEMODE_IS_NUCLEAR) + var/text = "
    The syndicate operatives were:" + + var/purchases = "" + var/TC_uses = 0 + + for(var/datum/mind/syndicate in syndicates) + + text += "
    [syndicate.key] was [syndicate.name] (" + if(syndicate.current) + if(syndicate.current.stat == DEAD) + text += "died" + else + text += "survived" + if(syndicate.current.real_name != syndicate.name) + text += " as [syndicate.current.real_name]" + else + text += "body destroyed" + text += ")" + for(var/obj/item/uplink/H in GLOB.world_uplinks) + if(H && H.uplink_owner && H.uplink_owner==syndicate.key) + TC_uses += H.used_TC + purchases += H.purchase_log + + text += "
    " + + text += "(Syndicates used [TC_uses] TC) [purchases]" + + if(TC_uses==0 && station_was_nuked && !is_operatives_are_dead()) + text += "" + + to_chat(world, text) + return 1 + +/proc/nukelastname(var/mob/M as mob) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea. Also praise Urist for copypasta ho. + var/randomname = pick(GLOB.last_names) + var/newname = sanitize(copytext(input(M,"You are the nuke operative [pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")]. Please choose a last name for your family.", "Name change",randomname),1,MAX_NAME_LEN)) + + if(!newname) + newname = randomname + + else + if(newname == "Unknown" || newname == "floor" || newname == "wall" || newname == "rwall" || newname == "_") + to_chat(M, "That name is reserved.") + return nukelastname(M) + + return newname + + +/datum/game_mode/nuclear/set_scoreboard_gvars() + var/foecount = 0 + for(var/datum/mind/M in SSticker.mode.syndicates) + foecount++ + if(!M || !M.current) + GLOB.score_opkilled++ + continue + + if(M.current.stat == DEAD) + GLOB.score_opkilled++ + + else if(M.current.restrained()) + GLOB.score_arrested++ + + if(foecount == GLOB.score_arrested) + GLOB.score_allarrested = 1 + + for(var/obj/machinery/nuclearbomb/nuke in world) + if(nuke.r_code == "Nope") continue + var/turf/T = get_turf(nuke) + var/area/A = T.loc + + var/list/thousand_penalty = list(/area/wizard_station, /area/solar, /area) + var/list/fiftythousand_penalty = list(/area/security/main, /area/security/brig, /area/security/armoury, /area/security/checkpoint2) + + if(is_type_in_list(A, thousand_penalty)) + GLOB.score_nuked_penalty = 1000 + + else if(is_type_in_list(A, fiftythousand_penalty)) + GLOB.score_nuked_penalty = 50000 + + else if(istype(A, /area/engine)) + GLOB.score_nuked_penalty = 100000 + + else + GLOB.score_nuked_penalty = 10000 + + break + + var/killpoints = GLOB.score_opkilled * 250 + var/arrestpoints = GLOB.score_arrested * 1000 + GLOB.score_crewscore += killpoints + GLOB.score_crewscore += arrestpoints + if(GLOB.score_nuked) + GLOB.score_crewscore -= GLOB.score_nuked_penalty + + + +/datum/game_mode/nuclear/get_scoreboard_stats() + var/foecount = 0 + var/crewcount = 0 + + var/diskdat = "" + var/bombdat = null + + for(var/datum/mind/M in SSticker.mode.syndicates) + foecount++ + + for(var/mob/living/C in world) + if(ishuman(C) || isAI(C) || isrobot(C)) + if(C.stat == 2) continue + if(!C.client) continue + crewcount++ + + var/obj/item/disk/nuclear/N = locate() in world + if(istype(N)) + var/atom/disk_loc = N.loc + while(!isturf(disk_loc)) + if(ismob(disk_loc)) + var/mob/M = disk_loc + diskdat += "Carried by [M.real_name] " + if(isobj(disk_loc)) + var/obj/O = disk_loc + diskdat += "in \a [O]" + disk_loc = disk_loc.loc + diskdat += "in [disk_loc.loc]" + + + if(!diskdat) + diskdat = "WARNING: Nuked_penalty could not be found, look at [__FILE__], [__LINE__]." + + var/dat = "" + dat += "Mode Statistics
    " + + dat += "Number of Operatives: [foecount]
    " + dat += "Number of Surviving Crew: [crewcount]
    " + + dat += "Final Location of Nuke: [bombdat]
    " + dat += "Final Location of Disk: [diskdat]
    " + + dat += "
    " + + dat += "Operatives Arrested: [GLOB.score_arrested] ([GLOB.score_arrested * 1000] Points)
    " + dat += "All Operatives Arrested: [GLOB.score_allarrested ? "Yes" : "No"] (Score tripled)
    " + + dat += "Operatives Killed: [GLOB.score_opkilled] ([GLOB.score_opkilled * 1000] Points)
    " + dat += "Station Destroyed: [GLOB.score_nuked ? "Yes" : "No"] (-[GLOB.score_nuked_penalty] Points)
    " + dat += "
    " + + return dat + +#undef NUKESCALINGMODIFIER diff --git a/code/game/gamemodes/nuclear/nuclear_challenge.dm b/code/game/gamemodes/nuclear/nuclear_challenge.dm index 35c26408c5b..48116911640 100644 --- a/code/game/gamemodes/nuclear/nuclear_challenge.dm +++ b/code/game/gamemodes/nuclear/nuclear_challenge.dm @@ -48,7 +48,7 @@ if(!check_allowed(user) || !war_declaration) return - event_announcement.Announce(war_declaration, "Declaration of War", 'sound/effects/siren.ogg') + GLOB.event_announcement.Announce(war_declaration, "Declaration of War", 'sound/effects/siren.ogg') to_chat(user, "You've attracted the attention of powerful forces within the syndicate. A bonus bundle of telecrystals has been granted to your team. Great things await you if you complete the mission.") to_chat(user, "Your bonus telecrystals have been split between your team's uplinks.") diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm index 714ccbf1d5f..8e38f106cc7 100644 --- a/code/game/gamemodes/nuclear/nuclearbomb.dm +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm @@ -1,472 +1,474 @@ -#define NUKE_INTACT 0 -#define NUKE_COVER_OFF 1 -#define NUKE_COVER_OPEN 2 -#define NUKE_SEALANT_OPEN 3 -#define NUKE_UNWRENCHED 4 -#define NUKE_MOBILE 5 - -var/bomb_set - -/obj/machinery/nuclearbomb - name = "\improper Nuclear Fission Explosive" - desc = "Uh oh. RUN!!!!" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "nuclearbomb0" - density = 1 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/deployable = 0 - var/extended = 0 - var/lighthack = 0 - var/timeleft = 120 - var/timing = 0 - var/r_code = "ADMIN" - var/code = "" - var/yes_code = 0 - var/safety = 1 - var/obj/item/disk/nuclear/auth = null - var/removal_stage = NUKE_INTACT - var/lastentered - var/is_syndicate = 0 - use_power = NO_POWER_USE - var/previous_level = "" - var/datum/wires/nuclearbomb/wires = null - -/obj/machinery/nuclearbomb/syndicate - is_syndicate = 1 - -/obj/machinery/nuclearbomb/New() - ..() - r_code = "[rand(10000, 99999.0)]"//Creates a random code upon object spawn. - wires = new/datum/wires/nuclearbomb(src) - previous_level = get_security_level() - GLOB.poi_list |= src - -/obj/machinery/nuclearbomb/Destroy() - QDEL_NULL(wires) - GLOB.poi_list.Remove(src) - return ..() - -/obj/machinery/nuclearbomb/process() - if(timing) - bomb_set = 1 //So long as there is one nuke timing, it means one nuke is armed. - timeleft = max(timeleft - 2, 0) // 2 seconds per process() - if(timeleft <= 0) - spawn - explode() - SSnanoui.update_uis(src) - return - -/obj/machinery/nuclearbomb/attackby(obj/item/O as obj, mob/user as mob, params) - if(istype(O, /obj/item/disk/nuclear)) - if(extended) - if(!user.drop_item()) - to_chat(user, "\The [O] is stuck to your hand!") - return - O.forceMove(src) - auth = O - add_fingerprint(user) - return attack_hand(user) - else - to_chat(user, "You need to deploy \the [src] first. Right click on the sprite, select 'Make Deployable' then click on \the [src] with an empty hand.") - return - return ..() - -/obj/machinery/nuclearbomb/crowbar_act(mob/user, obj/item/I) - if(!anchored) - return - if(removal_stage != NUKE_UNWRENCHED && removal_stage != NUKE_COVER_OFF) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(removal_stage == NUKE_COVER_OFF) - user.visible_message("[user] starts forcing open the bolt covers on [src].", "You start forcing open the anchoring bolt covers with [I]...") - if(!I.use_tool(src, user, 15, volume = I.tool_volume) || removal_stage != NUKE_COVER_OFF) - return - user.visible_message("[user] forces open the bolt covers on [src].", "You force open the bolt covers.") - removal_stage = NUKE_COVER_OPEN - else - user.visible_message("[user] begins lifting [src] off of the anchors.", "You begin lifting the device off the anchors...") - if(!I.use_tool(src, user, 80, volume = I.tool_volume) || removal_stage != NUKE_UNWRENCHED) - return - user.visible_message("[user] crowbars [src] off of the anchors. It can now be moved.", "You jam the crowbar under the nuclear device and lift it off its anchors. You can now move it!") - anchored = FALSE - removal_stage = NUKE_MOBILE - -/obj/machinery/nuclearbomb/wrench_act(mob/user, obj/item/I) - if(!anchored) - return - if(removal_stage != NUKE_SEALANT_OPEN) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - user.visible_message("[user] begins unwrenching the anchoring bolts on [src].", "You begin unwrenching the anchoring bolts...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume) || removal_stage != NUKE_SEALANT_OPEN) - return - user.visible_message("[user] unwrenches the anchoring bolts on [src].", "You unwrench the anchoring bolts.") - removal_stage = NUKE_UNWRENCHED - -/obj/machinery/nuclearbomb/multitool_act(mob/user, obj/item/I) - if(!panel_open) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - attack_hand(user) - -/obj/machinery/nuclearbomb/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(auth) - if(!panel_open) - panel_open = TRUE - overlays += image(icon, "npanel_open") - to_chat(user, "You unscrew the control panel of [src].") - else - panel_open = FALSE - overlays -= image(icon, "npanel_open") - to_chat(user, "You screw the control panel of [src] back on.") - else - if(!panel_open) - to_chat(user, "[src] emits a buzzing noise, the panel staying locked in.") - if(panel_open == TRUE) - panel_open = FALSE - overlays -= image(icon, "npanel_open") - to_chat(user, "You screw the control panel of [src] back on.") - flick("nuclearbombc", src) - -/obj/machinery/nuclearbomb/wirecutter_act(mob/user, obj/item/I) - if(!panel_open) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - attack_hand(user) - -/obj/machinery/nuclearbomb/welder_act(mob/user, obj/item/I) - . = TRUE - if(removal_stage != NUKE_INTACT && removal_stage != NUKE_COVER_OPEN) - return - if(!I.tool_use_check(user, 0)) - return - if(removal_stage == NUKE_INTACT) - visible_message("[user] starts cutting loose the anchoring bolt covers on [src].",\ - "You start cutting loose the anchoring bolt covers with [I]...",\ - "You hear welding.") - if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_INTACT) - return - visible_message("[user] cuts through the bolt covers on [src].",\ - "You cut through the bolt cover.") - removal_stage = NUKE_COVER_OFF - else if(removal_stage == NUKE_COVER_OPEN) - visible_message("[user] starts cutting apart the anchoring system sealant on [src].",\ - "You start cutting apart the anchoring system's sealant with [I]...",\ - "You hear welding.") - if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_COVER_OPEN) - return - visible_message("[user] cuts apart the anchoring system sealant on [src].",\ - "You cut apart the anchoring system's sealant.
    ") - removal_stage = NUKE_SEALANT_OPEN - -/obj/machinery/nuclearbomb/attack_ghost(mob/user as mob) - if(extended) - attack_hand(user) - -/obj/machinery/nuclearbomb/attack_hand(mob/user as mob) - if(extended) - if(panel_open) - wires.Interact(user) - else - ui_interact(user) - else if(deployable) - if(removal_stage != NUKE_MOBILE) - anchored = 1 - visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!") - else - visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") - if(!lighthack) - flick("nuclearbombc", src) - icon_state = "nuclearbomb1" - extended = 1 - return - -/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "nuclear_bomb.tmpl", "Nuke Control Panel", 450, 550, state = physical_state) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/nuclearbomb/ui_data(mob/user, datum/topic_state/state) - var/data[0] - data["is_syndicate"] = is_syndicate - data["hacking"] = 0 - data["auth"] = is_auth(user) - if(is_auth(user)) - if(yes_code) - data["authstatus"] = timing ? "Functional/Set" : "Functional" - else - data["authstatus"] = "Auth. S2" - else - if(timing) - data["authstatus"] = "Set" - else - data["authstatus"] = "Auth. S1" - data["safe"] = safety ? "Safe" : "Engaged" - data["time"] = timeleft - data["timer"] = timing - data["safety"] = safety - data["anchored"] = anchored - data["yescode"] = yes_code - data["message"] = "AUTH" - if(is_auth(user)) - data["message"] = code - if(yes_code) - data["message"] = "*****" - - return data - -/obj/machinery/nuclearbomb/verb/make_deployable() - set category = "Object" - set name = "Make Deployable" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - if(deployable) - to_chat(usr, "You close several panels to make [src] undeployable.") - deployable = 0 - else - to_chat(usr, "You adjust some panels to make [src] deployable.") - deployable = 1 - return - -/obj/machinery/nuclearbomb/proc/is_auth(var/mob/user) - if(auth) - return 1 - else if(user.can_admin_interact()) - return 1 - else - return 0 - -/obj/machinery/nuclearbomb/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["auth"]) - if(auth) - auth.loc = loc - yes_code = 0 - auth = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/disk/nuclear)) - usr.drop_item() - I.loc = src - auth = I - if(is_auth(usr)) - if(href_list["type"]) - if(href_list["type"] == "E") - if(code == r_code) - yes_code = 1 - code = null - else - code = "ERROR" - else - if(href_list["type"] == "R") - yes_code = 0 - code = null - else - lastentered = text("[]", href_list["type"]) - if(text2num(lastentered) == null) - var/turf/LOC = get_turf(usr) - message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0) - log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!") - else - code += lastentered - if(length(code) > 5) - code = "ERROR" - if(yes_code) - if(href_list["time"]) - var/time = text2num(href_list["time"]) - timeleft += time - timeleft = min(max(round(src.timeleft), 120), 600) - if(href_list["timer"]) - if(timing == -1.0) - SSnanoui.update_uis(src) - return - if(safety) - to_chat(usr, "The safety is still on.") - SSnanoui.update_uis(src) - return - timing = !(timing) - if(timing) - if(!lighthack) - icon_state = "nuclearbomb2" - if(!safety) - message_admins("[key_name_admin(usr)] engaged a nuclear bomb (JMP)") - if(!is_syndicate) - set_security_level("delta") - bomb_set = 1 //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N - else - bomb_set = 0 - else - if(!is_syndicate) - set_security_level(previous_level) - bomb_set = 0 - if(!lighthack) - icon_state = "nuclearbomb1" - if(href_list["safety"]) - safety = !(safety) - if(safety) - if(!is_syndicate) - set_security_level(previous_level) - timing = 0 - bomb_set = 0 - if(href_list["anchor"]) - if(removal_stage == NUKE_MOBILE) - anchored = 0 - visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") - SSnanoui.update_uis(src) - return - - if(!isinspace()) - anchored = !(anchored) - if(anchored) - visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.") - else - visible_message("The anchoring bolts slide back into the depths of [src].") - else - to_chat(usr, "There is nothing to anchor to!") - - SSnanoui.update_uis(src) - -/obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B) - if(timing == -1.0) - return - qdel(src) - -/obj/machinery/nuclearbomb/tesla_act(power, explosive) - ..() - if(explosive) - qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over - -#define NUKERANGE 80 -/obj/machinery/nuclearbomb/proc/explode() - if(safety) - timing = 0 - return - timing = -1.0 - yes_code = 0 - safety = 1 - if(!lighthack) - icon_state = "nuclearbomb3" - playsound(src,'sound/machines/alarm.ogg',100,0,5) - if(SSticker && SSticker.mode) - SSticker.mode.explosion_in_progress = 1 - sleep(100) - - enter_allowed = 0 - - var/off_station = 0 - var/turf/bomb_location = get_turf(src) - if( bomb_location && is_station_level(bomb_location.z) ) - if( (bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)) ) - off_station = 1 - else - off_station = 2 - - if(SSticker) - if(SSticker.mode && SSticker.mode.name == "nuclear emergency") - var/obj/docking_port/mobile/syndie_shuttle = SSshuttle.getShuttle("syndicate") - if(syndie_shuttle) - SSticker.mode:syndies_didnt_escape = is_station_level(syndie_shuttle.z) - SSticker.mode:nuke_off_station = off_station - SSticker.station_explosion_cinematic(off_station,null) - if(SSticker.mode) - SSticker.mode.explosion_in_progress = 0 - if(SSticker.mode.name == "nuclear emergency") - SSticker.mode:nukes_left -- - else if(off_station == 1) - to_chat(world, "A nuclear device was set off, but the explosion was out of reach of the station!") - else if(off_station == 2) - to_chat(world, "A nuclear device was set off, but the device was not on the station!") - else - to_chat(world, "The station was destoyed by the nuclear blast!") - - SSticker.mode.station_was_nuked = (off_station<2) //offstation==1 is a draw. the station becomes irradiated and needs to be evacuated. - //kinda shit but I couldn't get permission to do what I wanted to do. - - if(!SSticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is - world.Reboot("Station destroyed by Nuclear Device.", "end_error", "nuke - unhandled ending") - return - return - - -//==========DAT FUKKEN DISK=============== -/obj/item/disk/nuclear - name = "nuclear authentication disk" - desc = "Better keep this safe." - icon_state = "nucleardisk" - max_integrity = 250 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/disk/nuclear/unrestricted - desc = "Seems to have been stripped of its safeties, you better not lose it." - -/obj/item/disk/nuclear/New() - ..() - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - -/obj/item/disk/nuclear/process() - if(!check_disk_loc()) - var/holder = get(src, /mob) - if(holder) - to_chat(holder, "You can't help but feel that you just lost something back there...") - qdel(src) - - //station disk is allowed on z1, escape shuttle/pods, CC, and syndicate shuttles/base, reset otherwise -/obj/item/disk/nuclear/proc/check_disk_loc() - var/turf/T = get_turf(src) - var/area/A = get_area(src) - if(is_station_level(T.z)) - return TRUE - if(A.nad_allowed) - return TRUE - return FALSE - -/obj/item/disk/nuclear/unrestricted/check_disk_loc() - return TRUE - -/obj/item/disk/nuclear/Destroy(force) - var/turf/diskturf = get_turf(src) - - if(force) - message_admins("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z] - JMP":"nonexistent location"]).") - log_game("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z]":"nonexistent location"]).") - GLOB.poi_list.Remove(src) - STOP_PROCESSING(SSobj, src) - return ..() - - if(blobstart.len > 0) - GLOB.poi_list.Remove(src) - var/obj/item/disk/nuclear/NEWDISK = new(pick(blobstart)) - transfer_fingerprints_to(NEWDISK) - message_admins("[src] has been destroyed at ([diskturf.x], [diskturf.y], [diskturf.z] - JMP). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z] - JMP).") - log_game("[src] has been destroyed in ([diskturf.x], [diskturf.y], [diskturf.z]). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z]).") - return QDEL_HINT_HARDDEL_NOW - else - error("[src] was supposed to be destroyed, but we were unable to locate a blobstart landmark to spawn a new one.") - return QDEL_HINT_LETMELIVE // Cancel destruction unless forced. - -#undef NUKE_INTACT -#undef NUKE_COVER_OFF -#undef NUKE_COVER_OPEN -#undef NUKE_SEALANT_OPEN -#undef NUKE_UNWRENCHED -#undef NUKE_MOBILE +#define NUKE_INTACT 0 +#define NUKE_COVER_OFF 1 +#define NUKE_COVER_OPEN 2 +#define NUKE_SEALANT_OPEN 3 +#define NUKE_UNWRENCHED 4 +#define NUKE_MOBILE 5 + +GLOBAL_VAR(bomb_set) + +/obj/machinery/nuclearbomb + name = "\improper Nuclear Fission Explosive" + desc = "Uh oh. RUN!!!!" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "nuclearbomb0" + density = 1 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/deployable = 0 + var/extended = 0 + var/lighthack = 0 + var/timeleft = 120 + var/timing = 0 + var/r_code = "ADMIN" + var/code = "" + var/yes_code = 0 + var/safety = 1 + var/obj/item/disk/nuclear/auth = null + var/removal_stage = NUKE_INTACT + var/lastentered + var/is_syndicate = 0 + use_power = NO_POWER_USE + var/previous_level = "" + var/datum/wires/nuclearbomb/wires = null + +/obj/machinery/nuclearbomb/syndicate + is_syndicate = 1 + +/obj/machinery/nuclearbomb/New() + ..() + r_code = "[rand(10000, 99999.0)]"//Creates a random code upon object spawn. + wires = new/datum/wires/nuclearbomb(src) + previous_level = get_security_level() + GLOB.poi_list |= src + +/obj/machinery/nuclearbomb/Destroy() + QDEL_NULL(wires) + GLOB.poi_list.Remove(src) + return ..() + +/obj/machinery/nuclearbomb/process() + if(timing) + GLOB.bomb_set = 1 //So long as there is one nuke timing, it means one nuke is armed. + timeleft = max(timeleft - 2, 0) // 2 seconds per process() + if(timeleft <= 0) + INVOKE_ASYNC(src, .proc/explode) + SSnanoui.update_uis(src) + return + +/obj/machinery/nuclearbomb/attackby(obj/item/O as obj, mob/user as mob, params) + if(istype(O, /obj/item/disk/nuclear)) + if(extended) + if(!user.drop_item()) + to_chat(user, "\The [O] is stuck to your hand!") + return + O.forceMove(src) + auth = O + add_fingerprint(user) + return attack_hand(user) + else + to_chat(user, "You need to deploy \the [src] first. Right click on the sprite, select 'Make Deployable' then click on \the [src] with an empty hand.") + return + return ..() + +/obj/machinery/nuclearbomb/crowbar_act(mob/user, obj/item/I) + if(!anchored) + return + if(removal_stage != NUKE_UNWRENCHED && removal_stage != NUKE_COVER_OFF) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(removal_stage == NUKE_COVER_OFF) + user.visible_message("[user] starts forcing open the bolt covers on [src].", "You start forcing open the anchoring bolt covers with [I]...") + if(!I.use_tool(src, user, 15, volume = I.tool_volume) || removal_stage != NUKE_COVER_OFF) + return + user.visible_message("[user] forces open the bolt covers on [src].", "You force open the bolt covers.") + removal_stage = NUKE_COVER_OPEN + else + user.visible_message("[user] begins lifting [src] off of the anchors.", "You begin lifting the device off the anchors...") + if(!I.use_tool(src, user, 80, volume = I.tool_volume) || removal_stage != NUKE_UNWRENCHED) + return + user.visible_message("[user] crowbars [src] off of the anchors. It can now be moved.", "You jam the crowbar under the nuclear device and lift it off its anchors. You can now move it!") + anchored = FALSE + removal_stage = NUKE_MOBILE + +/obj/machinery/nuclearbomb/wrench_act(mob/user, obj/item/I) + if(!anchored) + return + if(removal_stage != NUKE_SEALANT_OPEN) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + user.visible_message("[user] begins unwrenching the anchoring bolts on [src].", "You begin unwrenching the anchoring bolts...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume) || removal_stage != NUKE_SEALANT_OPEN) + return + user.visible_message("[user] unwrenches the anchoring bolts on [src].", "You unwrench the anchoring bolts.") + removal_stage = NUKE_UNWRENCHED + +/obj/machinery/nuclearbomb/multitool_act(mob/user, obj/item/I) + if(!panel_open) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + attack_hand(user) + +/obj/machinery/nuclearbomb/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(auth) + if(!panel_open) + panel_open = TRUE + overlays += image(icon, "npanel_open") + to_chat(user, "You unscrew the control panel of [src].") + else + panel_open = FALSE + overlays -= image(icon, "npanel_open") + to_chat(user, "You screw the control panel of [src] back on.") + else + if(!panel_open) + to_chat(user, "[src] emits a buzzing noise, the panel staying locked in.") + if(panel_open == TRUE) + panel_open = FALSE + overlays -= image(icon, "npanel_open") + to_chat(user, "You screw the control panel of [src] back on.") + flick("nuclearbombc", src) + +/obj/machinery/nuclearbomb/wirecutter_act(mob/user, obj/item/I) + if(!panel_open) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + attack_hand(user) + +/obj/machinery/nuclearbomb/welder_act(mob/user, obj/item/I) + . = TRUE + if(removal_stage != NUKE_INTACT && removal_stage != NUKE_COVER_OPEN) + return + if(!I.tool_use_check(user, 0)) + return + if(removal_stage == NUKE_INTACT) + visible_message("[user] starts cutting loose the anchoring bolt covers on [src].",\ + "You start cutting loose the anchoring bolt covers with [I]...",\ + "You hear welding.") + if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_INTACT) + return + visible_message("[user] cuts through the bolt covers on [src].",\ + "You cut through the bolt cover.") + removal_stage = NUKE_COVER_OFF + else if(removal_stage == NUKE_COVER_OPEN) + visible_message("[user] starts cutting apart the anchoring system sealant on [src].",\ + "You start cutting apart the anchoring system's sealant with [I]...",\ + "You hear welding.") + if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_COVER_OPEN) + return + visible_message("[user] cuts apart the anchoring system sealant on [src].",\ + "You cut apart the anchoring system's sealant.
    ") + removal_stage = NUKE_SEALANT_OPEN + +/obj/machinery/nuclearbomb/attack_ghost(mob/user as mob) + if(extended) + attack_hand(user) + +/obj/machinery/nuclearbomb/attack_hand(mob/user as mob) + if(extended) + if(panel_open) + wires.Interact(user) + else + ui_interact(user) + else if(deployable) + if(removal_stage != NUKE_MOBILE) + anchored = 1 + visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!") + else + visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") + if(!lighthack) + flick("nuclearbombc", src) + icon_state = "nuclearbomb1" + extended = 1 + return + +/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "nuclear_bomb.tmpl", "Nuke Control Panel", 450, 550, state = GLOB.physical_state) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/nuclearbomb/ui_data(mob/user, datum/topic_state/state) + var/data[0] + data["is_syndicate"] = is_syndicate + data["hacking"] = 0 + data["auth"] = is_auth(user) + if(is_auth(user)) + if(yes_code) + data["authstatus"] = timing ? "Functional/Set" : "Functional" + else + data["authstatus"] = "Auth. S2" + else + if(timing) + data["authstatus"] = "Set" + else + data["authstatus"] = "Auth. S1" + data["safe"] = safety ? "Safe" : "Engaged" + data["time"] = timeleft + data["timer"] = timing + data["safety"] = safety + data["anchored"] = anchored + data["yescode"] = yes_code + data["message"] = "AUTH" + if(is_auth(user)) + data["message"] = code + if(yes_code) + data["message"] = "*****" + + return data + +/obj/machinery/nuclearbomb/verb/make_deployable() + set category = "Object" + set name = "Make Deployable" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + if(deployable) + to_chat(usr, "You close several panels to make [src] undeployable.") + deployable = 0 + else + to_chat(usr, "You adjust some panels to make [src] deployable.") + deployable = 1 + return + +/obj/machinery/nuclearbomb/proc/is_auth(var/mob/user) + if(auth) + return 1 + else if(user.can_admin_interact()) + return 1 + else + return 0 + +/obj/machinery/nuclearbomb/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["auth"]) + if(auth) + auth.loc = loc + yes_code = 0 + auth = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/disk/nuclear)) + usr.drop_item() + I.loc = src + auth = I + if(is_auth(usr)) + if(href_list["type"]) + if(href_list["type"] == "E") + if(code == r_code) + yes_code = 1 + code = null + else + code = "ERROR" + else + if(href_list["type"] == "R") + yes_code = 0 + code = null + else + lastentered = text("[]", href_list["type"]) + if(text2num(lastentered) == null) + var/turf/LOC = get_turf(usr) + message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0) + log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!") + else + code += lastentered + if(length(code) > 5) + code = "ERROR" + if(yes_code) + if(href_list["time"]) + var/time = text2num(href_list["time"]) + timeleft += time + timeleft = min(max(round(src.timeleft), 120), 600) + if(href_list["timer"]) + if(timing == -1.0) + SSnanoui.update_uis(src) + return + if(safety) + to_chat(usr, "The safety is still on.") + SSnanoui.update_uis(src) + return + timing = !(timing) + if(timing) + if(!lighthack) + icon_state = "nuclearbomb2" + if(!safety) + message_admins("[key_name_admin(usr)] engaged a nuclear bomb (JMP)") + if(!is_syndicate) + set_security_level("delta") + GLOB.bomb_set = 1 //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N + else + GLOB.bomb_set = 0 + else + if(!is_syndicate) + set_security_level(previous_level) + GLOB.bomb_set = 0 + if(!lighthack) + icon_state = "nuclearbomb1" + if(href_list["safety"]) + safety = !(safety) + if(safety) + if(!is_syndicate) + set_security_level(previous_level) + timing = 0 + GLOB.bomb_set = 0 + if(href_list["anchor"]) + if(removal_stage == NUKE_MOBILE) + anchored = 0 + visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") + SSnanoui.update_uis(src) + return + + if(!isinspace()) + anchored = !(anchored) + if(anchored) + visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.") + else + visible_message("The anchoring bolts slide back into the depths of [src].") + else + to_chat(usr, "There is nothing to anchor to!") + + SSnanoui.update_uis(src) + +/obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B) + if(timing == -1.0) + return + if(timing) //boom + INVOKE_ASYNC(src, .proc/explode) + return + qdel(src) + +/obj/machinery/nuclearbomb/tesla_act(power, explosive) + ..() + if(explosive) + qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over + +#define NUKERANGE 80 +/obj/machinery/nuclearbomb/proc/explode() + if(safety) + timing = 0 + return + timing = -1.0 + yes_code = 0 + safety = 1 + if(!lighthack) + icon_state = "nuclearbomb3" + playsound(src,'sound/machines/alarm.ogg',100,0,5) + if(SSticker && SSticker.mode) + SSticker.mode.explosion_in_progress = 1 + sleep(100) + + GLOB.enter_allowed = 0 + + var/off_station = 0 + var/turf/bomb_location = get_turf(src) + if( bomb_location && is_station_level(bomb_location.z) ) + if( (bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)) ) + off_station = 1 + else + off_station = 2 + + if(SSticker) + if(SSticker.mode && SSticker.mode.name == "nuclear emergency") + var/obj/docking_port/mobile/syndie_shuttle = SSshuttle.getShuttle("syndicate") + if(syndie_shuttle) + SSticker.mode:syndies_didnt_escape = is_station_level(syndie_shuttle.z) + SSticker.mode:nuke_off_station = off_station + SSticker.station_explosion_cinematic(off_station,null) + if(SSticker.mode) + SSticker.mode.explosion_in_progress = 0 + if(SSticker.mode.name == "nuclear emergency") + SSticker.mode:nukes_left -- + else if(off_station == 1) + to_chat(world, "A nuclear device was set off, but the explosion was out of reach of the station!") + else if(off_station == 2) + to_chat(world, "A nuclear device was set off, but the device was not on the station!") + else + to_chat(world, "The station was destoyed by the nuclear blast!") + + SSticker.mode.station_was_nuked = (off_station<2) //offstation==1 is a draw. the station becomes irradiated and needs to be evacuated. + //kinda shit but I couldn't get permission to do what I wanted to do. + + if(!SSticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is + world.Reboot("Station destroyed by Nuclear Device.", "end_error", "nuke - unhandled ending") + return + return + + +//==========DAT FUKKEN DISK=============== +/obj/item/disk/nuclear + name = "nuclear authentication disk" + desc = "Better keep this safe." + icon_state = "nucleardisk" + max_integrity = 250 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/disk/nuclear/unrestricted + desc = "Seems to have been stripped of its safeties, you better not lose it." + +/obj/item/disk/nuclear/New() + ..() + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + +/obj/item/disk/nuclear/process() + if(!check_disk_loc()) + var/holder = get(src, /mob) + if(holder) + to_chat(holder, "You can't help but feel that you just lost something back there...") + qdel(src) + + //station disk is allowed on z1, escape shuttle/pods, CC, and syndicate shuttles/base, reset otherwise +/obj/item/disk/nuclear/proc/check_disk_loc() + var/turf/T = get_turf(src) + var/area/A = get_area(src) + if(is_station_level(T.z)) + return TRUE + if(A.nad_allowed) + return TRUE + return FALSE + +/obj/item/disk/nuclear/unrestricted/check_disk_loc() + return TRUE + +/obj/item/disk/nuclear/Destroy(force) + var/turf/diskturf = get_turf(src) + + if(force) + message_admins("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z] - JMP":"nonexistent location"]).") + log_game("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z]":"nonexistent location"]).") + GLOB.poi_list.Remove(src) + STOP_PROCESSING(SSobj, src) + return ..() + + if(GLOB.blobstart.len > 0) + GLOB.poi_list.Remove(src) + var/obj/item/disk/nuclear/NEWDISK = new(pick(GLOB.blobstart)) + transfer_fingerprints_to(NEWDISK) + message_admins("[src] has been destroyed at ([diskturf.x], [diskturf.y], [diskturf.z] - JMP). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z] - JMP).") + log_game("[src] has been destroyed in ([diskturf.x], [diskturf.y], [diskturf.z]). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z]).") + return QDEL_HINT_HARDDEL_NOW + else + error("[src] was supposed to be destroyed, but we were unable to locate a blobstart landmark to spawn a new one.") + return QDEL_HINT_LETMELIVE // Cancel destruction unless forced. + +#undef NUKE_INTACT +#undef NUKE_COVER_OFF +#undef NUKE_COVER_OPEN +#undef NUKE_SEALANT_OPEN +#undef NUKE_UNWRENCHED +#undef NUKE_MOBILE diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 251c32716bb..e8f4c22e377 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -1,404 +1,409 @@ -/obj/item/pinpointer - name = "pinpointer" - icon = 'icons/obj/device.dmi' - icon_state = "pinoff" - flags = CONDUCT - slot_flags = SLOT_PDA | SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - materials = list(MAT_METAL=500) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/obj/item/disk/nuclear/the_disk = null - var/active = 0 - var/shows_nuke_timer = TRUE - var/icon_off = "pinoff" - var/icon_null = "pinonnull" - var/icon_direct = "pinondirect" - var/icon_close = "pinonclose" - var/icon_medium = "pinonmedium" - var/icon_far = "pinonfar" - -/obj/item/pinpointer/New() - ..() - GLOB.pinpointer_list += src - -/obj/item/pinpointer/Destroy() - GLOB.pinpointer_list -= src - active = 0 - the_disk = null - return ..() - -/obj/item/pinpointer/attack_self() - if(!active) - active = 1 - workdisk() - to_chat(usr, "You activate the pinpointer.") - else - active = 0 - icon_state = icon_off - to_chat(usr, "You deactivate the pinpointer.") - -/obj/item/pinpointer/proc/scandisk() - if(!the_disk) - the_disk = locate() - -/obj/item/pinpointer/proc/point_at(atom/target, spawnself = 1) - if(!active) - return - if(!target) - icon_state = icon_null - return - - var/turf/T = get_turf(target) - var/turf/L = get_turf(src) - - if(!(T && L) || (T.z != L.z)) - icon_state = icon_null - else - dir = get_dir(L, T) - switch(get_dist(L, T)) - if(-1) - icon_state = icon_direct - if(1 to 8) - icon_state = icon_close - if(9 to 16) - icon_state = icon_medium - if(16 to INFINITY) - icon_state = icon_far - if(spawnself) - spawn(5) - .() - -/obj/item/pinpointer/proc/workdisk() - scandisk() - point_at(the_disk, 0) - spawn(5) - .() - -/obj/item/pinpointer/examine(mob/user) - . = ..() - if(shows_nuke_timer) - for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines) - if(bomb.timing) - . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" - -/obj/item/pinpointer/advpinpointer - name = "advanced pinpointer" - desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." - var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. - var/turf/location = null - var/obj/target = null - -/obj/item/pinpointer/advpinpointer/attack_self() - if(!active) - active = 1 - if(mode == 0) - workdisk() - if(mode == 1) - point_at(location) - if(mode == 2) - point_at(target) - to_chat(usr, "You activate the pinpointer.") - else - active = 0 - icon_state = icon_off - to_chat(usr, "You deactivate the pinpointer.") - -/obj/item/pinpointer/advpinpointer/workdisk() - if(mode == 0) - scandisk() - point_at(the_disk, 0) - spawn(5) - .() - -/obj/item/pinpointer/advpinpointer/verb/toggle_mode() - set category = "Object" - set name = "Toggle Pinpointer Mode" - set src in usr - - if(usr.stat || usr.restrained()) - return - - active = 0 - icon_state = icon_off - target = null - location = null - - switch(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature")) - if("Location") - mode = 1 - - var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num - if(!locationx || !(usr in view(1,src))) - return - var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num - if(!locationy || !(usr in view(1,src))) - return - - var/turf/Z = get_turf(src) - - location = locate(locationx,locationy,Z.z) - - to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") - - - return attack_self() - - if("Disk Recovery") - mode = 0 - return attack_self() - - if("Other Signature") - mode = 2 - switch(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "Item" , "DNA")) - if("Item") - var/list/item_names[0] - var/list/item_paths[0] - for(var/objective in potential_theft_objectives) - var/datum/theft_objective/T = objective - var/name = initial(T.name) - item_names += name - item_paths[name] = initial(T.typepath) - var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in item_names - if(!targetitem) - return - - var/list/target_candidates = get_all_of_type(item_paths[targetitem], subtypes = TRUE) - for(var/obj/item/candidate in target_candidates) - if(!is_admin_level((get_turf(candidate)).z)) - target = candidate - break - - if(!target) - to_chat(usr, "Failed to locate [targetitem]!") - return - to_chat(usr, "You set the pinpointer to locate [targetitem].") - if("DNA") - var/DNAstring = input("Input DNA string to search for." , "Please Enter String." , "") - if(!DNAstring) - return - for(var/mob/living/carbon/C in GLOB.mob_list) - if(!C.dna) - continue - if(C.dna.unique_enzymes == DNAstring) - target = C - break - - return attack_self() - -/////////////////////// -//nuke op pinpointers// -/////////////////////// -/obj/item/pinpointer/nukeop - var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle - var/obj/docking_port/mobile/home = null - slot_flags = SLOT_BELT | SLOT_PDA - -/obj/item/pinpointer/nukeop/attack_self(mob/user as mob) - if(!active) - active = 1 - if(!mode) - workdisk() - to_chat(user, "Authentication Disk Locator active.") - else - worklocation() - to_chat(user, "Shuttle Locator active.") - else - active = 0 - icon_state = icon_off - to_chat(user, "You deactivate the pinpointer.") - -/obj/item/pinpointer/nukeop/workdisk() - if(!active) return - if(mode) //Check in case the mode changes while operating - worklocation() - return - if(bomb_set) //If the bomb is set, lead to the shuttle - mode = 1 //Ensures worklocation() continues to work - worklocation() - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep - visible_message("Shuttle Locator mode actived.") //Lets the mob holding it know that the mode has changed - return //Get outta here - scandisk() - if(!the_disk) - icon_state = icon_null - return - dir = get_dir(src, the_disk) - switch(get_dist(src, the_disk)) - if(0) - icon_state = icon_direct - if(1 to 8) - icon_state = icon_close - if(9 to 16) - icon_state = icon_medium - if(16 to INFINITY) - icon_state = icon_far - - spawn(5) .() - -/obj/item/pinpointer/nukeop/proc/worklocation() - if(!active) - return - if(!mode) - workdisk() - return - if(!bomb_set) - mode = 0 - workdisk() - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) - visible_message("Authentication Disk Locator mode actived.") - return - if(!home) - home = SSshuttle.getShuttle("syndicate") - if(!home) - icon_state = icon_null - return - if(loc.z != home.z) //If you are on a different z-level from the shuttle - icon_state = icon_null - else - dir = get_dir(src, home) - switch(get_dist(src, home)) - if(0) - icon_state = icon_direct - if(1 to 8) - icon_state = icon_close - if(9 to 16) - icon_state = icon_medium - if(16 to INFINITY) - icon_state = icon_far - - spawn(5) - .() - -/obj/item/pinpointer/operative - name = "operative pinpointer" - desc = "A pinpointer that leads to the first Syndicate operative detected." - var/mob/living/carbon/nearest_op = null - -/obj/item/pinpointer/operative/attack_self() - if(!usr.mind || !(usr.mind in SSticker.mode.syndicates)) - to_chat(usr, "AUTHENTICATION FAILURE. ACCESS DENIED.") - return 0 - if(!active) - active = 1 - workop() - to_chat(usr, "You activate the pinpointer.") - else - active = 0 - icon_state = icon_off - to_chat(usr, "You deactivate the pinpointer.") - -/obj/item/pinpointer/operative/proc/scan_for_ops() - if(active) - nearest_op = null //Resets nearest_op every time it scans - var/closest_distance = 1000 - for(var/mob/living/carbon/M in GLOB.mob_list) - if(M.mind && (M.mind in SSticker.mode.syndicates)) - if(get_dist(M, get_turf(src)) < closest_distance) //Actually points toward the nearest op, instead of a random one like it used to - nearest_op = M - -/obj/item/pinpointer/operative/proc/workop() - if(active) - scan_for_ops() - point_at(nearest_op, 0) - spawn(5) - .() - else - return 0 - -/obj/item/pinpointer/operative/examine(mob/user) - . = ..() - if(active) - if(nearest_op) - . += "Nearest operative detected is [nearest_op.real_name]." - else - . += "No operatives detected within scanning range." - -/obj/item/pinpointer/crew - name = "crew pinpointer" - desc = "A handheld tracking device that points to crew suit sensors." - shows_nuke_timer = FALSE - icon_state = "pinoff_crew" - icon_off = "pinoff_crew" - icon_null = "pinonnull_crew" - icon_direct = "pinondirect_crew" - icon_close = "pinonclose_crew" - icon_medium = "pinonmedium_crew" - icon_far = "pinonfar_crew" - -/obj/item/pinpointer/crew/proc/trackable(mob/living/carbon/human/H) - var/turf/here = get_turf(src) - if(istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - - // Suit sensors must be on maximum. - if(!U.has_sensor || U.sensor_mode < 3) - return FALSE - - var/turf/there = get_turf(U) - return there && there.z == here.z - - return FALSE - -/obj/item/pinpointer/crew/attack_self(mob/living/user) - if(active) - active = FALSE - icon_state = icon_off - user.visible_message("[user] deactivates [user.p_their()] pinpointer.", "You deactivate your pinpointer.") - return - - var/list/name_counts = list() - var/list/names = list() - - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(!trackable(H)) - continue - - var/name = "Unknown" - if(H.wear_id) - var/obj/item/card/id/I = H.wear_id.GetID() - if(I) - name = I.registered_name - - while(name in name_counts) - name_counts[name]++ - name = text("[] ([])", name, name_counts[name]) - names[name] = H - name_counts[name] = 1 - - if(!names.len) - user.visible_message("[user]'s pinpointer fails to detect a signal.", "Your pinpointer fails to detect a signal.") - return - - var/A = input(user, "Person to track", "Pinpoint") in names - if(!src || !user || (user.get_active_hand() != src) || user.incapacitated() || !A) - return - - var/target = names[A] - active = TRUE - user.visible_message("[user] activates [user.p_their()] pinpointer.", "You activate your pinpointer.") - point_at(target) - -/obj/item/pinpointer/crew/point_at(atom/target, spawnself = 1) - if(!active) - return - - if(!trackable(target) || !target) - icon_state = icon_null - return - - ..(target, spawnself = 0) - if(spawnself) - spawn(5) - .() - -/obj/item/pinpointer/crew/centcom - name = "centcom pinpointer" - desc = "A handheld tracking device that tracks crew based on remote centcom sensors." - -/obj/item/pinpointer/crew/centcom/trackable(mob/living/carbon/human/H) - var/turf/here = get_turf(src) - var/turf/there = get_turf(H) - return there && there.z == here.z \ No newline at end of file +/obj/item/pinpointer + name = "pinpointer" + icon = 'icons/obj/device.dmi' + icon_state = "pinoff" + flags = CONDUCT + slot_flags = SLOT_PDA | SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + materials = list(MAT_METAL=500) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/obj/item/disk/nuclear/the_disk = null + var/active = 0 + var/shows_nuke_timer = TRUE + var/icon_off = "pinoff" + var/icon_null = "pinonnull" + var/icon_direct = "pinondirect" + var/icon_close = "pinonclose" + var/icon_medium = "pinonmedium" + var/icon_far = "pinonfar" + +/obj/item/pinpointer/New() + ..() + GLOB.pinpointer_list += src + +/obj/item/pinpointer/Destroy() + GLOB.pinpointer_list -= src + active = 0 + the_disk = null + return ..() + +/obj/item/pinpointer/attack_self() + if(!active) + active = 1 + workdisk() + to_chat(usr, "You activate the pinpointer.") + else + active = 0 + icon_state = icon_off + to_chat(usr, "You deactivate the pinpointer.") + +/obj/item/pinpointer/proc/scandisk() + if(!the_disk) + the_disk = locate() + +/obj/item/pinpointer/proc/point_at(atom/target, spawnself = 1) + if(!active) + return + if(!target) + icon_state = icon_null + return + + var/turf/T = get_turf(target) + var/turf/L = get_turf(src) + + if(!(T && L) || (T.z != L.z)) + icon_state = icon_null + else + dir = get_dir(L, T) + switch(get_dist(L, T)) + if(-1) + icon_state = icon_direct + if(1 to 8) + icon_state = icon_close + if(9 to 16) + icon_state = icon_medium + if(16 to INFINITY) + icon_state = icon_far + if(spawnself) + spawn(5) + .() + +/obj/item/pinpointer/proc/workdisk() + scandisk() + point_at(the_disk, 0) + spawn(5) + .() + +/obj/item/pinpointer/examine(mob/user) + . = ..() + if(shows_nuke_timer) + for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines) + if(bomb.timing) + . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" + +/obj/item/pinpointer/advpinpointer + name = "advanced pinpointer" + desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." + var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. + var/modelocked = FALSE // If true, user cannot change mode. + var/turf/location = null + var/obj/target = null + +/obj/item/pinpointer/advpinpointer/attack_self() + if(!active) + active = 1 + if(mode == 0) + workdisk() + if(mode == 1) + point_at(location) + if(mode == 2) + point_at(target) + to_chat(usr, "You activate the pinpointer.") + else + active = 0 + icon_state = icon_off + to_chat(usr, "You deactivate the pinpointer.") + +/obj/item/pinpointer/advpinpointer/workdisk() + if(mode == 0) + scandisk() + point_at(the_disk, 0) + spawn(5) + .() + +/obj/item/pinpointer/advpinpointer/verb/toggle_mode() + set category = "Object" + set name = "Toggle Pinpointer Mode" + set src in usr + + if(usr.stat || usr.restrained()) + return + + if(modelocked) + to_chat(usr, "[src] is locked. It can only track one specific target.") + return + + active = 0 + icon_state = icon_off + target = null + location = null + + switch(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature")) + if("Location") + mode = 1 + + var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num + if(!locationx || !(usr in view(1,src))) + return + var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num + if(!locationy || !(usr in view(1,src))) + return + + var/turf/Z = get_turf(src) + + location = locate(locationx,locationy,Z.z) + + to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") + + + return attack_self() + + if("Disk Recovery") + mode = 0 + return attack_self() + + if("Other Signature") + mode = 2 + switch(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "Item" , "DNA")) + if("Item") + var/list/item_names[0] + var/list/item_paths[0] + for(var/objective in GLOB.potential_theft_objectives) + var/datum/theft_objective/T = objective + var/name = initial(T.name) + item_names += name + item_paths[name] = initial(T.typepath) + var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in item_names + if(!targetitem) + return + + var/list/target_candidates = get_all_of_type(item_paths[targetitem], subtypes = TRUE) + for(var/obj/item/candidate in target_candidates) + if(!is_admin_level((get_turf(candidate)).z)) + target = candidate + break + + if(!target) + to_chat(usr, "Failed to locate [targetitem]!") + return + to_chat(usr, "You set the pinpointer to locate [targetitem].") + if("DNA") + var/DNAstring = input("Input DNA string to search for." , "Please Enter String." , "") + if(!DNAstring) + return + for(var/mob/living/carbon/C in GLOB.mob_list) + if(!C.dna) + continue + if(C.dna.unique_enzymes == DNAstring) + target = C + break + + return attack_self() + +/////////////////////// +//nuke op pinpointers// +/////////////////////// +/obj/item/pinpointer/nukeop + var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle + var/obj/docking_port/mobile/home = null + slot_flags = SLOT_BELT | SLOT_PDA + +/obj/item/pinpointer/nukeop/attack_self(mob/user as mob) + if(!active) + active = 1 + if(!mode) + workdisk() + to_chat(user, "Authentication Disk Locator active.") + else + worklocation() + to_chat(user, "Shuttle Locator active.") + else + active = 0 + icon_state = icon_off + to_chat(user, "You deactivate the pinpointer.") + +/obj/item/pinpointer/nukeop/workdisk() + if(!active) return + if(mode) //Check in case the mode changes while operating + worklocation() + return + if(GLOB.bomb_set) //If the bomb is set, lead to the shuttle + mode = 1 //Ensures worklocation() continues to work + worklocation() + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep + visible_message("Shuttle Locator mode actived.") //Lets the mob holding it know that the mode has changed + return //Get outta here + scandisk() + if(!the_disk) + icon_state = icon_null + return + dir = get_dir(src, the_disk) + switch(get_dist(src, the_disk)) + if(0) + icon_state = icon_direct + if(1 to 8) + icon_state = icon_close + if(9 to 16) + icon_state = icon_medium + if(16 to INFINITY) + icon_state = icon_far + + spawn(5) .() + +/obj/item/pinpointer/nukeop/proc/worklocation() + if(!active) + return + if(!mode) + workdisk() + return + if(!GLOB.bomb_set) + mode = 0 + workdisk() + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) + visible_message("Authentication Disk Locator mode actived.") + return + if(!home) + home = SSshuttle.getShuttle("syndicate") + if(!home) + icon_state = icon_null + return + if(loc.z != home.z) //If you are on a different z-level from the shuttle + icon_state = icon_null + else + dir = get_dir(src, home) + switch(get_dist(src, home)) + if(0) + icon_state = icon_direct + if(1 to 8) + icon_state = icon_close + if(9 to 16) + icon_state = icon_medium + if(16 to INFINITY) + icon_state = icon_far + + spawn(5) + .() + +/obj/item/pinpointer/operative + name = "operative pinpointer" + desc = "A pinpointer that leads to the first Syndicate operative detected." + var/mob/living/carbon/nearest_op = null + +/obj/item/pinpointer/operative/attack_self() + if(!usr.mind || !(usr.mind in SSticker.mode.syndicates)) + to_chat(usr, "AUTHENTICATION FAILURE. ACCESS DENIED.") + return 0 + if(!active) + active = 1 + workop() + to_chat(usr, "You activate the pinpointer.") + else + active = 0 + icon_state = icon_off + to_chat(usr, "You deactivate the pinpointer.") + +/obj/item/pinpointer/operative/proc/scan_for_ops() + if(active) + nearest_op = null //Resets nearest_op every time it scans + var/closest_distance = 1000 + for(var/mob/living/carbon/M in GLOB.mob_list) + if(M.mind && (M.mind in SSticker.mode.syndicates)) + if(get_dist(M, get_turf(src)) < closest_distance) //Actually points toward the nearest op, instead of a random one like it used to + nearest_op = M + +/obj/item/pinpointer/operative/proc/workop() + if(active) + scan_for_ops() + point_at(nearest_op, 0) + spawn(5) + .() + else + return 0 + +/obj/item/pinpointer/operative/examine(mob/user) + . = ..() + if(active) + if(nearest_op) + . += "Nearest operative detected is [nearest_op.real_name]." + else + . += "No operatives detected within scanning range." + +/obj/item/pinpointer/crew + name = "crew pinpointer" + desc = "A handheld tracking device that points to crew suit sensors." + shows_nuke_timer = FALSE + icon_state = "pinoff_crew" + icon_off = "pinoff_crew" + icon_null = "pinonnull_crew" + icon_direct = "pinondirect_crew" + icon_close = "pinonclose_crew" + icon_medium = "pinonmedium_crew" + icon_far = "pinonfar_crew" + +/obj/item/pinpointer/crew/proc/trackable(mob/living/carbon/human/H) + var/turf/here = get_turf(src) + if(istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + + // Suit sensors must be on maximum. + if(!U.has_sensor || U.sensor_mode < 3) + return FALSE + + var/turf/there = get_turf(U) + return there && there.z == here.z + + return FALSE + +/obj/item/pinpointer/crew/attack_self(mob/living/user) + if(active) + active = FALSE + icon_state = icon_off + user.visible_message("[user] deactivates [user.p_their()] pinpointer.", "You deactivate your pinpointer.") + return + + var/list/name_counts = list() + var/list/names = list() + + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(!trackable(H)) + continue + + var/name = "Unknown" + if(H.wear_id) + var/obj/item/card/id/I = H.wear_id.GetID() + if(I) + name = I.registered_name + + while(name in name_counts) + name_counts[name]++ + name = text("[] ([])", name, name_counts[name]) + names[name] = H + name_counts[name] = 1 + + if(!names.len) + user.visible_message("[user]'s pinpointer fails to detect a signal.", "Your pinpointer fails to detect a signal.") + return + + var/A = input(user, "Person to track", "Pinpoint") in names + if(!src || !user || (user.get_active_hand() != src) || user.incapacitated() || !A) + return + + var/target = names[A] + active = TRUE + user.visible_message("[user] activates [user.p_their()] pinpointer.", "You activate your pinpointer.") + point_at(target) + +/obj/item/pinpointer/crew/point_at(atom/target, spawnself = 1) + if(!active) + return + + if(!trackable(target) || !target) + icon_state = icon_null + return + + ..(target, spawnself = 0) + if(spawnself) + spawn(5) + .() + +/obj/item/pinpointer/crew/centcom + name = "centcom pinpointer" + desc = "A handheld tracking device that tracks crew based on remote centcom sensors." + +/obj/item/pinpointer/crew/centcom/trackable(mob/living/carbon/human/H) + var/turf/here = get_turf(src) + var/turf/there = get_turf(H) + return there && there.z == here.z diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 89ecb1d103b..de15f62d09d 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1,809 +1,808 @@ -GLOBAL_LIST_EMPTY(all_objectives) - -var/list/potential_theft_objectives = subtypesof(/datum/theft_objective) - /datum/theft_objective/steal - /datum/theft_objective/number - /datum/theft_objective/unique - -/datum/objective - var/datum/mind/owner = null //Who owns the objective. - var/explanation_text = "Nothing" //What that person is supposed to do. - var/datum/mind/target = null //If they are focused on a particular person. - var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. - var/completed = 0 //currently only used for custom objectives. - var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead. - -/datum/objective/New(text) - GLOB.all_objectives += src - if(text) - explanation_text = text - -/datum/objective/Destroy() - GLOB.all_objectives -= src - return ..() - -/datum/objective/proc/check_completion() - return completed - -/datum/objective/proc/is_invalid_target(datum/mind/possible_target) - if(possible_target == owner) - return TARGET_INVALID_IS_OWNER - if(possible_target in owner.targets) - return TARGET_INVALID_IS_TARGET - if(!ishuman(possible_target.current)) - return TARGET_INVALID_NOT_HUMAN - if(!possible_target.current.stat == DEAD) - return TARGET_INVALID_DEAD - if(!possible_target.key) - return TARGET_INVALID_NOCKEY - if(possible_target.current) - var/turf/current_location = get_turf(possible_target.current) - if(current_location && !is_level_reachable(current_location.z)) - return TARGET_INVALID_UNREACHABLE - if(isgolem(possible_target.current)) - return TARGET_INVALID_GOLEM - if(possible_target.offstation_role) - return TARGET_INVALID_EVENT - - -/datum/objective/proc/find_target() - var/list/possible_targets = list() - for(var/datum/mind/possible_target in SSticker.minds) - if(is_invalid_target(possible_target)) - continue - - possible_targets += possible_target - - if(possible_targets.len > 0) - target = pick(possible_targets) - -/datum/objective/assassinate - martyr_compatible = 1 - -/datum/objective/assassinate/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/assassinate/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || iszombie(target)) - return 1 - if(issilicon(target.current) || isbrain(target.current)) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite - return 1 - if(!target.current.ckey) - return 1 - return 0 - return 1 - - -/datum/objective/mutiny - martyr_compatible = 1 - -/datum/objective/mutiny/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/mutiny/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client) - return 1 - var/turf/T = get_turf(target.current) - if(T && !is_station_level(T.z)) //If they leave the station they count as dead for this - return 1 - return 0 - return 1 - -/datum/objective/maroon - martyr_compatible = 1 - -/datum/objective/maroon/find_target() - ..() - if(target && target.current) - explanation_text = "Prevent [target.current.real_name], the [target.assigned_role] from escaping alive." - else - explanation_text = "Free Objective" - return target - -/datum/objective/maroon/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || iszombie(target)) - return 1 - if(!target.current.ckey) - return 1 - if(issilicon(target.current)) - return 1 - if(isbrain(target.current)) - return 1 - var/turf/T = get_turf(target.current) - if(is_admin_level(T.z)) - return 0 - return 1 - return 1 - - -/datum/objective/debrain //I want braaaainssss - martyr_compatible = 0 - -/datum/objective/debrain/find_target() - ..() - if(target && target.current) - explanation_text = "Steal the brain of [target.current.real_name] the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/debrain/check_completion() - if(!target)//If it's a free objective. - return 1 - if(!owner.current || owner.current.stat == DEAD) - return 0 - if(!target.current || !isbrain(target.current)) - return 0 - var/atom/A = target.current - while(A.loc) //check to see if the brainmob is on our person - A = A.loc - if(A == owner.current) - return 1 - return 0 - - -/datum/objective/protect //The opposite of killing a dude. - martyr_compatible = 1 - -/datum/objective/protect/find_target() - ..() - if(target && target.current) - explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/protect/check_completion() - if(!target) //If it's a free objective. - return 1 - if(target.current) - if(target.current.stat == DEAD || iszombie(target)) - return 0 - if(issilicon(target.current)) - return 0 - if(isbrain(target.current)) - return 0 - return 1 - return 0 - -/datum/objective/protect/mindslave //subytpe for mindslave implants - - -/datum/objective/hijack - martyr_compatible = 0 //Technically you won't get both anyway. - explanation_text = "Hijack the shuttle by escaping on it with no loyalist Nanotrasen crew on board and free. \ - Syndicate agents, other enemies of Nanotrasen, cyborgs, pets, and cuffed/restrained hostages may be allowed on the shuttle alive." - -/datum/objective/hijack/check_completion() - if(!owner.current || owner.current.stat) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - if(issilicon(owner.current)) - return 0 - - var/area/A = get_area(owner.current) - if(SSshuttle.emergency.areaInstance != A) - return 0 - - return SSshuttle.emergency.is_hijacked() - -/datum/objective/hijackclone - explanation_text = "Hijack the shuttle by ensuring only you (or your copies) escape." - martyr_compatible = 0 - -/datum/objective/hijackclone/check_completion() - if(!owner.current) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - - var/area/A = SSshuttle.emergency.areaInstance - - for(var/mob/living/player in GLOB.player_list) //Make sure nobody else is onboard - if(player.mind && player.mind != owner) - if(player.stat != DEAD) - if(issilicon(player)) - continue - if(get_area(player) == A) - if(player.real_name != owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) - return 0 - - for(var/mob/living/player in GLOB.player_list) //Make sure at least one of you is onboard - if(player.mind && player.mind != owner) - if(player.stat != DEAD) - if(issilicon(player)) - continue - if(get_area(player) == A) - if(player.real_name == owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) - return 1 - return 0 - -/datum/objective/block - explanation_text = "Do not allow any lifeforms, be it organic or synthetic to escape on the shuttle alive. AIs, Cyborgs, and pAIs are not considered alive." - martyr_compatible = 1 - -/datum/objective/block/check_completion() - if(!istype(owner.current, /mob/living/silicon)) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - if(!owner.current) - return 0 - - var/area/A = SSshuttle.emergency.areaInstance - var/list/protected_mobs = list(/mob/living/silicon/ai, /mob/living/silicon/pai, /mob/living/silicon/robot) - - for(var/mob/living/player in GLOB.player_list) - if(player.type in protected_mobs) - continue - - if(player.mind && player.stat != DEAD) - if(get_area(player) == A) - return 0 - - return 1 - -/datum/objective/escape - explanation_text = "Escape on the shuttle or an escape pod alive and free." - -/datum/objective/escape/check_completion() - if(issilicon(owner.current)) - return 0 - if(isbrain(owner.current)) - return 0 - if(!owner.current || owner.current.stat == DEAD || iszombie(owner)) - return 0 - if(SSticker.force_ending) //This one isn't their fault, so lets just assume good faith - return 1 - if(SSticker.mode.station_was_nuked) //If they escaped the blast somehow, let them win - return 1 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - var/turf/location = get_turf(owner.current) - if(!location) - return 0 - - if(istype(location, /turf/simulated/shuttle/floor4) || istype(location, /turf/simulated/floor/mineral/plastitanium/red/brig)) // Fails traitors if they are in the shuttle brig -- Polymorph - return 0 - - if(location.onCentcom() || location.onSyndieBase()) - return 1 - - return 0 - - -/datum/objective/escape/escape_with_identity - var/target_real_name // Has to be stored because the target's real_name can change over the course of the round - -/datum/objective/escape/escape_with_identity/find_target() - var/list/possible_targets = list() //Copypasta because NO_DNA races, yay for snowflakes. - for(var/datum/mind/possible_target in SSticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && possible_target.current.client) - var/mob/living/carbon/human/H = possible_target.current - if(!(NO_DNA in H.dna.species.species_traits)) - possible_targets += possible_target - if(possible_targets.len > 0) - target = pick(possible_targets) - if(target && target.current) - target_real_name = target.current.real_name - explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role] while wearing [target.p_their()] identification card." - else - explanation_text = "Free Objective" - -/datum/objective/escape/escape_with_identity/check_completion() - if(!target_real_name) - return 1 - if(!ishuman(owner.current)) - return 0 - var/mob/living/carbon/human/H = owner.current - if(..()) - if(H.dna.real_name == target_real_name) - if(H.get_id_name()== target_real_name) - return 1 - return 0 - -/datum/objective/die - explanation_text = "Die a glorious death." - -/datum/objective/die/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current) || iszombie(owner)) - return 1 - if(issilicon(owner.current) && owner.current != owner.original) - return 1 - return 0 - - - -/datum/objective/survive - explanation_text = "Stay alive until the end." - -/datum/objective/survive/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) - return 0 //Brains no longer win survive objectives. --NEO - if(issilicon(owner.current) && owner.current != owner.original) - return 0 - return 1 - -/datum/objective/nuclear - explanation_text = "Destroy the station with a nuclear device." - martyr_compatible = 1 - -/datum/objective/steal - var/datum/theft_objective/steal_target - martyr_compatible = 0 - var/theft_area - -/datum/objective/steal/proc/get_location() - if(steal_target.location_override) - return steal_target.location_override - var/list/obj/item/steal_candidates = get_all_of_type(steal_target.typepath, subtypes = TRUE) - for(var/obj/item/candidate in steal_candidates) - if(!is_admin_level(candidate.loc.z)) - theft_area = get_area(candidate.loc) - return "[theft_area]" - return "an unknown area" - -/datum/objective/steal/find_target() - var/loop=50 - while(!steal_target && loop > 0) - loop-- - var/thefttype = pick(potential_theft_objectives) - var/datum/theft_objective/O = new thefttype - if(owner.assigned_role in O.protected_jobs) - continue - if(O in owner.targets) - continue - if(O.flags & 2) - continue - steal_target = O - - explanation_text = "Steal [steal_target]. One was last seen in [get_location()]. " - if(islist(O.protected_jobs) && O.protected_jobs.len) - explanation_text += "It may also be in the possession of the [jointext(O.protected_jobs, ", ")]." - return - explanation_text = "Free Objective." - - -/datum/objective/steal/proc/select_target() - var/list/possible_items_all = potential_theft_objectives+"custom" - var/new_target = input("Select target:", "Objective target", null) as null|anything in possible_items_all - if(!new_target) return - if(new_target == "custom") - var/datum/theft_objective/O=new - O.typepath = input("Select type:","Type") as null|anything in typesof(/obj/item) - if(!O.typepath) return - var/tmp_obj = new O.typepath - var/custom_name = tmp_obj:name - qdel(tmp_obj) - O.name = sanitize(copytext(input("Enter target name:", "Objective target", custom_name) as text|null,1,MAX_NAME_LEN)) - if(!O.name) return - steal_target = O - explanation_text = "Steal [O.name]." - else - steal_target = new new_target - explanation_text = "Steal [steal_target.name]." - return steal_target - -/datum/objective/steal/check_completion() - if(!steal_target) - return 1 // Free Objective - - var/list/all_items = owner.current.GetAllContents() - - for(var/obj/I in all_items) - if(istype(I, steal_target.typepath)) - return steal_target.check_special_completion(I) - if(I.type in steal_target.altitems) - return steal_target.check_special_completion(I) - - -/datum/objective/steal/exchange - martyr_compatible = 0 - -/datum/objective/steal/exchange/proc/set_faction(var/faction,var/otheragent) - target = otheragent - var/datum/theft_objective/unique/targetinfo - if(faction == "red") - targetinfo = new /datum/theft_objective/unique/docs_blue - else if(faction == "blue") - targetinfo = new /datum/theft_objective/unique/docs_red - explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent" - steal_target = targetinfo - -/datum/objective/steal/exchange/backstab -/datum/objective/steal/exchange/backstab/set_faction(var/faction) - var/datum/theft_objective/unique/targetinfo - if(faction == "red") - targetinfo = new /datum/theft_objective/unique/docs_red - else if(faction == "blue") - targetinfo = new /datum/theft_objective/unique/docs_blue - explanation_text = "Do not give up or lose [targetinfo.name]." - steal_target = targetinfo - -/datum/objective/download -/datum/objective/download/proc/gen_amount_goal() - target_amount = rand(10,20) - explanation_text = "Download [target_amount] research levels." - return target_amount - - -/datum/objective/download/check_completion() - return 0 - - - -/datum/objective/capture -/datum/objective/capture/proc/gen_amount_goal() - target_amount = rand(5,10) - explanation_text = "Accumulate [target_amount] capture points." - return target_amount - - -/datum/objective/capture/check_completion()//Basically runs through all the mobs in the area to determine how much they are worth. - return 0 - - - - -/datum/objective/absorb -/datum/objective/absorb/proc/gen_amount_goal(var/lowbound = 4, var/highbound = 6) - target_amount = rand (lowbound,highbound) - if(SSticker) - var/n_p = 1 //autowin - if(SSticker.current_state == GAME_STATE_SETTING_UP) - for(var/mob/new_player/P in GLOB.player_list) - if(P.client && P.ready && P.mind != owner) - if(P.client.prefs && (P.client.prefs.species == "Machine")) // Special check for species that can't be absorbed. No better solution. - continue - n_p++ - else if(SSticker.current_state == GAME_STATE_PLAYING) - for(var/mob/living/carbon/human/P in GLOB.player_list) - if(NO_DNA in P.dna.species.species_traits) - continue - if(P.client && !(P.mind in SSticker.mode.changelings) && P.mind!=owner) - n_p++ - target_amount = min(target_amount, n_p) - - explanation_text = "Absorb [target_amount] compatible genomes." - return target_amount - -/datum/objective/absorb/check_completion() - if(owner && owner.changeling && owner.changeling.absorbed_dna && (owner.changeling.absorbedcount >= target_amount)) - return 1 - else - return 0 - -/datum/objective/destroy - martyr_compatible = 1 - var/target_real_name - -/datum/objective/destroy/find_target() - var/list/possible_targets = active_ais(1) - var/mob/living/silicon/ai/target_ai = pick(possible_targets) - target = target_ai.mind - if(target && target.current) - target_real_name = target.current.real_name - explanation_text = "Destroy [target_real_name], the AI." - else - explanation_text = "Free Objective" - return target - -/datum/objective/destroy/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || is_away_level(target.current.z) || !target.current.ckey) - return 1 - return 0 - return 1 - -/datum/objective/steal_five_of_type - explanation_text = "Steal at least five items!" - var/list/wanted_items = list() - -/datum/objective/steal_five_of_type/New() - ..() - wanted_items = typecacheof(wanted_items) - -/datum/objective/steal_five_of_type/check_completion() - var/stolen_count = 0 - if(!isliving(owner.current)) - return FALSE - var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. - for(var/obj/I in all_items) //Check for wanted items - if(is_type_in_typecache(I, wanted_items)) - stolen_count++ - return stolen_count >= 5 - -/datum/objective/steal_five_of_type/summon_guns - explanation_text = "Steal at least five guns!" - wanted_items = list(/obj/item/gun) - -/datum/objective/steal_five_of_type/summon_magic - explanation_text = "Steal at least five magical artefacts!" - wanted_items = list() - -/datum/objective/steal_five_of_type/summon_magic/New() - wanted_items = GLOB.summoned_magic_objectives - ..() - -/datum/objective/steal_five_of_type/summon_magic/check_completion() - var/stolen_count = 0 - if(!isliving(owner.current)) - return FALSE - var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. - for(var/obj/I in all_items) //Check for wanted items - if(istype(I, /obj/item/spellbook) && !istype(I, /obj/item/spellbook/oneuse)) - var/obj/item/spellbook/spellbook = I - if(spellbook.uses) //if the book still has powers... - stolen_count++ //it counts. nice. - if(istype(I, /obj/item/spellbook/oneuse)) - var/obj/item/spellbook/oneuse/oneuse = I - if(!oneuse.used) - stolen_count++ - else if(is_type_in_typecache(I, wanted_items)) - stolen_count++ - return stolen_count >= 5 - -/datum/objective/blood -/datum/objective/blood/proc/gen_amount_goal(low = 150, high = 400) - target_amount = rand(low,high) - target_amount = round(round(target_amount/5)*5) - explanation_text = "Accumulate at least [target_amount] total units of blood." - return target_amount - -/datum/objective/blood/check_completion() - if(owner && owner.vampire && owner.vampire.bloodtotal && owner.vampire.bloodtotal >= target_amount) - return 1 - else - return 0 - -// /vg/; Vox Inviolate for humans :V -/datum/objective/minimize_casualties - explanation_text = "Minimise casualties." -/datum/objective/minimize_casualties/check_completion() - if(owner.kills.len>5) return 0 - return 1 - -//Vox heist objectives. - -/datum/objective/heist -/datum/objective/heist/proc/choose_target() - return - -/datum/objective/heist/kidnap -/datum/objective/heist/kidnap/choose_target() - var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Station Engineer") - var/list/possible_targets = list() - var/list/priority_targets = list() - - for(var/datum/mind/possible_target in SSticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && (possible_target.assigned_role != possible_target.special_role) && !possible_target.offstation_role) - possible_targets += possible_target - for(var/role in roles) - if(possible_target.assigned_role == role) - priority_targets += possible_target - continue - - if(priority_targets.len > 0) - target = pick(priority_targets) - else if(possible_targets.len > 0) - target = pick(possible_targets) - - if(target && target.current) - explanation_text = "The Shoal has a need for [target.current.real_name], the [target.assigned_role]. Take [target.current.p_them()] alive." - else - explanation_text = "Free Objective" - return target - -/datum/objective/heist/kidnap/check_completion() - if(target && target.current) - if(target.current.stat == DEAD) - return 0 - - var/area/shuttle/vox/A = locate() //stupid fucking hardcoding - var/area/vox_station/B = locate() //but necessary - - for(var/mob/living/carbon/human/M in A) - if(target.current == M) - return 1 - for(var/mob/living/carbon/human/M in B) - if(target.current == M) - return 1 - else - return 0 - -/datum/objective/heist/loot -/datum/objective/heist/loot/choose_target() - var/loot = "an object" - switch(rand(1,8)) - if(1) - target = /obj/structure/particle_accelerator - target_amount = 6 - loot = "a complete particle accelerator" - if(2) - target = /obj/machinery/the_singularitygen - target_amount = 1 - loot = "a gravitational singularity generator" - if(3) - target = /obj/machinery/power/emitter - target_amount = 4 - loot = "four emitters" - if(4) - target = /obj/machinery/nuclearbomb - target_amount = 1 - loot = "a nuclear bomb" - if(5) - target = /obj/item/gun - target_amount = 6 - loot = "six guns. Tasers and other non-lethal guns are acceptable" - if(6) - target = /obj/item/gun/energy - target_amount = 4 - loot = "four energy guns" - if(7) - target = /obj/item/gun/energy/laser - target_amount = 2 - loot = "two laser guns" - if(8) - target = /obj/item/gun/energy/ionrifle - target_amount = 1 - loot = "an ion gun" - - explanation_text = "We are lacking in hardware. Steal or trade [loot]." - -/datum/objective/heist/loot/check_completion() - var/total_amount = 0 - - for(var/obj/O in locate(/area/shuttle/vox)) - if(istype(O, target)) - total_amount++ - for(var/obj/I in O.contents) - if(istype(I, target)) - total_amount++ - if(total_amount >= target_amount) - return 1 - - for(var/obj/O in locate(/area/vox_station)) - if(istype(O, target)) - total_amount++ - for(var/obj/I in O.contents) - if(istype(I, target)) - total_amount++ - if(total_amount >= target_amount) - return 1 - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - if(raider.current) - for(var/obj/O in raider.current.get_contents()) - if(istype(O,target)) - total_amount++ - if(total_amount >= target_amount) - return 1 - - return 0 - -/datum/objective/heist/salvage -/datum/objective/heist/salvage/choose_target() - switch(rand(1,8)) - if(1) - target = "metal" - target_amount = 300 - if(2) - target = "glass" - target_amount = 200 - if(3) - target = "plasteel" - target_amount = 100 - if(4) - target = "solid plasma" - target_amount = 100 - if(5) - target = "silver" - target_amount = 50 - if(6) - target = "gold" - target_amount = 20 - if(7) - target = "uranium" - target_amount = 20 - if(8) - target = "diamond" - target_amount = 20 - - explanation_text = "Ransack or trade with the station and escape with [target_amount] [target]." - -/datum/objective/heist/salvage/check_completion() - var/total_amount = 0 - - for(var/obj/item/O in locate(/area/shuttle/vox)) - var/obj/item/stack/sheet/S - if(istype(O,/obj/item/stack/sheet)) - if(O.name == target) - S = O - total_amount += S.get_amount() - - for(var/obj/I in O.contents) - if(istype(I,/obj/item/stack/sheet)) - if(I.name == target) - S = I - total_amount += S.get_amount() - - for(var/obj/item/O in locate(/area/vox_station)) - var/obj/item/stack/sheet/S - if(istype(O,/obj/item/stack/sheet)) - if(O.name == target) - S = O - total_amount += S.get_amount() - - for(var/obj/I in O.contents) - if(istype(I,/obj/item/stack/sheet)) - if(I.name == target) - S = I - total_amount += S.get_amount() - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - if(raider.current) - for(var/obj/item/O in raider.current.get_contents()) - if(istype(O,/obj/item/stack/sheet)) - if(O.name == target) - var/obj/item/stack/sheet/S = O - total_amount += S.get_amount() - - if(total_amount >= target_amount) return 1 - return 0 - - -/datum/objective/heist/inviolate_crew - explanation_text = "Do not leave any Vox behind, alive or dead." - -/datum/objective/heist/inviolate_crew/check_completion() - var/datum/game_mode/heist/H = SSticker.mode - if(H.is_raider_crew_safe()) - return 1 - return 0 - -/datum/objective/heist/inviolate_death - explanation_text = "Follow the Inviolate. Minimise death and loss of resources." - -/datum/objective/heist/inviolate_death/check_completion() - var/vox_allowed_kills = 3 // The number of people the vox can accidently kill. Mostly a counter to people killing themselves if a raider touches them to force fail. - var/vox_total_kills = 0 - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - vox_total_kills += raider.kills.len // Kills are listed in the mind; uses this to calculate vox kills - - if(vox_total_kills > vox_allowed_kills) return 0 - return 1 - - -// Traders -// These objectives have no check_completion, they exist only to tell Sol Traders what to aim for. - -/datum/objective/trade/proc/choose_target() - return - -/datum/objective/trade/plasma/choose_target() - explanation_text = "Acquire at least 15 sheets of plasma through trade." - -/datum/objective/trade/credits/choose_target() - explanation_text = "Acquire at least 10,000 credits through trade." - -//wizard - -/datum/objective/wizchaos - explanation_text = "Wreak havoc upon the station as much you can. Send those wandless Nanotrasen scum a message!" - completed = 1 +GLOBAL_LIST_EMPTY(all_objectives) + +GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) - /datum/theft_objective/steal - /datum/theft_objective/number - /datum/theft_objective/unique)) + +/datum/objective + var/datum/mind/owner = null //Who owns the objective. + var/explanation_text = "Nothing" //What that person is supposed to do. + var/datum/mind/target = null //If they are focused on a particular person. + var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. + var/completed = 0 //currently only used for custom objectives. + var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead. + +/datum/objective/New(text) + GLOB.all_objectives += src + if(text) + explanation_text = text + +/datum/objective/Destroy() + GLOB.all_objectives -= src + return ..() + +/datum/objective/proc/check_completion() + return completed + +/datum/objective/proc/is_invalid_target(datum/mind/possible_target) + if(possible_target == owner) + return TARGET_INVALID_IS_OWNER + if(possible_target in owner.targets) + return TARGET_INVALID_IS_TARGET + if(!ishuman(possible_target.current)) + return TARGET_INVALID_NOT_HUMAN + if(!possible_target.current.stat == DEAD) + return TARGET_INVALID_DEAD + if(!possible_target.key) + return TARGET_INVALID_NOCKEY + if(possible_target.current) + var/turf/current_location = get_turf(possible_target.current) + if(current_location && !is_level_reachable(current_location.z)) + return TARGET_INVALID_UNREACHABLE + if(isgolem(possible_target.current)) + return TARGET_INVALID_GOLEM + if(possible_target.offstation_role) + return TARGET_INVALID_EVENT + + +/datum/objective/proc/find_target() + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.minds) + if(is_invalid_target(possible_target)) + continue + + possible_targets += possible_target + + if(possible_targets.len > 0) + target = pick(possible_targets) + +/datum/objective/assassinate + martyr_compatible = 1 + +/datum/objective/assassinate/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/assassinate/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || iszombie(target)) + return 1 + if(issilicon(target.current) || isbrain(target.current)) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite + return 1 + if(!target.current.ckey) + return 1 + return 0 + return 1 + + +/datum/objective/mutiny + martyr_compatible = 1 + +/datum/objective/mutiny/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/mutiny/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client) + return 1 + var/turf/T = get_turf(target.current) + if(T && !is_station_level(T.z)) //If they leave the station they count as dead for this + return 1 + return 0 + return 1 + +/datum/objective/maroon + martyr_compatible = 1 + +/datum/objective/maroon/find_target() + ..() + if(target && target.current) + explanation_text = "Prevent [target.current.real_name], the [target.assigned_role] from escaping alive." + else + explanation_text = "Free Objective" + return target + +/datum/objective/maroon/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || iszombie(target)) + return 1 + if(!target.current.ckey) + return 1 + if(issilicon(target.current)) + return 1 + if(isbrain(target.current)) + return 1 + var/turf/T = get_turf(target.current) + if(is_admin_level(T.z)) + return 0 + return 1 + return 1 + + +/datum/objective/debrain //I want braaaainssss + martyr_compatible = 0 + +/datum/objective/debrain/find_target() + ..() + if(target && target.current) + explanation_text = "Steal the brain of [target.current.real_name] the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/debrain/check_completion() + if(!target)//If it's a free objective. + return 1 + if(!owner.current || owner.current.stat == DEAD) + return 0 + if(!target.current || !isbrain(target.current)) + return 0 + var/atom/A = target.current + while(A.loc) //check to see if the brainmob is on our person + A = A.loc + if(A == owner.current) + return 1 + return 0 + + +/datum/objective/protect //The opposite of killing a dude. + martyr_compatible = 1 + +/datum/objective/protect/find_target() + ..() + if(target && target.current) + explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/protect/check_completion() + if(!target) //If it's a free objective. + return 1 + if(target.current) + if(target.current.stat == DEAD || iszombie(target)) + return 0 + if(issilicon(target.current)) + return 0 + if(isbrain(target.current)) + return 0 + return 1 + return 0 + +/datum/objective/protect/mindslave //subytpe for mindslave implants + + +/datum/objective/hijack + martyr_compatible = 0 //Technically you won't get both anyway. + explanation_text = "Hijack the shuttle by escaping on it with no loyalist Nanotrasen crew on board and free. \ + Syndicate agents, other enemies of Nanotrasen, cyborgs, pets, and cuffed/restrained hostages may be allowed on the shuttle alive." + +/datum/objective/hijack/check_completion() + if(!owner.current || owner.current.stat) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + if(issilicon(owner.current)) + return 0 + + var/area/A = get_area(owner.current) + if(SSshuttle.emergency.areaInstance != A) + return 0 + + return SSshuttle.emergency.is_hijacked() + +/datum/objective/hijackclone + explanation_text = "Hijack the shuttle by ensuring only you (or your copies) escape." + martyr_compatible = 0 + +/datum/objective/hijackclone/check_completion() + if(!owner.current) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + + var/area/A = SSshuttle.emergency.areaInstance + + for(var/mob/living/player in GLOB.player_list) //Make sure nobody else is onboard + if(player.mind && player.mind != owner) + if(player.stat != DEAD) + if(issilicon(player)) + continue + if(get_area(player) == A) + if(player.real_name != owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) + return 0 + + for(var/mob/living/player in GLOB.player_list) //Make sure at least one of you is onboard + if(player.mind && player.mind != owner) + if(player.stat != DEAD) + if(issilicon(player)) + continue + if(get_area(player) == A) + if(player.real_name == owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) + return 1 + return 0 + +/datum/objective/block + explanation_text = "Do not allow any lifeforms, be it organic or synthetic to escape on the shuttle alive. AIs, Cyborgs, Maintenance drones, and pAIs are not considered alive." + martyr_compatible = 1 + +/datum/objective/block/check_completion() + if(!istype(owner.current, /mob/living/silicon)) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + if(!owner.current) + return 0 + + var/area/A = SSshuttle.emergency.areaInstance + + for(var/mob/living/player in GLOB.player_list) + if(issilicon(player)) + continue // If they're silicon, they're not considered alive, skip them. + + if(player.mind && player.stat != DEAD) + if(get_area(player) == A) + return 0 // If there are any other organic mobs on the shuttle, you failed the objective. + + return 1 + +/datum/objective/escape + explanation_text = "Escape on the shuttle or an escape pod alive and free." + +/datum/objective/escape/check_completion() + if(issilicon(owner.current)) + return 0 + if(isbrain(owner.current)) + return 0 + if(!owner.current || owner.current.stat == DEAD || iszombie(owner)) + return 0 + if(SSticker.force_ending) //This one isn't their fault, so lets just assume good faith + return 1 + if(SSticker.mode.station_was_nuked) //If they escaped the blast somehow, let them win + return 1 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + var/turf/location = get_turf(owner.current) + if(!location) + return 0 + + if(istype(location, /turf/simulated/shuttle/floor4) || istype(location, /turf/simulated/floor/mineral/plastitanium/red/brig)) // Fails traitors if they are in the shuttle brig -- Polymorph + return 0 + + if(location.onCentcom() || location.onSyndieBase()) + return 1 + + return 0 + + +/datum/objective/escape/escape_with_identity + var/target_real_name // Has to be stored because the target's real_name can change over the course of the round + +/datum/objective/escape/escape_with_identity/find_target() + var/list/possible_targets = list() //Copypasta because NO_DNA races, yay for snowflakes. + for(var/datum/mind/possible_target in SSticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && possible_target.current.client) + var/mob/living/carbon/human/H = possible_target.current + if(!(NO_DNA in H.dna.species.species_traits)) + possible_targets += possible_target + if(possible_targets.len > 0) + target = pick(possible_targets) + if(target && target.current) + target_real_name = target.current.real_name + explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role] while wearing [target.p_their()] identification card." + else + explanation_text = "Free Objective" + +/datum/objective/escape/escape_with_identity/check_completion() + if(!target_real_name) + return 1 + if(!ishuman(owner.current)) + return 0 + var/mob/living/carbon/human/H = owner.current + if(..()) + if(H.dna.real_name == target_real_name) + if(H.get_id_name()== target_real_name) + return 1 + return 0 + +/datum/objective/die + explanation_text = "Die a glorious death." + +/datum/objective/die/check_completion() + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current) || iszombie(owner)) + return 1 + if(issilicon(owner.current) && owner.current != owner.original) + return 1 + return 0 + + + +/datum/objective/survive + explanation_text = "Stay alive until the end." + +/datum/objective/survive/check_completion() + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) + return 0 //Brains no longer win survive objectives. --NEO + if(issilicon(owner.current) && owner.current != owner.original) + return 0 + return 1 + +/datum/objective/nuclear + explanation_text = "Destroy the station with a nuclear device." + martyr_compatible = 1 + +/datum/objective/steal + var/datum/theft_objective/steal_target + martyr_compatible = 0 + var/theft_area + +/datum/objective/steal/proc/get_location() + if(steal_target.location_override) + return steal_target.location_override + var/list/obj/item/steal_candidates = get_all_of_type(steal_target.typepath, subtypes = TRUE) + for(var/obj/item/candidate in steal_candidates) + if(!is_admin_level(candidate.loc.z)) + theft_area = get_area(candidate.loc) + return "[theft_area]" + return "an unknown area" + +/datum/objective/steal/find_target() + var/loop=50 + while(!steal_target && loop > 0) + loop-- + var/thefttype = pick(GLOB.potential_theft_objectives) + var/datum/theft_objective/O = new thefttype + if(owner.assigned_role in O.protected_jobs) + continue + if(O in owner.targets) + continue + if(O.flags & 2) + continue + steal_target = O + + explanation_text = "Steal [steal_target]. One was last seen in [get_location()]. " + if(islist(O.protected_jobs) && O.protected_jobs.len) + explanation_text += "It may also be in the possession of the [jointext(O.protected_jobs, ", ")]." + return + explanation_text = "Free Objective." + + +/datum/objective/steal/proc/select_target() + var/list/possible_items_all = GLOB.potential_theft_objectives+"custom" + var/new_target = input("Select target:", "Objective target", null) as null|anything in possible_items_all + if(!new_target) return + if(new_target == "custom") + var/datum/theft_objective/O=new + O.typepath = input("Select type:","Type") as null|anything in typesof(/obj/item) + if(!O.typepath) return + var/tmp_obj = new O.typepath + var/custom_name = tmp_obj:name + qdel(tmp_obj) + O.name = sanitize(copytext(input("Enter target name:", "Objective target", custom_name) as text|null,1,MAX_NAME_LEN)) + if(!O.name) return + steal_target = O + explanation_text = "Steal [O.name]." + else + steal_target = new new_target + explanation_text = "Steal [steal_target.name]." + return steal_target + +/datum/objective/steal/check_completion() + if(!steal_target) + return 1 // Free Objective + + var/list/all_items = owner.current.GetAllContents() + + for(var/obj/I in all_items) + if(istype(I, steal_target.typepath)) + return steal_target.check_special_completion(I) + if(I.type in steal_target.altitems) + return steal_target.check_special_completion(I) + + +/datum/objective/steal/exchange + martyr_compatible = 0 + +/datum/objective/steal/exchange/proc/set_faction(var/faction,var/otheragent) + target = otheragent + var/datum/theft_objective/unique/targetinfo + if(faction == "red") + targetinfo = new /datum/theft_objective/unique/docs_blue + else if(faction == "blue") + targetinfo = new /datum/theft_objective/unique/docs_red + explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent" + steal_target = targetinfo + +/datum/objective/steal/exchange/backstab +/datum/objective/steal/exchange/backstab/set_faction(var/faction) + var/datum/theft_objective/unique/targetinfo + if(faction == "red") + targetinfo = new /datum/theft_objective/unique/docs_red + else if(faction == "blue") + targetinfo = new /datum/theft_objective/unique/docs_blue + explanation_text = "Do not give up or lose [targetinfo.name]." + steal_target = targetinfo + +/datum/objective/download +/datum/objective/download/proc/gen_amount_goal() + target_amount = rand(10,20) + explanation_text = "Download [target_amount] research levels." + return target_amount + + +/datum/objective/download/check_completion() + return 0 + + + +/datum/objective/capture +/datum/objective/capture/proc/gen_amount_goal() + target_amount = rand(5,10) + explanation_text = "Accumulate [target_amount] capture points." + return target_amount + + +/datum/objective/capture/check_completion()//Basically runs through all the mobs in the area to determine how much they are worth. + return 0 + + + + +/datum/objective/absorb +/datum/objective/absorb/proc/gen_amount_goal(var/lowbound = 4, var/highbound = 6) + target_amount = rand (lowbound,highbound) + if(SSticker) + var/n_p = 1 //autowin + if(SSticker.current_state == GAME_STATE_SETTING_UP) + for(var/mob/new_player/P in GLOB.player_list) + if(P.client && P.ready && P.mind != owner) + if(P.client.prefs && (P.client.prefs.species == "Machine")) // Special check for species that can't be absorbed. No better solution. + continue + n_p++ + else if(SSticker.current_state == GAME_STATE_PLAYING) + for(var/mob/living/carbon/human/P in GLOB.player_list) + if(NO_DNA in P.dna.species.species_traits) + continue + if(P.client && !(P.mind in SSticker.mode.changelings) && P.mind!=owner) + n_p++ + target_amount = min(target_amount, n_p) + + explanation_text = "Absorb [target_amount] compatible genomes." + return target_amount + +/datum/objective/absorb/check_completion() + if(owner && owner.changeling && owner.changeling.absorbed_dna && (owner.changeling.absorbedcount >= target_amount)) + return 1 + else + return 0 + +/datum/objective/destroy + martyr_compatible = 1 + var/target_real_name + +/datum/objective/destroy/find_target() + var/list/possible_targets = active_ais(1) + var/mob/living/silicon/ai/target_ai = pick(possible_targets) + target = target_ai.mind + if(target && target.current) + target_real_name = target.current.real_name + explanation_text = "Destroy [target_real_name], the AI." + else + explanation_text = "Free Objective" + return target + +/datum/objective/destroy/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || is_away_level(target.current.z) || !target.current.ckey) + return 1 + return 0 + return 1 + +/datum/objective/steal_five_of_type + explanation_text = "Steal at least five items!" + var/list/wanted_items = list() + +/datum/objective/steal_five_of_type/New() + ..() + wanted_items = typecacheof(wanted_items) + +/datum/objective/steal_five_of_type/check_completion() + var/stolen_count = 0 + if(!isliving(owner.current)) + return FALSE + var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. + for(var/obj/I in all_items) //Check for wanted items + if(is_type_in_typecache(I, wanted_items)) + stolen_count++ + return stolen_count >= 5 + +/datum/objective/steal_five_of_type/summon_guns + explanation_text = "Steal at least five guns!" + wanted_items = list(/obj/item/gun) + +/datum/objective/steal_five_of_type/summon_magic + explanation_text = "Steal at least five magical artefacts!" + wanted_items = list() + +/datum/objective/steal_five_of_type/summon_magic/New() + wanted_items = GLOB.summoned_magic_objectives + ..() + +/datum/objective/steal_five_of_type/summon_magic/check_completion() + var/stolen_count = 0 + if(!isliving(owner.current)) + return FALSE + var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. + for(var/obj/I in all_items) //Check for wanted items + if(istype(I, /obj/item/spellbook) && !istype(I, /obj/item/spellbook/oneuse)) + var/obj/item/spellbook/spellbook = I + if(spellbook.uses) //if the book still has powers... + stolen_count++ //it counts. nice. + if(istype(I, /obj/item/spellbook/oneuse)) + var/obj/item/spellbook/oneuse/oneuse = I + if(!oneuse.used) + stolen_count++ + else if(is_type_in_typecache(I, wanted_items)) + stolen_count++ + return stolen_count >= 5 + +/datum/objective/blood +/datum/objective/blood/proc/gen_amount_goal(low = 150, high = 400) + target_amount = rand(low,high) + target_amount = round(round(target_amount/5)*5) + explanation_text = "Accumulate at least [target_amount] total units of blood." + return target_amount + +/datum/objective/blood/check_completion() + if(owner && owner.vampire && owner.vampire.bloodtotal && owner.vampire.bloodtotal >= target_amount) + return 1 + else + return 0 + +// /vg/; Vox Inviolate for humans :V +/datum/objective/minimize_casualties + explanation_text = "Minimise casualties." +/datum/objective/minimize_casualties/check_completion() + if(owner.kills.len>5) return 0 + return 1 + +//Vox heist objectives. + +/datum/objective/heist +/datum/objective/heist/proc/choose_target() + return + +/datum/objective/heist/kidnap +/datum/objective/heist/kidnap/choose_target() + var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Station Engineer") + var/list/possible_targets = list() + var/list/priority_targets = list() + + for(var/datum/mind/possible_target in SSticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && (possible_target.assigned_role != possible_target.special_role) && !possible_target.offstation_role) + possible_targets += possible_target + for(var/role in roles) + if(possible_target.assigned_role == role) + priority_targets += possible_target + continue + + if(priority_targets.len > 0) + target = pick(priority_targets) + else if(possible_targets.len > 0) + target = pick(possible_targets) + + if(target && target.current) + explanation_text = "The Shoal has a need for [target.current.real_name], the [target.assigned_role]. Take [target.current.p_them()] alive." + else + explanation_text = "Free Objective" + return target + +/datum/objective/heist/kidnap/check_completion() + if(target && target.current) + if(target.current.stat == DEAD) + return 0 + + var/area/shuttle/vox/A = locate() //stupid fucking hardcoding + var/area/vox_station/B = locate() //but necessary + + for(var/mob/living/carbon/human/M in A) + if(target.current == M) + return 1 + for(var/mob/living/carbon/human/M in B) + if(target.current == M) + return 1 + else + return 0 + +/datum/objective/heist/loot +/datum/objective/heist/loot/choose_target() + var/loot = "an object" + switch(rand(1,8)) + if(1) + target = /obj/structure/particle_accelerator + target_amount = 6 + loot = "a complete particle accelerator" + if(2) + target = /obj/machinery/the_singularitygen + target_amount = 1 + loot = "a gravitational singularity generator" + if(3) + target = /obj/machinery/power/emitter + target_amount = 4 + loot = "four emitters" + if(4) + target = /obj/machinery/nuclearbomb + target_amount = 1 + loot = "a nuclear bomb" + if(5) + target = /obj/item/gun + target_amount = 6 + loot = "six guns. Tasers and other non-lethal guns are acceptable" + if(6) + target = /obj/item/gun/energy + target_amount = 4 + loot = "four energy guns" + if(7) + target = /obj/item/gun/energy/laser + target_amount = 2 + loot = "two laser guns" + if(8) + target = /obj/item/gun/energy/ionrifle + target_amount = 1 + loot = "an ion gun" + + explanation_text = "We are lacking in hardware. Steal or trade [loot]." + +/datum/objective/heist/loot/check_completion() + var/total_amount = 0 + + for(var/obj/O in locate(/area/shuttle/vox)) + if(istype(O, target)) + total_amount++ + for(var/obj/I in O.contents) + if(istype(I, target)) + total_amount++ + if(total_amount >= target_amount) + return 1 + + for(var/obj/O in locate(/area/vox_station)) + if(istype(O, target)) + total_amount++ + for(var/obj/I in O.contents) + if(istype(I, target)) + total_amount++ + if(total_amount >= target_amount) + return 1 + + var/datum/game_mode/heist/H = SSticker.mode + for(var/datum/mind/raider in H.raiders) + if(raider.current) + for(var/obj/O in raider.current.get_contents()) + if(istype(O,target)) + total_amount++ + if(total_amount >= target_amount) + return 1 + + return 0 + +/datum/objective/heist/salvage +/datum/objective/heist/salvage/choose_target() + switch(rand(1,8)) + if(1) + target = "metal" + target_amount = 300 + if(2) + target = "glass" + target_amount = 200 + if(3) + target = "plasteel" + target_amount = 100 + if(4) + target = "solid plasma" + target_amount = 100 + if(5) + target = "silver" + target_amount = 50 + if(6) + target = "gold" + target_amount = 20 + if(7) + target = "uranium" + target_amount = 20 + if(8) + target = "diamond" + target_amount = 20 + + explanation_text = "Ransack or trade with the station and escape with [target_amount] [target]." + +/datum/objective/heist/salvage/check_completion() + var/total_amount = 0 + + for(var/obj/item/O in locate(/area/shuttle/vox)) + var/obj/item/stack/sheet/S + if(istype(O,/obj/item/stack/sheet)) + if(O.name == target) + S = O + total_amount += S.get_amount() + + for(var/obj/I in O.contents) + if(istype(I,/obj/item/stack/sheet)) + if(I.name == target) + S = I + total_amount += S.get_amount() + + for(var/obj/item/O in locate(/area/vox_station)) + var/obj/item/stack/sheet/S + if(istype(O,/obj/item/stack/sheet)) + if(O.name == target) + S = O + total_amount += S.get_amount() + + for(var/obj/I in O.contents) + if(istype(I,/obj/item/stack/sheet)) + if(I.name == target) + S = I + total_amount += S.get_amount() + + var/datum/game_mode/heist/H = SSticker.mode + for(var/datum/mind/raider in H.raiders) + if(raider.current) + for(var/obj/item/O in raider.current.get_contents()) + if(istype(O,/obj/item/stack/sheet)) + if(O.name == target) + var/obj/item/stack/sheet/S = O + total_amount += S.get_amount() + + if(total_amount >= target_amount) return 1 + return 0 + + +/datum/objective/heist/inviolate_crew + explanation_text = "Do not leave any Vox behind, alive or dead." + +/datum/objective/heist/inviolate_crew/check_completion() + var/datum/game_mode/heist/H = SSticker.mode + if(H.is_raider_crew_safe()) + return 1 + return 0 + +/datum/objective/heist/inviolate_death + explanation_text = "Follow the Inviolate. Minimise death and loss of resources." + +/datum/objective/heist/inviolate_death/check_completion() + var/vox_allowed_kills = 3 // The number of people the vox can accidently kill. Mostly a counter to people killing themselves if a raider touches them to force fail. + var/vox_total_kills = 0 + + var/datum/game_mode/heist/H = SSticker.mode + for(var/datum/mind/raider in H.raiders) + vox_total_kills += raider.kills.len // Kills are listed in the mind; uses this to calculate vox kills + + if(vox_total_kills > vox_allowed_kills) return 0 + return 1 + + +// Traders +// These objectives have no check_completion, they exist only to tell Sol Traders what to aim for. + +/datum/objective/trade/proc/choose_target() + return + +/datum/objective/trade/plasma/choose_target() + explanation_text = "Acquire at least 15 sheets of plasma through trade." + +/datum/objective/trade/credits/choose_target() + explanation_text = "Acquire at least 10,000 credits through trade." + +//wizard + +/datum/objective/wizchaos + explanation_text = "Wreak havoc upon the station as much you can. Send those wandless Nanotrasen scum a message!" + completed = 1 diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 9f7e14ccef2..00ce24b6c46 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -1,449 +1,450 @@ -// To add a rev to the list of revolutionaries, make sure it's rev (with if(ticker.mode.name == "revolution)), -// then call ticker.mode:add_revolutionary(_THE_PLAYERS_MIND_) -// nothing else needs to be done, as that proc will check if they are a valid target. -// Just make sure the converter is a head before you call it! -// To remove a rev (from brainwashing or w/e), call ticker.mode:remove_revolutionary(_THE_PLAYERS_MIND_), -// this will also check they're not a head, so it can just be called freely -// If the game somtimes isn't registering a win properly, then ticker.mode.check_win() isn't being called somewhere. - -/datum/game_mode - var/list/datum/mind/head_revolutionaries = list() - var/list/datum/mind/revolutionaries = list() - -/datum/game_mode/revolution - name = "revolution" - config_tag = "revolution" - restricted_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician") - required_players = 20 - required_enemies = 1 - recommended_enemies = 3 - - var/finished = 0 - var/check_counter = 0 - var/max_headrevs = 3 - var/list/datum/mind/heads_to_kill = list() - var/list/possible_revolutionaries = list() - -/////////////////////////// -//Announces the game type// -/////////////////////////// -/datum/game_mode/revolution/announce() - to_chat(world, "The current game mode is - Revolution!") - to_chat(world, "Some crewmembers are attempting to start a revolution!
    \nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
    \nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).
    ") - - -/////////////////////////////////////////////////////////////////////////////// -//Gets the round setup, cancelling if there's not enough players at the start// -/////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/revolution/pre_setup() - possible_revolutionaries = get_players_for_role(ROLE_REV) - - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - - for(var/i=1 to max_headrevs) - if(possible_revolutionaries.len==0) - break - var/datum/mind/lenin = pick(possible_revolutionaries) - possible_revolutionaries -= lenin - head_revolutionaries += lenin - lenin.restricted_roles = restricted_jobs - - if(head_revolutionaries.len < required_enemies) - return FALSE - - return TRUE - - -/datum/game_mode/revolution/post_setup() - var/list/heads = get_living_heads() - var/list/sec = get_living_sec() - var/weighted_score = min(max(round(heads.len - ((8 - sec.len) / 3)),1),max_headrevs) - - while(weighted_score < head_revolutionaries.len) //das vi danya - var/datum/mind/trotsky = pick(head_revolutionaries) - possible_revolutionaries += trotsky - head_revolutionaries -= trotsky - update_rev_icons_removed(trotsky) - - for(var/datum/mind/rev_mind in head_revolutionaries) - log_game("[key_name(rev_mind)] has been selected as a head rev") - for(var/datum/mind/head_mind in heads) - mark_for_death(rev_mind, head_mind) - - addtimer(CALLBACK(src, .proc/equip_revolutionary, rev_mind.current), rand(10, 100)) - - for(var/datum/mind/rev_mind in head_revolutionaries) - greet_revolutionary(rev_mind) - modePlayer += head_revolutionaries - if(SSshuttle) - SSshuttle.emergencyNoEscape = 1 - ..() - - -/datum/game_mode/revolution/process() - check_counter++ - if(check_counter >= 5) - if(!finished) - check_heads() - SSticker.mode.check_win() - check_counter = 0 - return FALSE - - -/datum/game_mode/proc/forge_revolutionary_objectives(datum/mind/rev_mind) - var/list/heads = get_living_heads() - for(var/datum/mind/head_mind in heads) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = rev_mind - rev_obj.target = head_mind - rev_obj.explanation_text = "Assassinate or exile [head_mind.name], the [head_mind.assigned_role]." - rev_mind.objectives += rev_obj - -/datum/game_mode/proc/greet_revolutionary(datum/mind/rev_mind, you_are=1) - var/obj_count = 1 - update_rev_icons_added(rev_mind) - if(you_are) - to_chat(rev_mind.current, "You are a member of the revolutionaries' leadership!") - for(var/datum/objective/objective in rev_mind.objectives) - to_chat(rev_mind.current, "Objective #[obj_count]: [objective.explanation_text]") - rev_mind.special_role = SPECIAL_ROLE_HEAD_REV - obj_count++ - -///////////////////////////////////////////////////////////////////////////////// -//This are equips the rev heads with their gear, and makes the clown not clumsy// -///////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/proc/equip_revolutionary(mob/living/carbon/human/mob) - if(!istype(mob)) - return - - if(mob.mind) - if(mob.mind.assigned_role == "Clown") - to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") - mob.mutations.Remove(CLUMSY) - var/datum/action/innate/toggle_clumsy/A = new - A.Grant(mob) - - var/obj/item/flash/T = new(mob) - var/obj/item/toy/crayon/spraycan/R = new(mob) - var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob) - - var/list/slots = list ( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/where = mob.equip_in_one_of_slots(T, slots) - var/where2 = mob.equip_in_one_of_slots(C, slots) - mob.equip_in_one_of_slots(R,slots) - - mob.update_icons() - - if(!where2) - to_chat(mob, "The Syndicate were unfortunately unable to get you a chameleon security HUD.") - else - to_chat(mob, "The chameleon security HUD in your [where2] will help you keep track of who is mindshield-implanted, and unable to be recruited.") - - if(!where) - to_chat(mob, "The Syndicate were unfortunately unable to get you a flash.") - else - to_chat(mob, "The flash in your [where] will help you to persuade the crew to join your cause.") - return 1 - -///////////////////////////////// -//Gives head revs their targets// -///////////////////////////////// -/datum/game_mode/revolution/proc/mark_for_death(datum/mind/rev_mind, datum/mind/head_mind) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = rev_mind - rev_obj.target = head_mind - rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]." - rev_mind.objectives += rev_obj - heads_to_kill += head_mind - -//////////////////////////////////////////// -//Checks if new heads have joined midround// -//////////////////////////////////////////// -/datum/game_mode/revolution/proc/check_heads() - var/list/heads = get_all_heads() - var/list/sec = get_all_sec() - if(heads_to_kill.len < heads.len) - var/list/new_heads = heads - heads_to_kill - for(var/datum/mind/head_mind in new_heads) - for(var/datum/mind/rev_mind in head_revolutionaries) - mark_for_death(rev_mind, head_mind) - - if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3))) - latejoin_headrev() - -/////////////////////////////// -//Adds a new headrev midround// -/////////////////////////////// -/datum/game_mode/revolution/proc/latejoin_headrev() - if(revolutionaries) //Head Revs are not in this list - var/list/promotable_revs = list() - for(var/datum/mind/khrushchev in revolutionaries) - if(khrushchev.current && khrushchev.current.client && khrushchev.current.stat != DEAD) - if(ROLE_REV in khrushchev.current.client.prefs.be_special) - promotable_revs += khrushchev - if(promotable_revs.len) - var/datum/mind/stalin = pick(promotable_revs) - revolutionaries -= stalin - head_revolutionaries += stalin - log_game("[key_name(stalin)] has been promoted to a head rev") - equip_revolutionary(stalin.current) - forge_revolutionary_objectives(stalin) - greet_revolutionary(stalin) - -////////////////////////////////////// -//Checks if the revs have won or not// -////////////////////////////////////// -/datum/game_mode/revolution/check_win() - if(check_rev_victory()) - finished = 1 - else if(check_heads_victory()) - finished = 2 - return - -/////////////////////////////// -//Checks if the round is over// -/////////////////////////////// -/datum/game_mode/revolution/check_finished() - if(config.continuous_rounds) - if(finished != 0) - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - command_announcement.Announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg') - return ..() - if(finished != 0) - return TRUE - else - return ..() - -/////////////////////////////////////////////////// -//Deals with converting players to the revolution// -/////////////////////////////////////////////////// -/datum/game_mode/proc/add_revolutionary(datum/mind/rev_mind) - if(rev_mind.assigned_role in command_positions) - return 0 - var/mob/living/carbon/human/H = rev_mind.current//Check to see if the potential rev is implanted - if(ismindshielded(H)) - return 0 - if((rev_mind in revolutionaries) || (rev_mind in head_revolutionaries)) - return 0 - revolutionaries += rev_mind - if(iscarbon(rev_mind.current)) - var/mob/living/carbon/carbon_mob = rev_mind.current - carbon_mob.Silence(5) - carbon_mob.flash_eyes(1, 1) - rev_mind.current.Stun(5) - to_chat(rev_mind.current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") - rev_mind.current.create_attack_log("Has been converted to the revolution!") - rev_mind.special_role = SPECIAL_ROLE_REV - update_rev_icons_added(rev_mind) - if(jobban_isbanned(rev_mind.current, ROLE_REV) || jobban_isbanned(rev_mind.current, ROLE_SYNDICATE)) - replace_jobbanned_player(rev_mind.current, ROLE_REV) - return 1 -////////////////////////////////////////////////////////////////////////////// -//Deals with players being converted from the revolution (Not a rev anymore)// // Modified to handle borged MMIs. Accepts another var if the target is being borged at the time -- Polymorph. -////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/proc/remove_revolutionary(datum/mind/rev_mind , beingborged) - var/remove_head = 0 - if(beingborged && (rev_mind in head_revolutionaries)) - head_revolutionaries -= rev_mind - remove_head = 1 - - if((rev_mind in revolutionaries) || remove_head) - revolutionaries -= rev_mind - rev_mind.special_role = null - rev_mind.current.create_attack_log("Has renounced the revolution!") - - if(beingborged) - to_chat(rev_mind.current, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]") - message_admins("[key_name_admin(rev_mind.current)] [ADMIN_QUE(rev_mind.current,"?")] ([ADMIN_FLW(rev_mind.current,"FLW")]) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.") - - else - rev_mind.current.Paralyse(5) - to_chat(rev_mind.current, "You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...") - - update_rev_icons_removed(rev_mind) - for(var/mob/living/M in view(rev_mind.current)) - if(beingborged) - to_chat(M, "The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.") - - else - to_chat(M, "[rev_mind.current] looks like [rev_mind.current.p_they()] just remembered [rev_mind.current.p_their()] real allegiance!") - -///////////////////////////////////// -//Adds the rev hud to a new convert// -///////////////////////////////////// -/datum/game_mode/proc/update_rev_icons_added(datum/mind/rev_mind) - var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] - revhud.join_hud(rev_mind.current) - set_antag_hud(rev_mind.current, ((rev_mind in head_revolutionaries) ? "hudheadrevolutionary" : "hudrevolutionary")) - -///////////////////////////////////////// -//Removes the hud from deconverted revs// -///////////////////////////////////////// -/datum/game_mode/proc/update_rev_icons_removed(datum/mind/rev_mind) - var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] - revhud.leave_hud(rev_mind.current) - set_antag_hud(rev_mind.current, null) - -////////////////////////// -//Checks for rev victory// -////////////////////////// -/datum/game_mode/revolution/proc/check_rev_victory() - for(var/datum/mind/rev_mind in head_revolutionaries) - for(var/datum/objective/mutiny/objective in rev_mind.objectives) - if(!(objective.check_completion())) - return FALSE - - return TRUE - -///////////////////////////// -//Checks for a head victory// -///////////////////////////// -/datum/game_mode/revolution/proc/check_heads_victory() - for(var/datum/mind/rev_mind in head_revolutionaries) - var/turf/T = get_turf(rev_mind.current) - if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != DEAD) && rev_mind.current.client && T && is_station_level(T.z)) - if(ishuman(rev_mind.current)) - return FALSE - return TRUE - -////////////////////////////////////////////////////////////////////// -//Announces the end of the game with all relavent information stated// -////////////////////////////////////////////////////////////////////// -/datum/game_mode/revolution/declare_completion() - if(finished == 1) - feedback_set_details("round_end_result","revolution win - heads killed") - to_chat(world, "The heads of staff were killed or exiled! The revolutionaries win!") - else if(finished == 2) - feedback_set_details("round_end_result","revolution loss - rev heads killed") - to_chat(world, "The heads of staff managed to stop the revolution!") - ..() - return TRUE - -/datum/game_mode/proc/auto_declare_completion_revolution() - var/list/targets = list() - if(head_revolutionaries.len || GAMEMODE_IS_REVOLUTION) - var/num_revs = 0 - var/num_survivors = 0 - for(var/mob/living/carbon/survivor in GLOB.living_mob_list) - if(survivor.ckey) - num_survivors++ - if(survivor.mind) - if((survivor.mind in head_revolutionaries) || (survivor.mind in revolutionaries)) - num_revs++ - if(num_survivors) - to_chat(world, "[TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%") // % of loyal crew - var/text = "
    The head revolutionaries were:" - for(var/datum/mind/headrev in head_revolutionaries) - text += printplayer(headrev, 1) - text += "
    " - to_chat(world, text) - - if(revolutionaries.len || GAMEMODE_IS_REVOLUTION) - var/text = "
    The revolutionaries were:" - for(var/datum/mind/rev in revolutionaries) - text += printplayer(rev, 1) - text += "
    " - to_chat(world, text) - - if( head_revolutionaries.len || revolutionaries.len || GAMEMODE_IS_REVOLUTION ) - var/text = "
    The heads of staff were:" - var/list/heads = get_all_heads() - for(var/datum/mind/head in heads) - var/target = (head in targets) - if(target) - text += "Target" - text += printplayer(head, 1) - text += "
    " - to_chat(world, text) - -/datum/game_mode/revolution/set_scoreboard_gvars() - var/foecount = 0 - for(var/datum/mind/M in SSticker.mode.head_revolutionaries) - foecount++ - if(!M || !M.current) - score_opkilled++ - continue - - if(M.current.stat == DEAD) - score_opkilled++ - - else if(M.current.restrained()) - score_arrested++ - - if(foecount == score_arrested) - score_allarrested = 1 - - for(var/mob/living/carbon/human/player in world) - if(player.mind) - var/role = player.mind.assigned_role - if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) - if(player.stat == DEAD) - score_deadcommand++ - - - var/arrestpoints = score_arrested * 1000 - var/killpoints = score_opkilled * 500 - var/comdeadpts = score_deadcommand * 500 - if(score_traitorswon) - score_crewscore -= 10000 - - score_crewscore += arrestpoints - score_crewscore += killpoints - score_crewscore -= comdeadpts - - -/datum/game_mode/revolution/get_scoreboard_stats() - var/foecount = 0 - var/comcount = 0 - var/revcount = 0 - var/loycount = 0 - for(var/datum/mind/M in SSticker.mode:head_revolutionaries) - if(M.current && M.current.stat != DEAD) - foecount++ - for(var/datum/mind/M in SSticker.mode:revolutionaries) - if(M.current && M.current.stat != DEAD) - revcount++ - for(var/mob/living/carbon/human/player in world) - if(player.mind) - var/role = player.mind.assigned_role - if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) - if(player.stat != DEAD) - comcount++ - else - if(player.mind in SSticker.mode.revolutionaries) continue - loycount++ - - for(var/mob/living/silicon/X in world) - if(X.stat != DEAD) - loycount++ - - - var/dat = "" - - dat += "Mode Statistics
    " - dat += "Number of Surviving Revolution Heads: [foecount]
    " - dat += "Number of Surviving Command Staff: [comcount]
    " - dat += "Number of Surviving Revolutionaries: [revcount]
    " - dat += "Number of Surviving Loyal Crew: [loycount]
    " - - dat += "
    " - dat += "Revolution Heads Arrested: [score_arrested] ([score_arrested * 1000] Points)
    " - dat += "All Revolution Heads Arrested: [score_allarrested ? "Yes" : "No"] (Score tripled)
    " - - dat += "Revolution Heads Slain: [score_opkilled] ([score_opkilled * 500] Points)
    " - dat += "Command Staff Slain: [score_deadcommand] (-[score_deadcommand * 500] Points)
    " - dat += "Revolution Successful: [score_traitorswon ? "Yes" : "No"] (-[score_traitorswon * 10000] Points)
    " - dat += "
    " - - return dat +// To add a rev to the list of revolutionaries, make sure it's rev (with if(ticker.mode.name == "revolution)), +// then call ticker.mode:add_revolutionary(_THE_PLAYERS_MIND_) +// nothing else needs to be done, as that proc will check if they are a valid target. +// Just make sure the converter is a head before you call it! +// To remove a rev (from brainwashing or w/e), call ticker.mode:remove_revolutionary(_THE_PLAYERS_MIND_), +// this will also check they're not a head, so it can just be called freely +// If the game somtimes isn't registering a win properly, then ticker.mode.check_win() isn't being called somewhere. + +/datum/game_mode + var/list/datum/mind/head_revolutionaries = list() + var/list/datum/mind/revolutionaries = list() + +/datum/game_mode/revolution + name = "revolution" + config_tag = "revolution" + restricted_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician") + required_players = 20 + required_enemies = 1 + recommended_enemies = 3 + + var/finished = 0 + var/check_counter = 0 + var/max_headrevs = 3 + var/list/datum/mind/heads_to_kill = list() + var/list/possible_revolutionaries = list() + +/////////////////////////// +//Announces the game type// +/////////////////////////// +/datum/game_mode/revolution/announce() + to_chat(world, "The current game mode is - Revolution!") + to_chat(world, "Some crewmembers are attempting to start a revolution!
    \nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
    \nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).
    ") + + +/////////////////////////////////////////////////////////////////////////////// +//Gets the round setup, cancelling if there's not enough players at the start// +/////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/revolution/pre_setup() + possible_revolutionaries = get_players_for_role(ROLE_REV) + + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + + for(var/i=1 to max_headrevs) + if(possible_revolutionaries.len==0) + break + var/datum/mind/lenin = pick(possible_revolutionaries) + possible_revolutionaries -= lenin + head_revolutionaries += lenin + lenin.restricted_roles = restricted_jobs + + if(head_revolutionaries.len < required_enemies) + return FALSE + + return TRUE + + +/datum/game_mode/revolution/post_setup() + var/list/heads = get_living_heads() + var/list/sec = get_living_sec() + var/weighted_score = min(max(round(heads.len - ((8 - sec.len) / 3)),1),max_headrevs) + + while(weighted_score < head_revolutionaries.len) //das vi danya + var/datum/mind/trotsky = pick(head_revolutionaries) + possible_revolutionaries += trotsky + head_revolutionaries -= trotsky + update_rev_icons_removed(trotsky) + + for(var/datum/mind/rev_mind in head_revolutionaries) + log_game("[key_name(rev_mind)] has been selected as a head rev") + for(var/datum/mind/head_mind in heads) + mark_for_death(rev_mind, head_mind) + + addtimer(CALLBACK(src, .proc/equip_revolutionary, rev_mind.current), rand(10, 100)) + + for(var/datum/mind/rev_mind in head_revolutionaries) + greet_revolutionary(rev_mind) + modePlayer += head_revolutionaries + if(SSshuttle) + SSshuttle.emergencyNoEscape = 1 + ..() + + +/datum/game_mode/revolution/process() + check_counter++ + if(check_counter >= 5) + if(!finished) + check_heads() + SSticker.mode.check_win() + check_counter = 0 + return FALSE + + +/datum/game_mode/proc/forge_revolutionary_objectives(datum/mind/rev_mind) + var/list/heads = get_living_heads() + for(var/datum/mind/head_mind in heads) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = rev_mind + rev_obj.target = head_mind + rev_obj.explanation_text = "Assassinate or exile [head_mind.name], the [head_mind.assigned_role]." + rev_mind.objectives += rev_obj + +/datum/game_mode/proc/greet_revolutionary(datum/mind/rev_mind, you_are=1) + var/obj_count = 1 + update_rev_icons_added(rev_mind) + if(you_are) + to_chat(rev_mind.current, "You are a member of the revolutionaries' leadership!") + for(var/datum/objective/objective in rev_mind.objectives) + to_chat(rev_mind.current, "Objective #[obj_count]: [objective.explanation_text]") + rev_mind.special_role = SPECIAL_ROLE_HEAD_REV + obj_count++ + +///////////////////////////////////////////////////////////////////////////////// +//This are equips the rev heads with their gear, and makes the clown not clumsy// +///////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/proc/equip_revolutionary(mob/living/carbon/human/mob) + if(!istype(mob)) + return + + if(mob.mind) + if(mob.mind.assigned_role == "Clown") + to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + mob.mutations.Remove(CLUMSY) + var/datum/action/innate/toggle_clumsy/A = new + A.Grant(mob) + + var/obj/item/flash/T = new(mob) + var/obj/item/toy/crayon/spraycan/R = new(mob) + var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob) + + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = mob.equip_in_one_of_slots(T, slots) + var/where2 = mob.equip_in_one_of_slots(C, slots) + mob.equip_in_one_of_slots(R,slots) + + mob.update_icons() + + if(!where2) + to_chat(mob, "The Syndicate were unfortunately unable to get you a chameleon security HUD.") + else + to_chat(mob, "The chameleon security HUD in your [where2] will help you keep track of who is mindshield-implanted, and unable to be recruited.") + + if(!where) + to_chat(mob, "The Syndicate were unfortunately unable to get you a flash.") + else + to_chat(mob, "The flash in your [where] will help you to persuade the crew to join your cause.") + return 1 + +///////////////////////////////// +//Gives head revs their targets// +///////////////////////////////// +/datum/game_mode/revolution/proc/mark_for_death(datum/mind/rev_mind, datum/mind/head_mind) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = rev_mind + rev_obj.target = head_mind + rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]." + rev_mind.objectives += rev_obj + heads_to_kill += head_mind + +//////////////////////////////////////////// +//Checks if new heads have joined midround// +//////////////////////////////////////////// +/datum/game_mode/revolution/proc/check_heads() + var/list/heads = get_all_heads() + var/list/sec = get_all_sec() + if(heads_to_kill.len < heads.len) + var/list/new_heads = heads - heads_to_kill + for(var/datum/mind/head_mind in new_heads) + for(var/datum/mind/rev_mind in head_revolutionaries) + mark_for_death(rev_mind, head_mind) + + if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3))) + latejoin_headrev() + +/////////////////////////////// +//Adds a new headrev midround// +/////////////////////////////// +/datum/game_mode/revolution/proc/latejoin_headrev() + if(revolutionaries) //Head Revs are not in this list + var/list/promotable_revs = list() + for(var/datum/mind/khrushchev in revolutionaries) + if(khrushchev.current && khrushchev.current.client && khrushchev.current.stat != DEAD) + if(ROLE_REV in khrushchev.current.client.prefs.be_special) + promotable_revs += khrushchev + if(promotable_revs.len) + var/datum/mind/stalin = pick(promotable_revs) + revolutionaries -= stalin + head_revolutionaries += stalin + log_game("[key_name(stalin)] has been promoted to a head rev") + equip_revolutionary(stalin.current) + forge_revolutionary_objectives(stalin) + greet_revolutionary(stalin) + +////////////////////////////////////// +//Checks if the revs have won or not// +////////////////////////////////////// +/datum/game_mode/revolution/check_win() + if(check_rev_victory()) + finished = 1 + else if(check_heads_victory()) + finished = 2 + return + +/////////////////////////////// +//Checks if the round is over// +/////////////////////////////// +/datum/game_mode/revolution/check_finished() + if(config.continuous_rounds) + if(finished != 0) + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + GLOB.command_announcement.Announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg') + return ..() + if(finished != 0) + return TRUE + else + return ..() + +/////////////////////////////////////////////////// +//Deals with converting players to the revolution// +/////////////////////////////////////////////////// +/datum/game_mode/proc/add_revolutionary(datum/mind/rev_mind) + if(rev_mind.assigned_role in GLOB.command_positions) + return 0 + var/mob/living/carbon/human/H = rev_mind.current//Check to see if the potential rev is implanted + if(ismindshielded(H)) + return 0 + if((rev_mind in revolutionaries) || (rev_mind in head_revolutionaries)) + return 0 + revolutionaries += rev_mind + if(iscarbon(rev_mind.current)) + var/mob/living/carbon/carbon_mob = rev_mind.current + carbon_mob.Silence(5) + carbon_mob.flash_eyes(1, 1) + rev_mind.current.Stun(5) + to_chat(rev_mind.current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") + rev_mind.current.create_attack_log("Has been converted to the revolution!") + rev_mind.current.create_log(CONVERSION_LOG, "converted to the revolution") + rev_mind.special_role = SPECIAL_ROLE_REV + update_rev_icons_added(rev_mind) + if(jobban_isbanned(rev_mind.current, ROLE_REV) || jobban_isbanned(rev_mind.current, ROLE_SYNDICATE)) + replace_jobbanned_player(rev_mind.current, ROLE_REV) + return 1 +////////////////////////////////////////////////////////////////////////////// +//Deals with players being converted from the revolution (Not a rev anymore)// // Modified to handle borged MMIs. Accepts another var if the target is being borged at the time -- Polymorph. +////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/proc/remove_revolutionary(datum/mind/rev_mind , beingborged) + var/remove_head = 0 + if(beingborged && (rev_mind in head_revolutionaries)) + head_revolutionaries -= rev_mind + remove_head = 1 + + if((rev_mind in revolutionaries) || remove_head) + revolutionaries -= rev_mind + rev_mind.special_role = null + rev_mind.current.create_attack_log("Has renounced the revolution!") + rev_mind.current.create_log(CONVERSION_LOG, "renounced the revolution") + if(beingborged) + to_chat(rev_mind.current, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]") + message_admins("[key_name_admin(rev_mind.current)] [ADMIN_QUE(rev_mind.current,"?")] ([ADMIN_FLW(rev_mind.current,"FLW")]) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.") + + else + rev_mind.current.Paralyse(5) + to_chat(rev_mind.current, "You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...") + + update_rev_icons_removed(rev_mind) + for(var/mob/living/M in view(rev_mind.current)) + if(beingborged) + to_chat(M, "The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.") + + else + to_chat(M, "[rev_mind.current] looks like [rev_mind.current.p_they()] just remembered [rev_mind.current.p_their()] real allegiance!") + +///////////////////////////////////// +//Adds the rev hud to a new convert// +///////////////////////////////////// +/datum/game_mode/proc/update_rev_icons_added(datum/mind/rev_mind) + var/datum/atom_hud/antag/revhud = GLOB.huds[ANTAG_HUD_REV] + revhud.join_hud(rev_mind.current) + set_antag_hud(rev_mind.current, ((rev_mind in head_revolutionaries) ? "hudheadrevolutionary" : "hudrevolutionary")) + +///////////////////////////////////////// +//Removes the hud from deconverted revs// +///////////////////////////////////////// +/datum/game_mode/proc/update_rev_icons_removed(datum/mind/rev_mind) + var/datum/atom_hud/antag/revhud = GLOB.huds[ANTAG_HUD_REV] + revhud.leave_hud(rev_mind.current) + set_antag_hud(rev_mind.current, null) + +////////////////////////// +//Checks for rev victory// +////////////////////////// +/datum/game_mode/revolution/proc/check_rev_victory() + for(var/datum/mind/rev_mind in head_revolutionaries) + for(var/datum/objective/mutiny/objective in rev_mind.objectives) + if(!(objective.check_completion())) + return FALSE + + return TRUE + +///////////////////////////// +//Checks for a head victory// +///////////////////////////// +/datum/game_mode/revolution/proc/check_heads_victory() + for(var/datum/mind/rev_mind in head_revolutionaries) + var/turf/T = get_turf(rev_mind.current) + if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != DEAD) && rev_mind.current.client && T && is_station_level(T.z)) + if(ishuman(rev_mind.current)) + return FALSE + return TRUE + +////////////////////////////////////////////////////////////////////// +//Announces the end of the game with all relavent information stated// +////////////////////////////////////////////////////////////////////// +/datum/game_mode/revolution/declare_completion() + if(finished == 1) + feedback_set_details("round_end_result","revolution win - heads killed") + to_chat(world, "The heads of staff were killed or exiled! The revolutionaries win!") + else if(finished == 2) + feedback_set_details("round_end_result","revolution loss - rev heads killed") + to_chat(world, "The heads of staff managed to stop the revolution!") + ..() + return TRUE + +/datum/game_mode/proc/auto_declare_completion_revolution() + var/list/targets = list() + if(head_revolutionaries.len || GAMEMODE_IS_REVOLUTION) + var/num_revs = 0 + var/num_survivors = 0 + for(var/mob/living/carbon/survivor in GLOB.living_mob_list) + if(survivor.ckey) + num_survivors++ + if(survivor.mind) + if((survivor.mind in head_revolutionaries) || (survivor.mind in revolutionaries)) + num_revs++ + if(num_survivors) + to_chat(world, "[TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%") // % of loyal crew + var/text = "
    The head revolutionaries were:" + for(var/datum/mind/headrev in head_revolutionaries) + text += printplayer(headrev, 1) + text += "
    " + to_chat(world, text) + + if(revolutionaries.len || GAMEMODE_IS_REVOLUTION) + var/text = "
    The revolutionaries were:" + for(var/datum/mind/rev in revolutionaries) + text += printplayer(rev, 1) + text += "
    " + to_chat(world, text) + + if( head_revolutionaries.len || revolutionaries.len || GAMEMODE_IS_REVOLUTION ) + var/text = "
    The heads of staff were:" + var/list/heads = get_all_heads() + for(var/datum/mind/head in heads) + var/target = (head in targets) + if(target) + text += "Target" + text += printplayer(head, 1) + text += "
    " + to_chat(world, text) + +/datum/game_mode/revolution/set_scoreboard_gvars() + var/foecount = 0 + for(var/datum/mind/M in SSticker.mode.head_revolutionaries) + foecount++ + if(!M || !M.current) + GLOB.score_opkilled++ + continue + + if(M.current.stat == DEAD) + GLOB.score_opkilled++ + + else if(M.current.restrained()) + GLOB.score_arrested++ + + if(foecount == GLOB.score_arrested) + GLOB.score_allarrested = 1 + + for(var/mob/living/carbon/human/player in world) + if(player.mind) + var/role = player.mind.assigned_role + if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) + if(player.stat == DEAD) + GLOB.score_deadcommand++ + + + var/arrestpoints = GLOB.score_arrested * 1000 + var/killpoints = GLOB.score_opkilled * 500 + var/comdeadpts = GLOB.score_deadcommand * 500 + if(GLOB.score_traitorswon) + GLOB.score_crewscore -= 10000 + + GLOB.score_crewscore += arrestpoints + GLOB.score_crewscore += killpoints + GLOB.score_crewscore -= comdeadpts + + +/datum/game_mode/revolution/get_scoreboard_stats() + var/foecount = 0 + var/comcount = 0 + var/revcount = 0 + var/loycount = 0 + for(var/datum/mind/M in SSticker.mode:head_revolutionaries) + if(M.current && M.current.stat != DEAD) + foecount++ + for(var/datum/mind/M in SSticker.mode:revolutionaries) + if(M.current && M.current.stat != DEAD) + revcount++ + for(var/mob/living/carbon/human/player in world) + if(player.mind) + var/role = player.mind.assigned_role + if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) + if(player.stat != DEAD) + comcount++ + else + if(player.mind in SSticker.mode.revolutionaries) continue + loycount++ + + for(var/mob/living/silicon/X in world) + if(X.stat != DEAD) + loycount++ + + + var/dat = "" + + dat += "Mode Statistics
    " + dat += "Number of Surviving Revolution Heads: [foecount]
    " + dat += "Number of Surviving Command Staff: [comcount]
    " + dat += "Number of Surviving Revolutionaries: [revcount]
    " + dat += "Number of Surviving Loyal Crew: [loycount]
    " + + dat += "
    " + dat += "Revolution Heads Arrested: [GLOB.score_arrested] ([GLOB.score_arrested * 1000] Points)
    " + dat += "All Revolution Heads Arrested: [GLOB.score_allarrested ? "Yes" : "No"] (Score tripled)
    " + + dat += "Revolution Heads Slain: [GLOB.score_opkilled] ([GLOB.score_opkilled * 500] Points)
    " + dat += "Command Staff Slain: [GLOB.score_deadcommand] (-[GLOB.score_deadcommand * 500] Points)
    " + dat += "Revolution Successful: [GLOB.score_traitorswon ? "Yes" : "No"] (-[GLOB.score_traitorswon * 10000] Points)
    " + dat += "
    " + + return dat diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 216c946fc97..69eaef9051b 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -1,161 +1,161 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -var/hsboxspawn = 1 -var/list - hrefs = list( - "hsbsuit" = "Suit Up (Space Travel Gear)", - "hsbmetal" = "Spawn 50 Metal", - "hsbglass" = "Spawn 50 Glass", - "hsbairlock" = "Spawn Airlock", - "hsbregulator" = "Spawn Air Regulator", - "hsbfilter" = "Spawn Air Filter", - "hsbcanister" = "Spawn Canister", - "hsbfueltank" = "Spawn Welding Fuel Tank", - "hsbwater tank" = "Spawn Water Tank", - "hsbtoolbox" = "Spawn Toolbox", - "hsbmedkit" = "Spawn Medical Kit") - -mob - var/datum/hSB/sandbox = null - proc - CanBuild() - if(master_mode == "sandbox") - sandbox = new/datum/hSB - sandbox.owner = src.ckey - if(src.client.holder) - sandbox.admin = 1 - verbs += new/mob/proc/sandbox_panel - sandbox_panel() - if(sandbox) - sandbox.update() - -datum/hSB - var/owner = null - var/admin = 0 - proc - update() - var/hsbpanel = "
    h_Sandbox Panel

    " - if(admin) - hsbpanel += "Administration Tools:
    " - hsbpanel += "- Toggle Object Spawning

    " - hsbpanel += "Regular Tools:
    " - for(var/T in hrefs) - hsbpanel += "- [hrefs[T]]
    " - if(hsboxspawn) - hsbpanel += "- Spawn Object

    " - usr << browse(hsbpanel, "window=hsbpanel") - Topic(href, href_list) - if(!(src.owner == usr.ckey)) return - if(!usr) return //I guess this is possible if they log out or die with the panel open? It happened. - if(href_list["hsb"]) - switch(href_list["hsb"]) - if("hsbtobj") - if(!admin) return - if(hsboxspawn) - to_chat(world, "Sandbox: [usr.key] has disabled object spawning!") - hsboxspawn = 0 - return - if(!hsboxspawn) - to_chat(world, "Sandbox: [usr.key] has enabled object spawning!") - hsboxspawn = 1 - return - if("hsbsuit") - var/mob/living/carbon/human/P = usr - if(P.wear_suit) - P.wear_suit.loc = P.loc - P.wear_suit.layer = initial(P.wear_suit.layer) - P.wear_suit.plane = initial(P.wear_suit.plane) - P.wear_suit = null - P.wear_suit = new/obj/item/clothing/suit/space(P) - P.wear_suit.layer = ABOVE_HUD_LAYER - P.wear_suit.plane = ABOVE_HUD_PLANE - if(P.head) - P.head.loc = P.loc - P.head.layer = initial(P.head.layer) - P.head.plane = initial(P.head.plane) - P.head = null - P.head = new/obj/item/clothing/head/helmet/space(P) - P.head.layer = ABOVE_HUD_LAYER - P.head.plane = ABOVE_HUD_PLANE - if(P.wear_mask) - P.wear_mask.loc = P.loc - P.wear_mask.layer = initial(P.wear_mask.layer) - P.wear_mask.plane = initial(P.wear_mask.plane) - P.wear_mask = null - P.wear_mask = new/obj/item/clothing/mask/gas(P) - P.wear_mask.layer = ABOVE_HUD_LAYER - P.wear_mask.plane = ABOVE_HUD_PLANE - if(P.back) - P.back.loc = P.loc - P.back.layer = initial(P.back.layer) - P.back.plane = initial(P.back.plane) - P.back = null - P.back = new/obj/item/tank/jetpack(P) - P.back.layer = ABOVE_HUD_LAYER - P.back.plane = ABOVE_HUD_PLANE - P.internal = P.back - if("hsbmetal") - var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/metal - hsb.amount = 50 - hsb.loc = usr.loc - if("hsbglass") - var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/glass - hsb.amount = 50 - hsb.loc = usr.loc - if("hsbairlock") - var/obj/machinery/door/hsb = new/obj/machinery/door/airlock - - //TODO: DEFERRED make this better, with an HTML window or something instead of 15 popups - hsb.req_access = list() - var/accesses = get_all_accesses() - for(var/A in accesses) - if(alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", "Yes", "No") == "Yes") - hsb.req_access += A - - hsb.loc = usr.loc - to_chat(usr, "Sandbox: Created an airlock.") - if("hsbcanister") - var/list/hsbcanisters = subtypesof(/obj/machinery/portable_atmospherics/canister/) - var/hsbcanister = input(usr, "Choose a canister to spawn.", "Sandbox:") in hsbcanisters + "Cancel" - if(!(hsbcanister == "Cancel")) - new hsbcanister(usr.loc) - if("hsbfueltank") - //var/obj/hsb = new/obj/weldfueltank - //hsb.loc = usr.loc - if("hsbwatertank") - //var/obj/hsb = new/obj/watertank - //hsb.loc = usr.loc - if("hsbtoolbox") - var/obj/item/storage/hsb = new/obj/item/storage/toolbox/mechanical - for(var/obj/item/radio/T in hsb) - qdel(T) - new/obj/item/crowbar (hsb) - hsb.loc = usr.loc - if("hsbmedkit") - var/obj/item/storage/firstaid/hsb = new/obj/item/storage/firstaid/regular - hsb.loc = usr.loc - if("hsbobj") - if(!hsboxspawn) return - - var/list/selectable = list() - for(var/O in typesof(/obj/item/)) - //Note, these istypes don't work - if(istype(O, /obj/item/gun)) - continue - if(istype(O, /obj/item/assembly)) - continue - if(istype(O, /obj/item/camera)) - continue - if(istype(O, /obj/item/cloaking_device)) - continue - if(istype(O, /obj/item/dummy)) - continue - if(istype(O, /obj/item/melee/energy/sword/saber)) - continue - if(istype(O, /obj/structure)) - continue - selectable += O - - var/hsbitem = input(usr, "Choose an object to spawn.", "Sandbox:") in selectable + "Cancel" - if(hsbitem != "Cancel") - new hsbitem(usr.loc) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +// Someone needs to properly path this file, or just delete it since its unticked and was last properly touched >4 years ago +GLOBAL_VAR_INIT(hsboxspawn, 1) +GLOBAL_LIST_INIT(sandbox_hrefs, list( + "hsbsuit" = "Suit Up (Space Travel Gear)", + "hsbmetal" = "Spawn 50 Metal", + "hsbglass" = "Spawn 50 Glass", + "hsbairlock" = "Spawn Airlock", + "hsbregulator" = "Spawn Air Regulator", + "hsbfilter" = "Spawn Air Filter", + "hsbcanister" = "Spawn Canister", + "hsbfueltank" = "Spawn Welding Fuel Tank", + "hsbwater tank" = "Spawn Water Tank", + "hsbtoolbox" = "Spawn Toolbox", + "hsbmedkit" = "Spawn Medical Kit")) + +mob + var/datum/hSB/sandbox = null + proc + CanBuild() + if(master_mode == "sandbox") + sandbox = new/datum/hSB + sandbox.owner = src.ckey + if(src.client.holder) + sandbox.admin = 1 + verbs += new/mob/proc/sandbox_panel + sandbox_panel() + if(sandbox) + sandbox.update() + +datum/hSB + var/owner = null + var/admin = 0 + proc + update() + var/hsbpanel = "
    h_Sandbox Panel

    " + if(admin) + hsbpanel += "Administration Tools:
    " + hsbpanel += "- Toggle Object Spawning

    " + hsbpanel += "Regular Tools:
    " + for(var/T in GLOB.sandbox_hrefs) + hsbpanel += "- [GLOB.sandbox_hrefs[T]]
    " + if(hsboxspawn) + hsbpanel += "- Spawn Object

    " + usr << browse(hsbpanel, "window=hsbpanel") + Topic(href, href_list) + if(!(src.owner == usr.ckey)) return + if(!usr) return //I guess this is possible if they log out or die with the panel open? It happened. + if(href_list["hsb"]) + switch(href_list["hsb"]) + if("hsbtobj") + if(!admin) return + if(hsboxspawn) + to_chat(world, "Sandbox: [usr.key] has disabled object spawning!") + hsboxspawn = 0 + return + if(!hsboxspawn) + to_chat(world, "Sandbox: [usr.key] has enabled object spawning!") + hsboxspawn = 1 + return + if("hsbsuit") + var/mob/living/carbon/human/P = usr + if(P.wear_suit) + P.wear_suit.loc = P.loc + P.wear_suit.layer = initial(P.wear_suit.layer) + P.wear_suit.plane = initial(P.wear_suit.plane) + P.wear_suit = null + P.wear_suit = new/obj/item/clothing/suit/space(P) + P.wear_suit.layer = ABOVE_HUD_LAYER + P.wear_suit.plane = ABOVE_HUD_PLANE + if(P.head) + P.head.loc = P.loc + P.head.layer = initial(P.head.layer) + P.head.plane = initial(P.head.plane) + P.head = null + P.head = new/obj/item/clothing/head/helmet/space(P) + P.head.layer = ABOVE_HUD_LAYER + P.head.plane = ABOVE_HUD_PLANE + if(P.wear_mask) + P.wear_mask.loc = P.loc + P.wear_mask.layer = initial(P.wear_mask.layer) + P.wear_mask.plane = initial(P.wear_mask.plane) + P.wear_mask = null + P.wear_mask = new/obj/item/clothing/mask/gas(P) + P.wear_mask.layer = ABOVE_HUD_LAYER + P.wear_mask.plane = ABOVE_HUD_PLANE + if(P.back) + P.back.loc = P.loc + P.back.layer = initial(P.back.layer) + P.back.plane = initial(P.back.plane) + P.back = null + P.back = new/obj/item/tank/jetpack(P) + P.back.layer = ABOVE_HUD_LAYER + P.back.plane = ABOVE_HUD_PLANE + P.internal = P.back + if("hsbmetal") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/metal + hsb.amount = 50 + hsb.loc = usr.loc + if("hsbglass") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/glass + hsb.amount = 50 + hsb.loc = usr.loc + if("hsbairlock") + var/obj/machinery/door/hsb = new/obj/machinery/door/airlock + + //TODO: DEFERRED make this better, with an HTML window or something instead of 15 popups + hsb.req_access = list() + var/accesses = get_all_accesses() + for(var/A in accesses) + if(alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", "Yes", "No") == "Yes") + hsb.req_access += A + + hsb.loc = usr.loc + to_chat(usr, "Sandbox: Created an airlock.") + if("hsbcanister") + var/list/hsbcanisters = subtypesof(/obj/machinery/portable_atmospherics/canister/) + var/hsbcanister = input(usr, "Choose a canister to spawn.", "Sandbox:") in hsbcanisters + "Cancel" + if(!(hsbcanister == "Cancel")) + new hsbcanister(usr.loc) + if("hsbfueltank") + //var/obj/hsb = new/obj/weldfueltank + //hsb.loc = usr.loc + if("hsbwatertank") + //var/obj/hsb = new/obj/watertank + //hsb.loc = usr.loc + if("hsbtoolbox") + var/obj/item/storage/hsb = new/obj/item/storage/toolbox/mechanical + for(var/obj/item/radio/T in hsb) + qdel(T) + new/obj/item/crowbar (hsb) + hsb.loc = usr.loc + if("hsbmedkit") + var/obj/item/storage/firstaid/hsb = new/obj/item/storage/firstaid/regular + hsb.loc = usr.loc + if("hsbobj") + if(!hsboxspawn) return + + var/list/selectable = list() + for(var/O in typesof(/obj/item/)) + //Note, these istypes don't work + if(istype(O, /obj/item/gun)) + continue + if(istype(O, /obj/item/assembly)) + continue + if(istype(O, /obj/item/camera)) + continue + if(istype(O, /obj/item/cloaking_device)) + continue + if(istype(O, /obj/item/dummy)) + continue + if(istype(O, /obj/item/melee/energy/sword/saber)) + continue + if(istype(O, /obj/structure)) + continue + selectable += O + + var/hsbitem = input(usr, "Choose an object to spawn.", "Sandbox:") in selectable + "Cancel" + if(hsbitem != "Cancel") + new hsbitem(usr.loc) diff --git a/code/game/gamemodes/sandbox/sandbox.dm b/code/game/gamemodes/sandbox/sandbox.dm index 663759c961c..7b6978f4a1b 100644 --- a/code/game/gamemodes/sandbox/sandbox.dm +++ b/code/game/gamemodes/sandbox/sandbox.dm @@ -1,21 +1,21 @@ -/datum/game_mode/sandbox - name = "sandbox" - config_tag = "sandbox" - required_players = 0 - - uplink_welcome = "Syndicate Uplink Console:" - uplink_uses = 10 - -/datum/game_mode/sandbox/announce() - to_chat(world, "The current game mode is - Sandbox!") - to_chat(world, "Build your own station with the sandbox-panel command!") - -/datum/game_mode/sandbox/pre_setup() - for(var/mob/M in GLOB.player_list) - M.CanBuild() - return 1 - -/datum/game_mode/sandbox/post_setup() - ..() - if(emergency_shuttle) - emergency_shuttle.no_escape = 1 +/datum/game_mode/sandbox + name = "sandbox" + config_tag = "sandbox" + required_players = 0 + + uplink_welcome = "Syndicate Uplink Console:" + uplink_uses = 10 + +/datum/game_mode/sandbox/announce() + to_chat(world, "The current game mode is - Sandbox!") + to_chat(world, "Build your own station with the sandbox-panel command!") + +/datum/game_mode/sandbox/pre_setup() + for(var/mob/M in GLOB.player_list) + M.CanBuild() + return 1 + +/datum/game_mode/sandbox/post_setup() + ..() + if(emergency_shuttle) + emergency_shuttle.no_escape = 1 diff --git a/code/game/gamemodes/scoreboard.dm b/code/game/gamemodes/scoreboard.dm index 379b4fbdcd9..069b0915c00 100644 --- a/code/game/gamemodes/scoreboard.dm +++ b/code/game/gamemodes/scoreboard.dm @@ -22,12 +22,12 @@ // Who is alive/dead, who escaped for(var/mob/living/silicon/ai/I in GLOB.mob_list) if(I.stat == DEAD && is_station_level(I.z)) - score_deadaipenalty++ - score_deadcrew++ + GLOB.score_deadaipenalty++ + GLOB.score_deadcrew++ for(var/mob/living/carbon/human/I in GLOB.mob_list) if(I.stat == DEAD && is_station_level(I.z)) - score_deadcrew++ + GLOB.score_deadcrew++ if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) for(var/mob/living/player in GLOB.mob_list) @@ -36,7 +36,7 @@ var/turf/location = get_turf(player.loc) var/area/escape_zone = locate(/area/shuttle/escape) if(location in escape_zone) - score_escapees++ + GLOB.score_escapees++ @@ -50,21 +50,21 @@ var/turf/location = get_turf(E.loc) var/area/escape_zone = SSshuttle.emergency.areaInstance - if(E.stat != DEAD && location in escape_zone) // Escapee Scores + if(E.stat != DEAD && (location in escape_zone)) // Escapee Scores cash_score = get_score_container_worth(E) - if(cash_score > score_richestcash) - score_richestcash = cash_score - score_richestname = E.real_name - score_richestjob = E.job - score_richestkey = E.key + if(cash_score > GLOB.score_richestcash) + GLOB.score_richestcash = cash_score + GLOB.score_richestname = E.real_name + GLOB.score_richestjob = E.job + GLOB.score_richestkey = E.key dmg_score = E.getBruteLoss() + E.getFireLoss() + E.getToxLoss() + E.getOxyLoss() - if(dmg_score > score_dmgestdamage) - score_dmgestdamage = dmg_score - score_dmgestname = E.real_name - score_dmgestjob = E.job - score_dmgestkey = E.key + if(dmg_score > GLOB.score_dmgestdamage) + GLOB.score_dmgestdamage = dmg_score + GLOB.score_dmgestname = E.real_name + GLOB.score_dmgestjob = E.job + GLOB.score_dmgestkey = E.key if(SSticker && SSticker.mode) SSticker.mode.set_scoreboard_gvars() @@ -75,73 +75,73 @@ if(!is_station_level(A.z)) continue for(var/obj/item/stock_parts/cell/C in A.contents) if(C.charge < 2300) - score_powerloss++ //200 charge leeway + GLOB.score_powerloss++ //200 charge leeway // Check how much uncleaned mess is on the station for(var/obj/effect/decal/cleanable/M in world) if(!is_station_level(M.z)) continue if(istype(M, /obj/effect/decal/cleanable/blood/gibs)) - score_mess += 3 + GLOB.score_mess += 3 if(istype(M, /obj/effect/decal/cleanable/blood)) - score_mess += 1 + GLOB.score_mess += 1 if(istype(M, /obj/effect/decal/cleanable/vomit)) - score_mess += 1 + GLOB.score_mess += 1 // Bonus Modifiers //var/traitorwins = score_traitorswon - var/deathpoints = score_deadcrew * 25 //done - var/researchpoints = score_researchdone * 30 - var/eventpoints = score_eventsendured * 50 - var/escapoints = score_escapees * 25 //done - var/harvests = score_stuffharvested * 5 //done - var/shipping = score_stuffshipped * 5 - var/mining = score_oremined * 2 //done - var/meals = score_meals * 5 //done, but this only counts cooked meals, not drinks served - var/power = score_powerloss * 20 + var/deathpoints = GLOB.score_deadcrew * 25 //done + var/researchpoints = GLOB.score_researchdone * 30 + var/eventpoints = GLOB.score_eventsendured * 50 + var/escapoints = GLOB.score_escapees * 25 //done + var/harvests = GLOB.score_stuffharvested * 5 //done + var/shipping = GLOB.score_stuffshipped * 5 + var/mining = GLOB.score_oremined * 2 //done + var/meals = GLOB.score_meals * 5 //done, but this only counts cooked meals, not drinks served + var/power = GLOB.score_powerloss * 20 var/messpoints - if(score_mess != 0) - messpoints = score_mess //done - var/plaguepoints = score_disease * 30 + if(GLOB.score_mess != 0) + messpoints = GLOB.score_mess //done + var/plaguepoints = GLOB.score_disease * 30 // Good Things - score_crewscore += shipping - score_crewscore += harvests - score_crewscore += mining - score_crewscore += researchpoints - score_crewscore += eventpoints - score_crewscore += escapoints + GLOB.score_crewscore += shipping + GLOB.score_crewscore += harvests + GLOB.score_crewscore += mining + GLOB.score_crewscore += researchpoints + GLOB.score_crewscore += eventpoints + GLOB.score_crewscore += escapoints if(power == 0) - score_crewscore += 2500 - score_powerbonus = 1 + GLOB.score_crewscore += 2500 + GLOB.score_powerbonus = 1 - if(score_mess == 0) - score_crewscore += 3000 - score_messbonus = 1 + if(GLOB.score_mess == 0) + GLOB.score_crewscore += 3000 + GLOB.score_messbonus = 1 - score_crewscore += meals - if(score_allarrested) - score_crewscore *= 3 // This needs to be here for the bonus to be applied properly + GLOB.score_crewscore += meals + if(GLOB.score_allarrested) + GLOB.score_crewscore *= 3 // This needs to be here for the bonus to be applied properly - score_crewscore -= deathpoints - if(score_deadaipenalty) - score_crewscore -= 250 - score_crewscore -= power + GLOB.score_crewscore -= deathpoints + if(GLOB.score_deadaipenalty) + GLOB.score_crewscore -= 250 + GLOB.score_crewscore -= power - score_crewscore -= messpoints - score_crewscore -= plaguepoints + GLOB.score_crewscore -= messpoints + GLOB.score_crewscore -= plaguepoints // Show the score - might add "ranks" later to_chat(world, "The crew's final score is:") - to_chat(world, "[score_crewscore]") + to_chat(world, "[GLOB.score_crewscore]") for(var/mob/E in GLOB.player_list) if(E.client && !E.get_preference(DISABLE_SCOREBOARD)) E.scorestats() @@ -178,30 +178,30 @@ General Statistics
    The Good:
    - Useful Items Shipped: [score_stuffshipped] ([score_stuffshipped * 5] Points)
    - Hydroponics Harvests: [score_stuffharvested] ([score_stuffharvested * 5] Points)
    - Ore Mined: [score_oremined] ([score_oremined * 2] Points)
    - Refreshments Prepared: [score_meals] ([score_meals * 5] Points)
    - Research Completed: [score_researchdone] ([score_researchdone * 30] Points)
    "} - if(SSshuttle.emergency.mode == SHUTTLE_ENDGAME) dat += "Shuttle Escapees: [score_escapees] ([score_escapees * 25] Points)
    " - dat += {"Random Events Endured: [score_eventsendured] ([score_eventsendured * 50] Points)
    - Whole Station Powered: [score_powerbonus ? "Yes" : "No"] ([score_powerbonus * 2500] Points)
    - Ultra-Clean Station: [score_mess ? "No" : "Yes"] ([score_messbonus * 3000] Points)

    + Useful Items Shipped: [GLOB.score_stuffshipped] ([GLOB.score_stuffshipped * 5] Points)
    + Hydroponics Harvests: [GLOB.score_stuffharvested] ([GLOB.score_stuffharvested * 5] Points)
    + Ore Mined: [GLOB.score_oremined] ([GLOB.score_oremined * 2] Points)
    + Refreshments Prepared: [GLOB.score_meals] ([GLOB.score_meals * 5] Points)
    + Research Completed: [GLOB.score_researchdone] ([GLOB.score_researchdone * 30] Points)
    "} + if(SSshuttle.emergency.mode == SHUTTLE_ENDGAME) dat += "Shuttle Escapees: [GLOB.score_escapees] ([GLOB.score_escapees * 25] Points)
    " + dat += {"Random Events Endured: [GLOB.score_eventsendured] ([GLOB.score_eventsendured * 50] Points)
    + Whole Station Powered: [GLOB.score_powerbonus ? "Yes" : "No"] ([GLOB.score_powerbonus * 2500] Points)
    + Ultra-Clean Station: [GLOB.score_mess ? "No" : "Yes"] ([GLOB.score_messbonus * 3000] Points)

    The bad:
    - Dead bodies on Station: [score_deadcrew] (-[score_deadcrew * 25] Points)
    - Uncleaned Messes: [score_mess] (-[score_mess] Points)
    - Station Power Issues: [score_powerloss] (-[score_powerloss * 20] Points)
    - Rampant Diseases: [score_disease] (-[score_disease * 30] Points)
    - AI Destroyed: [score_deadaipenalty ? "Yes" : "No"] (-[score_deadaipenalty * 250] Points)

    + Dead bodies on Station: [GLOB.score_deadcrew] (-[GLOB.score_deadcrew * 25] Points)
    + Uncleaned Messes: [GLOB.score_mess] (-[GLOB.score_mess] Points)
    + Station Power Issues: [GLOB.score_powerloss] (-[GLOB.score_powerloss * 20] Points)
    + Rampant Diseases: [GLOB.score_disease] (-[GLOB.score_disease * 30] Points)
    + AI Destroyed: [GLOB.score_deadaipenalty ? "Yes" : "No"] (-[GLOB.score_deadaipenalty * 250] Points)

    The Weird
    - Food Eaten: [score_foodeaten] bites/sips
    - Times a Clown was Abused: [score_clownabuse]

    + Food Eaten: [GLOB.score_foodeaten] bites/sips
    + Times a Clown was Abused: [GLOB.score_clownabuse]

    "} - if(score_escapees) - dat += {"Richest Escapee: [score_richestname], [score_richestjob]: $[num2text(score_richestcash,50)] ([score_richestkey])
    - Most Battered Escapee: [score_dmgestname], [score_dmgestjob]: [score_dmgestdamage] damage ([score_dmgestkey])
    "} + if(GLOB.score_escapees) + dat += {"Richest Escapee: [GLOB.score_richestname], [GLOB.score_richestjob]: $[num2text(GLOB.score_richestcash,50)] ([GLOB.score_richestkey])
    + Most Battered Escapee: [GLOB.score_dmgestname], [GLOB.score_dmgestjob]: [GLOB.score_dmgestdamage] damage ([GLOB.score_dmgestkey])
    "} else if(SSshuttle.emergency.mode <= SHUTTLE_STRANDED) dat += "The station wasn't evacuated!
    " @@ -212,11 +212,11 @@ dat += {"

    - FINAL SCORE: [score_crewscore]
    + FINAL SCORE: [GLOB.score_crewscore]
    "} var/score_rating = "The Aristocrats!" - switch(score_crewscore) + switch(GLOB.score_crewscore) if(-99999 to -50000) score_rating = "Even the Singularity Deserves Better" if(-49999 to -5000) score_rating = "Singularity Fodder" if(-4999 to -1000) score_rating = "You're All Fired" diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm index 78e6cea4987..da171113dd0 100644 --- a/code/game/gamemodes/setupgame.dm +++ b/code/game/gamemodes/setupgame.dm @@ -1,164 +1,164 @@ -/proc/getAssignedBlock(var/name,var/list/blocksLeft, var/activity_bounds=DNA_DEFAULT_BOUNDS, var/good=0) - if(blocksLeft.len==0) - warning("[name]: No more blocks left to assign!") - return 0 - var/assigned = pick(blocksLeft) - blocksLeft.Remove(assigned) - if(good) - good_blocks += assigned - else - bad_blocks += assigned - assigned_blocks[assigned]=name - dna_activity_bounds[assigned]=activity_bounds - //Debug message_admins("[name] assigned to block #[assigned].") -// testing("[name] assigned to block #[assigned].") - return assigned - -/proc/setupgenetics() - - if(prob(50)) - BLOCKADD = rand(-300,300) - if(prob(75)) - DIFFMUT = rand(0,20) - - -//Thanks to nexis for the fancy code -// BITCH I AIN'T DONE YET - - // SE blocks to assign. - var/list/numsToAssign=new() - for(var/i=1;iBecame a thrall") + new_thrall_mind.current.create_log(CONVERSION_LOG, "Became a thrall") new_thrall_mind.current.add_language("Shadowling Hivemind") new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/lesser_shadow_walk(null)) new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_vision/thrall(null)) @@ -171,7 +172,7 @@ Made by Xhuis replace_jobbanned_player(new_thrall_mind.current, ROLE_SHADOWLING) if(!victory_warning_announced && (length(shadowling_thralls) >= warning_threshold))//are the slings very close to winning? victory_warning_announced = TRUE //then let's give the station a warning - command_announcement.Announce("Large concentration of psychic bluespace energy detected by long-ranged scanners. Shadowling ascension event imminent. Prevent it at all costs!", "Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') + GLOB.command_announcement.Announce("Large concentration of psychic bluespace energy detected by long-ranged scanners. Shadowling ascension event imminent. Prevent it at all costs!", "Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') return 1 /datum/game_mode/proc/remove_thrall(datum/mind/thrall_mind, var/kill = 0) @@ -179,6 +180,7 @@ Made by Xhuis return 0 //If there is no mind, the mind isn't a thrall, or the mind's mob isn't alive, return shadowling_thralls.Remove(thrall_mind) thrall_mind.current.create_attack_log("Dethralled") + thrall_mind.current.create_log(CONVERSION_LOG, "Dethralled") thrall_mind.special_role = null update_shadow_icons_removed(thrall_mind) for(var/obj/effect/proc_holder/spell/S in thrall_mind.spell_list) @@ -236,6 +238,7 @@ Made by Xhuis update_shadow_icons_removed(ling_mind) shadows.Remove(ling_mind) ling_mind.current.create_attack_log("Deshadowlinged") + ling_mind.current.create_log(CONVERSION_LOG, "Deshadowlinged") ling_mind.special_role = null for(var/obj/effect/proc_holder/spell/S in ling_mind.spell_list) ling_mind.RemoveSpell(S) @@ -322,12 +325,12 @@ Made by Xhuis */ /datum/game_mode/proc/update_shadow_icons_added(datum/mind/shadow_mind) - var/datum/atom_hud/antag/shadow_hud = huds[ANTAG_HUD_SHADOW] + var/datum/atom_hud/antag/shadow_hud = GLOB.huds[ANTAG_HUD_SHADOW] shadow_hud.join_hud(shadow_mind.current) set_antag_hud(shadow_mind.current, ((shadow_mind in shadows) ? "hudshadowling" : "hudshadowlingthrall")) /datum/game_mode/proc/update_shadow_icons_removed(datum/mind/shadow_mind) //This should never actually occur, but it's here anyway. - var/datum/atom_hud/antag/shadow_hud = huds[ANTAG_HUD_SHADOW] + var/datum/atom_hud/antag/shadow_hud = GLOB.huds[ANTAG_HUD_SHADOW] shadow_hud.leave_hud(shadow_mind.current) set_antag_hud(shadow_mind.current, null) diff --git a/code/game/gamemodes/shadowling/shadowling_abilities.dm b/code/game/gamemodes/shadowling/shadowling_abilities.dm index 1671c4a03b0..37f7296bd4e 100644 --- a/code/game/gamemodes/shadowling/shadowling_abilities.dm +++ b/code/game/gamemodes/shadowling/shadowling_abilities.dm @@ -716,7 +716,7 @@ if(SSshuttle.emergency.mode == SHUTTLE_CALL) var/more_minutes = 6000 var/timer = SSshuttle.emergency.timeLeft(1) + more_minutes - event_announcement.Announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 10 minutes and the shuttle is unable to be recalled.", "System Failure", 'sound/misc/notice1.ogg') + GLOB.event_announcement.Announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 10 minutes and the shuttle is unable to be recalled.", "System Failure", 'sound/misc/notice1.ogg') SSshuttle.emergency.setTimer(timer) SSshuttle.emergency.canRecall = FALSE user.mind.spell_list.Remove(src) //Can only be used once! diff --git a/code/game/gamemodes/shadowling/shadowling_items.dm b/code/game/gamemodes/shadowling/shadowling_items.dm index 431d986bfcd..8a86715a907 100644 --- a/code/game/gamemodes/shadowling/shadowling_items.dm +++ b/code/game/gamemodes/shadowling/shadowling_items.dm @@ -82,4 +82,4 @@ flash_protect = -1 vision_flags = SEE_MOBS lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - flags = ABSTRACT | NODROP \ No newline at end of file + flags = ABSTRACT | NODROP diff --git a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm index 514a16174b1..7896a4aab61 100644 --- a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm +++ b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm @@ -1,5 +1,5 @@ //In here: Hatch and Ascendance -var/list/possibleShadowlingNames = list("U'ruan", "Y`shej", "Nex", "Hel-uae", "Noaey'gief", "Mii`mahza", "Amerziox", "Gyrg-mylin", "Kanet'pruunance", "Vigistaezian") //Unpronouncable 2: electric boogalo) +GLOBAL_LIST_INIT(possibleShadowlingNames, list("U'ruan", "Y`shej", "Nex", "Hel-uae", "Noaey'gief", "Mii`mahza", "Amerziox", "Gyrg-mylin", "Kanet'pruunance", "Vigistaezian")) //Unpronouncable 2: electric boogalo) /obj/effect/proc_holder/spell/targeted/shadowling_hatch name = "Hatch" desc = "Casts off your disguise." @@ -66,8 +66,8 @@ var/list/possibleShadowlingNames = list("U'ruan", "Y`shej", "Nex", "Hel-uae", "N H.status_flags = temp_flags sleep(10) playsound(H.loc, 'sound/effects/ghost.ogg', 100, 1) - var/newNameId = pick(possibleShadowlingNames) - possibleShadowlingNames.Remove(newNameId) + var/newNameId = pick(GLOB.possibleShadowlingNames) + GLOB.possibleShadowlingNames.Remove(newNameId) H.real_name = newNameId H.name = user.real_name H.SetStunned(0) diff --git a/code/game/gamemodes/steal_items.dm b/code/game/gamemodes/steal_items.dm index de9f8fc0eb7..78a05f904a8 100644 --- a/code/game/gamemodes/steal_items.dm +++ b/code/game/gamemodes/steal_items.dm @@ -76,12 +76,12 @@ datum/theft_objective/ai/check_special_completion(var/obj/item/aicard/C) /datum/theft_objective/blueprints name = "the station blueprints" - typepath = /obj/item/areaeditor/blueprints + typepath = /obj/item/areaeditor/blueprints/ce protected_jobs = list("Chief Engineer") altitems = list(/obj/item/photo) /datum/objective_item/steal/blueprints/check_special_completion(obj/item/I) - if(istype(I, /obj/item/areaeditor/blueprints)) + if(istype(I, /obj/item/areaeditor/blueprints/ce)) return 1 if(istype(I, /obj/item/photo)) var/obj/item/photo/P = I diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index ae131286afd..c7ab4490f48 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -1,147 +1,147 @@ -/datum/game_mode - // this includes admin-appointed traitors and multitraitors. Easy! - var/list/datum/mind/traitors = list() - var/list/datum/mind/implanter = list() - var/list/datum/mind/implanted = list() - - var/datum/mind/exchange_red - var/datum/mind/exchange_blue - -/datum/game_mode/traitor - name = "traitor" - config_tag = "traitor" - restricted_jobs = list("Cyborg")//They are part of the AI if he is traitor so are they, they use to get double chances - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Internal Affairs Agent", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") - required_players = 0 - required_enemies = 1 - recommended_enemies = 4 - - var/list/datum/mind/pre_traitors = list() - var/traitors_possible = 4 //hard limit on traitors if scaling is turned off - var/const/traitor_scaling_coeff = 5.0 //how much does the amount of players get divided by to determine traitors - var/antag_datum = /datum/antagonist/traitor //what type of antag to create - -/datum/game_mode/traitor/announce() - to_chat(world, "The current game mode is - Traitor!") - to_chat(world, "There is a syndicate traitor on the station. Do not let the traitor succeed!") - - -/datum/game_mode/traitor/pre_setup() - - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - var/list/possible_traitors = get_players_for_role(ROLE_TRAITOR) - - // stop setup if no possible traitors - if(!possible_traitors.len) - return 0 - - var/num_traitors = 1 - - if(config.traitor_scaling) - num_traitors = max(1, round((num_players())/(traitor_scaling_coeff))) - else - num_traitors = max(1, min(num_players(), traitors_possible)) - - for(var/j = 0, j < num_traitors, j++) - if(!possible_traitors.len) - break - var/datum/mind/traitor = pick(possible_traitors) - pre_traitors += traitor - traitor.special_role = SPECIAL_ROLE_TRAITOR - traitor.restricted_roles = restricted_jobs - possible_traitors.Remove(traitor) - - if(!pre_traitors.len) - return 0 - return 1 - - -/datum/game_mode/traitor/post_setup() - for(var/datum/mind/traitor in pre_traitors) - var/datum/antagonist/traitor/new_antag = new antag_datum() - addtimer(CALLBACK(traitor, /datum/mind.proc/add_antag_datum, new_antag), rand(10,100)) - if(!exchange_blue) - exchange_blue = -1 //Block latejoiners from getting exchange objectives - ..() - - -/datum/game_mode/traitor/declare_completion() - ..() - return//Traitors will be checked as part of check_extra_completion. Leaving this here as a reminder. - -/datum/game_mode/traitor/process() - // Make sure all objectives are processed regularly, so that objectives - // which can be checked mid-round are checked mid-round. - for(var/datum/mind/traitor_mind in traitors) - for(var/datum/objective/objective in traitor_mind.objectives) - objective.check_completion() - return 0 - - -/datum/game_mode/proc/auto_declare_completion_traitor() - if(traitors.len) - var/text = "The traitors were:" - for(var/datum/mind/traitor in traitors) - var/traitorwin = 1 - - text += "
    [traitor.key] was [traitor.name] (" - if(traitor.current) - if(traitor.current.stat == DEAD) - text += "died" - else - text += "survived" - if(traitor.current.real_name != traitor.name) - text += " as [traitor.current.real_name]" - else - text += "body destroyed" - text += ")" - - - var/TC_uses = 0 - var/uplink_true = 0 - var/purchases = "" - for(var/obj/item/uplink/H in world_uplinks) - if(H && H.uplink_owner && H.uplink_owner==traitor.key) - TC_uses += H.used_TC - uplink_true=1 - purchases += H.purchase_log - - if(uplink_true) text += " (used [TC_uses] TC) [purchases]" - - - if(traitor.objectives && traitor.objectives.len)//If the traitor had no objectives, don't need to process this. - var/count = 1 - for(var/datum/objective/objective in traitor.objectives) - if(objective.check_completion()) - text += "
    Objective #[count]: [objective.explanation_text] Success!" - feedback_add_details("traitor_objective","[objective.type]|SUCCESS") - else - text += "
    Objective #[count]: [objective.explanation_text] Fail." - feedback_add_details("traitor_objective","[objective.type]|FAIL") - traitorwin = 0 - count++ - - var/special_role_text - if(traitor.special_role) - special_role_text = lowertext(traitor.special_role) - else - special_role_text = "antagonist" - - - if(traitorwin) - text += "
    The [special_role_text] was successful!" - feedback_add_details("traitor_success","SUCCESS") - else - text += "
    The [special_role_text] has failed!" - feedback_add_details("traitor_success","FAIL") - - var/phrases = jointext(GLOB.syndicate_code_phrase, ", ") - var/responses = jointext(GLOB.syndicate_code_response, ", ") - - text += "

    The code phrases were: [phrases]
    \ - The code responses were: [responses]

    " - - to_chat(world, text) - return 1 +/datum/game_mode + // this includes admin-appointed traitors and multitraitors. Easy! + var/list/datum/mind/traitors = list() + var/list/datum/mind/implanter = list() + var/list/datum/mind/implanted = list() + + var/datum/mind/exchange_red + var/datum/mind/exchange_blue + +/datum/game_mode/traitor + name = "traitor" + config_tag = "traitor" + restricted_jobs = list("Cyborg")//They are part of the AI if he is traitor so are they, they use to get double chances + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Internal Affairs Agent", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + required_players = 0 + required_enemies = 1 + recommended_enemies = 4 + + var/list/datum/mind/pre_traitors = list() + var/traitors_possible = 4 //hard limit on traitors if scaling is turned off + var/const/traitor_scaling_coeff = 5.0 //how much does the amount of players get divided by to determine traitors + var/antag_datum = /datum/antagonist/traitor //what type of antag to create + +/datum/game_mode/traitor/announce() + to_chat(world, "The current game mode is - Traitor!") + to_chat(world, "There is a syndicate traitor on the station. Do not let the traitor succeed!") + + +/datum/game_mode/traitor/pre_setup() + + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + var/list/possible_traitors = get_players_for_role(ROLE_TRAITOR) + + // stop setup if no possible traitors + if(!possible_traitors.len) + return 0 + + var/num_traitors = 1 + + if(config.traitor_scaling) + num_traitors = max(1, round((num_players())/(traitor_scaling_coeff))) + else + num_traitors = max(1, min(num_players(), traitors_possible)) + + for(var/j = 0, j < num_traitors, j++) + if(!possible_traitors.len) + break + var/datum/mind/traitor = pick(possible_traitors) + pre_traitors += traitor + traitor.special_role = SPECIAL_ROLE_TRAITOR + traitor.restricted_roles = restricted_jobs + possible_traitors.Remove(traitor) + + if(!pre_traitors.len) + return 0 + return 1 + + +/datum/game_mode/traitor/post_setup() + for(var/datum/mind/traitor in pre_traitors) + var/datum/antagonist/traitor/new_antag = new antag_datum() + addtimer(CALLBACK(traitor, /datum/mind.proc/add_antag_datum, new_antag), rand(10,100)) + if(!exchange_blue) + exchange_blue = -1 //Block latejoiners from getting exchange objectives + ..() + + +/datum/game_mode/traitor/declare_completion() + ..() + return//Traitors will be checked as part of check_extra_completion. Leaving this here as a reminder. + +/datum/game_mode/traitor/process() + // Make sure all objectives are processed regularly, so that objectives + // which can be checked mid-round are checked mid-round. + for(var/datum/mind/traitor_mind in traitors) + for(var/datum/objective/objective in traitor_mind.objectives) + objective.check_completion() + return 0 + + +/datum/game_mode/proc/auto_declare_completion_traitor() + if(traitors.len) + var/text = "The traitors were:" + for(var/datum/mind/traitor in traitors) + var/traitorwin = 1 + + text += "
    [traitor.key] was [traitor.name] (" + if(traitor.current) + if(traitor.current.stat == DEAD) + text += "died" + else + text += "survived" + if(traitor.current.real_name != traitor.name) + text += " as [traitor.current.real_name]" + else + text += "body destroyed" + text += ")" + + + var/TC_uses = 0 + var/uplink_true = 0 + var/purchases = "" + for(var/obj/item/uplink/H in GLOB.world_uplinks) + if(H && H.uplink_owner && H.uplink_owner==traitor.key) + TC_uses += H.used_TC + uplink_true=1 + purchases += H.purchase_log + + if(uplink_true) text += " (used [TC_uses] TC) [purchases]" + + + if(traitor.objectives && traitor.objectives.len)//If the traitor had no objectives, don't need to process this. + var/count = 1 + for(var/datum/objective/objective in traitor.objectives) + if(objective.check_completion()) + text += "
    Objective #[count]: [objective.explanation_text] Success!" + feedback_add_details("traitor_objective","[objective.type]|SUCCESS") + else + text += "
    Objective #[count]: [objective.explanation_text] Fail." + feedback_add_details("traitor_objective","[objective.type]|FAIL") + traitorwin = 0 + count++ + + var/special_role_text + if(traitor.special_role) + special_role_text = lowertext(traitor.special_role) + else + special_role_text = "antagonist" + + + if(traitorwin) + text += "
    The [special_role_text] was successful!" + feedback_add_details("traitor_success","SUCCESS") + else + text += "
    The [special_role_text] has failed!" + feedback_add_details("traitor_success","FAIL") + + var/phrases = jointext(GLOB.syndicate_code_phrase, ", ") + var/responses = jointext(GLOB.syndicate_code_response, ", ") + + text += "

    The code phrases were: [phrases]
    \ + The code responses were: [responses]

    " + + to_chat(world, text) + return 1 diff --git a/code/game/gamemodes/vampire/traitor_vamp.dm b/code/game/gamemodes/vampire/traitor_vamp.dm index 406275c709f..d69b9a23518 100644 --- a/code/game/gamemodes/vampire/traitor_vamp.dm +++ b/code/game/gamemodes/vampire/traitor_vamp.dm @@ -32,6 +32,7 @@ slaved.masters += vampire vampire.som = slaved //we MIGT want to mindslave someone vampire.restricted_roles = restricted_jobs + vampire.special_role = SPECIAL_ROLE_VAMPIRE ..() return 1 else @@ -44,4 +45,4 @@ forge_vampire_objectives(vampire) greet_vampire(vampire) update_vampire_icons_added(vampire) - ..() \ No newline at end of file + ..() diff --git a/code/game/gamemodes/vampire/vampire.dm b/code/game/gamemodes/vampire/vampire.dm index ac5ddc92f98..6c22313ef6e 100644 --- a/code/game/gamemodes/vampire/vampire.dm +++ b/code/game/gamemodes/vampire/vampire.dm @@ -348,6 +348,7 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha SSticker.mode.vampires -= vampire_mind vampire_mind.special_role = null vampire_mind.current.create_attack_log("De-vampired") + vampire_mind.current.create_log(CONVERSION_LOG, "De-vampired") if(vampire_mind.vampire) vampire_mind.vampire.remove_vampire_powers() QDEL_NULL(vampire_mind.vampire) @@ -359,12 +360,12 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha //prepare for copypaste /datum/game_mode/proc/update_vampire_icons_added(datum/mind/vampire_mind) - var/datum/atom_hud/antag/vamp_hud = huds[ANTAG_HUD_VAMPIRE] + var/datum/atom_hud/antag/vamp_hud = GLOB.huds[ANTAG_HUD_VAMPIRE] vamp_hud.join_hud(vampire_mind.current) set_antag_hud(vampire_mind.current, ((vampire_mind in vampires) ? "hudvampire" : "hudvampirethrall")) /datum/game_mode/proc/update_vampire_icons_removed(datum/mind/vampire_mind) - var/datum/atom_hud/antag/vampire_hud = huds[ANTAG_HUD_VAMPIRE] + var/datum/atom_hud/antag/vampire_hud = GLOB.huds[ANTAG_HUD_VAMPIRE] vampire_hud.leave_hud(vampire_mind.current) set_antag_hud(vampire_mind.current, null) diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm index 6c9674d130e..032bab0b6e8 100644 --- a/code/game/gamemodes/vampire/vampire_powers.dm +++ b/code/game/gamemodes/vampire/vampire_powers.dm @@ -397,7 +397,7 @@ /obj/effect/proc_holder/spell/vampire/bats/choose_targets(mob/user = usr) var/list/turf/locs = new - for(var/direction in alldirs) //looking for bat spawns + for(var/direction in GLOB.alldirs) //looking for bat spawns if(locs.len == num_bats) //we found 2 locations and thats all we need break var/turf/T = get_step(usr, direction) //getting a loc in that direction @@ -606,4 +606,4 @@ add_attack_logs(M, src, "Vampire-sired") mind.make_Vampire() revive() - Weaken(20) \ No newline at end of file + Weaken(20) diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm index 187ac0f0ea1..da7c6d4e1de 100644 --- a/code/game/gamemodes/wizard/artefact.dm +++ b/code/game/gamemodes/wizard/artefact.dm @@ -99,7 +99,7 @@ new_objective:target = H:mind new_objective.explanation_text = "Protect [H.real_name], the wizard." M.mind.objectives += new_objective - SSticker.mode.traitors += M.mind + SSticker.mode.apprentices += M.mind M.mind.special_role = SPECIAL_ROLE_WIZARD_APPRENTICE SSticker.mode.update_wiz_icons_added(M.mind) M.faction = list("wizard") @@ -226,7 +226,7 @@ user.ghostize(1) /////////////////////Multiverse Blade//////////////////// -var/global/list/multiverse = list() +GLOBAL_LIST_EMPTY(multiverse) /obj/item/multisword name = "multiverse sword" @@ -252,11 +252,11 @@ var/global/list/multiverse = list() /obj/item/multisword/New() ..() - multiverse |= src + GLOB.multiverse |= src /obj/item/multisword/Destroy() - multiverse.Remove(src) + GLOB.multiverse.Remove(src) return ..() /obj/item/multisword/attack(mob/living/M as mob, mob/living/user as mob) //to prevent accidental friendly fire or out and out grief. @@ -303,7 +303,7 @@ var/global/list/multiverse = list() evil = FALSE else cooldown = world.time + cooldown_between_uses - for(var/obj/item/multisword/M in multiverse) + for(var/obj/item/multisword/M in GLOB.multiverse) if(M.assigned == assigned) M.cooldown = cooldown @@ -582,10 +582,10 @@ var/global/list/multiverse = list() W.access = duplicated_id.access W.icon_state = duplicated_id.icon_state else - W.access += access_maint_tunnels + W.access += ACCESS_MAINT_TUNNELS W.icon_state = "centcom" else - W.access += access_maint_tunnels + W.access += ACCESS_MAINT_TUNNELS W.icon_state = "centcom" W.assignment = "Multiverse Traveller" W.registered_name = M.real_name @@ -842,7 +842,7 @@ var/global/list/multiverse = list() user.unset_machine() if("r_leg","l_leg") to_chat(user, "You move the doll's legs around.") - var/turf/T = get_step(target,pick(cardinal)) + var/turf/T = get_step(target,pick(GLOB.cardinal)) target.Move(T) if("r_arm","l_arm") //use active hand on random nearby mob diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm index 57a4f4e17de..cc52b431249 100644 --- a/code/game/gamemodes/wizard/raginmages.dm +++ b/code/game/gamemodes/wizard/raginmages.dm @@ -145,7 +145,7 @@ /datum/game_mode/wizard/raginmages/proc/makeBody(var/mob/dead/observer/G) if(!G || !G.key) return // Let's not steal someone's soul here - var/mob/living/carbon/human/new_character = new(pick(latejoin)) + var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin)) G.client.prefs.copy_to(new_character) new_character.key = G.key return new_character diff --git a/code/game/gamemodes/wizard/rightandwrong.dm b/code/game/gamemodes/wizard/rightandwrong.dm index 5f2dc094724..82f6d833d16 100644 --- a/code/game/gamemodes/wizard/rightandwrong.dm +++ b/code/game/gamemodes/wizard/rightandwrong.dm @@ -109,6 +109,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) H.mind.add_antag_datum(/datum/antagonist/survivalist/guns) H.create_attack_log("was made into a survivalist, and trusts no one!") + H.create_log(CONVERSION_LOG, "was made into a survivalist") var/gun_type = pick(GLOB.summoned_guns) var/obj/item/gun/G = new gun_type(get_turf(H)) @@ -130,6 +131,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) H.mind.add_antag_datum(/datum/antagonist/survivalist/magic) H.create_attack_log("was made into a survivalist, and trusts no one!") + H.create_log(CONVERSION_LOG, "was made into a survivalist") var/magic_type = pick(GLOB.summoned_magic) var/lucky = FALSE @@ -166,4 +168,4 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) if(summon_type == SUMMON_MAGIC) give_magic(H) else - give_guns(H) \ No newline at end of file + give_guns(H) diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index d97ec2af5fd..4bda254d930 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -1,381 +1,378 @@ -/obj/item/soulstone - name = "Soul Stone Shard" - icon = 'icons/obj/wizard.dmi' - icon_state = "soulstone" - item_state = "electronic" - desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artifact's power." - w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_BELT - origin_tech = "bluespace=4;materials=5" - - var/optional = FALSE //does this soulstone ask the victim whether they want to be turned into a shade - var/usability = FALSE // Can this soul stone be used by anyone, or only cultists/wizards? - var/reusable = TRUE // Can this soul stone be used more than once? - var/spent = FALSE // If the soul stone can only be used once, has it been used? - - var/opt_in = FALSE // for tracking during the 'optional' bit - -/obj/item/soulstone/proc/can_use(mob/living/user) - if(iscultist(user) || iswizard(user) || usability) - return TRUE - - return FALSE - -/obj/item/soulstone/proc/was_used() - if(!reusable) - spent = TRUE - name = "dull [name]" - desc = "A fragment of the legendary treasure known simply as \ - the 'Soul Stone'. The shard lies still, dull and lifeless; \ - whatever spark it once held long extinguished." - -/obj/item/soulstone/anybody - usability = TRUE - -/obj/item/soulstone/anybody/chaplain - name = "mysterious old shard" - reusable = FALSE - optional = TRUE - -/obj/item/soulstone/pickup(mob/living/user) - . = ..() - if(!can_use(user)) - to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly.") - user.Dizzy(120) - -//////////////////////////////Capturing//////////////////////////////////////////////////////// -/obj/item/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob) - if(!can_use(user)) - user.Paralyse(5) - to_chat(user, "Your body is wracked with debilitating pain!") - return - - if(spent) - to_chat(user, "There is no power left in the shard.") - return - - if(!ishuman(M)) //If target is not a human - return ..() - - if(M.has_brain_worms()) //Borer stuff - RR - to_chat(user, "This being is corrupted by an alien intelligence and cannot be soul trapped.") - return ..() - - if(jobban_isbanned(M, "cultist") || jobban_isbanned(M, "Syndicate")) - to_chat(user, "A mysterious force prevents you from trapping this being's soul.") - return ..() - - if(iscultist(user) && iscultist(M)) - to_chat(user, "\"Come now, do not capture your fellow's soul.\"") - return ..() - - M.create_attack_log("Has had their soul captured with [src.name] by [key_name(user)]") - user.create_attack_log("Used the [src.name] to capture the soul of [key_name(M)]") - - if(optional) - if(!M.ckey) - to_chat(user, "They have no soul!") - return - - to_chat(user, "You attempt to channel [M]'s soul into [src]. You must give the soul some time to react and stand still...") - - var/mob/player_mob = M - if(M.get_ghost())//in case our player ghosted and we need to throw the alert at their ghost instead - player_mob = M.get_ghost() - var/client/player_client = player_mob.client - to_chat(player_mob, "[user] is trying to capture your soul into [src]! Click the button in the top right of the game window to respond.") - player_client << 'sound/misc/notice2.ogg' - window_flash(player_client) - - var/obj/screen/alert/notify_soulstone/A = player_mob.throw_alert("\ref[src]_soulstone_thingy", /obj/screen/alert/notify_soulstone) - if(player_client.prefs && player_client.prefs.UI_style) - A.icon = ui_style2icon(player_client.prefs.UI_style) - - //pass the stuff to the alert itself - A.stone = src - A.stoner = user.real_name - - //layer shenanigans to make the alert display the soulstone - var/old_layer = layer - var/old_plane = plane - layer = FLOAT_LAYER - plane = FLOAT_PLANE - A.overlays += src - layer = old_layer - plane = old_plane - - //give the victim 10 seconds to respond - sleep(10 SECONDS) - - if(!opt_in) - to_chat(user, "The soul resists your attempts at capturing it!") - return - - opt_in = FALSE - - if(spent)//checking one more time against shenanigans - return - - add_attack_logs(user, M, "Stolestone'd with [name]") - transfer_soul("VICTIM", M, user) - return - -///////////////////Options for using captured souls/////////////////////////////////////// -/obj/item/soulstone/attack_self(mob/user) - if(!in_range(src, user)) - return - - if(!can_use(user)) - user.Paralyse(5) - to_chat(user, "Your body is wracked with debilitating pain!") - return - - user.set_machine(src) - var/dat = "Soul Stone
    " - for(var/mob/living/simple_animal/shade/A in src) - dat += "Captured Soul: [A.name]
    " - dat += {"Summon Shade"} - dat += "
    " - dat += {" Close"} - user << browse(dat, "window=aicard") - onclose(user, "aicard") - return - -/obj/item/soulstone/Topic(href, href_list) - var/mob/U = usr - if(!in_range(src, U) || U.machine != src || !can_use(usr)) - U << browse(null, "window=aicard") - U.unset_machine() - return - - add_fingerprint(U) - U.set_machine(src) - - switch(href_list["choice"])//Now we switch based on choice. - if("Close") - U << browse(null, "window=aicard") - U.unset_machine() - return - - if("Summon") - for(var/mob/living/simple_animal/shade/A in src) - A.status_flags &= ~GODMODE - A.canmove = 1 - A.forceMove(get_turf(usr)) - A.cancel_camera() - icon_state = "soulstone" - name = initial(name) - if(iswizard(usr) || usability) - to_chat(A, "You have been released from your prison, but you are still bound to [usr.real_name]'s will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.") - else if(iscultist(usr)) - to_chat(A, "You have been released from your prison, but you are still bound to the cult's will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.") - was_used() - attack_self(U) - -///////////////////////////Transferring to constructs///////////////////////////////////////////////////// -/obj/structure/constructshell - name = "empty shell" - icon = 'icons/obj/wizard.dmi' - icon_state = "construct-cult" - desc = "A wicked machine used by those skilled in magical arts. It is inactive" - -/obj/structure/constructshell/examine(mob/user) - . = ..() - if(in_range(user, src) && (iscultist(user) || iswizard(user) || user.stat == DEAD)) - . += "A construct shell, used to house bound souls from a soulstone." - . += "Placing a soulstone with a soul into this shell allows you to produce your choice of the following:" - . += "An Artificer, which can produce more shells and soulstones, as well as fortifications." - . += "A Wraith, which does high damage and can jaunt through walls, though it is quite fragile." - . += "A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow." - -/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob, params) - if(istype(O, /obj/item/soulstone)) - var/obj/item/soulstone/SS = O - if(!SS.can_use(user)) - to_chat(user, "An overwhelming feeling of dread comes over you as you attempt to place the soulstone into the shell. It would be wise to be rid of this quickly.") - user.Dizzy(120) - return - SS.transfer_soul("CONSTRUCT",src,user) - SS.was_used() - else - return ..() - -////////////////////////////Proc for moving soul in and out off stone////////////////////////////////////// - -/obj/item/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob) - switch(choice) - if("FORCE") - var/obj/item/soulstone/SS = src - var/mob/living/T = target - if(T.client != null) - SS.init_shade(T, U) - else - to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...") - T.Paralyse(20) - if(!SS.getCultGhost(T,U)) - T.dust() //If we can't get a ghost, kill the sacrifice anyway. - - if("VICTIM") - var/mob/living/carbon/human/T = target - var/obj/item/soulstone/SS = src - if(T.stat == 0) - to_chat(U, "Capture failed!: Kill or maim the victim first!") - else - if(!T.client_mobs_in_contents?.len) - to_chat(U, "They have no soul!") - else - if(T.client == null) - to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...") - SS.getCultGhost(T,U) - else - if(SS.contents.len) - to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") - else - SS.init_shade(T, U, vic = 1) - - if("SHADE") - var/mob/living/simple_animal/shade/T = target - var/obj/item/soulstone/SS = src - if(!SS.can_use(U)) - U.Paralyse(5) - to_chat(U, "Your body is wracked with debilitating pain!") - return - if(T.stat == DEAD) - to_chat(U, "Capture failed!: The shade has already been banished!") - else - if(SS.contents.len) - to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") - else - T.loc = SS //put shade in stone - T.status_flags |= GODMODE - T.canmove = 0 - T.health = T.maxHealth - T.faction |= "\ref[U]" - SS.icon_state = "soulstone2" - to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form") - to_chat(U, "Capture successful!: [T.name]'s has been recaptured and stored within the soul stone.") - if("CONSTRUCT") - var/obj/structure/constructshell/T = target - var/obj/item/soulstone/SS = src - var/mob/living/simple_animal/shade/SH = locate() in SS - if(SH) - var/construct_class = alert(U, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer") - switch(construct_class) - if("Juggernaut") - var/mob/living/simple_animal/hostile/construct/armoured/C = new /mob/living/simple_animal/hostile/construct/armoured (get_turf(T.loc)) - to_chat(C, "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, create shield walls and even deflect energy weapons, and rip apart enemies and walls alike.") - init_construct(C,SH,SS,T) - - if("Wraith") - var/mob/living/simple_animal/hostile/construct/wraith/C = new /mob/living/simple_animal/hostile/construct/wraith (get_turf(T.loc)) - to_chat(C, "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") - init_construct(C,SH,SS,T) - - if("Artificer") - var/mob/living/simple_animal/hostile/construct/builder/C = new /mob/living/simple_animal/hostile/construct/builder (get_turf(T.loc)) - to_chat(C, "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, use magic missile, repair allied constructs (by clicking on them), and most important of all create new constructs (Use your Artificer spell to summon a new construct shell and Summon Soulstone to create a new soulstone).") - init_construct(C,SH,SS,T) - else - to_chat(U, "Creation failed!: The soul stone is empty! Go kill someone!") - return - -/proc/init_construct(mob/living/simple_animal/hostile/construct/C, mob/living/simple_animal/shade/SH, obj/item/soulstone/SS, obj/structure/constructshell/T) - SH.mind.transfer_to(C) - if(iscultist(C)) - var/datum/action/innate/cultcomm/CC = new() - CC.Grant(C) //We have to grant the cult comms again because they're lost during the mind transfer. - qdel(T) - qdel(SH) - to_chat(C, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - C.cancel_camera() - qdel(SS) - -/proc/makeNewConstruct(var/mob/living/simple_animal/hostile/construct/ctype, var/mob/target, var/mob/stoner = null, cultoverride = 0) - if(jobban_isbanned(target, "cultist") || jobban_isbanned(target, "Syndicate")) - return - var/mob/living/simple_animal/hostile/construct/newstruct = new ctype(get_turf(target)) - newstruct.faction |= "\ref[stoner]" - newstruct.key = target.key - if(stoner && iscultist(stoner) || cultoverride) - if(SSticker.mode.name == "cult") - SSticker.mode:add_cultist(newstruct.mind) - else - SSticker.mode.cult+=newstruct.mind - SSticker.mode.update_cult_icons_added(newstruct.mind) - if(stoner && iswizard(stoner)) - to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - else if(stoner && iscultist(stoner)) - to_chat(newstruct, "You are still bound to serve the cult, follow their orders and help them complete their goals at all costs.") - else - to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - newstruct.cancel_camera() - -/obj/item/soulstone/proc/init_shade(mob/living/T, mob/U, vic = 0) - new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton - T.invisibility = 101 - var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc ) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = T - flick("dust-h", animation) - qdel(animation) - var/path = get_shade_type() - var/mob/living/simple_animal/shade/S = new path(src) - S.status_flags |= GODMODE //So they won't die inside the stone somehow - S.canmove = 0//Can't move out of the soul stone - S.name = "Shade of [T.real_name]" - S.real_name = "Shade of [T.real_name]" - S.key = T.key - S.cancel_camera() - name = "soulstone: Shade of [T.real_name]" - icon_state = "soulstone2" - if(U) - S.faction |= "\ref[U]" //Add the master as a faction, allowing inter-mob cooperation - if(iswizard(U)) - SSticker.mode.update_wiz_icons_added(S.mind) - S.mind.special_role = SPECIAL_ROLE_WIZARD_APPRENTICE - if(iscultist(U)) - SSticker.mode.add_cultist(S.mind, 0) - S.mind.special_role = SPECIAL_ROLE_CULTIST - S.mind.store_memory("Serve the cult's will.") - to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.") - else - S.mind.store_memory("Serve [U.real_name], your creator.") - to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help them succeed in their goals at all costs.") - if(vic && U) - to_chat(U, "Capture successful!: [T.real_name]'s soul has been ripped from [U.p_their()] body and stored within the soul stone.") - if(isrobot(T))//Robots have to dust or else they spill out an empty robot brain, and unequiping them spills robot components that shouldn't spawn. - T.dust() - else - for(var/obj/item/W in T) - T.unEquip(W) - qdel(T) - -/obj/item/soulstone/proc/get_shade_type() - return /mob/living/simple_animal/shade/cult - -/obj/item/soulstone/anybody/get_shade_type() - return /mob/living/simple_animal/shade - -/obj/item/soulstone/proc/getCultGhost(mob/living/T, mob/U) - var/mob/dead/observer/chosen_ghost - - for(var/mob/dead/observer/ghost in GLOB.player_list) //We put them back in their body - if(ghost.mind && ghost.mind.current == T && ghost.client) - chosen_ghost = ghost - break - - if(!chosen_ghost) //Failing that, we grab a ghost - var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100) - if(consenting_candidates.len) - chosen_ghost = pick(consenting_candidates) - if(!T) - return 0 - if(!chosen_ghost) - to_chat(U, "There were no spirits willing to become a shade.") - return 0 - if(contents.len) //If they used the soulstone on someone else in the meantime - return 0 - T.ckey = chosen_ghost.ckey - init_shade(T, U) - return 1 +/obj/item/soulstone + name = "Soul Stone Shard" + icon = 'icons/obj/wizard.dmi' + icon_state = "soulstone" + item_state = "electronic" + desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artifact's power." + w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_BELT + origin_tech = "bluespace=4;materials=5" + + var/optional = FALSE //does this soulstone ask the victim whether they want to be turned into a shade + var/usability = FALSE // Can this soul stone be used by anyone, or only cultists/wizards? + var/reusable = TRUE // Can this soul stone be used more than once? + var/spent = FALSE // If the soul stone can only be used once, has it been used? + + var/opt_in = FALSE // for tracking during the 'optional' bit + +/obj/item/soulstone/proc/can_use(mob/living/user) + if(iscultist(user) || iswizard(user) || usability) + return TRUE + + return FALSE + +/obj/item/soulstone/proc/was_used() + if(!reusable) + spent = TRUE + name = "dull [name]" + desc = "A fragment of the legendary treasure known simply as \ + the 'Soul Stone'. The shard lies still, dull and lifeless; \ + whatever spark it once held long extinguished." + +/obj/item/soulstone/anybody + usability = TRUE + +/obj/item/soulstone/anybody/chaplain + name = "mysterious old shard" + reusable = FALSE + optional = TRUE + +/obj/item/soulstone/pickup(mob/living/user) + . = ..() + if(!can_use(user)) + to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly.") + user.Dizzy(120) + +//////////////////////////////Capturing//////////////////////////////////////////////////////// +/obj/item/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob) + if(!can_use(user)) + user.Paralyse(5) + to_chat(user, "Your body is wracked with debilitating pain!") + return + + if(spent) + to_chat(user, "There is no power left in the shard.") + return + + if(!ishuman(M)) //If target is not a human + return ..() + + if(M.has_brain_worms()) //Borer stuff - RR + to_chat(user, "This being is corrupted by an alien intelligence and cannot be soul trapped.") + return ..() + + if(jobban_isbanned(M, "cultist") || jobban_isbanned(M, "Syndicate")) + to_chat(user, "A mysterious force prevents you from trapping this being's soul.") + return ..() + + if(iscultist(user) && iscultist(M)) + to_chat(user, "\"Come now, do not capture your fellow's soul.\"") + return ..() + + if(optional) + if(!M.ckey) + to_chat(user, "They have no soul!") + return + + to_chat(user, "You attempt to channel [M]'s soul into [src]. You must give the soul some time to react and stand still...") + + var/mob/player_mob = M + if(M.get_ghost())//in case our player ghosted and we need to throw the alert at their ghost instead + player_mob = M.get_ghost() + var/client/player_client = player_mob.client + to_chat(player_mob, "[user] is trying to capture your soul into [src]! Click the button in the top right of the game window to respond.") + player_client << 'sound/misc/notice2.ogg' + window_flash(player_client) + + var/obj/screen/alert/notify_soulstone/A = player_mob.throw_alert("\ref[src]_soulstone_thingy", /obj/screen/alert/notify_soulstone) + if(player_client.prefs && player_client.prefs.UI_style) + A.icon = ui_style2icon(player_client.prefs.UI_style) + + //pass the stuff to the alert itself + A.stone = src + A.stoner = user.real_name + + //layer shenanigans to make the alert display the soulstone + var/old_layer = layer + var/old_plane = plane + layer = FLOAT_LAYER + plane = FLOAT_PLANE + A.overlays += src + layer = old_layer + plane = old_plane + + //give the victim 10 seconds to respond + sleep(10 SECONDS) + + if(!opt_in) + to_chat(user, "The soul resists your attempts at capturing it!") + return + + opt_in = FALSE + + if(spent)//checking one more time against shenanigans + return + + add_attack_logs(user, M, "Stolestone'd with [name]") + transfer_soul("VICTIM", M, user) + return + +///////////////////Options for using captured souls/////////////////////////////////////// +/obj/item/soulstone/attack_self(mob/user) + if(!in_range(src, user)) + return + + if(!can_use(user)) + user.Paralyse(5) + to_chat(user, "Your body is wracked with debilitating pain!") + return + + user.set_machine(src) + var/dat = "Soul Stone
    " + for(var/mob/living/simple_animal/shade/A in src) + dat += "Captured Soul: [A.name]
    " + dat += {"Summon Shade"} + dat += "
    " + dat += {" Close"} + user << browse(dat, "window=aicard") + onclose(user, "aicard") + return + +/obj/item/soulstone/Topic(href, href_list) + var/mob/U = usr + if(!in_range(src, U) || U.machine != src || !can_use(usr)) + U << browse(null, "window=aicard") + U.unset_machine() + return + + add_fingerprint(U) + U.set_machine(src) + + switch(href_list["choice"])//Now we switch based on choice. + if("Close") + U << browse(null, "window=aicard") + U.unset_machine() + return + + if("Summon") + for(var/mob/living/simple_animal/shade/A in src) + A.status_flags &= ~GODMODE + A.canmove = 1 + A.forceMove(get_turf(usr)) + A.cancel_camera() + icon_state = "soulstone" + name = initial(name) + if(iswizard(usr) || usability) + to_chat(A, "You have been released from your prison, but you are still bound to [usr.real_name]'s will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.") + else if(iscultist(usr)) + to_chat(A, "You have been released from your prison, but you are still bound to the cult's will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.") + was_used() + attack_self(U) + +///////////////////////////Transferring to constructs///////////////////////////////////////////////////// +/obj/structure/constructshell + name = "empty shell" + icon = 'icons/obj/wizard.dmi' + icon_state = "construct-cult" + desc = "A wicked machine used by those skilled in magical arts. It is inactive" + +/obj/structure/constructshell/examine(mob/user) + . = ..() + if(in_range(user, src) && (iscultist(user) || iswizard(user) || user.stat == DEAD)) + . += "A construct shell, used to house bound souls from a soulstone." + . += "Placing a soulstone with a soul into this shell allows you to produce your choice of the following:" + . += "An Artificer, which can produce more shells and soulstones, as well as fortifications." + . += "A Wraith, which does high damage and can jaunt through walls, though it is quite fragile." + . += "A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow." + +/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob, params) + if(istype(O, /obj/item/soulstone)) + var/obj/item/soulstone/SS = O + if(!SS.can_use(user)) + to_chat(user, "An overwhelming feeling of dread comes over you as you attempt to place the soulstone into the shell. It would be wise to be rid of this quickly.") + user.Dizzy(120) + return + SS.transfer_soul("CONSTRUCT",src,user) + SS.was_used() + else + return ..() + +////////////////////////////Proc for moving soul in and out off stone////////////////////////////////////// + +/obj/item/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob) + switch(choice) + if("FORCE") + var/obj/item/soulstone/SS = src + var/mob/living/T = target + if(T.client != null) + SS.init_shade(T, U) + else + to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...") + T.Paralyse(20) + if(!SS.getCultGhost(T,U)) + T.dust() //If we can't get a ghost, kill the sacrifice anyway. + + if("VICTIM") + var/mob/living/carbon/human/T = target + var/obj/item/soulstone/SS = src + if(T.stat == 0) + to_chat(U, "Capture failed!: Kill or maim the victim first!") + else + if(!T.client_mobs_in_contents?.len) + to_chat(U, "They have no soul!") + else + if(T.client == null) + to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...") + SS.getCultGhost(T,U) + else + if(SS.contents.len) + to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") + else + SS.init_shade(T, U, vic = 1) + + if("SHADE") + var/mob/living/simple_animal/shade/T = target + var/obj/item/soulstone/SS = src + if(!SS.can_use(U)) + U.Paralyse(5) + to_chat(U, "Your body is wracked with debilitating pain!") + return + if(T.stat == DEAD) + to_chat(U, "Capture failed!: The shade has already been banished!") + else + if(SS.contents.len) + to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") + else + T.loc = SS //put shade in stone + T.status_flags |= GODMODE + T.canmove = 0 + T.health = T.maxHealth + T.faction |= "\ref[U]" + SS.icon_state = "soulstone2" + to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form") + to_chat(U, "Capture successful!: [T.name]'s has been recaptured and stored within the soul stone.") + if("CONSTRUCT") + var/obj/structure/constructshell/T = target + var/obj/item/soulstone/SS = src + var/mob/living/simple_animal/shade/SH = locate() in SS + if(SH) + var/construct_class = alert(U, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer") + switch(construct_class) + if("Juggernaut") + var/mob/living/simple_animal/hostile/construct/armoured/C = new /mob/living/simple_animal/hostile/construct/armoured (get_turf(T.loc)) + to_chat(C, "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, create shield walls and even deflect energy weapons, and rip apart enemies and walls alike.") + init_construct(C,SH,SS,T) + + if("Wraith") + var/mob/living/simple_animal/hostile/construct/wraith/C = new /mob/living/simple_animal/hostile/construct/wraith (get_turf(T.loc)) + to_chat(C, "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") + init_construct(C,SH,SS,T) + + if("Artificer") + var/mob/living/simple_animal/hostile/construct/builder/C = new /mob/living/simple_animal/hostile/construct/builder (get_turf(T.loc)) + to_chat(C, "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, use magic missile, repair allied constructs (by clicking on them), and most important of all create new constructs (Use your Artificer spell to summon a new construct shell and Summon Soulstone to create a new soulstone).") + init_construct(C,SH,SS,T) + else + to_chat(U, "Creation failed!: The soul stone is empty! Go kill someone!") + return + +/proc/init_construct(mob/living/simple_animal/hostile/construct/C, mob/living/simple_animal/shade/SH, obj/item/soulstone/SS, obj/structure/constructshell/T) + SH.mind.transfer_to(C) + if(iscultist(C)) + var/datum/action/innate/cultcomm/CC = new() + CC.Grant(C) //We have to grant the cult comms again because they're lost during the mind transfer. + qdel(T) + qdel(SH) + to_chat(C, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + C.cancel_camera() + qdel(SS) + +/proc/makeNewConstruct(var/mob/living/simple_animal/hostile/construct/ctype, var/mob/target, var/mob/stoner = null, cultoverride = 0) + if(jobban_isbanned(target, "cultist") || jobban_isbanned(target, "Syndicate")) + return + var/mob/living/simple_animal/hostile/construct/newstruct = new ctype(get_turf(target)) + newstruct.faction |= "\ref[stoner]" + newstruct.key = target.key + if(stoner && iscultist(stoner) || cultoverride) + if(SSticker.mode.name == "cult") + SSticker.mode:add_cultist(newstruct.mind) + else + SSticker.mode.cult+=newstruct.mind + SSticker.mode.update_cult_icons_added(newstruct.mind) + if(stoner && iswizard(stoner)) + to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + else if(stoner && iscultist(stoner)) + to_chat(newstruct, "You are still bound to serve the cult, follow their orders and help them complete their goals at all costs.") + else + to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + newstruct.cancel_camera() + +/obj/item/soulstone/proc/init_shade(mob/living/T, mob/U, vic = 0) + new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton + T.invisibility = 101 + var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc ) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = T + flick("dust-h", animation) + qdel(animation) + var/path = get_shade_type() + var/mob/living/simple_animal/shade/S = new path(src) + S.status_flags |= GODMODE //So they won't die inside the stone somehow + S.canmove = 0//Can't move out of the soul stone + S.name = "Shade of [T.real_name]" + S.real_name = "Shade of [T.real_name]" + S.key = T.key + S.cancel_camera() + name = "soulstone: Shade of [T.real_name]" + icon_state = "soulstone2" + if(U) + S.faction |= "\ref[U]" //Add the master as a faction, allowing inter-mob cooperation + if(iswizard(U)) + SSticker.mode.update_wiz_icons_added(S.mind) + S.mind.special_role = SPECIAL_ROLE_WIZARD_APPRENTICE + if(iscultist(U)) + SSticker.mode.add_cultist(S.mind, 0) + S.mind.special_role = SPECIAL_ROLE_CULTIST + S.mind.store_memory("Serve the cult's will.") + to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.") + else + S.mind.store_memory("Serve [U.real_name], your creator.") + to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help them succeed in their goals at all costs.") + if(vic && U) + to_chat(U, "Capture successful!: [T.real_name]'s soul has been ripped from [U.p_their()] body and stored within the soul stone.") + if(isrobot(T))//Robots have to dust or else they spill out an empty robot brain, and unequiping them spills robot components that shouldn't spawn. + T.dust() + else + for(var/obj/item/W in T) + T.unEquip(W) + qdel(T) + +/obj/item/soulstone/proc/get_shade_type() + return /mob/living/simple_animal/shade/cult + +/obj/item/soulstone/anybody/get_shade_type() + return /mob/living/simple_animal/shade + +/obj/item/soulstone/proc/getCultGhost(mob/living/T, mob/U) + var/mob/dead/observer/chosen_ghost + + for(var/mob/dead/observer/ghost in GLOB.player_list) //We put them back in their body + if(ghost.mind && ghost.mind.current == T && ghost.client) + chosen_ghost = ghost + break + + if(!chosen_ghost) //Failing that, we grab a ghost + var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100) + if(consenting_candidates.len) + chosen_ghost = pick(consenting_candidates) + if(!T) + return 0 + if(!chosen_ghost) + to_chat(U, "There were no spirits willing to become a shade.") + return 0 + if(contents.len) //If they used the soulstone on someone else in the meantime + return 0 + T.ckey = chosen_ghost.ckey + init_shade(T, U) + return 1 diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index 0b24362cf1c..a7eb5d135e9 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -1,1004 +1,1061 @@ -/datum/spellbook_entry - var/name = "Entry Name" - - var/spell_type = null - var/desc = "" - var/category = "Offensive" - var/log_name = "XX" //What it shows up as in logs - var/cost = 2 - var/refundable = 1 - var/surplus = -1 // -1 for infinite, not used by anything atm - var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell - var/buy_word = "Learn" - var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook - -/datum/spellbook_entry/proc/IsSpellAvailable() // For config prefs / gamemode restrictions - these are round applied - return 1 - -/datum/spellbook_entry/proc/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) // Specific circumstances - if(book.uses= aspell.level_max) - to_chat(user, "This spell cannot be improved further.") - return 0 - else - aspell.name = initial(aspell.name) - aspell.spell_level++ - aspell.charge_max = round(initial(aspell.charge_max) - aspell.spell_level * (initial(aspell.charge_max) - aspell.cooldown_min)/ aspell.level_max) - if(aspell.charge_max < aspell.charge_counter) - aspell.charge_counter = aspell.charge_max - switch(aspell.spell_level) - if(1) - to_chat(user, "You have improved [aspell.name] into Efficient [aspell.name].") - aspell.name = "Efficient [aspell.name]" - if(2) - to_chat(user, "You have further improved [aspell.name] into Quickened [aspell.name].") - aspell.name = "Quickened [aspell.name]" - if(3) - to_chat(user, "You have further improved [aspell.name] into Free [aspell.name].") - aspell.name = "Free [aspell.name]" - if(4) - to_chat(user, "You have further improved [aspell.name] into Instant [aspell.name].") - aspell.name = "Instant [aspell.name]" - if(aspell.spell_level >= aspell.level_max) - to_chat(user, "This spell cannot be strengthened any further.") - return 1 - //No same spell found - just learn it - feedback_add_details("wizard_spell_learned",log_name) - user.mind.AddSpell(S) - to_chat(user, "You have learned [S.name].") - return 1 - -/datum/spellbook_entry/proc/CanRefund(mob/living/carbon/human/user, obj/item/spellbook/book) - if(!refundable) - return 0 - if(!S) - S = new spell_type() - for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) - if(initial(S.name) == initial(aspell.name)) - return 1 - return 0 - -/datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user, obj/item/spellbook/book) //return point value or -1 for failure - var/area/wizard_station/A = locate() - if(!(user in A.contents)) - to_chat(user, "You can only refund spells at the wizard lair") - return -1 - if(!S) - S = new spell_type() - var/spell_levels = 0 - for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) - if(initial(S.name) == initial(aspell.name)) - spell_levels = aspell.spell_level - user.mind.spell_list.Remove(aspell) - QDEL_NULL(S) - return cost * (spell_levels+1) - return -1 - -/datum/spellbook_entry/proc/GetInfo() - if(!S) - S = new spell_type() - var/dat ="" - dat += "[initial(S.name)]" - if(S.charge_type == "recharge") - dat += " Cooldown:[S.charge_max/10]" - dat += " Cost:[cost]
    " - dat += "[S.desc][desc]
    " - dat += "[S.clothes_req?"Needs wizard garb":"Can be cast without wizard garb"]
    " - return dat - -//Main category - Spells -//Offensive -/datum/spellbook_entry/blind - name = "Blind" - spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind - log_name = "BD" - category = "Offensive" - cost = 1 - -/datum/spellbook_entry/lightningbolt - name = "Lightning Bolt" - spell_type = /obj/effect/proc_holder/spell/targeted/lightning - log_name = "LB" - category = "Offensive" - cost = 1 - -/datum/spellbook_entry/cluwne - name = "Curse of the Cluwne" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/cluwne - log_name = "CC" - category = "Offensive" - -/datum/spellbook_entry/banana_touch - name = "Banana Touch" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/banana - log_name = "BT" - cost = 1 - -/datum/spellbook_entry/mime_malaise - name = "Mime Malaise" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/mime_malaise - log_name = "MI" - cost = 1 - -/datum/spellbook_entry/horseman - name = "Curse of the Horseman" - spell_type = /obj/effect/proc_holder/spell/targeted/horsemask - log_name = "HH" - category = "Offensive" - -/datum/spellbook_entry/disintegrate - name = "Disintegrate" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/disintegrate - log_name = "DG" - category = "Offensive" - -/datum/spellbook_entry/fireball - name = "Fireball" - spell_type = /obj/effect/proc_holder/spell/fireball - log_name = "FB" - category = "Offensive" - -/datum/spellbook_entry/fleshtostone - name = "Flesh to Stone" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone - log_name = "FS" - category = "Offensive" - -/datum/spellbook_entry/mutate - name = "Mutate" - spell_type = /obj/effect/proc_holder/spell/targeted/genetic/mutate - log_name = "MU" - category = "Offensive" - -/datum/spellbook_entry/rod_form - name = "Rod Form" - spell_type = /obj/effect/proc_holder/spell/targeted/rod_form - log_name = "RF" - category = "Offensive" - -/datum/spellbook_entry/infinite_guns - name = "Lesser Summon Guns" - spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns - log_name = "IG" - category = "Offensive" - cost = 4 - -//Defensive -/datum/spellbook_entry/disabletech - name = "Disable Tech" - spell_type = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech - log_name = "DT" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/forcewall - name = "Force Wall" - spell_type = /obj/effect/proc_holder/spell/targeted/forcewall - log_name = "FW" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/greaterforcewall - name = "Greater Force Wall" - spell_type = /obj/effect/proc_holder/spell/targeted/forcewall/greater - log_name = "GFW" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/repulse - name = "Repulse" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/repulse - log_name = "RP" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/smoke - name = "Smoke" - spell_type = /obj/effect/proc_holder/spell/targeted/smoke - log_name = "SM" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/lichdom - name = "Bind Soul" - spell_type = /obj/effect/proc_holder/spell/targeted/lichdom - log_name = "LD" - category = "Defensive" - -/datum/spellbook_entry/lichdom/IsSpellAvailable() - if(SSticker.mode.name == "ragin' mages") - return FALSE - else - return TRUE - -/datum/spellbook_entry/magicm - name = "Magic Missile" - spell_type = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile - log_name = "MM" - category = "Defensive" - -/datum/spellbook_entry/timestop - name = "Time Stop" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop - log_name = "TS" - category = "Defensive" - -//Mobility -/datum/spellbook_entry/knock - name = "Knock" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock - log_name = "KN" - category = "Mobility" - cost = 1 - -/datum/spellbook_entry/blink - name = "Blink" - spell_type = /obj/effect/proc_holder/spell/targeted/turf_teleport/blink - log_name = "BL" - category = "Mobility" - -/datum/spellbook_entry/jaunt - name = "Ethereal Jaunt" - spell_type = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt - log_name = "EJ" - category = "Mobility" - -/datum/spellbook_entry/greaterknock - name = "Greater Knock" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock/greater - log_name = "GK" - category = "Mobility" - refundable = 0 //global effect on cast - -/datum/spellbook_entry/mindswap - name = "Mindswap" - spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer - log_name = "MT" - category = "Mobility" - -/datum/spellbook_entry/teleport - name = "Teleport" - spell_type = /obj/effect/proc_holder/spell/targeted/area_teleport/teleport - log_name = "TP" - category = "Mobility" - -//Assistance -/datum/spellbook_entry/charge - name = "Charge" - spell_type = /obj/effect/proc_holder/spell/targeted/charge - log_name = "CH" - category = "Assistance" - cost = 1 - -/datum/spellbook_entry/summonitem - name = "Summon Item" - spell_type = /obj/effect/proc_holder/spell/targeted/summonitem - log_name = "IS" - category = "Assistance" - cost = 1 - -/datum/spellbook_entry/noclothes - name = "Remove Clothes Requirement" - spell_type = /obj/effect/proc_holder/spell/noclothes - log_name = "NC" - category = "Assistance" - -//Rituals -/datum/spellbook_entry/summon - name = "Summon Stuff" - category = "Rituals" - refundable = FALSE - buy_word = "Cast" - var/active = FALSE - -/datum/spellbook_entry/summon/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) - return ..() && !active - -/datum/spellbook_entry/summon/GetInfo() - var/dat ="" - dat += "[name]" - if(cost>0) - dat += " Cost:[cost]
    " - else - dat += " No Cost
    " - dat += "[desc]
    " - if(active) - dat += "Already cast!
    " - return dat - -/datum/spellbook_entry/summon/ghosts - name = "Summon Ghosts" - desc = "Spook the crew out by making them see dead people. Be warned, ghosts are capricious and occasionally vindicative, and some will use their incredibly minor abilities to frustrate you." - cost = 0 - log_name = "SGH" - -/datum/spellbook_entry/summon/ghosts/IsSpellAvailable() - if(!SSticker.mode) // In case spellbook is placed on map - return FALSE - if(SSticker.mode.name == "ragin' mages") - return FALSE - else - return TRUE - -/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - new /datum/event/wizard/ghost() - active = TRUE - to_chat(user, "You have cast summon ghosts!") - playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1) - return TRUE - -/datum/spellbook_entry/summon/guns - name = "Summon Guns" - desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. There is a good chance that they will shoot each other first." - log_name = "SG" - -/datum/spellbook_entry/summon/guns/IsSpellAvailable() - if(!SSticker.mode) // In case spellbook is placed on map - return FALSE - if(SSticker.mode.name == "ragin' mages") - return FALSE - else - return TRUE - -/datum/spellbook_entry/summon/guns/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - feedback_add_details("wizard_spell_learned", log_name) - rightandwrong(SUMMON_GUNS, user, 10) - active = TRUE - playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) - to_chat(user, "You have cast summon guns!") - return TRUE - -/datum/spellbook_entry/summon/magic - name = "Summon Magic" - desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time." - log_name = "SU" - -/datum/spellbook_entry/summon/magic/IsSpellAvailable() - if(!SSticker.mode) // In case spellbook is placed on map - return FALSE - if(SSticker.mode.name == "ragin' mages") - return FALSE - else - return TRUE - -/datum/spellbook_entry/summon/magic/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - feedback_add_details("wizard_spell_learned", log_name) - rightandwrong(SUMMON_MAGIC, user, 10) - active = TRUE - playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) - to_chat(user, "You have cast summon magic!") - return TRUE - -//Main category - Magical Items -/datum/spellbook_entry/item - name = "Buy Item" - refundable = 0 - buy_word = "Summon" - var/item_path = null - -/datum/spellbook_entry/item/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - user.put_in_hands(new item_path) - feedback_add_details("wizard_spell_learned", log_name) - return 1 - -/datum/spellbook_entry/item/GetInfo() - var/dat ="" - dat += "[name]" - dat += " Cost:[cost]
    " - dat += "[desc]
    " - if(surplus>=0) - dat += "[surplus] left.
    " - return dat - -//Artefacts -/datum/spellbook_entry/item/necrostone - name = "A Necromantic Stone" - desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command." - item_path = /obj/item/necromantic_stone - log_name = "NS" - category = "Artefacts" - -/datum/spellbook_entry/item/scryingorb - name = "Scrying Orb" - desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you x-ray vision." - item_path = /obj/item/scrying - log_name = "SO" - category = "Artefacts" - -/datum/spellbook_entry/item/scryingorb/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - if(..()) - if(!(XRAY in user.mutations)) - user.mutations.Add(XRAY) - user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS) - user.see_in_dark = 8 - user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - to_chat(user, "The walls suddenly disappear.") - return TRUE - -/datum/spellbook_entry/item/soulstones - name = "Six Soul Stone Shards and the spell Artificer" - desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. The spell Artificer allows you to create arcane machines for the captured souls to pilot." - item_path = /obj/item/storage/belt/soulstone/full - log_name = "SS" - category = "Artefacts" - -/datum/spellbook_entry/item/soulstones/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - . = ..() - if(.) - user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/construct(null)) - return . - -/datum/spellbook_entry/item/wands - name = "Wand Assortment" - desc = "A collection of wands that allow for a wide variety of utility. Wands do not recharge, so be conservative in use. Comes in a handy belt." - item_path = /obj/item/storage/belt/wands/full - log_name = "WA" - category = "Artefacts" - -//Weapons and Armors -/datum/spellbook_entry/item/battlemage - name = "Battlemage Armour" - desc = "An ensorceled suit of armour, protected by a powerful shield. The shield can completely negate sixteen attacks before being permanently depleted." - item_path = /obj/item/clothing/suit/space/hardsuit/shielded/wizard - limit = 1 - category = "Weapons and Armors" - log_name = "BMA" - -/datum/spellbook_entry/item/battlemage_charge - name = "Battlemage Armour Charges" - desc = "A powerful defensive rune, it will grant eight additional charges to a suit of battlemage armour." - item_path = /obj/item/wizard_armour_charge - category = "Weapons and Armors" - cost = 1 - log_name = "BMAC" - -/datum/spellbook_entry/item/mjolnir - name = "Mjolnir" - desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power." - item_path = /obj/item/twohanded/mjollnir - log_name = "MJ" - category = "Weapons and Armors" - -/datum/spellbook_entry/item/singularity_hammer - name = "Singularity Hammer" - desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everthing nearby to the point of impact." - item_path = /obj/item/twohanded/singularityhammer - log_name = "SI" - category = "Weapons and Armors" - -//Staves -/datum/spellbook_entry/item/staffdoor - name = "Staff of Door Creation" - desc = "A particular staff that can mold solid metal into ornate wooden doors. Useful for getting around in the absence of other transportation. Does not work on glass." - item_path = /obj/item/gun/magic/staff/door - log_name = "SD" - category = "Staves" - cost = 1 - -/datum/spellbook_entry/item/staffhealing - name = "Staff of Healing" - desc = "An altruistic staff that can heal the lame and raise the dead." - item_path = /obj/item/gun/magic/staff/healing - log_name = "SH" - category = "Staves" - cost = 1 - -/datum/spellbook_entry/item/staffslipping - name = "Staff of Slipping" - desc = "A staff that shoots magical bananas. These bananas will either slip or stun the target when hit. Surprisingly reliable!" - item_path = /obj/item/gun/magic/staff/slipping - log_name = "SL" - category = "Staves" - cost = 1 - -/datum/spellbook_entry/item/staffanimation - name = "Staff of Animation" - desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines." - item_path = /obj/item/gun/magic/staff/animate - log_name = "SA" - category = "Staves" - -/datum/spellbook_entry/item/staffchange - name = "Staff of Change" - desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." - item_path = /obj/item/gun/magic/staff/change - log_name = "ST" - category = "Staves" - -/datum/spellbook_entry/item/staffchaos - name = "Staff of Chaos" - desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended." - item_path = /obj/item/gun/magic/staff/chaos - log_name = "SC" - category = "Staves" - -//Summons -/datum/spellbook_entry/item/oozebottle - name = "Bottle of Ooze" - desc = "A bottle of magically infused ooze, which will awake an all-consuming Morph, capable of cunningly disguising itself as any object it comes in contact with and even casting some very basic spells. Be careful though, as Morph diet includes Wizards." - item_path = /obj/item/antag_spawner/morph - log_name = "BO" - category = "Summons" - limit = 3 - cost = 1 - -/datum/spellbook_entry/item/hugbottle - name = "Bottle of Tickles" - desc = "A bottle of magically infused fun, the smell of which will \ - attract adorable extradimensional beings when broken. These beings \ - are similar to slaughter demons, but are a little weaker and they do not permamently \ - kill their victims, instead putting them in an extradimensional hugspace, \ - to be released on the demon's death. Chaotic, but not ultimately \ - damaging. The crew's reaction to the other hand could be very \ - destructive." - item_path = /obj/item/antag_spawner/slaughter_demon/laughter - log_name = "HB" - category = "Summons" - limit = 3 - cost = 1 // Non-destructive; it's just a jape, sibling! - -/datum/spellbook_entry/item/bloodbottle - name = "Bottle of Blood" - desc = "A bottle of magically infused blood, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned by blood magic are indiscriminate in their killing, and you yourself may become a victim." - item_path = /obj/item/antag_spawner/slaughter_demon - log_name = "BB" - category = "Summons" - limit = 3 - -/datum/spellbook_entry/item/contract - name = "Contract of Apprenticeship" - desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side." - item_path = /obj/item/contract - log_name = "CT" - category = "Summons" - -/datum/spellbook_entry/item/tarotdeck - name = "Guardian Deck" - desc = "A deck of guardian tarot cards, capable of binding a personal guardian to your body. There are multiple types of guardian available, but all of them will transfer some amount of damage to you. \ - It would be wise to avoid buying these with anything capable of causing you to swap bodies with others." - item_path = /obj/item/guardiancreator - log_name = "TD" - category = "Summons" - limit = 1 - -/obj/item/spellbook - name = "spell book" - desc = "The legendary book of spells of the wizard." - icon = 'icons/obj/library.dmi' - icon_state = "spellbook" - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_TINY - var/uses = 10 - var/temp = null - var/op = 1 - var/tab = null - var/main_tab = null - var/mob/living/carbon/human/owner - var/list/datum/spellbook_entry/entries = list() - var/list/categories = list() - var/list/main_categories = list("Spells", "Magical Items") - var/list/spell_categories = list("Offensive", "Defensive", "Mobility", "Assistance", "Rituals") - var/list/item_categories = list("Artefacts", "Weapons and Armors", "Staves", "Summons") - -/obj/item/spellbook/proc/initialize() - var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon - for(var/T in entry_types) - var/datum/spellbook_entry/E = new T - if(E.IsSpellAvailable()) - entries |= E - categories |= E.category - else - qdel(E) - main_tab = main_categories[1] - tab = categories[1] - -/obj/item/spellbook/New() - ..() - initialize() - -/obj/item/spellbook/attackby(obj/item/O as obj, mob/user as mob, params) - if(istype(O, /obj/item/contract)) - var/obj/item/contract/contract = O - if(contract.used) - to_chat(user, "The contract has been used, you can't get your points back now!") - else - to_chat(user, "You feed the contract back into the spellbook, refunding your points.") - uses+=2 - qdel(O) - return - - if(istype(O, /obj/item/antag_spawner/slaughter_demon)) - to_chat(user, "On second thought, maybe summoning a demon is a bad idea. You refund your points.") - if(istype(O, /obj/item/antag_spawner/slaughter_demon/laughter)) - uses += 1 - for(var/datum/spellbook_entry/item/hugbottle/HB in entries) - if(!isnull(HB.limit)) - HB.limit++ - else - uses += 2 - for(var/datum/spellbook_entry/item/bloodbottle/BB in entries) - if(!isnull(BB.limit)) - BB.limit++ - qdel(O) - return - - if(istype(O, /obj/item/antag_spawner/morph)) - to_chat(user, "On second thought, maybe awakening a morph is a bad idea. You refund your points.") - uses += 1 - for(var/datum/spellbook_entry/item/oozebottle/OB in entries) - if(!isnull(OB.limit)) - OB.limit++ - qdel(O) - return - return ..() - -/obj/item/spellbook/proc/GetCategoryHeader(category) - var/dat = "" - switch(category) - if("Offensive") - dat += "Spells geared towards debilitating and destroying.

    " - dat += "For spells: the number after the spell name is the cooldown time.
    " - dat += "You can reduce this number by spending more points on the spell.
    " - if("Defensive") - dat += "Spells geared towards improving your survivabilty or reducing foes ability to attack.

    " - dat += "For spells: the number after the spell name is the cooldown time.
    " - dat += "You can reduce this number by spending more points on the spell.
    " - if("Mobility") - dat += "Spells geared towards improving your ability to move. It is a good idea to take at least one.

    " - dat += "For spells: the number after the spell name is the cooldown time.
    " - dat += "You can reduce this number by spending more points on the spell.
    " - if("Assistance") - dat += "Spells geared towards improving your other items and abilities.

    " - dat += "For spells: the number after the spell name is the cooldown time.
    " - dat += "You can reduce this number by spending more points on the spell.
    " - if("Rituals") - dat += "These powerful spells are capable of changing the very fabric of reality. Not always in your favour.
    " - if("Weapons and Armors") - dat += "Various weapons and armors to crush your enemies and protect you from harm.

    " - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
    " - if("Staves") - dat += "Various staves granting you their power, which they slowly recharge over time.

    " - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
    " - if("Artefacts") - dat += "Various magical artefacts to aid you.

    " - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
    " - if("Summons") - dat += "Magical items geared towards bringing in outside forces to aid you.

    " - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
    " - return dat - -/obj/item/spellbook/proc/wrap(content) - var/dat = "" - dat +="Spellbook" - dat += {" - - - - "} - dat += {"[content]"} - return dat - -/obj/item/spellbook/attack_self(mob/user as mob) - if(!owner) - to_chat(user, "You bind the spellbook to yourself.") - owner = user - return - if(user != owner) - to_chat(user, "The [name] does not recognize you as it's owner and refuses to open!") - return - user.set_machine(src) - var/dat = "" - - dat += "
      " - var/list/cat_dat = list() - for(var/main_category in main_categories) - cat_dat[main_category] = "
      " - dat += "
    • [main_category]
    • " - dat += "
    " - switch(main_tab) - if("Spells") - dat += "
      " - for(var/category in categories) - if(category in item_categories) - continue - cat_dat[category] = "
      " - dat += "
    • [category]
    • " - dat += "
    • Points remaining : [uses]
    • " - if("Magical Items") - dat += "
        " - for(var/category in categories) - if(category in spell_categories) - continue - cat_dat[category] = "
        " - dat += "
      • [category]
      • " - dat += "
      • Points remaining : [uses]
      • " - dat += "
      " - - var/datum/spellbook_entry/E - for(var/i=1,i<=entries.len,i++) - var/spell_info = "" - E = entries[i] - spell_info += E.GetInfo() - if(E.CanBuy(user,src)) - spell_info+= "[E.buy_word]
      " - else - spell_info+= "Can't [E.buy_word]
      " - if(E.CanRefund(user,src)) - spell_info+= "Refund
      " - spell_info += "
      " - if(cat_dat[E.category]) - cat_dat[E.category] += spell_info - - for(var/category in categories) - dat += "
      " - dat += GetCategoryHeader(category) - dat += cat_dat[category] - dat += "
      " - - user << browse(wrap(dat), "window=spellbook;size=800x600") - onclose(user, "spellbook") - return - -/obj/item/spellbook/Topic(href, href_list) - if(..()) - return 1 - var/mob/living/carbon/human/H = usr - - if(!ishuman(H)) - return 1 - - if(H.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE) - temp = "If you got caught sneaking a peak from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not." - return 1 - - var/datum/spellbook_entry/E = null - if(loc == H || (in_range(src, H) && istype(loc, /turf))) - H.set_machine(src) - if(href_list["buy"]) - E = entries[text2num(href_list["buy"])] - if(E && E.CanBuy(H,src)) - if(E.Buy(H,src)) - if(E.limit) - E.limit-- - uses -= E.cost - else if(href_list["refund"]) - E = entries[text2num(href_list["refund"])] - if(E && E.refundable) - var/result = E.Refund(H,src) - if(result > 0) - if(!isnull(E.limit)) - E.limit += result - uses += result - else if(href_list["mainpage"]) - main_tab = sanitize(href_list["mainpage"]) - tab = sanitize(href_list["page"]) - if(main_tab == "Spells") - tab = spell_categories[1] - else if(main_tab == "Magical Items") - tab = item_categories[1] - else if(href_list["page"]) - tab = sanitize(href_list["page"]) - attack_self(H) - return 1 - -//Single Use Spellbooks -/obj/item/spellbook/oneuse - var/spell = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic - var/spellname = "sandbox" - var/used = 0 - name = "spellbook of " - uses = 1 - desc = "This template spellbook was never meant for the eyes of man..." - -/obj/item/spellbook/oneuse/New() - ..() - name += spellname - -/obj/item/spellbook/oneuse/initialize() //No need to init - return - -/obj/item/spellbook/oneuse/attack_self(mob/user) - var/obj/effect/proc_holder/spell/S = new spell - for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) - if(knownspell.type == S.type) - if(user.mind) - if(user.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE || user.mind.special_role == SPECIAL_ROLE_WIZARD) - to_chat(user, "You're already far more versed in this spell than this flimsy how-to book can provide.") - else - to_chat(user, "You've already read this one.") - return - if(used) - recoil(user) - else - user.mind.AddSpell(S) - to_chat(user, "you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!") - user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).") - onlearned(user) - -/obj/item/spellbook/oneuse/proc/recoil(mob/user) - user.visible_message("[src] glows in a black light!") - -/obj/item/spellbook/oneuse/proc/onlearned(mob/user) - used = 1 - user.visible_message("[src] glows dark for a second!") - -/obj/item/spellbook/oneuse/attackby() - return - -/obj/item/spellbook/oneuse/fireball - spell = /obj/effect/proc_holder/spell/fireball - spellname = "fireball" - icon_state = "bookfireball" - desc = "This book feels warm to the touch." - -/obj/item/spellbook/oneuse/fireball/recoil(mob/user as mob) - ..() - explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) - qdel(src) - -/obj/item/spellbook/oneuse/smoke - spell = /obj/effect/proc_holder/spell/targeted/smoke - spellname = "smoke" - icon_state = "booksmoke" - desc = "This book is overflowing with the dank arts." - -/obj/item/spellbook/oneuse/smoke/recoil(mob/user as mob) - ..() - to_chat(user, "Your stomach rumbles...") - user.adjust_nutrition(-200) - -/obj/item/spellbook/oneuse/blind - spell = /obj/effect/proc_holder/spell/targeted/trigger/blind - spellname = "blind" - icon_state = "bookblind" - desc = "This book looks blurry, no matter how you look at it." - -/obj/item/spellbook/oneuse/blind/recoil(mob/user as mob) - ..() - to_chat(user, "You go blind!") - user.EyeBlind(10) - -/obj/item/spellbook/oneuse/mindswap - spell = /obj/effect/proc_holder/spell/targeted/mind_transfer - spellname = "mindswap" - icon_state = "bookmindswap" - desc = "This book's cover is pristine, though its pages look ragged and torn." - var/mob/stored_swap = null //Used in used book recoils to store an identity for mindswaps - -/obj/item/spellbook/oneuse/mindswap/onlearned() - spellname = pick("fireball","smoke","blind","forcewall","knock","horses","charge") - icon_state = "book[spellname]" - name = "spellbook of [spellname]" //Note, desc doesn't change by design - ..() - -/obj/item/spellbook/oneuse/mindswap/recoil(mob/user) - ..() - if(stored_swap in GLOB.dead_mob_list) - stored_swap = null - if(!stored_swap) - stored_swap = user - to_chat(user, "For a moment you feel like you don't even know who you are anymore.") - return - if(stored_swap == user) - to_chat(user, "You stare at the book some more, but there doesn't seem to be anything else to learn...") - return - - var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new - swapper.cast(user, stored_swap, 1) - - to_chat(stored_swap, "You're suddenly somewhere else... and someone else?!") - to_chat(user, "Suddenly you're staring at [src] again... where are you, who are you?!") - stored_swap = null - -/obj/item/spellbook/oneuse/forcewall - spell = /obj/effect/proc_holder/spell/targeted/forcewall - spellname = "forcewall" - icon_state = "bookforcewall" - desc = "This book has a dedication to mimes everywhere inside the front cover." - -/obj/item/spellbook/oneuse/forcewall/recoil(mob/user as mob) - ..() - to_chat(user, "You suddenly feel very solid!") - var/obj/structure/closet/statue/S = new /obj/structure/closet/statue(user.loc, user) - S.timer = 30 - user.drop_item() - -/obj/item/spellbook/oneuse/knock - spell = /obj/effect/proc_holder/spell/aoe_turf/knock - spellname = "knock" - icon_state = "bookknock" - desc = "This book is hard to hold closed properly." - -/obj/item/spellbook/oneuse/knock/recoil(mob/user as mob) - ..() - to_chat(user, "You're knocked down!") - user.Weaken(20) - -/obj/item/spellbook/oneuse/horsemask - spell = /obj/effect/proc_holder/spell/targeted/horsemask - spellname = "horses" - icon_state = "bookhorses" - desc = "This book is more horse than your mind has room for." - -/obj/item/spellbook/oneuse/horsemask/recoil(mob/living/carbon/user as mob) - if(istype(user, /mob/living/carbon/human)) - to_chat(user, "HOR-SIE HAS RISEN") - var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead - magichead.flags |= NODROP | DROPDEL //curses! - magichead.flags_inv = null //so you can still see their face - magichead.voicechange = 1 //NEEEEIIGHH - if(!user.unEquip(user.wear_mask)) - qdel(user.wear_mask) - user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) - qdel(src) - else - to_chat(user, "I say thee neigh") - -/obj/item/spellbook/oneuse/charge - spell = /obj/effect/proc_holder/spell/targeted/charge - spellname = "charging" - icon_state = "bookcharge" - desc = "This book is made of 100% post-consumer wizard." - -/obj/item/spellbook/oneuse/charge/recoil(mob/user as mob) - ..() - to_chat(user, "[src] suddenly feels very warm!") - empulse(src, 1, 1) - -/obj/item/spellbook/oneuse/summonitem - spell = /obj/effect/proc_holder/spell/targeted/summonitem - spellname = "instant summons" - icon_state = "booksummons" - desc = "This book is bright and garish, very hard to miss." - -/obj/item/spellbook/oneuse/summonitem/recoil(mob/user as mob) - ..() - to_chat(user, "[src] suddenly vanishes!") - qdel(src) - -/obj/item/spellbook/oneuse/fake_gib - spell = /obj/effect/proc_holder/spell/targeted/touch/fake_disintegrate - spellname = "disintegrate" - icon_state = "bookfireball" - desc = "This book feels like it will rip stuff apart." - -/obj/item/spellbook/oneuse/sacredflame - spell = /obj/effect/proc_holder/spell/targeted/sacred_flame - spellname = "sacred flame" - icon_state = "booksacredflame" - desc = "Become one with the flames that burn within... and invite others to do so as well." - -/obj/item/spellbook/oneuse/random - icon_state = "random_book" - -/obj/item/spellbook/oneuse/random/initialize() - . = ..() - var/static/banned_spells = list(/obj/item/spellbook/oneuse/mime, /obj/item/spellbook/oneuse/mime/fingergun, /obj/item/spellbook/oneuse/mime/fingergun/fake, /obj/item/spellbook/oneuse/mime/greaterwall) - var/real_type = pick(subtypesof(/obj/item/spellbook/oneuse) - banned_spells) - new real_type(loc) - qdel(src) \ No newline at end of file +/datum/spellbook_entry + var/name = "Entry Name" + + var/spell_type = null + var/desc = "" + var/category = "Offensive" + var/log_name = "XX" //What it shows up as in logs + var/cost = 2 + var/refundable = TRUE + var/surplus = -1 // -1 for infinite, not used by anything atm + var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell + var/buy_word = "Learn" + var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook + +/datum/spellbook_entry/proc/IsSpellAvailable() // For config prefs / gamemode restrictions - these are round applied + return TRUE + +/datum/spellbook_entry/proc/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) // Specific circumstances + if(book.uses < cost || limit == 0) + return FALSE + return TRUE + +/datum/spellbook_entry/proc/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) //return TRUE on success + if(!S) + S = new spell_type() + + return LearnSpell(user, book, S) + +/datum/spellbook_entry/proc/LearnSpell(mob/living/carbon/human/user, obj/item/spellbook/book, obj/effect/proc_holder/spell/newspell) + for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) + if(initial(newspell.name) == initial(aspell.name)) // Not using directly in case it was learned from one spellbook then upgraded in another + if(aspell.spell_level >= aspell.level_max) + to_chat(user, "This spell cannot be improved further.") + return FALSE + else + aspell.name = initial(aspell.name) + aspell.spell_level++ + aspell.charge_max = round(initial(aspell.charge_max) - aspell.spell_level * (initial(aspell.charge_max) - aspell.cooldown_min) / aspell.level_max) + if(aspell.charge_max < aspell.charge_counter) + aspell.charge_counter = aspell.charge_max + switch(aspell.spell_level) + if(1) + to_chat(user, "You have improved [aspell.name] into Efficient [aspell.name].") + aspell.name = "Efficient [aspell.name]" + if(2) + to_chat(user, "You have further improved [aspell.name] into Quickened [aspell.name].") + aspell.name = "Quickened [aspell.name]" + if(3) + to_chat(user, "You have further improved [aspell.name] into Free [aspell.name].") + aspell.name = "Free [aspell.name]" + if(4) + to_chat(user, "You have further improved [aspell.name] into Instant [aspell.name].") + aspell.name = "Instant [aspell.name]" + if(aspell.spell_level >= aspell.level_max) + to_chat(user, "This spell cannot be strengthened any further.") + return TRUE + //No same spell found - just learn it + feedback_add_details("wizard_spell_learned", log_name) + user.mind.AddSpell(newspell) + to_chat(user, "You have learned [newspell.name].") + return TRUE + +/datum/spellbook_entry/proc/CanRefund(mob/living/carbon/human/user, obj/item/spellbook/book) + if(!refundable) + return FALSE + if(!S) + S = new spell_type() + for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) + if(initial(S.name) == initial(aspell.name)) + return TRUE + return FALSE + +/datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user, obj/item/spellbook/book) //return point value or -1 for failure + var/area/wizard_station/A = locate() + if(!(user in A.contents)) + to_chat(user, "You can only refund spells at the wizard lair.") + return -1 + if(!S) //This happens when the spell's source is from another spellbook, from loadouts, or adminery, this create a new template temporary spell + S = new spell_type() + var/spell_levels = 0 + for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) + if(initial(S.name) == initial(aspell.name)) + spell_levels = aspell.spell_level + user.mind.spell_list.Remove(aspell) + qdel(aspell) + if(S) //If we created a temporary spell above, delete it now. + qdel(S) + return cost * (spell_levels + 1) + return -1 + +/datum/spellbook_entry/proc/GetInfo() + if(!S) + S = new spell_type() + var/dat ="" + dat += "[initial(S.name)]" + if(S.charge_type == "recharge") + dat += " Cooldown:[S.charge_max/10]" + dat += " Cost:[cost]
      " + dat += "[S.desc][desc]
      " + dat += "[S.clothes_req?"Needs wizard garb":"Can be cast without wizard garb"]
      " + return dat + +//Main category - Spells +//Offensive +/datum/spellbook_entry/blind + name = "Blind" + spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind + log_name = "BD" + category = "Offensive" + cost = 1 + +/datum/spellbook_entry/lightningbolt + name = "Lightning Bolt" + spell_type = /obj/effect/proc_holder/spell/targeted/lightning + log_name = "LB" + category = "Offensive" + cost = 1 + +/datum/spellbook_entry/cluwne + name = "Curse of the Cluwne" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/cluwne + log_name = "CC" + category = "Offensive" + +/datum/spellbook_entry/banana_touch + name = "Banana Touch" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/banana + log_name = "BT" + cost = 1 + +/datum/spellbook_entry/mime_malaise + name = "Mime Malaise" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/mime_malaise + log_name = "MI" + cost = 1 + +/datum/spellbook_entry/horseman + name = "Curse of the Horseman" + spell_type = /obj/effect/proc_holder/spell/targeted/horsemask + log_name = "HH" + category = "Offensive" + +/datum/spellbook_entry/disintegrate + name = "Disintegrate" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/disintegrate + log_name = "DG" + category = "Offensive" + +/datum/spellbook_entry/fireball + name = "Fireball" + spell_type = /obj/effect/proc_holder/spell/fireball + log_name = "FB" + category = "Offensive" + +/datum/spellbook_entry/fleshtostone + name = "Flesh to Stone" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone + log_name = "FS" + category = "Offensive" + +/datum/spellbook_entry/mutate + name = "Mutate" + spell_type = /obj/effect/proc_holder/spell/targeted/genetic/mutate + log_name = "MU" + category = "Offensive" + +/datum/spellbook_entry/rod_form + name = "Rod Form" + spell_type = /obj/effect/proc_holder/spell/targeted/rod_form + log_name = "RF" + category = "Offensive" + +/datum/spellbook_entry/infinite_guns + name = "Lesser Summon Guns" + spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns + log_name = "IG" + category = "Offensive" + cost = 4 + +//Defensive +/datum/spellbook_entry/disabletech + name = "Disable Tech" + spell_type = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech + log_name = "DT" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/forcewall + name = "Force Wall" + spell_type = /obj/effect/proc_holder/spell/targeted/forcewall + log_name = "FW" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/greaterforcewall + name = "Greater Force Wall" + spell_type = /obj/effect/proc_holder/spell/targeted/forcewall/greater + log_name = "GFW" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/repulse + name = "Repulse" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/repulse + log_name = "RP" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/smoke + name = "Smoke" + spell_type = /obj/effect/proc_holder/spell/targeted/smoke + log_name = "SM" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/lichdom + name = "Bind Soul" + spell_type = /obj/effect/proc_holder/spell/targeted/lichdom + log_name = "LD" + category = "Defensive" + +/datum/spellbook_entry/lichdom/IsSpellAvailable() + if(SSticker.mode.name == "ragin' mages") + return FALSE + else + return TRUE + +/datum/spellbook_entry/magicm + name = "Magic Missile" + spell_type = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile + log_name = "MM" + category = "Defensive" + +/datum/spellbook_entry/timestop + name = "Time Stop" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop + log_name = "TS" + category = "Defensive" + +//Mobility +/datum/spellbook_entry/knock + name = "Knock" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock + log_name = "KN" + category = "Mobility" + cost = 1 + +/datum/spellbook_entry/blink + name = "Blink" + spell_type = /obj/effect/proc_holder/spell/targeted/turf_teleport/blink + log_name = "BL" + category = "Mobility" + +/datum/spellbook_entry/jaunt + name = "Ethereal Jaunt" + spell_type = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt + log_name = "EJ" + category = "Mobility" + +/datum/spellbook_entry/greaterknock + name = "Greater Knock" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock/greater + log_name = "GK" + category = "Mobility" + refundable = 0 //global effect on cast + +/datum/spellbook_entry/mindswap + name = "Mindswap" + spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer + log_name = "MT" + category = "Mobility" + +/datum/spellbook_entry/teleport + name = "Teleport" + spell_type = /obj/effect/proc_holder/spell/targeted/area_teleport/teleport + log_name = "TP" + category = "Mobility" + +//Assistance +/datum/spellbook_entry/charge + name = "Charge" + spell_type = /obj/effect/proc_holder/spell/targeted/charge + log_name = "CH" + category = "Assistance" + cost = 1 + +/datum/spellbook_entry/summonitem + name = "Summon Item" + spell_type = /obj/effect/proc_holder/spell/targeted/summonitem + log_name = "IS" + category = "Assistance" + cost = 1 + +/datum/spellbook_entry/noclothes + name = "Remove Clothes Requirement" + spell_type = /obj/effect/proc_holder/spell/noclothes + log_name = "NC" + category = "Assistance" + +//Rituals +/datum/spellbook_entry/summon + name = "Summon Stuff" + category = "Rituals" + refundable = FALSE + buy_word = "Cast" + var/active = FALSE + +/datum/spellbook_entry/summon/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) + return ..() && !active + +/datum/spellbook_entry/summon/GetInfo() + var/dat ="" + dat += "[name]" + if(cost>0) + dat += " Cost:[cost]
      " + else + dat += " No Cost
      " + dat += "[desc]
      " + if(active) + dat += "Already cast!
      " + return dat + +/datum/spellbook_entry/summon/ghosts + name = "Summon Ghosts" + desc = "Spook the crew out by making them see dead people. Be warned, ghosts are capricious and occasionally vindicative, and some will use their incredibly minor abilities to frustrate you." + cost = 0 + log_name = "SGH" + +/datum/spellbook_entry/summon/ghosts/IsSpellAvailable() + if(!SSticker.mode) // In case spellbook is placed on map + return FALSE + if(SSticker.mode.name == "ragin' mages") + return FALSE + else + return TRUE + +/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + new /datum/event/wizard/ghost() + active = TRUE + to_chat(user, "You have cast summon ghosts!") + playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1) + return TRUE + +/datum/spellbook_entry/summon/guns + name = "Summon Guns" + desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. There is a good chance that they will shoot each other first." + log_name = "SG" + +/datum/spellbook_entry/summon/guns/IsSpellAvailable() + if(!SSticker.mode) // In case spellbook is placed on map + return FALSE + if(SSticker.mode.name == "ragin' mages") + return FALSE + else + return TRUE + +/datum/spellbook_entry/summon/guns/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + feedback_add_details("wizard_spell_learned", log_name) + rightandwrong(SUMMON_GUNS, user, 10) + active = TRUE + playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) + to_chat(user, "You have cast summon guns!") + return TRUE + +/datum/spellbook_entry/summon/magic + name = "Summon Magic" + desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time." + log_name = "SU" + +/datum/spellbook_entry/summon/magic/IsSpellAvailable() + if(!SSticker.mode) // In case spellbook is placed on map + return FALSE + if(SSticker.mode.name == "ragin' mages") + return FALSE + else + return TRUE + +/datum/spellbook_entry/summon/magic/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + feedback_add_details("wizard_spell_learned", log_name) + rightandwrong(SUMMON_MAGIC, user, 10) + active = TRUE + playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) + to_chat(user, "You have cast summon magic!") + return TRUE + +//Main category - Magical Items +/datum/spellbook_entry/item + name = "Buy Item" + refundable = 0 + buy_word = "Summon" + var/item_path = null + +/datum/spellbook_entry/item/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + user.put_in_hands(new item_path) + feedback_add_details("wizard_spell_learned", log_name) + return 1 + +/datum/spellbook_entry/item/GetInfo() + var/dat ="" + dat += "[name]" + dat += " Cost:[cost]
      " + dat += "[desc]
      " + if(surplus>=0) + dat += "[surplus] left.
      " + return dat + +//Artefacts +/datum/spellbook_entry/item/necrostone + name = "A Necromantic Stone" + desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command." + item_path = /obj/item/necromantic_stone + log_name = "NS" + category = "Artefacts" + +/datum/spellbook_entry/item/scryingorb + name = "Scrying Orb" + desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you x-ray vision." + item_path = /obj/item/scrying + log_name = "SO" + category = "Artefacts" + +/datum/spellbook_entry/item/scryingorb/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + if(..()) + if(!(XRAY in user.mutations)) + user.mutations.Add(XRAY) + user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS) + user.see_in_dark = 8 + user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + to_chat(user, "The walls suddenly disappear.") + return TRUE + +/datum/spellbook_entry/item/soulstones + name = "Six Soul Stone Shards and the spell Artificer" + desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. The spell Artificer allows you to create arcane machines for the captured souls to pilot." + item_path = /obj/item/storage/belt/soulstone/full + log_name = "SS" + category = "Artefacts" + +/datum/spellbook_entry/item/soulstones/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + . = ..() + if(.) + user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/construct(null)) + return . + +/datum/spellbook_entry/item/wands + name = "Wand Assortment" + desc = "A collection of wands that allow for a wide variety of utility. Wands do not recharge, so be conservative in use. Comes in a handy belt." + item_path = /obj/item/storage/belt/wands/full + log_name = "WA" + category = "Artefacts" + +//Weapons and Armors +/datum/spellbook_entry/item/battlemage + name = "Battlemage Armour" + desc = "An ensorceled suit of armour, protected by a powerful shield. The shield can completely negate sixteen attacks before being permanently depleted. Despite appearance it is NOT spaceproof." + item_path = /obj/item/clothing/suit/space/hardsuit/shielded/wizard + limit = 1 + category = "Weapons and Armors" + log_name = "BMA" + +/datum/spellbook_entry/item/battlemage_charge + name = "Battlemage Armour Charges" + desc = "A powerful defensive rune, it will grant eight additional charges to a suit of battlemage armour." + item_path = /obj/item/wizard_armour_charge + category = "Weapons and Armors" + cost = 1 + log_name = "BMAC" + +/datum/spellbook_entry/item/mjolnir + name = "Mjolnir" + desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power." + item_path = /obj/item/twohanded/mjollnir + log_name = "MJ" + category = "Weapons and Armors" + +/datum/spellbook_entry/item/singularity_hammer + name = "Singularity Hammer" + desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everthing nearby to the point of impact." + item_path = /obj/item/twohanded/singularityhammer + log_name = "SI" + category = "Weapons and Armors" + +//Staves +/datum/spellbook_entry/item/staffdoor + name = "Staff of Door Creation" + desc = "A particular staff that can mold solid metal into ornate wooden doors. Useful for getting around in the absence of other transportation. Does not work on glass." + item_path = /obj/item/gun/magic/staff/door + log_name = "SD" + category = "Staves" + cost = 1 + +/datum/spellbook_entry/item/staffhealing + name = "Staff of Healing" + desc = "An altruistic staff that can heal the lame and raise the dead." + item_path = /obj/item/gun/magic/staff/healing + log_name = "SH" + category = "Staves" + cost = 1 + +/datum/spellbook_entry/item/staffslipping + name = "Staff of Slipping" + desc = "A staff that shoots magical bananas. These bananas will either slip or stun the target when hit. Surprisingly reliable!" + item_path = /obj/item/gun/magic/staff/slipping + log_name = "SL" + category = "Staves" + cost = 1 + +/datum/spellbook_entry/item/staffanimation + name = "Staff of Animation" + desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines." + item_path = /obj/item/gun/magic/staff/animate + log_name = "SA" + category = "Staves" + +/datum/spellbook_entry/item/staffchange + name = "Staff of Change" + desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." + item_path = /obj/item/gun/magic/staff/change + log_name = "ST" + category = "Staves" + +/datum/spellbook_entry/item/staffchaos + name = "Staff of Chaos" + desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended." + item_path = /obj/item/gun/magic/staff/chaos + log_name = "SC" + category = "Staves" + +//Summons +/datum/spellbook_entry/item/oozebottle + name = "Bottle of Ooze" + desc = "A bottle of magically infused ooze, which will awake an all-consuming Morph, capable of cunningly disguising itself as any object it comes in contact with and even casting some very basic spells. Be careful though, as Morph diet includes Wizards." + item_path = /obj/item/antag_spawner/morph + log_name = "BO" + category = "Summons" + limit = 3 + cost = 1 + +/datum/spellbook_entry/item/hugbottle + name = "Bottle of Tickles" + desc = "A bottle of magically infused fun, the smell of which will \ + attract adorable extradimensional beings when broken. These beings \ + are similar to slaughter demons, but are a little weaker and they do not permamently \ + kill their victims, instead putting them in an extradimensional hugspace, \ + to be released on the demon's death. Chaotic, but not ultimately \ + damaging. The crew's reaction to the other hand could be very \ + destructive." + item_path = /obj/item/antag_spawner/slaughter_demon/laughter + log_name = "HB" + category = "Summons" + limit = 3 + cost = 1 // Non-destructive; it's just a jape, sibling! + +/datum/spellbook_entry/item/bloodbottle + name = "Bottle of Blood" + desc = "A bottle of magically infused blood, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned by blood magic are indiscriminate in their killing, and you yourself may become a victim." + item_path = /obj/item/antag_spawner/slaughter_demon + log_name = "BB" + category = "Summons" + limit = 3 + +/datum/spellbook_entry/item/contract + name = "Contract of Apprenticeship" + desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side." + item_path = /obj/item/contract + log_name = "CT" + category = "Summons" + +/datum/spellbook_entry/item/tarotdeck + name = "Guardian Deck" + desc = "A deck of guardian tarot cards, capable of binding a personal guardian to your body. There are multiple types of guardian available, but all of them will transfer some amount of damage to you. \ + It would be wise to avoid buying these with anything capable of causing you to swap bodies with others." + item_path = /obj/item/guardiancreator + log_name = "TD" + category = "Summons" + limit = 1 + +//Spell loadouts datum, list of loadouts is in wizloadouts.dm +/datum/spellbook_entry/loadout + name = "Standard Loadout" + cost = 10 + category = "Standard" + refundable = FALSE + buy_word = "Summon" + var/list/items_path = list() + var/list/spells_path = list() + var/destroy_spellbook = FALSE //Destroy the spellbook when bought, for loadouts containing non-standard items/spells, otherwise wiz can refund spells + +/datum/spellbook_entry/loadout/GetInfo() + var/dat = "" + dat += "[name]" + if(cost > 0) + dat += " Cost:[cost]
      " + else + dat += " No Cost
      " + dat += "[desc]
      " + return dat + +/datum/spellbook_entry/loadout/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + if(destroy_spellbook) + var/response = alert(user, "The [src] loadout cannot be refunded once bought. Are you sure this is what you want?", "No refunds!", "No", "Yes") + if(response == "No") + return FALSE + to_chat(user, "[book] crumbles to ashes as you acquire its knowledge.") + qdel(book) + else if(items_path.len) + var/response = alert(user, "The [src] loadout contains items that will not be refundable if bought. Are you sure this is what you want?", "No refunds!", "No", "Yes") + if(response == "No") + return FALSE + if(items_path.len) + var/obj/item/storage/box/wizard/B = new(src) + for(var/path in items_path) + new path(B) + user.put_in_hands(B) + for(var/path in spells_path) + var/obj/effect/proc_holder/spell/S = new path() + LearnSpell(user, book, S) + return TRUE + +/obj/item/spellbook + name = "spell book" + desc = "The legendary book of spells of the wizard." + icon = 'icons/obj/library.dmi' + icon_state = "spellbook" + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_TINY + var/uses = 10 + var/temp = null + var/op = 1 + var/tab = null + var/main_tab = null + var/mob/living/carbon/human/owner + var/list/datum/spellbook_entry/entries = list() + var/list/categories = list() + var/list/main_categories = list("Spells", "Magical Items", "Loadouts") + var/list/spell_categories = list("Offensive", "Defensive", "Mobility", "Assistance", "Rituals") + var/list/item_categories = list("Artefacts", "Weapons and Armors", "Staves", "Summons") + var/list/loadout_categories = list("Standard", "Unique") + +/obj/item/spellbook/proc/initialize() + var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon - /datum/spellbook_entry/loadout + for(var/T in entry_types) + var/datum/spellbook_entry/E = new T + if(E.IsSpellAvailable()) + entries |= E + categories |= E.category + else + qdel(E) + main_tab = main_categories[1] + tab = categories[1] + +/obj/item/spellbook/New() + ..() + initialize() + +/obj/item/spellbook/attackby(obj/item/O as obj, mob/user as mob, params) + if(istype(O, /obj/item/contract)) + var/obj/item/contract/contract = O + if(contract.used) + to_chat(user, "The contract has been used, you can't get your points back now!") + else + to_chat(user, "You feed the contract back into the spellbook, refunding your points.") + uses+=2 + qdel(O) + return + + if(istype(O, /obj/item/antag_spawner/slaughter_demon)) + to_chat(user, "On second thought, maybe summoning a demon is a bad idea. You refund your points.") + if(istype(O, /obj/item/antag_spawner/slaughter_demon/laughter)) + uses += 1 + for(var/datum/spellbook_entry/item/hugbottle/HB in entries) + if(!isnull(HB.limit)) + HB.limit++ + else + uses += 2 + for(var/datum/spellbook_entry/item/bloodbottle/BB in entries) + if(!isnull(BB.limit)) + BB.limit++ + qdel(O) + return + + if(istype(O, /obj/item/antag_spawner/morph)) + to_chat(user, "On second thought, maybe awakening a morph is a bad idea. You refund your points.") + uses += 1 + for(var/datum/spellbook_entry/item/oozebottle/OB in entries) + if(!isnull(OB.limit)) + OB.limit++ + qdel(O) + return + return ..() + +/obj/item/spellbook/proc/GetCategoryHeader(category) + var/dat = "" + switch(category) + if("Offensive") + dat += "Spells geared towards debilitating and destroying.

      " + dat += "For spells: the number after the spell name is the cooldown time.
      " + dat += "You can reduce this number by spending more points on the spell.
      " + if("Defensive") + dat += "Spells geared towards improving your survivabilty or reducing foes ability to attack.

      " + dat += "For spells: the number after the spell name is the cooldown time.
      " + dat += "You can reduce this number by spending more points on the spell.
      " + if("Mobility") + dat += "Spells geared towards improving your ability to move. It is a good idea to take at least one.

      " + dat += "For spells: the number after the spell name is the cooldown time.
      " + dat += "You can reduce this number by spending more points on the spell.
      " + if("Assistance") + dat += "Spells geared towards improving your other items and abilities.

      " + dat += "For spells: the number after the spell name is the cooldown time.
      " + dat += "You can reduce this number by spending more points on the spell.
      " + if("Rituals") + dat += "These powerful spells are capable of changing the very fabric of reality. Not always in your favour.
      " + if("Weapons and Armors") + dat += "Various weapons and armors to crush your enemies and protect you from harm.

      " + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
      " + if("Staves") + dat += "Various staves granting you their power, which they slowly recharge over time.

      " + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
      " + if("Artefacts") + dat += "Various magical artefacts to aid you.

      " + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
      " + if("Summons") + dat += "Magical items geared towards bringing in outside forces to aid you.

      " + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
      " + if("Standard") + dat += "These battle-tested spell sets are easy to use and provide good balance between offense and defense.

      " + dat += "They all cost, and are worth, 10 spell points. You are able to refund any of the spells included as long as you stay in the wizard den.
      " + if("Unique") + dat += "These esoteric loadouts usually contain spells or items that cannot be bought elsewhere in this spellbook.

      " + dat += "Recommended for experienced wizards looking for something new. No refunds once purchased!
      " + return dat + +/obj/item/spellbook/proc/wrap(content) + var/dat = "" + dat +="Spellbook" + dat += {" + + + + "} + dat += {"[content]"} + return dat + +/obj/item/spellbook/attack_self(mob/user as mob) + if(!owner) + to_chat(user, "You bind the spellbook to yourself.") + owner = user + return + if(user != owner) + to_chat(user, "The [name] does not recognize you as it's owner and refuses to open!") + return + user.set_machine(src) + var/dat = "" + + dat += "
        " + var/list/cat_dat = list() + for(var/main_category in main_categories) + cat_dat[main_category] = "
        " + dat += "
      • [main_category]
      • " + dat += "
      " + dat += "
        " + switch(main_tab) + if("Spells") + for(var/category in categories) + if(category in spell_categories) + cat_dat[category] = "
        " + dat += "
      • [category]
      • " + if("Magical Items") + for(var/category in categories) + if(category in item_categories) + cat_dat[category] = "
        " + dat += "
      • [category]
      • " + if("Loadouts") + for(var/category in categories) + if(category in loadout_categories) + cat_dat[category] = "
        " + dat += "
      • [category]
      • " + dat += "
      • Points remaining : [uses]
      • " + dat += "
      " + + var/datum/spellbook_entry/E + for(var/i=1,i<=entries.len,i++) + var/spell_info = "" + E = entries[i] + spell_info += E.GetInfo() + if(E.CanBuy(user,src)) + spell_info+= "[E.buy_word]
      " + else + spell_info+= "Can't [E.buy_word]
      " + if(E.CanRefund(user,src)) + spell_info+= "Refund
      " + spell_info += "
      " + if(cat_dat[E.category]) + cat_dat[E.category] += spell_info + + for(var/category in categories) + dat += "
      " + dat += GetCategoryHeader(category) + dat += cat_dat[category] + dat += "
      " + + user << browse(wrap(dat), "window=spellbook;size=800x600") + onclose(user, "spellbook") + return + +/obj/item/spellbook/Topic(href, href_list) + if(..()) + return 1 + var/mob/living/carbon/human/H = usr + + if(!ishuman(H)) + return 1 + + if(H.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE) + temp = "If you got caught sneaking a peak from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not." + return 1 + + var/datum/spellbook_entry/E = null + if(loc == H || (in_range(src, H) && istype(loc, /turf))) + H.set_machine(src) + if(href_list["buy"]) + E = entries[text2num(href_list["buy"])] + if(E && E.CanBuy(H,src)) + if(E.Buy(H,src)) + if(E.limit) + E.limit-- + uses -= E.cost + else if(href_list["refund"]) + E = entries[text2num(href_list["refund"])] + if(E && E.refundable) + var/result = E.Refund(H,src) + if(result > 0) + if(!isnull(E.limit)) + E.limit += result + uses += result + else if(href_list["mainpage"]) + main_tab = sanitize(href_list["mainpage"]) + tab = sanitize(href_list["page"]) + if(main_tab == "Spells") + tab = spell_categories[1] + else if(main_tab == "Magical Items") + tab = item_categories[1] + else if(main_tab == "Loadouts") + tab = loadout_categories[1] + else if(href_list["page"]) + tab = sanitize(href_list["page"]) + attack_self(H) + return 1 + +//Single Use Spellbooks +/obj/item/spellbook/oneuse + var/spell = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic + var/spellname = "sandbox" + var/used = 0 + name = "spellbook of " + uses = 1 + desc = "This template spellbook was never meant for the eyes of man..." + +/obj/item/spellbook/oneuse/New() + ..() + name += spellname + +/obj/item/spellbook/oneuse/initialize() //No need to init + return + +/obj/item/spellbook/oneuse/attack_self(mob/user) + var/obj/effect/proc_holder/spell/S = new spell + for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) + if(knownspell.type == S.type) + if(user.mind) + if(user.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE || user.mind.special_role == SPECIAL_ROLE_WIZARD) + to_chat(user, "You're already far more versed in this spell than this flimsy how-to book can provide.") + else + to_chat(user, "You've already read this one.") + return + if(used) + recoil(user) + else + user.mind.AddSpell(S) + to_chat(user, "you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!") + user.create_log(MISC_LOG, "learned the spell [spellname] ([S])") + user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).") + onlearned(user) + +/obj/item/spellbook/oneuse/proc/recoil(mob/user) + user.visible_message("[src] glows in a black light!") + +/obj/item/spellbook/oneuse/proc/onlearned(mob/user) + used = 1 + user.visible_message("[src] glows dark for a second!") + +/obj/item/spellbook/oneuse/attackby() + return + +/obj/item/spellbook/oneuse/fireball + spell = /obj/effect/proc_holder/spell/fireball + spellname = "fireball" + icon_state = "bookfireball" + desc = "This book feels warm to the touch." + +/obj/item/spellbook/oneuse/fireball/recoil(mob/user as mob) + ..() + explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) + qdel(src) + +/obj/item/spellbook/oneuse/smoke + spell = /obj/effect/proc_holder/spell/targeted/smoke + spellname = "smoke" + icon_state = "booksmoke" + desc = "This book is overflowing with the dank arts." + +/obj/item/spellbook/oneuse/smoke/recoil(mob/user as mob) + ..() + to_chat(user, "Your stomach rumbles...") + user.adjust_nutrition(-200) + +/obj/item/spellbook/oneuse/blind + spell = /obj/effect/proc_holder/spell/targeted/trigger/blind + spellname = "blind" + icon_state = "bookblind" + desc = "This book looks blurry, no matter how you look at it." + +/obj/item/spellbook/oneuse/blind/recoil(mob/user as mob) + ..() + to_chat(user, "You go blind!") + user.EyeBlind(10) + +/obj/item/spellbook/oneuse/mindswap + spell = /obj/effect/proc_holder/spell/targeted/mind_transfer + spellname = "mindswap" + icon_state = "bookmindswap" + desc = "This book's cover is pristine, though its pages look ragged and torn." + var/mob/stored_swap = null //Used in used book recoils to store an identity for mindswaps + +/obj/item/spellbook/oneuse/mindswap/onlearned() + spellname = pick("fireball","smoke","blind","forcewall","knock","horses","charge") + icon_state = "book[spellname]" + name = "spellbook of [spellname]" //Note, desc doesn't change by design + ..() + +/obj/item/spellbook/oneuse/mindswap/recoil(mob/user) + ..() + if(stored_swap in GLOB.dead_mob_list) + stored_swap = null + if(!stored_swap) + stored_swap = user + to_chat(user, "For a moment you feel like you don't even know who you are anymore.") + return + if(stored_swap == user) + to_chat(user, "You stare at the book some more, but there doesn't seem to be anything else to learn...") + return + + var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new + swapper.cast(user, stored_swap, 1) + + to_chat(stored_swap, "You're suddenly somewhere else... and someone else?!") + to_chat(user, "Suddenly you're staring at [src] again... where are you, who are you?!") + stored_swap = null + +/obj/item/spellbook/oneuse/forcewall + spell = /obj/effect/proc_holder/spell/targeted/forcewall + spellname = "forcewall" + icon_state = "bookforcewall" + desc = "This book has a dedication to mimes everywhere inside the front cover." + +/obj/item/spellbook/oneuse/forcewall/recoil(mob/user as mob) + ..() + to_chat(user, "You suddenly feel very solid!") + var/obj/structure/closet/statue/S = new /obj/structure/closet/statue(user.loc, user) + S.timer = 30 + user.drop_item() + +/obj/item/spellbook/oneuse/knock + spell = /obj/effect/proc_holder/spell/aoe_turf/knock + spellname = "knock" + icon_state = "bookknock" + desc = "This book is hard to hold closed properly." + +/obj/item/spellbook/oneuse/knock/recoil(mob/user as mob) + ..() + to_chat(user, "You're knocked down!") + user.Weaken(20) + +/obj/item/spellbook/oneuse/horsemask + spell = /obj/effect/proc_holder/spell/targeted/horsemask + spellname = "horses" + icon_state = "bookhorses" + desc = "This book is more horse than your mind has room for." + +/obj/item/spellbook/oneuse/horsemask/recoil(mob/living/carbon/user as mob) + if(istype(user, /mob/living/carbon/human)) + to_chat(user, "HOR-SIE HAS RISEN") + var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead + magichead.flags |= NODROP | DROPDEL //curses! + magichead.flags_inv = null //so you can still see their face + magichead.voicechange = 1 //NEEEEIIGHH + if(!user.unEquip(user.wear_mask)) + qdel(user.wear_mask) + user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) + qdel(src) + else + to_chat(user, "I say thee neigh") + +/obj/item/spellbook/oneuse/charge + spell = /obj/effect/proc_holder/spell/targeted/charge + spellname = "charging" + icon_state = "bookcharge" + desc = "This book is made of 100% post-consumer wizard." + +/obj/item/spellbook/oneuse/charge/recoil(mob/user as mob) + ..() + to_chat(user, "[src] suddenly feels very warm!") + empulse(src, 1, 1) + +/obj/item/spellbook/oneuse/summonitem + spell = /obj/effect/proc_holder/spell/targeted/summonitem + spellname = "instant summons" + icon_state = "booksummons" + desc = "This book is bright and garish, very hard to miss." + +/obj/item/spellbook/oneuse/summonitem/recoil(mob/user as mob) + ..() + to_chat(user, "[src] suddenly vanishes!") + qdel(src) + +/obj/item/spellbook/oneuse/fake_gib + spell = /obj/effect/proc_holder/spell/targeted/touch/fake_disintegrate + spellname = "disintegrate" + icon_state = "bookfireball" + desc = "This book feels like it will rip stuff apart." + +/obj/item/spellbook/oneuse/sacredflame + spell = /obj/effect/proc_holder/spell/targeted/sacred_flame + spellname = "sacred flame" + icon_state = "booksacredflame" + desc = "Become one with the flames that burn within... and invite others to do so as well." + +/obj/item/spellbook/oneuse/random + icon_state = "random_book" + +/obj/item/spellbook/oneuse/random/initialize() + . = ..() + var/static/banned_spells = list(/obj/item/spellbook/oneuse/mime, /obj/item/spellbook/oneuse/mime/fingergun, /obj/item/spellbook/oneuse/mime/fingergun/fake, /obj/item/spellbook/oneuse/mime/greaterwall) + var/real_type = pick(subtypesof(/obj/item/spellbook/oneuse) - banned_spells) + new real_type(loc) + qdel(src) diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 131afd84fff..a0bb4c349af 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -1,282 +1,293 @@ -/datum/game_mode - var/list/datum/mind/wizards = list() - -/datum/game_mode/wizard - name = "wizard" - config_tag = "wizard" - required_players = 20 - required_enemies = 1 - recommended_enemies = 1 - var/use_huds = 1 - - var/finished = 0 - var/but_wait_theres_more = 0 - -/datum/game_mode/wizard/announce() - to_chat(world, "The current game mode is - Wizard!") - to_chat(world, "There is a SPACE WIZARD on the station. You can't let him achieve his objective!") - -/datum/game_mode/wizard/can_start()//This could be better, will likely have to recode it later - if(!..()) - return 0 - var/list/datum/mind/possible_wizards = get_players_for_role(ROLE_WIZARD) - if(possible_wizards.len==0) - return 0 - var/datum/mind/wizard = pick(possible_wizards) - - wizards += wizard - modePlayer += wizard - wizard.assigned_role = SPECIAL_ROLE_WIZARD //So they aren't chosen for other jobs. - wizard.special_role = SPECIAL_ROLE_WIZARD - wizard.original = wizard.current - if(wizardstart.len == 0) - to_chat(wizard.current, "A starting location for you could not be found, please report this bug!") - return 0 - return 1 - -/datum/game_mode/wizard/pre_setup() - for(var/datum/mind/wiz in wizards) - wiz.current.loc = pick(wizardstart) - ..() - return 1 - -/datum/game_mode/wizard/post_setup() - for(var/datum/mind/wizard in wizards) - log_game("[key_name(wizard)] has been selected as a Wizard") - forge_wizard_objectives(wizard) - //learn_basic_spells(wizard.current) - equip_wizard(wizard.current) - name_wizard(wizard.current) - greet_wizard(wizard) - if(use_huds) - update_wiz_icons_added(wizard) - - ..() - -/datum/game_mode/proc/remove_wizard(datum/mind/wizard_mind) - if(wizard_mind in wizards) - SSticker.mode.wizards -= wizard_mind - wizard_mind.special_role = null - wizard_mind.current.create_attack_log("De-wizarded") - wizard_mind.current.spellremove(wizard_mind.current) - wizard_mind.current.faction = list("Station") - if(issilicon(wizard_mind.current)) - to_chat(wizard_mind.current, "You have been turned into a robot! You can feel your magical powers fading away...") - else - to_chat(wizard_mind.current, "You have been brainwashed! You are no longer a wizard.") - SSticker.mode.update_wiz_icons_removed(wizard_mind) - -/datum/game_mode/proc/update_wiz_icons_added(datum/mind/wiz_mind) - var/datum/atom_hud/antag/wizhud = huds[ANTAG_HUD_WIZ] - wizhud.join_hud(wiz_mind.current) - set_antag_hud(wiz_mind.current, ((wiz_mind in wizards) ? "hudwizard" : "apprentice")) - -/datum/game_mode/proc/update_wiz_icons_removed(datum/mind/wiz_mind) - var/datum/atom_hud/antag/wizhud = huds[ANTAG_HUD_WIZ] - wizhud.leave_hud(wiz_mind.current) - set_antag_hud(wiz_mind.current, null) - -/datum/game_mode/proc/forge_wizard_objectives(var/datum/mind/wizard) - var/datum/objective/wizchaos/wiz_objective = new - wiz_objective.owner = wizard - wizard.objectives += wiz_objective - return - -/datum/game_mode/proc/name_wizard(mob/living/carbon/human/wizard_mob) - //Allows the wizard to choose a custom name or go with a random one. Spawn 0 so it does not lag the round starting. - var/wizard_name_first = pick(GLOB.wizard_first) - var/wizard_name_second = pick(GLOB.wizard_second) - var/randomname = "[wizard_name_first] [wizard_name_second]" - spawn(0) - var/newname = sanitize(copytext(input(wizard_mob, "You are the Space Wizard. Would you like to change your name to something else?", "Name change", randomname) as null|text,1,MAX_NAME_LEN)) - - if(!newname) - newname = randomname - - wizard_mob.real_name = newname - wizard_mob.name = newname - if(wizard_mob.mind) - wizard_mob.mind.name = newname - return - -/datum/game_mode/proc/greet_wizard(var/datum/mind/wizard, var/you_are=1) - addtimer(CALLBACK(wizard.current, /mob/.proc/playsound_local, null, 'sound/ambience/antag/ragesmages.ogg', 100, 0), 30) - if(you_are) - to_chat(wizard.current, "You are the Space Wizard!") - to_chat(wizard.current, "The Space Wizards Federation has given you the following tasks:") - - var/obj_count = 1 - for(var/datum/objective/objective in wizard.objectives) - to_chat(wizard.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - -/*/datum/game_mode/proc/learn_basic_spells(mob/living/carbon/human/wizard_mob) - if(!istype(wizard_mob)) - return - if(!config.feature_object_spell_system) - wizard_mob.verbs += /client/proc/jaunt - wizard_mob.mind.special_verbs += /client/proc/jaunt - else - wizard_mob.spell_list += new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(usr) -*/ - -/datum/game_mode/proc/equip_wizard(mob/living/carbon/human/wizard_mob) - if(!istype(wizard_mob)) - return - - //So zards properly get their items when they are admin-made. - qdel(wizard_mob.wear_suit) - qdel(wizard_mob.head) - qdel(wizard_mob.shoes) - qdel(wizard_mob.r_hand) - qdel(wizard_mob.r_store) - qdel(wizard_mob.l_store) - - wizard_mob.equip_to_slot_or_del(new /obj/item/radio/headset(wizard_mob), slot_l_ear) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(wizard_mob), slot_w_uniform) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(wizard_mob), slot_shoes) - if(!isplasmaman(wizard_mob)) //handled in the species file for plasmen on the afterjob equip proc for now - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(wizard_mob), slot_wear_suit) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(wizard_mob), slot_head) - wizard_mob.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(wizard_mob), slot_back) - wizard_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival(wizard_mob), slot_in_backpack) - wizard_mob.equip_to_slot_or_del(new /obj/item/teleportation_scroll(wizard_mob), slot_r_store) - var/obj/item/spellbook/spellbook = new /obj/item/spellbook(wizard_mob) - spellbook.owner = wizard_mob - wizard_mob.equip_to_slot_or_del(spellbook, slot_r_hand) - - wizard_mob.faction = list("wizard") - - wizard_mob.dna.species.after_equip_job(null, wizard_mob) - - to_chat(wizard_mob, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.") - to_chat(wizard_mob, "The spellbook is bound to you, and others cannot use it.") - to_chat(wizard_mob, "In your pockets you will find a teleport scroll. Use it as needed.") - wizard_mob.mind.store_memory("Remember: do not forget to prepare your spells.") - wizard_mob.update_icons() - wizard_mob.gene_stability += DEFAULT_GENE_STABILITY //magic - return 1 - -// Checks if the game should end due to all wizards and apprentices being dead, or MMI'd/Borged -/datum/game_mode/wizard/check_finished() - var/wizards_alive = 0 - var/traitors_alive = 0 - - // Wizards - for(var/datum/mind/wizard in wizards) - if(!istype(wizard.current,/mob/living/carbon)) - continue - if(wizard.current.stat==DEAD) - continue - if(istype(wizard.current, /obj/item/mmi)) // wizard is in an MMI, don't count them as alive - continue - wizards_alive++ - - // Apprentices - classified as "traitors" - if(!wizards_alive) - for(var/datum/mind/traitor in traitors) - if(!istype(traitor.current,/mob/living/carbon)) - continue - if(traitor.current.stat==DEAD) - continue - if(istype(traitor.current, /obj/item/mmi)) // apprentice is in an MMI, don't count them as alive - continue - traitors_alive++ - - if(wizards_alive || traitors_alive || but_wait_theres_more) - return ..() - else - finished = 1 - return 1 - -/datum/game_mode/wizard/declare_completion(var/ragin = 0) - if(finished && !ragin) - feedback_set_details("round_end_result","wizard loss - wizard killed") - to_chat(world, " The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!") - ..() - return 1 - -/datum/game_mode/proc/auto_declare_completion_wizard() - if(wizards.len) - var/text = "
      the wizards/witches were:" - - for(var/datum/mind/wizard in wizards) - - text += "
      [wizard.key] was [wizard.name] (" - if(wizard.current) - if(wizard.current.stat == DEAD) - text += "died" - else - text += "survived" - if(wizard.current.real_name != wizard.name) - text += " as [wizard.current.real_name]" - else - text += "body destroyed" - text += ")" - - var/count = 1 - var/wizardwin = 1 - for(var/datum/objective/objective in wizard.objectives) - if(objective.check_completion()) - text += "
      Objective #[count]: [objective.explanation_text] Success!" - feedback_add_details("wizard_objective","[objective.type]|SUCCESS") - else - text += "
      Objective #[count]: [objective.explanation_text] Fail." - feedback_add_details("wizard_objective","[objective.type]|FAIL") - wizardwin = 0 - count++ - - if(wizard.current && wizard.current.stat!=DEAD && wizardwin) - text += "
      The wizard was successful!" - feedback_add_details("wizard_success","SUCCESS") - else - text += "
      The wizard has failed!" - feedback_add_details("wizard_success","FAIL") - if(wizard.spell_list) - text += "
      [wizard.name] used the following spells: " - var/i = 1 - for(var/obj/effect/proc_holder/spell/S in wizard.spell_list) - text += "[S.name]" - if(wizard.spell_list.len > i) - text += ", " - i++ - text += "
      " - - to_chat(world, text) - return 1 - -//OTHER PROCS - -//To batch-remove wizard spells. Linked to mind.dm -/mob/proc/spellremove(mob/M) - if(!mind) - return - for(var/obj/effect/proc_holder/spell/spell_to_remove in mind.spell_list) - qdel(spell_to_remove) - mind.spell_list -= spell_to_remove - -//To batch-remove mob spells. -/mob/proc/mobspellremove(mob/M) - for(var/obj/effect/proc_holder/spell/spell_to_remove in mob_spell_list) - qdel(spell_to_remove) - mob_spell_list -= spell_to_remove - -/*Checks if the wizard can cast spells. -Made a proc so this is not repeated 14 (or more) times.*/ -/mob/proc/casting() -//Removed the stat check because not all spells require clothing now. - if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe)) - to_chat(usr, "I don't feel strong enough without my robe.") - return 0 - if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal)) - to_chat(usr, "I don't feel strong enough without my sandals.") - return 0 - if(!istype(usr:head, /obj/item/clothing/head/wizard)) - to_chat(usr, "I don't feel strong enough without my hat.") - return 0 - else - return 1 - -/proc/iswizard(mob/living/M as mob) - return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.wizards) +/datum/game_mode + var/list/datum/mind/wizards = list() + var/list/datum/mind/apprentices = list() + +/datum/game_mode/wizard + name = "wizard" + config_tag = "wizard" + required_players = 20 + required_enemies = 1 + recommended_enemies = 1 + var/use_huds = 1 + + var/finished = 0 + var/but_wait_theres_more = 0 + +/datum/game_mode/wizard/announce() + to_chat(world, "The current game mode is - Wizard!") + to_chat(world, "There is a SPACE WIZARD on the station. You can't let him achieve his objective!") + +/datum/game_mode/wizard/can_start()//This could be better, will likely have to recode it later + if(!..()) + return 0 + var/list/datum/mind/possible_wizards = get_players_for_role(ROLE_WIZARD) + if(possible_wizards.len==0) + return 0 + var/datum/mind/wizard = pick(possible_wizards) + + wizards += wizard + modePlayer += wizard + wizard.assigned_role = SPECIAL_ROLE_WIZARD //So they aren't chosen for other jobs. + wizard.special_role = SPECIAL_ROLE_WIZARD + wizard.original = wizard.current + if(GLOB.wizardstart.len == 0) + to_chat(wizard.current, "A starting location for you could not be found, please report this bug!") + return 0 + return 1 + +/datum/game_mode/wizard/pre_setup() + for(var/datum/mind/wiz in wizards) + wiz.current.loc = pick(GLOB.wizardstart) + ..() + return 1 + +/datum/game_mode/wizard/post_setup() + for(var/datum/mind/wizard in wizards) + log_game("[key_name(wizard)] has been selected as a Wizard") + forge_wizard_objectives(wizard) + //learn_basic_spells(wizard.current) + equip_wizard(wizard.current) + name_wizard(wizard.current) + greet_wizard(wizard) + if(use_huds) + update_wiz_icons_added(wizard) + + ..() + +/datum/game_mode/proc/remove_wizard(datum/mind/wizard_mind) + if(wizard_mind in wizards) + SSticker.mode.wizards -= wizard_mind + wizard_mind.special_role = null + wizard_mind.current.create_attack_log("De-wizarded") + wizard_mind.current.create_log(CONVERSION_LOG, "De-wizarded") + wizard_mind.current.spellremove(wizard_mind.current) + wizard_mind.current.faction = list("Station") + if(issilicon(wizard_mind.current)) + to_chat(wizard_mind.current, "You have been turned into a robot! You can feel your magical powers fading away...") + else + to_chat(wizard_mind.current, "You have been brainwashed! You are no longer a wizard.") + SSticker.mode.update_wiz_icons_removed(wizard_mind) + +/datum/game_mode/proc/update_wiz_icons_added(datum/mind/wiz_mind) + var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ] + wizhud.join_hud(wiz_mind.current) + set_antag_hud(wiz_mind.current, ((wiz_mind in wizards) ? "hudwizard" : "apprentice")) + +/datum/game_mode/proc/update_wiz_icons_removed(datum/mind/wiz_mind) + var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ] + wizhud.leave_hud(wiz_mind.current) + set_antag_hud(wiz_mind.current, null) + +/datum/game_mode/proc/forge_wizard_objectives(var/datum/mind/wizard) + var/datum/objective/wizchaos/wiz_objective = new + wiz_objective.owner = wizard + wizard.objectives += wiz_objective + return + +/datum/game_mode/proc/name_wizard(mob/living/carbon/human/wizard_mob) + //Allows the wizard to choose a custom name or go with a random one. Spawn 0 so it does not lag the round starting. + var/wizard_name_first = pick(GLOB.wizard_first) + var/wizard_name_second = pick(GLOB.wizard_second) + var/randomname = "[wizard_name_first] [wizard_name_second]" + spawn(0) + var/newname = sanitize(copytext(input(wizard_mob, "You are the Space Wizard. Would you like to change your name to something else?", "Name change", randomname) as null|text,1,MAX_NAME_LEN)) + + if(!newname) + newname = randomname + + wizard_mob.real_name = newname + wizard_mob.name = newname + if(wizard_mob.mind) + wizard_mob.mind.name = newname + return + +/datum/game_mode/proc/greet_wizard(var/datum/mind/wizard, var/you_are=1) + addtimer(CALLBACK(wizard.current, /mob/.proc/playsound_local, null, 'sound/ambience/antag/ragesmages.ogg', 100, 0), 30) + if(you_are) + to_chat(wizard.current, "You are the Space Wizard!") + to_chat(wizard.current, "The Space Wizards Federation has given you the following tasks:") + + var/obj_count = 1 + for(var/datum/objective/objective in wizard.objectives) + to_chat(wizard.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + return + +/*/datum/game_mode/proc/learn_basic_spells(mob/living/carbon/human/wizard_mob) + if(!istype(wizard_mob)) + return + if(!config.feature_object_spell_system) + wizard_mob.verbs += /client/proc/jaunt + wizard_mob.mind.special_verbs += /client/proc/jaunt + else + wizard_mob.spell_list += new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(usr) +*/ + +/datum/game_mode/proc/equip_wizard(mob/living/carbon/human/wizard_mob) + if(!istype(wizard_mob)) + return + + //So zards properly get their items when they are admin-made. + qdel(wizard_mob.wear_suit) + qdel(wizard_mob.head) + qdel(wizard_mob.shoes) + qdel(wizard_mob.r_hand) + qdel(wizard_mob.r_store) + qdel(wizard_mob.l_store) + + if(isplasmaman(wizard_mob)) + wizard_mob.equipOutfit(new /datum/outfit/plasmaman/wizard) + wizard_mob.internal = wizard_mob.r_hand + wizard_mob.update_action_buttons_icon() + else + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(wizard_mob), slot_w_uniform) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(wizard_mob), slot_head) + wizard_mob.dna.species.after_equip_job(null, wizard_mob) + wizard_mob.rejuvenate() //fix any damage taken by naked vox/plasmamen/etc while round setups + wizard_mob.equip_to_slot_or_del(new /obj/item/radio/headset(wizard_mob), slot_l_ear) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(wizard_mob), slot_shoes) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(wizard_mob), slot_wear_suit) + wizard_mob.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(wizard_mob), slot_back) + if(wizard_mob.dna.species.speciesbox) + wizard_mob.equip_to_slot_or_del(new wizard_mob.dna.species.speciesbox(wizard_mob), slot_in_backpack) + else + wizard_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival(wizard_mob), slot_in_backpack) + wizard_mob.equip_to_slot_or_del(new /obj/item/teleportation_scroll(wizard_mob), slot_r_store) + var/obj/item/spellbook/spellbook = new /obj/item/spellbook(wizard_mob) + spellbook.owner = wizard_mob + wizard_mob.equip_to_slot_or_del(spellbook, slot_l_hand) + + wizard_mob.faction = list("wizard") + + + + to_chat(wizard_mob, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.") + to_chat(wizard_mob, "The spellbook is bound to you, and others cannot use it.") + to_chat(wizard_mob, "In your pockets you will find a teleport scroll. Use it as needed.") + wizard_mob.mind.store_memory("Remember: do not forget to prepare your spells.") + wizard_mob.update_icons() + wizard_mob.gene_stability += DEFAULT_GENE_STABILITY //magic + return TRUE + +// Checks if the game should end due to all wizards and apprentices being dead, or MMI'd/Borged +/datum/game_mode/wizard/check_finished() + var/wizards_alive = 0 + var/apprentices_alive = 0 + + // Wizards + for(var/datum/mind/wizard in wizards) + if(!istype(wizard.current,/mob/living/carbon)) + continue + if(wizard.current.stat==DEAD) + continue + if(istype(wizard.current, /obj/item/mmi)) // wizard is in an MMI, don't count them as alive + continue + wizards_alive++ + + // Apprentices + if(!wizards_alive) + for(var/datum/mind/apprentice in apprentices) + if(!istype(apprentice.current,/mob/living/carbon)) + continue + if(apprentice.current.stat==DEAD) + continue + if(istype(apprentice.current, /obj/item/mmi)) // apprentice is in an MMI, don't count them as alive + continue + apprentices_alive++ + + if(wizards_alive || apprentices_alive || but_wait_theres_more) + return ..() + else + finished = 1 + return 1 + +/datum/game_mode/wizard/declare_completion(var/ragin = 0) + if(finished && !ragin) + feedback_set_details("round_end_result","wizard loss - wizard killed") + to_chat(world, " The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!") + ..() + return 1 + +/datum/game_mode/proc/auto_declare_completion_wizard() + if(wizards.len) + var/text = "
      the wizards/witches were:" + + for(var/datum/mind/wizard in wizards) + + text += "
      [wizard.key] was [wizard.name] (" + if(wizard.current) + if(wizard.current.stat == DEAD) + text += "died" + else + text += "survived" + if(wizard.current.real_name != wizard.name) + text += " as [wizard.current.real_name]" + else + text += "body destroyed" + text += ")" + + var/count = 1 + var/wizardwin = 1 + for(var/datum/objective/objective in wizard.objectives) + if(objective.check_completion()) + text += "
      Objective #[count]: [objective.explanation_text] Success!" + feedback_add_details("wizard_objective","[objective.type]|SUCCESS") + else + text += "
      Objective #[count]: [objective.explanation_text] Fail." + feedback_add_details("wizard_objective","[objective.type]|FAIL") + wizardwin = 0 + count++ + + if(wizard.current && wizard.current.stat!=DEAD && wizardwin) + text += "
      The wizard was successful!" + feedback_add_details("wizard_success","SUCCESS") + else + text += "
      The wizard has failed!" + feedback_add_details("wizard_success","FAIL") + if(wizard.spell_list) + text += "
      [wizard.name] used the following spells: " + var/i = 1 + for(var/obj/effect/proc_holder/spell/S in wizard.spell_list) + text += "[S.name]" + if(wizard.spell_list.len > i) + text += ", " + i++ + text += "
      " + + to_chat(world, text) + return 1 + +//OTHER PROCS + +//To batch-remove wizard spells. Linked to mind.dm +/mob/proc/spellremove(mob/M) + if(!mind) + return + for(var/obj/effect/proc_holder/spell/spell_to_remove in mind.spell_list) + qdel(spell_to_remove) + mind.spell_list -= spell_to_remove + +//To batch-remove mob spells. +/mob/proc/mobspellremove(mob/M) + for(var/obj/effect/proc_holder/spell/spell_to_remove in mob_spell_list) + qdel(spell_to_remove) + mob_spell_list -= spell_to_remove + +/*Checks if the wizard can cast spells. +Made a proc so this is not repeated 14 (or more) times.*/ +/mob/proc/casting() +//Removed the stat check because not all spells require clothing now. + if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe)) + to_chat(usr, "I don't feel strong enough without my robe.") + return 0 + if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal)) + to_chat(usr, "I don't feel strong enough without my sandals.") + return 0 + if(!istype(usr:head, /obj/item/clothing/head/wizard)) + to_chat(usr, "I don't feel strong enough without my hat.") + return 0 + else + return 1 + +/proc/iswizard(mob/living/M as mob) + return istype(M) && M.mind && SSticker && SSticker.mode && ((M.mind in SSticker.mode.wizards) || (M.mind in SSticker.mode.apprentices)) diff --git a/code/game/gamemodes/wizard/wizloadouts.dm b/code/game/gamemodes/wizard/wizloadouts.dm new file mode 100644 index 00000000000..0e53d30f1bf --- /dev/null +++ b/code/game/gamemodes/wizard/wizloadouts.dm @@ -0,0 +1,71 @@ +//Contains wizard loadouts and associated unique spells + +//Standard loadouts, which are meant to be suggestions for beginners. Should always be worth exactly 10 spell points, and only contain standard wizard spells/items. +/datum/spellbook_entry/loadout/mutant + name = "Offense Focus : Mutant" + desc = "A spellset focused around the Mutate spell as its main source of damage, which provides stun protection, laser eyes, and strong punches.
      \ + Ethereal Jaunt and Blink provide escape and mobility, while Magic Missile and Disintegrate can be used together for dangerous or key targets.
      \ + As this set lacks any form of healing or resurrection, healing items should be acquired from the station, and you should be careful to avoid being hurt in the first place.

      \ + Provides Mutate, Ethereal Jaunt, Blink, Magic Missile, and Disintegrate." + log_name = "OM" + spells_path = list(/obj/effect/proc_holder/spell/targeted/genetic/mutate, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/targeted/turf_teleport/blink, \ + /obj/effect/proc_holder/spell/targeted/projectile/magic_missile, /obj/effect/proc_holder/spell/targeted/touch/disintegrate) + +/datum/spellbook_entry/loadout/lich + name = "Defense Focus : Lich" + desc = "This spellset uses the Bind Soul spell to safeguard your life as a lich and allow for more dangerous offensive spells to be used.
      \ + Ethereal Jaunt provides escape, Fireball and Rod Form are your offensive spells, and Disable Tech and Greater Forcewall provides utility in disabling sec equipment or blocking their path.
      \ + Care should be taken in hiding the item you choose as your phylactery after using Bind Soul, as you cannot revive if it destroyed or too far from your body!

      \ +
      Provides Bind Soul, Ethereal Jaunt, Fireball, Rod Form, Disable Tech, and Greater Forcewall." + log_name = "DL" + spells_path = list(/obj/effect/proc_holder/spell/targeted/lichdom, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/fireball, \ + /obj/effect/proc_holder/spell/targeted/rod_form, /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech, /obj/effect/proc_holder/spell/targeted/forcewall/greater) + +/datum/spellbook_entry/loadout/wands + name = "Utility Focus : Wands" + desc = "This set contain a Belt of Wands, providing offensive, defensive, and utility wands. Wands have limited charges, but can be partially recharged with the Charge spell included.
      \ + Ethereal Jaunt and Blink provide escape and mobility, while Disintegrate and Repulse can be used to annihilate or push away anyone that gets too close to you.
      \ + Do not lose any of your wands to the station's crew, as they are extremely deadly even in their hands. Remember that the Revive wand can be used on yourself for a full heal!

      \ +
      Provides a Belt of Wands, Charge, Ethereal Jaunt, Blink, Repulse, and Disintegrate." + log_name = "UW" + items_path = list(/obj/item/storage/belt/wands/full) + spells_path = list(/obj/effect/proc_holder/spell/targeted/charge, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/targeted/turf_teleport/blink, \ + /obj/effect/proc_holder/spell/aoe_turf/repulse, /obj/effect/proc_holder/spell/targeted/touch/disintegrate) + +//Unique loadouts, which are more gimmicky. Should contain some unique spell or item that separates it from just buying standard wiz spells, and be balanced around a 10 spell point cost. +/datum/spellbook_entry/loadout/mimewiz + name = "Silencio" + desc = "...

      \ +
      Provides Finger Gun and Invisible Greater Wall manuals, Mime Robes, a Cane and Duct Tape, Ethereal Jaunt, Blink, Teleport, Mime Malaise, Knock, and Stop Time." + log_name = "SHH" + items_path = list(/obj/item/spellbook/oneuse/mime/fingergun, /obj/item/spellbook/oneuse/mime/greaterwall, /obj/item/clothing/suit/wizrobe/mime, /obj/item/clothing/head/wizard/mime, \ + /obj/item/clothing/mask/gas/mime/wizard, /obj/item/clothing/shoes/sandal/marisa, /obj/item/cane, /obj/item/stack/tape_roll) + spells_path = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/targeted/turf_teleport/blink, /obj/effect/proc_holder/spell/targeted/area_teleport/teleport, \ + /obj/effect/proc_holder/spell/targeted/touch/mime_malaise, /obj/effect/proc_holder/spell/aoe_turf/knock, /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop) + category = "Unique" + destroy_spellbook = TRUE + +/datum/spellbook_entry/loadout/mimewiz/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + if(user.mind) + user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mime/speak(null)) + user.mind.miming = TRUE + ..() + +/datum/spellbook_entry/loadout/gunreaper + name = "Gunslinging Reaper" + desc = "Cloned over and over, the souls aboard this station yearn for a deserved rest.
      \ + Bring them to the afterlife, one trigger pull at a time.
      \ + You will likely need to scavenge additional ammo or weapons aboard the station.

      \ +
      Provides a .357 Revolver, 4 speedloaders of ammo, Ethereal Jaunt, Blink, Summon Item, No Clothes, and Bind Soul, with a unique outfit." + log_name = "GR" + items_path = list(/obj/item/gun/projectile/revolver, /obj/item/ammo_box/a357, /obj/item/ammo_box/a357, /obj/item/ammo_box/a357, /obj/item/ammo_box/a357, /obj/item/clothing/under/syndicate) + spells_path = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/targeted/turf_teleport/blink, \ + /obj/effect/proc_holder/spell/targeted/summonitem, /obj/effect/proc_holder/spell/noclothes, /obj/effect/proc_holder/spell/targeted/lichdom/gunslinger) + category = "Unique" + destroy_spellbook = TRUE + +/obj/effect/proc_holder/spell/targeted/lichdom/gunslinger/equip_lich(mob/living/carbon/human/H) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/det_suit(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(H), slot_gloves) + H.equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(H), slot_w_uniform) diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index a31f694f7fc..7f93fd9fef6 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -1,629 +1,514 @@ -/var/const/access_security = 1 // Security equipment -/var/const/access_brig = 2 // Brig timers and permabrig -/var/const/access_armory = 3 -/var/const/access_forensics_lockers= 4 -/var/const/access_medical = 5 -/var/const/access_morgue = 6 -/var/const/access_tox = 7 -/var/const/access_tox_storage = 8 -/var/const/access_genetics = 9 -/var/const/access_engine = 10 -/var/const/access_engine_equip = 11 -/var/const/access_maint_tunnels = 12 -/var/const/access_external_airlocks = 13 -/var/const/access_emergency_storage = 14 -/var/const/access_change_ids = 15 -/var/const/access_ai_upload = 16 -/var/const/access_teleporter = 17 -/var/const/access_eva = 18 -/var/const/access_heads = 19 -/var/const/access_captain = 20 -/var/const/access_all_personal_lockers = 21 -/var/const/access_chapel_office = 22 -/var/const/access_tech_storage = 23 -/var/const/access_atmospherics = 24 -/var/const/access_bar = 25 -/var/const/access_janitor = 26 -/var/const/access_crematorium = 27 -/var/const/access_kitchen = 28 -/var/const/access_robotics = 29 -/var/const/access_rd = 30 -/var/const/access_cargo = 31 -/var/const/access_construction = 32 -/var/const/access_chemistry = 33 -/var/const/access_cargo_bot = 34 -/var/const/access_hydroponics = 35 -/var/const/access_manufacturing = 36 -/var/const/access_library = 37 -/var/const/access_lawyer = 38 -/var/const/access_virology = 39 -/var/const/access_cmo = 40 -/var/const/access_qm = 41 -/var/const/access_court = 42 -/var/const/access_clown = 43 -/var/const/access_mime = 44 -/var/const/access_surgery = 45 -/var/const/access_theatre = 46 -/var/const/access_research = 47 -/var/const/access_mining = 48 -/var/const/access_mining_office = 49 //not in use -/var/const/access_mailsorting = 50 -/var/const/access_mint = 51 -/var/const/access_mint_vault = 52 -/var/const/access_heads_vault = 53 -/var/const/access_mining_station = 54 -/var/const/access_xenobiology = 55 -/var/const/access_ce = 56 -/var/const/access_hop = 57 -/var/const/access_hos = 58 -/var/const/access_RC_announce = 59 //Request console announcements -/var/const/access_keycard_auth = 60 //Used for events which require at least two people to confirm them -/var/const/access_tcomsat = 61 // has access to the entire telecomms satellite / machinery -/var/const/access_gateway = 62 -/var/const/access_sec_doors = 63 // Security front doors -/var/const/access_psychiatrist = 64 // Psychiatrist's office -/var/const/access_xenoarch = 65 -/var/const/access_paramedic = 66 -/var/const/access_blueshield = 67 -/var/const/access_salvage_captain = 69 // Salvage ship captain's quarters -/var/const/access_mechanic = 70 -/var/const/access_pilot = 71 -/var/const/access_ntrep = 73 -/var/const/access_magistrate = 74 -/var/const/access_minisat = 75 -/var/const/access_mineral_storeroom = 76 -/var/const/access_network = 77 - -/var/const/access_weapons = 99 //Weapon authorization for secbots - - //BEGIN CENTCOM ACCESS -/var/const/access_cent_general = 101//General facilities. -/var/const/access_cent_living = 102//Living quarters. -/var/const/access_cent_medical = 103//Medical. -/var/const/access_cent_security = 104//Security. -/var/const/access_cent_storage = 105//Storage areas. -/var/const/access_cent_shuttles = 106//Shuttle docks. -/var/const/access_cent_telecomms = 107//Telecomms. -/var/const/access_cent_teleporter = 108//Teleporter -/var/const/access_cent_specops = 109//Special Ops. -/var/const/access_cent_specops_commander = 110//Special Ops Commander. -/var/const/access_cent_blackops = 111//Black Ops. -/var/const/access_cent_thunder = 112//Thunderdome. -/var/const/access_cent_bridge = 113//Bridge. -/var/const/access_cent_commander = 114//Commander's Office/ID computer. - -//The Syndicate -/var/const/access_syndicate = 150//General Syndicate Access -/var/const/access_syndicate_leader = 151//Nuke Op Leader Access -/var/const/access_vox = 152//Vox Access -/var/const/access_syndicate_command = 153//Admin syndi officer - -//Trade Stations -var/const/access_trade_sol = 160 - -//MONEY -/var/const/access_crate_cash = 200 - -//Awaymissions -/var/const/access_away01 = 271 - -//Ghost roles -var/const/access_free_golems = 300 - -/obj/var/list/req_access = null -/obj/var/req_access_txt = "0" -/obj/var/list/req_one_access = null -/obj/var/req_one_access_txt = "0" - -//returns 1 if this mob has sufficient access to use this object -/obj/proc/allowed(mob/M) - //check if we don't require any access at all - if(check_access()) - return 1 - - if(!M) - return 0 - - var/acc = M.get_access() //see mob.dm - - if(acc == IGNORE_ACCESS || M.can_admin_interact()) - return 1 //Mob ignores access - - else - return check_access_list(acc) - - return 0 - -/obj/item/proc/GetAccess() - return list() - -/obj/item/proc/GetID() - return null - -/obj/proc/generate_req_lists() - //These generations have been moved out of /obj/New() because they were slowing down the creation of objects that never even used the access system. - if(!req_access) - req_access = list() - if(req_access_txt) - var/list/req_access_str = splittext(req_access_txt, ";") - for(var/x in req_access_str) - var/n = text2num(x) - if(n) - req_access += n - - if(!req_one_access) - req_one_access = list() - if(req_one_access_txt) - var/list/req_one_access_str = splittext(req_one_access_txt,";") - for(var/x in req_one_access_str) - var/n = text2num(x) - if(n) - req_one_access += n - -/obj/proc/check_access(obj/item/I) - var/list/L - if(I) - L = I.GetAccess() - else - L = list() - return check_access_list(L) - -/obj/proc/check_access_list(var/list/L) - generate_req_lists() - - if(!L) - return 0 - if(!istype(L, /list)) - return 0 - return has_access(req_access, req_one_access, L) - -/proc/has_access(var/list/req_access, var/list/req_one_access, var/list/accesses) - for(var/req in req_access) - if(!(req in accesses)) //doesn't have this access - return 0 - if(req_one_access.len) - for(var/req in req_one_access) - if(req in accesses) //has an access from the single access list - return 1 - return 0 - return 1 - -/proc/get_centcom_access(job) - switch(job) - if("VIP Guest") - return list(access_cent_general, access_cent_living) - if("Custodian") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_storage) - if("Thunderdome Overseer") - return list(access_cent_general, access_cent_thunder) - if("Emergency Response Team Member") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_specops) + get_all_accesses() - if("Emergency Response Team Leader") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_specops, access_cent_specops_commander) + get_all_accesses() - if("Medical Officer") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_storage) + get_all_accesses() - if("Intel Officer") - return list(access_cent_general, access_cent_living, access_cent_security, access_cent_storage) + get_all_accesses() - if("Research Officer") - return list(access_cent_general, access_cent_specops, access_cent_medical, access_cent_storage, access_cent_telecomms, access_cent_teleporter) + get_all_accesses() - if("Death Commando") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_specops, access_cent_specops_commander, access_cent_blackops) + get_all_accesses() - if("Deathsquad Officer") - return get_all_centcom_access() + get_all_accesses() - if("NT Undercover Operative") - return get_all_centcom_access() + get_all_accesses() - if("Special Operations Officer") - return get_all_centcom_access() + get_all_accesses() - if("Nanotrasen Navy Representative") - return get_all_centcom_access() + get_all_accesses() - if("Nanotrasen Navy Officer") - return get_all_centcom_access() + get_all_accesses() - if("Nanotrasen Navy Captain") - return get_all_centcom_access() + get_all_accesses() - if("Supreme Commander") - return get_all_centcom_access() + get_all_accesses() - -/proc/get_syndicate_access(job) - switch(job) - if("Syndicate Operative") - return list(access_syndicate) - if("Syndicate Operative Leader") - return list(access_syndicate, access_syndicate_leader) - if("Syndicate Agent") - return list(access_syndicate, access_maint_tunnels) - if("Vox Raider") - return list(access_vox) - if("Vox Trader") - return list(access_vox) - if("Syndicate Commando") - return list(access_syndicate, access_syndicate_leader) - if("Syndicate Officer") - return list(access_syndicate, access_syndicate_leader, access_syndicate_command) - -/proc/get_all_accesses() - return list(access_security, access_sec_doors, access_brig, 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_psychiatrist, access_cmo, access_qm, access_clown, access_mime, access_surgery, - access_theatre, access_research, access_mining, access_mailsorting, - access_heads_vault, access_mining_station, access_xenobiology, access_ce, access_hop, access_hos, access_RC_announce, - access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_paramedic, access_blueshield, access_mechanic,access_weapons, - access_pilot, access_ntrep, access_magistrate, access_mineral_storeroom, access_minisat, access_network) - -/proc/get_all_centcom_access() - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_shuttles, access_cent_telecomms, access_cent_teleporter, access_cent_specops, access_cent_specops_commander, access_cent_blackops, access_cent_thunder, access_cent_bridge, access_cent_commander) - -/proc/get_all_syndicate_access() - return list(access_syndicate, access_syndicate_leader, access_vox, access_syndicate_command) - -/proc/get_all_misc_access() - return list(access_salvage_captain, access_trade_sol, access_crate_cash, access_away01) - -/proc/get_absolutely_all_accesses() - return (get_all_accesses() | get_all_centcom_access() | get_all_syndicate_access() | get_all_misc_access()) - -/proc/get_region_accesses(code) - switch(code) - if(REGION_ALL) - return get_all_accesses() - if(REGION_GENERAL) //station general - return list(access_kitchen, access_bar, access_hydroponics, access_janitor, access_chapel_office, access_crematorium, access_library, access_theatre, access_lawyer, access_magistrate, access_clown, access_mime) - if(REGION_SECURITY) //security - return list(access_sec_doors, access_weapons, access_security, access_brig, access_armory, access_forensics_lockers, access_court, access_pilot, access_hos) - if(REGION_MEDBAY) //medbay - return list(access_medical, access_genetics, access_morgue, access_chemistry, access_psychiatrist, access_virology, access_surgery, access_cmo, access_paramedic) - if(REGION_RESEARCH) //research - return list(access_research, access_tox, access_tox_storage, access_genetics, access_robotics, access_xenobiology, access_xenoarch, access_minisat, access_rd, access_network) - if(REGION_ENGINEERING) //engineering and maintenance - return list(access_construction, access_maint_tunnels, access_engine, access_engine_equip, access_external_airlocks, access_tech_storage, access_atmospherics, access_minisat, access_ce, access_mechanic) - if(REGION_SUPPLY) //supply - return list(access_mailsorting, access_mining, access_mining_station, access_mineral_storeroom, access_cargo, access_qm) - if(REGION_COMMAND) //command - return list(access_heads, access_RC_announce, access_keycard_auth, access_change_ids, access_ai_upload, access_teleporter, access_eva, access_tcomsat, access_gateway, access_all_personal_lockers, access_heads_vault, access_blueshield, access_ntrep, access_hop, access_captain) - if(REGION_CENTCOMM) //because why the heck not - return get_all_centcom_access() + get_all_accesses() - -/proc/get_region_accesses_name(code) - switch(code) - if(REGION_ALL) - return "All" - if(REGION_GENERAL) //station general - return "General" - if(REGION_SECURITY) //security - return "Security" - if(REGION_MEDBAY) //medbay - return "Medbay" - if(REGION_RESEARCH) //research - return "Research" - if(REGION_ENGINEERING) //engineering and maintenance - return "Engineering" - if(REGION_SUPPLY) //supply - return "Supply" - if(REGION_COMMAND) //command - return "Command" - if(REGION_CENTCOMM) //CC - return "CentComm" - - -/proc/get_access_desc(A) - switch(A) - if(access_cargo) - return "Cargo Bay" - if(access_cargo_bot) - return "Cargo Bot Delivery" - if(access_security) - return "Security" - if(access_brig) - return "Holding Cells" - if(access_court) - return "Courtroom" - if(access_forensics_lockers) - return "Forensics" - if(access_medical) - return "Medical" - if(access_genetics) - return "Genetics Lab" - if(access_morgue) - return "Morgue" - if(access_tox) - return "R&D Lab" - if(access_tox_storage) - return "Toxins Lab" - if(access_chemistry) - return "Chemistry Lab" - if(access_rd) - return "Research Director" - if(access_bar) - return "Bar" - if(access_janitor) - return "Custodial Closet" - if(access_engine) - return "Engineering" - if(access_engine_equip) - return "Power Equipment" - if(access_maint_tunnels) - return "Maintenance" - if(access_external_airlocks) - return "External Airlocks" - if(access_emergency_storage) - return "Emergency Storage" - if(access_change_ids) - return "ID Computer" - if(access_ai_upload) - return "AI Upload" - if(access_teleporter) - return "Teleporter" - if(access_eva) - return "EVA" - if(access_heads) - return "Bridge" - if(access_captain) - return "Captain" - if(access_all_personal_lockers) - return "Personal Lockers" - if(access_chapel_office) - return "Chapel Office" - if(access_tech_storage) - return "Technical Storage" - if(access_atmospherics) - return "Atmospherics" - if(access_crematorium) - return "Crematorium" - if(access_armory) - return "Armory" - if(access_construction) - return "Construction Areas" - if(access_kitchen) - return "Kitchen" - if(access_hydroponics) - return "Hydroponics" - if(access_library) - return "Library" - if(access_lawyer) - return "Law Office" - if(access_robotics) - return "Robotics" - if(access_virology) - return "Virology" - if(access_psychiatrist) - return "Psychiatrist's Office" - if(access_cmo) - return "Chief Medical Officer" - if(access_qm) - return "Quartermaster" - if(access_clown) - return "Clown's Office" - if(access_mime) - return "Mime's Office" - if(access_surgery) - return "Surgery" - if(access_theatre) - return "Theatre" - if(access_manufacturing) - return "Manufacturing" - if(access_research) - return "Science" - if(access_mining) - return "Mining" - if(access_mining_office) - return "Mining Office" - if(access_mailsorting) - return "Cargo Office" - if(access_mint) - return "Mint" - if(access_mint_vault) - return "Mint Vault" - if(access_heads_vault) - return "Main Vault" - if(access_mining_station) - return "Mining EVA" - if(access_xenobiology) - return "Xenobiology Lab" - if(access_xenoarch) - return "Xenoarchaeology" - if(access_hop) - return "Head of Personnel" - if(access_hos) - return "Head of Security" - if(access_ce) - return "Chief Engineer" - if(access_RC_announce) - return "RC Announcements" - if(access_keycard_auth) - return "Keycode Auth. Device" - if(access_tcomsat) - return "Telecommunications" - if(access_network) - return "Network Access" - if(access_gateway) - return "Gateway" - if(access_sec_doors) - return "Brig" - if(access_blueshield) - return "Blueshield" - if(access_ntrep) - return "Nanotrasen Rep." - if(access_paramedic) - return "Paramedic" - if(access_mechanic) - return "Mechanic Workshop" - if(access_pilot) - return "Security Pod Pilot" - if(access_magistrate) - return "Magistrate" - if(access_mineral_storeroom) - return "Mineral Storage" - if(access_minisat) - return "AI Satellite" - if(access_weapons) - return "Weapon Permit" - -/proc/get_centcom_access_desc(A) - switch(A) - if(access_cent_general) - return "General Access" - if(access_cent_living) - return "Living Quarters" - if(access_cent_medical) - return "Medical" - if(access_cent_security) - return "Security" - if(access_cent_storage) - return "Storage" - if(access_cent_shuttles) - return "Shuttles" - if(access_cent_telecomms) - return "Telecommunications" - if(access_cent_teleporter) - return "Teleporter" - if(access_cent_specops) - return "Special Ops" - if(access_cent_specops_commander) - return "Special Ops Commander" - if(access_cent_blackops) - return "Black Ops" - if(access_cent_thunder) - return "Thunderdome" - if(access_cent_bridge) - return "Bridge" - if(access_cent_commander) - return "Commander" - -/proc/get_syndicate_access_desc(A) - switch(A) - if(access_syndicate) - return "Syndicate Operative" - if(access_syndicate_leader) - return "Syndicate Operative Leader" - if(access_vox) - return "Vox" - if(access_syndicate_command) - return "Syndicate Command" - -/proc/get_all_jobs() - var/list/all_jobs = list() - var/list/all_datums = subtypesof(/datum/job) - all_datums.Remove(list(/datum/job/ai,/datum/job/cyborg)) - var/datum/job/jobdatum - for(var/jobtype in all_datums) - jobdatum = new jobtype - all_jobs.Add(jobdatum.title) - return all_jobs - -/proc/get_all_centcom_jobs() - return list("VIP Guest","Custodian","Thunderdome Overseer","Emergency Response Team Member","Emergency Response Team Leader","Intel Officer","Medical Officer","Death Commando","Research Officer","Deathsquad Officer","Special Operations Officer","Nanotrasen Navy Representative","Nanotrasen Navy Officer","Nanotrasen Navy Captain","Supreme Commander") - -//gets the actual job rank (ignoring alt titles) -//this is used solely for sechuds -/obj/proc/GetJobRealName() - if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) - return - - var/rank - var/assignment - if(istype(src, /obj/item/pda)) - if(src:id) - rank = src:id:rank - assignment = src:id:assignment - else if(istype(src, /obj/item/card/id)) - rank = src:rank - assignment = src:assignment - - if( rank in GLOB.joblist ) - return rank - - if( assignment in GLOB.joblist ) - return assignment - - return "Unknown" - -//gets the alt title, failing that the actual job rank -//this is unused -/obj/proc/sdsdsd() //GetJobDisplayName - if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) - return - - var/assignment - if(istype(src, /obj/item/pda)) - if(src:id) - assignment = src:id:assignment - else if(istype(src, /obj/item/card/id)) - assignment = src:assignment - - if(assignment) - return assignment - - return "Unknown" - -proc/GetIdCard(var/mob/living/carbon/human/H) - if(H.wear_id) - var/id = H.wear_id.GetID() - if(id) - return id - if(H.get_active_hand()) - var/obj/item/I = H.get_active_hand() - return I.GetID() - -proc/FindNameFromID(var/mob/living/carbon/human/H) - ASSERT(istype(H)) - var/obj/item/card/id/C = H.get_active_hand() - if( istype(C) || istype(C, /obj/item/pda) ) - var/obj/item/card/id/ID = C - - if( istype(C, /obj/item/pda) ) - var/obj/item/pda/pda = C - ID = pda.id - if(!istype(ID)) - ID = null - - if(ID) - return ID.registered_name - - C = H.wear_id - - if( istype(C) || istype(C, /obj/item/pda) ) - var/obj/item/card/id/ID = C - - if( istype(C, /obj/item/pda) ) - var/obj/item/pda/pda = C - ID = pda.id - if(!istype(ID)) - ID = null - - if(ID) - return ID.registered_name - -proc/get_all_job_icons() //For all existing HUD icons - return GLOB.joblist + list("Prisoner") - -/obj/proc/GetJobName() //Used in secHUD icon generation - var/assignmentName = "Unknown" - var/rankName = "Unknown" - if(istype(src, /obj/item/pda)) - var/obj/item/pda/P = src - assignmentName = P.ownjob - rankName = P.ownrank - else if(istype(src, /obj/item/card/id)) - var/obj/item/card/id/I = src - assignmentName = I.assignment - rankName = I.rank - - - var/job_icons = get_all_job_icons() - var/centcom = get_all_centcom_jobs() - - if(assignmentName in centcom) //Return with the NT logo if it is a Centcom job - return "Centcom" - if(rankName in centcom) - return "Centcom" - - if(assignmentName in job_icons) //Check if the job has a hud icon - return assignmentName - if(rankName in job_icons) - return rankName - - return "Unknown" //Return unknown if none of the above apply +/obj/var/list/req_access = null +/obj/var/req_access_txt = "0" +/obj/var/list/req_one_access = null +/obj/var/req_one_access_txt = "0" + +//returns 1 if this mob has sufficient access to use this object +/obj/proc/allowed(mob/M) + //check if we don't require any access at all + if(check_access()) + return 1 + + if(!M) + return 0 + + var/acc = M.get_access() //see mob.dm + + if(acc == IGNORE_ACCESS || M.can_admin_interact()) + return 1 //Mob ignores access + + else + return check_access_list(acc) + + return 0 + +/obj/item/proc/GetAccess() + return list() + +/obj/item/proc/GetID() + return null + +/obj/proc/generate_req_lists() + //These generations have been moved out of /obj/New() because they were slowing down the creation of objects that never even used the access system. + if(!req_access) + req_access = list() + if(req_access_txt) + var/list/req_access_str = splittext(req_access_txt, ";") + for(var/x in req_access_str) + var/n = text2num(x) + if(n) + req_access += n + + if(!req_one_access) + req_one_access = list() + if(req_one_access_txt) + var/list/req_one_access_str = splittext(req_one_access_txt,";") + for(var/x in req_one_access_str) + var/n = text2num(x) + if(n) + req_one_access += n + +/obj/proc/check_access(obj/item/I) + var/list/L + if(I) + L = I.GetAccess() + else + L = list() + return check_access_list(L) + +/obj/proc/check_access_list(var/list/L) + generate_req_lists() + + if(!L) + return 0 + if(!istype(L, /list)) + return 0 + return has_access(req_access, req_one_access, L) + +/proc/has_access(var/list/req_access, var/list/req_one_access, var/list/accesses) + for(var/req in req_access) + if(!(req in accesses)) //doesn't have this access + return 0 + if(req_one_access.len) + for(var/req in req_one_access) + if(req in accesses) //has an access from the single access list + return 1 + return 0 + return 1 + +/proc/get_centcom_access(job) + switch(job) + if("VIP Guest") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING) + if("Custodian") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) + if("Thunderdome Overseer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_THUNDER) + if("Emergency Response Team Member") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS) + get_all_accesses() + if("Emergency Response Team Leader") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER) + get_all_accesses() + if("Medical Officer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) + get_all_accesses() + if("Intel Officer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE) + get_all_accesses() + if("Research Officer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE, ACCESS_CENT_TELECOMMS, ACCESS_CENT_TELEPORTER) + get_all_accesses() + if("Death Commando") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER, ACCESS_CENT_BLACKOPS) + get_all_accesses() + if("Deathsquad Officer") + return get_all_centcom_access() + get_all_accesses() + if("NT Undercover Operative") + return get_all_centcom_access() + get_all_accesses() + if("Special Operations Officer") + return get_all_centcom_access() + get_all_accesses() + if("Nanotrasen Navy Representative") + return get_all_centcom_access() + get_all_accesses() + if("Nanotrasen Navy Officer") + return get_all_centcom_access() + get_all_accesses() + if("Nanotrasen Navy Captain") + return get_all_centcom_access() + get_all_accesses() + if("Supreme Commander") + return get_all_centcom_access() + get_all_accesses() + +/proc/get_syndicate_access(job) + switch(job) + if("Syndicate Operative") + return list(ACCESS_SYNDICATE) + if("Syndicate Operative Leader") + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) + if("Syndicate Agent") + return list(ACCESS_SYNDICATE, ACCESS_MAINT_TUNNELS) + if("Vox Raider") + return list(ACCESS_VOX) + if("Vox Trader") + return list(ACCESS_VOX) + if("Syndicate Commando") + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) + if("Syndicate Officer") + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_SYNDICATE_COMMAND) + +/proc/get_all_accesses() + return list(ACCESS_MINISAT, ACCESS_AI_UPLOAD, ACCESS_ARMORY, ACCESS_ATMOSPHERICS, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_BLUESHIELD, + ACCESS_HEADS, ACCESS_CAPTAIN, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_CHAPEL_OFFICE, ACCESS_CE, ACCESS_CHEMISTRY, ACCESS_CLOWN, ACCESS_CMO, + ACCESS_COURT, ACCESS_CONSTRUCTION, ACCESS_CREMATORIUM, ACCESS_JANITOR, ACCESS_ENGINE, ACCESS_EVA, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_FORENSICS_LOCKERS, + ACCESS_GENETICS, ACCESS_GATEWAY, ACCESS_BRIG, ACCESS_HOP, ACCESS_HOS, ACCESS_HYDROPONICS, ACCESS_CHANGE_IDS, ACCESS_KEYCARD_AUTH, ACCESS_KITCHEN, + ACCESS_LAWYER, ACCESS_LIBRARY, ACCESS_MAGISTRATE, ACCESS_MAINT_TUNNELS, ACCESS_HEADS_VAULT, ACCESS_MEDICAL, ACCESS_MECHANIC, ACCESS_MIME, + ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_MORGUE, ACCESS_NETWORK, ACCESS_NTREP, ACCESS_PARAMEDIC, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_ENGINE_EQUIP, ACCESS_PSYCHIATRIST, ACCESS_QM, ACCESS_RD, ACCESS_RC_ANNOUNCE, ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_RESEARCH, ACCESS_SECURITY, ACCESS_PILOT, + ACCESS_SURGERY, ACCESS_TECH_STORAGE, ACCESS_TELEPORTER, ACCESS_THEATRE, ACCESS_TCOMSAT, ACCESS_TOX_STORAGE, ACCESS_VIROLOGY, ACCESS_WEAPONS, ACCESS_XENOBIOLOGY, + ACCESS_XENOARCH) + +/proc/get_all_centcom_access() + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SHUTTLES, ACCESS_CENT_TELECOMMS, ACCESS_CENT_TELEPORTER, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER, ACCESS_CENT_BLACKOPS, ACCESS_CENT_THUNDER, ACCESS_CENT_BRIDGE, ACCESS_CENT_COMMANDER) + +/proc/get_all_syndicate_access() + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_VOX, ACCESS_SYNDICATE_COMMAND) + +/proc/get_all_misc_access() + return list(ACCESS_SALVAGE_CAPTAIN, ACCESS_TRADE_SOL, ACCESS_CRATE_CASH, ACCESS_AWAY01) + +/proc/get_absolutely_all_accesses() + return (get_all_accesses() | get_all_centcom_access() | get_all_syndicate_access() | get_all_misc_access()) + +/proc/get_region_accesses(code) + switch(code) + if(REGION_ALL) + return get_all_accesses() + if(REGION_GENERAL) //station general + return list(ACCESS_KITCHEN, ACCESS_BAR, ACCESS_HYDROPONICS, ACCESS_JANITOR, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_LIBRARY, ACCESS_THEATRE, ACCESS_LAWYER, ACCESS_MAGISTRATE, ACCESS_CLOWN, ACCESS_MIME) + if(REGION_SECURITY) //security + return list(ACCESS_SEC_DOORS, ACCESS_WEAPONS, ACCESS_SECURITY, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, ACCESS_PILOT, ACCESS_HOS) + if(REGION_MEDBAY) //medbay + return list(ACCESS_MEDICAL, ACCESS_GENETICS, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_PSYCHIATRIST, ACCESS_VIROLOGY, ACCESS_SURGERY, ACCESS_CMO, ACCESS_PARAMEDIC) + if(REGION_RESEARCH) //research + return list(ACCESS_RESEARCH, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_GENETICS, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_RD, ACCESS_NETWORK) + if(REGION_ENGINEERING) //engineering and maintenance + return list(ACCESS_CONSTRUCTION, ACCESS_MAINT_TUNNELS, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_TECH_STORAGE, ACCESS_ATMOSPHERICS, ACCESS_MINISAT, ACCESS_CE, ACCESS_MECHANIC) + if(REGION_SUPPLY) //supply + return list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO, ACCESS_QM) + if(REGION_COMMAND) //command + return list(ACCESS_HEADS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_TELEPORTER, ACCESS_EVA, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_HEADS_VAULT, ACCESS_BLUESHIELD, ACCESS_NTREP, ACCESS_HOP, ACCESS_CAPTAIN) + if(REGION_CENTCOMM) //because why the heck not + return get_all_centcom_access() + get_all_accesses() + +/proc/get_region_accesses_name(code) + switch(code) + if(REGION_ALL) + return "All" + if(REGION_GENERAL) //station general + return "General" + if(REGION_SECURITY) //security + return "Security" + if(REGION_MEDBAY) //medbay + return "Medbay" + if(REGION_RESEARCH) //research + return "Research" + if(REGION_ENGINEERING) //engineering and maintenance + return "Engineering" + if(REGION_SUPPLY) //supply + return "Supply" + if(REGION_COMMAND) //command + return "Command" + if(REGION_CENTCOMM) //CC + return "CentComm" + + +/proc/get_access_desc(A) + switch(A) + if(ACCESS_CARGO) + return "Cargo Bay" + if(ACCESS_CARGO_BOT) + return "Cargo Bot Delivery" + if(ACCESS_SECURITY) + return "Security" + if(ACCESS_BRIG) + return "Holding Cells" + if(ACCESS_COURT) + return "Courtroom" + if(ACCESS_FORENSICS_LOCKERS) + return "Forensics" + if(ACCESS_MEDICAL) + return "Medical" + if(ACCESS_GENETICS) + return "Genetics Lab" + if(ACCESS_MORGUE) + return "Morgue" + if(ACCESS_TOX) + return "R&D Lab" + if(ACCESS_TOX_STORAGE) + return "Toxins Lab" + if(ACCESS_CHEMISTRY) + return "Chemistry Lab" + if(ACCESS_RD) + return "Research Director" + if(ACCESS_BAR) + return "Bar" + if(ACCESS_JANITOR) + return "Custodial Closet" + if(ACCESS_ENGINE) + return "Engineering" + if(ACCESS_ENGINE_EQUIP) + return "Power Equipment" + if(ACCESS_MAINT_TUNNELS) + return "Maintenance" + if(ACCESS_EXTERNAL_AIRLOCKS) + return "External Airlocks" + if(ACCESS_EMERGENCY_STORAGE) + return "Emergency Storage" + if(ACCESS_CHANGE_IDS) + return "ID Computer" + if(ACCESS_AI_UPLOAD) + return "AI Upload" + if(ACCESS_TELEPORTER) + return "Teleporter" + if(ACCESS_EVA) + return "EVA" + if(ACCESS_HEADS) + return "Bridge" + if(ACCESS_CAPTAIN) + return "Captain" + if(ACCESS_ALL_PERSONAL_LOCKERS) + return "Personal Lockers" + if(ACCESS_CHAPEL_OFFICE) + return "Chapel Office" + if(ACCESS_TECH_STORAGE) + return "Technical Storage" + if(ACCESS_ATMOSPHERICS) + return "Atmospherics" + if(ACCESS_CREMATORIUM) + return "Crematorium" + if(ACCESS_ARMORY) + return "Armory" + if(ACCESS_CONSTRUCTION) + return "Construction Areas" + if(ACCESS_KITCHEN) + return "Kitchen" + if(ACCESS_HYDROPONICS) + return "Hydroponics" + if(ACCESS_LIBRARY) + return "Library" + if(ACCESS_LAWYER) + return "Law Office" + if(ACCESS_ROBOTICS) + return "Robotics" + if(ACCESS_VIROLOGY) + return "Virology" + if(ACCESS_PSYCHIATRIST) + return "Psychiatrist's Office" + if(ACCESS_CMO) + return "Chief Medical Officer" + if(ACCESS_QM) + return "Quartermaster" + if(ACCESS_CLOWN) + return "Clown's Office" + if(ACCESS_MIME) + return "Mime's Office" + if(ACCESS_SURGERY) + return "Surgery" + if(ACCESS_THEATRE) + return "Theatre" + if(ACCESS_MANUFACTURING) + return "Manufacturing" + if(ACCESS_RESEARCH) + return "Science" + if(ACCESS_MINING) + return "Mining" + if(ACCESS_MINING_OFFICE) + return "Mining Office" + if(ACCESS_MAILSORTING) + return "Cargo Office" + if(ACCESS_MINT) + return "Mint" + if(ACCESS_MINT_VAULT) + return "Mint Vault" + if(ACCESS_HEADS_VAULT) + return "Main Vault" + if(ACCESS_MINING_STATION) + return "Mining EVA" + if(ACCESS_XENOBIOLOGY) + return "Xenobiology Lab" + if(ACCESS_XENOARCH) + return "Xenoarchaeology" + if(ACCESS_HOP) + return "Head of Personnel" + if(ACCESS_HOS) + return "Head of Security" + if(ACCESS_CE) + return "Chief Engineer" + if(ACCESS_RC_ANNOUNCE) + return "RC Announcements" + if(ACCESS_KEYCARD_AUTH) + return "Keycode Auth. Device" + if(ACCESS_TCOMSAT) + return "Telecommunications" + if(ACCESS_NETWORK) + return "Network Access" + if(ACCESS_GATEWAY) + return "Gateway" + if(ACCESS_SEC_DOORS) + return "Brig" + if(ACCESS_BLUESHIELD) + return "Blueshield" + if(ACCESS_NTREP) + return "Nanotrasen Rep." + if(ACCESS_PARAMEDIC) + return "Paramedic" + if(ACCESS_MECHANIC) + return "Mechanic Workshop" + if(ACCESS_PILOT) + return "Security Pod Pilot" + if(ACCESS_MAGISTRATE) + return "Magistrate" + if(ACCESS_MINERAL_STOREROOM) + return "Mineral Storage" + if(ACCESS_MINISAT) + return "AI Satellite" + if(ACCESS_WEAPONS) + return "Weapon Permit" + +/proc/get_centcom_access_desc(A) + switch(A) + if(ACCESS_CENT_GENERAL) + return "General Access" + if(ACCESS_CENT_LIVING) + return "Living Quarters" + if(ACCESS_CENT_MEDICAL) + return "Medical" + if(ACCESS_CENT_SECURITY) + return "Security" + if(ACCESS_CENT_STORAGE) + return "Storage" + if(ACCESS_CENT_SHUTTLES) + return "Shuttles" + if(ACCESS_CENT_TELECOMMS) + return "Telecommunications" + if(ACCESS_CENT_TELEPORTER) + return "Teleporter" + if(ACCESS_CENT_SPECOPS) + return "Special Ops" + if(ACCESS_CENT_SPECOPS_COMMANDER) + return "Special Ops Commander" + if(ACCESS_CENT_BLACKOPS) + return "Black Ops" + if(ACCESS_CENT_THUNDER) + return "Thunderdome" + if(ACCESS_CENT_BRIDGE) + return "Bridge" + if(ACCESS_CENT_COMMANDER) + return "Commander" + +/proc/get_syndicate_access_desc(A) + switch(A) + if(ACCESS_SYNDICATE) + return "Syndicate Operative" + if(ACCESS_SYNDICATE_LEADER) + return "Syndicate Operative Leader" + if(ACCESS_VOX) + return "Vox" + if(ACCESS_SYNDICATE_COMMAND) + return "Syndicate Command" + +/proc/get_all_jobs() + var/list/all_jobs = list() + var/list/all_datums = subtypesof(/datum/job) + all_datums.Remove(list(/datum/job/ai,/datum/job/cyborg)) + var/datum/job/jobdatum + for(var/jobtype in all_datums) + jobdatum = new jobtype + all_jobs.Add(jobdatum.title) + return all_jobs + +/proc/get_all_centcom_jobs() + return list("VIP Guest","Custodian","Thunderdome Overseer","Emergency Response Team Member","Emergency Response Team Leader","Intel Officer","Medical Officer","Death Commando","Research Officer","Deathsquad Officer","Special Operations Officer","Nanotrasen Navy Representative","Nanotrasen Navy Officer","Nanotrasen Navy Captain","Supreme Commander") + +//gets the actual job rank (ignoring alt titles) +//this is used solely for sechuds +/obj/proc/GetJobRealName() + if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) + return + + var/rank + var/assignment + if(istype(src, /obj/item/pda)) + if(src:id) + rank = src:id:rank + assignment = src:id:assignment + else if(istype(src, /obj/item/card/id)) + rank = src:rank + assignment = src:assignment + + if( rank in GLOB.joblist ) + return rank + + if( assignment in GLOB.joblist ) + return assignment + + return "Unknown" + +//gets the alt title, failing that the actual job rank +//this is unused +/obj/proc/sdsdsd() //GetJobDisplayName + if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) + return + + var/assignment + if(istype(src, /obj/item/pda)) + if(src:id) + assignment = src:id:assignment + else if(istype(src, /obj/item/card/id)) + assignment = src:assignment + + if(assignment) + return assignment + + return "Unknown" + +proc/GetIdCard(var/mob/living/carbon/human/H) + if(H.wear_id) + var/id = H.wear_id.GetID() + if(id) + return id + if(H.get_active_hand()) + var/obj/item/I = H.get_active_hand() + return I.GetID() + +proc/FindNameFromID(var/mob/living/carbon/human/H) + ASSERT(istype(H)) + var/obj/item/card/id/C = H.get_active_hand() + if( istype(C) || istype(C, /obj/item/pda) ) + var/obj/item/card/id/ID = C + + if( istype(C, /obj/item/pda) ) + var/obj/item/pda/pda = C + ID = pda.id + if(!istype(ID)) + ID = null + + if(ID) + return ID.registered_name + + C = H.wear_id + + if( istype(C) || istype(C, /obj/item/pda) ) + var/obj/item/card/id/ID = C + + if( istype(C, /obj/item/pda) ) + var/obj/item/pda/pda = C + ID = pda.id + if(!istype(ID)) + ID = null + + if(ID) + return ID.registered_name + +proc/get_all_job_icons() //For all existing HUD icons + return GLOB.joblist + list("Prisoner") + +/obj/proc/GetJobName() //Used in secHUD icon generation + var/assignmentName = "Unknown" + var/rankName = "Unknown" + if(istype(src, /obj/item/pda)) + var/obj/item/pda/P = src + assignmentName = P.ownjob + rankName = P.ownrank + else if(istype(src, /obj/item/card/id)) + var/obj/item/card/id/I = src + assignmentName = I.assignment + rankName = I.rank + + + var/job_icons = get_all_job_icons() + var/centcom = get_all_centcom_jobs() + + if(assignmentName in centcom) //Return with the NT logo if it is a Centcom job + return "Centcom" + if(rankName in centcom) + return "Centcom" + + if(assignmentName in job_icons) //Check if the job has a hud icon + return assignmentName + if(rankName in job_icons) + return rankName + + return "Unknown" //Return unknown if none of the above apply diff --git a/code/game/jobs/job/central.dm b/code/game/jobs/job/central.dm index a20476155d7..f91460ba234 100644 --- a/code/game/jobs/job/central.dm +++ b/code/game/jobs/job/central.dm @@ -2,8 +2,8 @@ // General-purpose CC official. Can hear out grievances, investigate cases, issue demotions, etc. /datum/job/ntnavyofficer title = "Nanotrasen Navy Officer" - flag = CENTCOM - department_flag = CENTCOM + flag = JOB_CENTCOM + department_flag = JOB_CENTCOM // This gets its job as its own flag because admin jobs dont have flags total_positions = 5 spawn_positions = 5 supervisors = "the admins" @@ -34,6 +34,9 @@ /obj/item/implant/dust ) backpack = /obj/item/storage/backpack/satchel + backpack_contents = list( + /obj/item/stamp/centcom = 1, + ) box = /obj/item/storage/box/centcomofficer cybernetic_implants = list( /obj/item/organ/internal/cyberimp/chest/nutriment/plus @@ -48,8 +51,8 @@ // CC Officials who lead ERTs, Death Squads, etc. /datum/job/ntspecops title = "Special Operations Officer" - flag = CENTCOM - department_flag = CENTCOM + flag = JOB_CENTCOM + department_flag = JOB_CENTCOM // This gets its job as its own flag because admin jobs dont have flags total_positions = 5 spawn_positions = 5 supervisors = "the admins" @@ -100,4 +103,4 @@ . = ..() if(visualsOnly) return - H.mind.offstation_role = TRUE \ No newline at end of file + H.mind.offstation_role = TRUE diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index 4535e1c6c41..6adc55a89f1 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -1,28 +1,28 @@ -/datum/job/civilian - title = "Civilian" - flag = CIVILIAN - department_flag = SUPPORT - total_positions = -1 - spawn_positions = -1 - supervisors = "the head of personnel" - department_head = list("Head of Personnel") - selection_color = "#dddddd" - access = list() //See /datum/job/assistant/get_access() - minimal_access = list() //See /datum/job/assistant/get_access() - alt_titles = list("Tourist","Businessman","Trader","Assistant") - outfit = /datum/outfit/job/assistant - -/datum/job/civilian/get_access() - if(config.assistant_maint) - return list(access_maint_tunnels) - else - return list() - -/datum/outfit/job/assistant - name = "Civilian" - jobtype = /datum/job/civilian - - uniform = /obj/item/clothing/under/color/random - shoes = /obj/item/clothing/shoes/black - - +/datum/job/civilian + title = "Civilian" + flag = JOB_CIVILIAN + department_flag = JOBCAT_SUPPORT + total_positions = -1 + spawn_positions = -1 + supervisors = "the head of personnel" + department_head = list("Head of Personnel") + selection_color = "#dddddd" + access = list() //See /datum/job/assistant/get_access() + minimal_access = list() //See /datum/job/assistant/get_access() + alt_titles = list("Tourist","Businessman","Trader","Assistant") + outfit = /datum/outfit/job/assistant + +/datum/job/civilian/get_access() + if(config.assistant_maint) + return list(ACCESS_MAINT_TUNNELS) + else + return list() + +/datum/outfit/job/assistant + name = "Civilian" + jobtype = /datum/job/civilian + + uniform = /obj/item/clothing/under/color/random + shoes = /obj/item/clothing/shoes/black + + diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm index ecae6919813..84f903dd2b1 100644 --- a/code/game/jobs/job/engineering.dm +++ b/code/game/jobs/job/engineering.dm @@ -1,153 +1,153 @@ -/datum/job/chief_engineer - title = "Chief Engineer" - flag = CHIEF - department_flag = ENGSEC - total_positions = 1 - spawn_positions = 1 - is_engineering = 1 - supervisors = "the captain" - department_head = list("Captain") - selection_color = "#ffeeaa" - req_admin_notify = 1 - access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, - access_teleporter, access_external_airlocks, access_atmospherics, access_emergency_storage, access_eva, - access_heads, access_construction, access_sec_doors, - access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_minisat, access_mechanic, access_mineral_storeroom) - minimal_access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, - access_teleporter, access_external_airlocks, access_atmospherics, access_emergency_storage, access_eva, - access_heads, access_construction, access_sec_doors, - access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_minisat, access_mechanic, access_mineral_storeroom) - minimal_player_age = 21 - exp_requirements = 300 - exp_type = EXP_TYPE_ENGINEERING - outfit = /datum/outfit/job/chief_engineer - -/datum/outfit/job/chief_engineer - name = "Chief Engineer" - jobtype = /datum/job/chief_engineer - - uniform = /obj/item/clothing/under/rank/chief_engineer - belt = /obj/item/storage/belt/utility/chief/full - gloves = /obj/item/clothing/gloves/color/black/ce - shoes = /obj/item/clothing/shoes/brown - head = /obj/item/clothing/head/hardhat/white - l_ear = /obj/item/radio/headset/heads/ce - id = /obj/item/card/id/ce - l_pocket = /obj/item/t_scanner - pda = /obj/item/pda/heads/ce - backpack_contents = list( - /obj/item/melee/classic_baton/telescopic = 1 - ) - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/engineering - box = /obj/item/storage/box/engineer - - -/datum/job/engineer - title = "Station Engineer" - flag = ENGINEER - department_flag = ENGSEC - total_positions = 5 - spawn_positions = 5 - is_engineering = 1 - supervisors = "the chief engineer" - department_head = list("Chief Engineer") - selection_color = "#fff5cc" - access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics, access_mineral_storeroom) - minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_mineral_storeroom) - alt_titles = list("Maintenance Technician","Engine Technician","Electrician") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/engineer - -/datum/outfit/job/engineer - name = "Station Engineer" - jobtype = /datum/job/engineer - - uniform = /obj/item/clothing/under/rank/engineer - belt = /obj/item/storage/belt/utility/full - shoes = /obj/item/clothing/shoes/workboots - head = /obj/item/clothing/head/hardhat - l_ear = /obj/item/radio/headset/headset_eng - id = /obj/item/card/id/engineering - l_pocket = /obj/item/t_scanner - pda = /obj/item/pda/engineering - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/engineering - box = /obj/item/storage/box/engineer - - - -/datum/job/atmos - title = "Life Support Specialist" - flag = ATMOSTECH - department_flag = ENGSEC - total_positions = 3 - spawn_positions = 2 - is_engineering = 1 - supervisors = "the chief engineer" - department_head = list("Chief Engineer") - selection_color = "#fff5cc" - access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics, access_mineral_storeroom) - minimal_access = list(access_eva, access_atmospherics, access_maint_tunnels, access_external_airlocks, access_emergency_storage, access_construction, access_mineral_storeroom, access_tech_storage) - alt_titles = list("Atmospheric Technician") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/atmos - -/datum/outfit/job/atmos - name = "Life Support Specialist" - jobtype = /datum/job/atmos - - uniform = /obj/item/clothing/under/rank/atmospheric_technician - belt = /obj/item/storage/belt/utility/atmostech - shoes = /obj/item/clothing/shoes/workboots - l_ear = /obj/item/radio/headset/headset_eng - id = /obj/item/card/id/engineering - pda = /obj/item/pda/atmos - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/atmos - box = /obj/item/storage/box/engineer - -/datum/job/mechanic - title = "Mechanic" - flag = MECHANIC - department_flag = KARMA - total_positions = 1 - spawn_positions = 1 - is_engineering = 1 - supervisors = "the chief engineer" - department_head = list("Chief Engineer") - selection_color = "#fff5cc" - access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_mechanic, access_external_airlocks, access_mineral_storeroom) - minimal_access = list(access_maint_tunnels, access_emergency_storage, access_mechanic, access_external_airlocks, access_mineral_storeroom) - outfit = /datum/outfit/job/mechanic - -/datum/outfit/job/mechanic - name = "Mechanic" - jobtype = /datum/job/mechanic - - uniform = /obj/item/clothing/under/rank/mechanic - belt = /obj/item/storage/belt/utility/full - shoes = /obj/item/clothing/shoes/workboots - head = /obj/item/clothing/head/hardhat - l_ear = /obj/item/radio/headset/headset_eng - id = /obj/item/card/id/engineering - r_pocket = /obj/item/t_scanner - pda = /obj/item/pda/engineering - backpack_contents = list( - /obj/item/pod_paint_bucket = 1 - ) - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/engineering - box = /obj/item/storage/box/engineer +/datum/job/chief_engineer + title = "Chief Engineer" + flag = JOB_CHIEF + department_flag = JOBCAT_ENGSEC + total_positions = 1 + spawn_positions = 1 + is_engineering = 1 + supervisors = "the captain" + department_head = list("Captain") + selection_color = "#ffeeaa" + req_admin_notify = 1 + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) + minimal_player_age = 21 + exp_requirements = 300 + exp_type = EXP_TYPE_ENGINEERING + outfit = /datum/outfit/job/chief_engineer + +/datum/outfit/job/chief_engineer + name = "Chief Engineer" + jobtype = /datum/job/chief_engineer + + uniform = /obj/item/clothing/under/rank/chief_engineer + belt = /obj/item/storage/belt/utility/chief/full + gloves = /obj/item/clothing/gloves/color/black/ce + shoes = /obj/item/clothing/shoes/brown + head = /obj/item/clothing/head/hardhat/white + l_ear = /obj/item/radio/headset/heads/ce + id = /obj/item/card/id/ce + l_pocket = /obj/item/t_scanner + pda = /obj/item/pda/heads/ce + backpack_contents = list( + /obj/item/melee/classic_baton/telescopic = 1 + ) + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/engineering + box = /obj/item/storage/box/engineer + + +/datum/job/engineer + title = "Station Engineer" + flag = JOB_ENGINEER + department_flag = JOBCAT_ENGSEC + total_positions = 5 + spawn_positions = 5 + is_engineering = 1 + supervisors = "the chief engineer" + department_head = list("Chief Engineer") + selection_color = "#fff5cc" + access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM) + alt_titles = list("Maintenance Technician","Engine Technician","Electrician") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/engineer + +/datum/outfit/job/engineer + name = "Station Engineer" + jobtype = /datum/job/engineer + + uniform = /obj/item/clothing/under/rank/engineer + belt = /obj/item/storage/belt/utility/full + shoes = /obj/item/clothing/shoes/workboots + head = /obj/item/clothing/head/hardhat + l_ear = /obj/item/radio/headset/headset_eng + id = /obj/item/card/id/engineering + l_pocket = /obj/item/t_scanner + pda = /obj/item/pda/engineering + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/engineering + box = /obj/item/storage/box/engineer + + + +/datum/job/atmos + title = "Life Support Specialist" + flag = JOB_ATMOSTECH + department_flag = JOBCAT_ENGSEC + total_positions = 3 + spawn_positions = 2 + is_engineering = 1 + supervisors = "the chief engineer" + department_head = list("Chief Engineer") + selection_color = "#fff5cc" + access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_EVA, ACCESS_ATMOSPHERICS, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_EMERGENCY_STORAGE, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE) + alt_titles = list("Atmospheric Technician") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/atmos + +/datum/outfit/job/atmos + name = "Life Support Specialist" + jobtype = /datum/job/atmos + + uniform = /obj/item/clothing/under/rank/atmospheric_technician + belt = /obj/item/storage/belt/utility/atmostech + shoes = /obj/item/clothing/shoes/workboots + l_ear = /obj/item/radio/headset/headset_eng + id = /obj/item/card/id/engineering + pda = /obj/item/pda/atmos + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/atmos + box = /obj/item/storage/box/engineer + +/datum/job/mechanic + title = "Mechanic" + flag = JOB_MECHANIC + department_flag = JOBCAT_KARMA + total_positions = 1 + spawn_positions = 1 + is_engineering = 1 + supervisors = "the chief engineer" + department_head = list("Chief Engineer") + selection_color = "#fff5cc" + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECHANIC, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_EMERGENCY_STORAGE, ACCESS_MECHANIC, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MINERAL_STOREROOM) + outfit = /datum/outfit/job/mechanic + +/datum/outfit/job/mechanic + name = "Mechanic" + jobtype = /datum/job/mechanic + + uniform = /obj/item/clothing/under/rank/mechanic + belt = /obj/item/storage/belt/utility/full + shoes = /obj/item/clothing/shoes/workboots + head = /obj/item/clothing/head/hardhat + l_ear = /obj/item/radio/headset/headset_eng + id = /obj/item/card/id/engineering + r_pocket = /obj/item/t_scanner + pda = /obj/item/pda/engineering + backpack_contents = list( + /obj/item/pod_paint_bucket = 1 + ) + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/engineering + box = /obj/item/storage/box/engineer diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 9def19d2970..a30799f08b9 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -1,272 +1,272 @@ -/datum/job - - //The name of the job - var/title = "NOPE" - - //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access - var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) - var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) - - //Bitflags for the job - var/flag = 0 - var/department_flag = 0 - var/department_head = list() - - //Players will be allowed to spawn in as jobs that are set to "Station" - var/list/faction = list("Station") - - //How many players can be this job - var/total_positions = 0 - - //How many players can spawn in as this job - var/spawn_positions = 0 - - //How many players have this job - var/current_positions = 0 - - //Supervisors, who this person answers to directly - var/supervisors = "" - - //Sellection screen color - var/selection_color = "#ffffff" - - //List of alternate titles, if any - var/list/alt_titles - - //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. - var/req_admin_notify - - //Various Departmental identifiers - var/is_supply - var/is_service - var/is_command - var/is_legal - var/is_engineering - var/is_medical - var/is_science - var/is_security - - //If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) - var/minimal_player_age = 0 - - var/exp_requirements = 0 - var/exp_type = "" - - var/disabilities_allowed = 1 - - var/admin_only = 0 - var/spawn_ert = 0 - var/syndicate_command = 0 - - var/outfit = null - - ///////////////////////////////// - // /vg/ feature: Job Objectives! - ///////////////////////////////// - var/required_objectives=list() // Objectives that are ALWAYS added. - var/optional_objectives=list() // Objectives that are SOMETIMES added. - -//Only override this proc -/datum/job/proc/after_spawn(mob/living/carbon/human/H) - -/datum/job/proc/announce(mob/living/carbon/human/H) - -/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE) - if(!H) - return 0 - - H.dna.species.before_equip_job(src, H, visualsOnly) - - if(outfit) - H.equipOutfit(outfit, visualsOnly) - - H.dna.species.after_equip_job(src, H, visualsOnly) - - if(!visualsOnly && announce) - announce(H) - -/datum/job/proc/get_access() - if(!config) //Needed for robots. - return src.minimal_access.Copy() - - if(config.jobs_have_minimal_access) - return src.minimal_access.Copy() - else - return src.access.Copy() - -//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 -/datum/job/proc/player_old_enough(client/C) - if(available_in_days(C) == 0) - return 1 //Available in 0 days = available right now = player is old enough to play. - return 0 - - -/datum/job/proc/available_in_days(client/C) - if(!C) - return 0 - if(!config.use_age_restriction_for_jobs) - return 0 - if(!isnum(C.player_age)) - return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced - if(!isnum(minimal_player_age)) - return 0 - - return max(0, minimal_player_age - C.player_age) - -/datum/job/proc/barred_by_disability(client/C) - if(!C) - return 0 - if(disabilities_allowed) - return 0 - var/list/prohibited_disabilities = list(DISABILITY_FLAG_BLIND, DISABILITY_FLAG_DEAF, DISABILITY_FLAG_MUTE, DISABILITY_FLAG_DIZZY) - for(var/i = 1, i < prohibited_disabilities.len, i++) - var/this_disability = prohibited_disabilities[i] - if(C.prefs.disabilities & this_disability) - return 1 - return 0 - -/datum/job/proc/is_position_available() - return (current_positions < total_positions) || (total_positions == -1) - -/datum/outfit/job - name = "Standard Gear" - collect_not_del = TRUE // we don't want anyone to lose their job shit - - var/allow_loadout = TRUE - var/allow_backbag_choice = TRUE - var/jobtype = null - - uniform = /obj/item/clothing/under/color/grey - id = /obj/item/card/id - l_ear = /obj/item/radio/headset - back = /obj/item/storage/backpack - shoes = /obj/item/clothing/shoes/black - pda = /obj/item/pda - - var/backpack = /obj/item/storage/backpack - var/satchel = /obj/item/storage/backpack/satchel_norm - var/dufflebag = /obj/item/storage/backpack/duffel - box = /obj/item/storage/box/survival - - var/tmp/list/gear_leftovers = list() - -/datum/outfit/job/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - if(allow_backbag_choice) - switch(H.backbag) - if(GBACKPACK) - back = /obj/item/storage/backpack //Grey backpack - if(GSATCHEL) - back = /obj/item/storage/backpack/satchel_norm //Grey satchel - if(GDUFFLEBAG) - back = /obj/item/storage/backpack/duffel //Grey Dufflebag - if(LSATCHEL) - back = /obj/item/storage/backpack/satchel //Leather Satchel - if(DSATCHEL) - back = satchel //Department satchel - if(DDUFFLEBAG) - back = dufflebag //Department dufflebag - else - back = backpack //Department backpack - - if(box && H.dna.species.speciesbox) - box = H.dna.species.speciesbox - - if(allow_loadout && H.client && (H.client.prefs.gear && H.client.prefs.gear.len)) - for(var/gear in H.client.prefs.gear) - var/datum/gear/G = gear_datums[gear] - if(G) - var/permitted = FALSE - - if(G.allowed_roles) - if(name in G.allowed_roles) - permitted = TRUE - else - permitted = TRUE - - if(G.whitelisted && (G.whitelisted != H.dna.species.name || !is_alien_whitelisted(H, G.whitelisted))) - permitted = FALSE - - if(!permitted) - to_chat(H, "Your current job or whitelist status does not permit you to spawn with [gear]!") - continue - - if(G.slot) - if(H.equip_to_slot_or_del(G.spawn_item(H), G.slot)) - to_chat(H, "Equipping you with [gear]!") - else - gear_leftovers += G - else - gear_leftovers += G - -/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - if(visualsOnly) - return - - imprint_idcard(H) - - H.sec_hud_set_ID() - - imprint_pda(H) - - if(gear_leftovers.len) - for(var/datum/gear/G in gear_leftovers) - var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.gear[G.display_name])) - if(istype(placed_in)) - if(isturf(placed_in)) - to_chat(H, "Placing [G.display_name] on [placed_in]!") - else - to_chat(H, "Placing [G.display_name] in [placed_in.name].") - continue - if(H.equip_to_appropriate_slot(G)) - to_chat(H, "Placing [G.display_name] in your inventory!") - continue - if(H.put_in_hands(G)) - to_chat(H, "Placing [G.display_name] in your hands!") - continue - to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.") - qdel(G) - - qdel(gear_leftovers) - - return 1 - -/datum/outfit/job/proc/imprint_idcard(mob/living/carbon/human/H) - var/datum/job/J = SSjobs.GetJobType(jobtype) - if(!J) - J = SSjobs.GetJob(H.job) - - var/alt_title - if(H.mind) - alt_title = H.mind.role_alt_title - - var/obj/item/card/id/C = H.wear_id - if(istype(C)) - C.access = J.get_access() - C.registered_name = H.real_name - C.rank = J.title - C.assignment = alt_title ? alt_title : J.title - C.sex = capitalize(H.gender) - C.age = H.age - C.name = "[C.registered_name]'s ID Card ([C.assignment])" - C.photo = get_id_photo(H) - - if(H.mind && H.mind.initial_account) - C.associated_account_number = H.mind.initial_account.account_number - C.owner_uid = H.UID() - C.owner_ckey = H.ckey - -/datum/outfit/job/proc/imprint_pda(mob/living/carbon/human/H) - var/obj/item/pda/PDA = H.wear_pda - var/obj/item/card/id/C = H.wear_id - if(istype(PDA) && istype(C)) - PDA.owner = H.real_name - PDA.ownjob = C.assignment - PDA.ownrank = C.rank - PDA.name = "PDA-[H.real_name] ([PDA.ownjob])" - -/datum/job/proc/would_accept_job_transfer_from_player(mob/player) - if(!guest_jobbans(title)) // actually checks if job is a whitelisted position - return TRUE - if(!istype(player)) - return FALSE - return is_job_whitelisted(player, title) +/datum/job + + //The name of the job + var/title = "NOPE" + + //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access + var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) + var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) + + //Bitflags for the job + var/flag = 0 + var/department_flag = 0 + var/department_head = list() + + //How many players can be this job + var/total_positions = 0 + + //How many players can spawn in as this job + var/spawn_positions = 0 + + //How many players have this job + var/current_positions = 0 + + //Supervisors, who this person answers to directly + var/supervisors = "" + + //Sellection screen color + var/selection_color = "#ffffff" + + //List of alternate titles, if any + var/list/alt_titles + + //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. + var/req_admin_notify + + //Various Departmental identifiers + var/is_supply + var/is_service + var/is_command + var/is_legal + var/is_engineering + var/is_medical + var/is_science + var/is_security + + //If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) + var/minimal_player_age = 0 + + var/exp_requirements = 0 + var/exp_type = "" + + var/disabilities_allowed = 1 + var/transfer_allowed = TRUE // If false, ID computer will always discourage transfers to this job, even if player is eligible + + var/admin_only = 0 + var/spawn_ert = 0 + var/syndicate_command = 0 + + var/outfit = null + + ///////////////////////////////// + // /vg/ feature: Job Objectives! + ///////////////////////////////// + var/required_objectives=list() // Objectives that are ALWAYS added. + var/optional_objectives=list() // Objectives that are SOMETIMES added. + +//Only override this proc +/datum/job/proc/after_spawn(mob/living/carbon/human/H) + +/datum/job/proc/announce(mob/living/carbon/human/H) + +/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE) + if(!H) + return 0 + + H.dna.species.before_equip_job(src, H, visualsOnly) + + if(outfit) + H.equipOutfit(outfit, visualsOnly) + + H.dna.species.after_equip_job(src, H, visualsOnly) + + if(!visualsOnly && announce) + announce(H) + +/datum/job/proc/get_access() + if(!config) //Needed for robots. + return src.minimal_access.Copy() + + if(config.jobs_have_minimal_access) + return src.minimal_access.Copy() + else + return src.access.Copy() + +//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 +/datum/job/proc/player_old_enough(client/C) + if(available_in_days(C) == 0) + return 1 //Available in 0 days = available right now = player is old enough to play. + return 0 + + +/datum/job/proc/available_in_days(client/C) + if(!C) + return 0 + if(!config.use_age_restriction_for_jobs) + return 0 + if(!isnum(C.player_age)) + return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced + if(!isnum(minimal_player_age)) + return 0 + + return max(0, minimal_player_age - C.player_age) + +/datum/job/proc/barred_by_disability(client/C) + if(!C) + return 0 + if(disabilities_allowed) + return 0 + var/list/prohibited_disabilities = list(DISABILITY_FLAG_BLIND, DISABILITY_FLAG_DEAF, DISABILITY_FLAG_MUTE, DISABILITY_FLAG_DIZZY) + for(var/i = 1, i < prohibited_disabilities.len, i++) + var/this_disability = prohibited_disabilities[i] + if(C.prefs.disabilities & this_disability) + return 1 + return 0 + +/datum/job/proc/is_position_available() + return (current_positions < total_positions) || (total_positions == -1) + +/datum/outfit/job + name = "Standard Gear" + collect_not_del = TRUE // we don't want anyone to lose their job shit + + var/allow_loadout = TRUE + var/allow_backbag_choice = TRUE + var/jobtype = null + + uniform = /obj/item/clothing/under/color/grey + id = /obj/item/card/id + l_ear = /obj/item/radio/headset + back = /obj/item/storage/backpack + shoes = /obj/item/clothing/shoes/black + pda = /obj/item/pda + + var/backpack = /obj/item/storage/backpack + var/satchel = /obj/item/storage/backpack/satchel_norm + var/dufflebag = /obj/item/storage/backpack/duffel + box = /obj/item/storage/box/survival + + var/tmp/list/gear_leftovers = list() + +/datum/outfit/job/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + if(allow_backbag_choice) + switch(H.backbag) + if(GBACKPACK) + back = /obj/item/storage/backpack //Grey backpack + if(GSATCHEL) + back = /obj/item/storage/backpack/satchel_norm //Grey satchel + if(GDUFFLEBAG) + back = /obj/item/storage/backpack/duffel //Grey Dufflebag + if(LSATCHEL) + back = /obj/item/storage/backpack/satchel //Leather Satchel + if(DSATCHEL) + back = satchel //Department satchel + if(DDUFFLEBAG) + back = dufflebag //Department dufflebag + else + back = backpack //Department backpack + + if(box && H.dna.species.speciesbox) + box = H.dna.species.speciesbox + + if(allow_loadout && H.client && (H.client.prefs.loadout_gear && H.client.prefs.loadout_gear.len)) + for(var/gear in H.client.prefs.loadout_gear) + var/datum/gear/G = GLOB.gear_datums[gear] + if(G) + var/permitted = FALSE + + if(G.allowed_roles) + if(name in G.allowed_roles) + permitted = TRUE + else + permitted = TRUE + + if(G.whitelisted && (G.whitelisted != H.dna.species.name || !is_alien_whitelisted(H, G.whitelisted))) + permitted = FALSE + + if(!permitted) + to_chat(H, "Your current job or whitelist status does not permit you to spawn with [gear]!") + continue + + if(G.slot) + if(H.equip_to_slot_or_del(G.spawn_item(H), G.slot)) + to_chat(H, "Equipping you with [gear]!") + else + gear_leftovers += G + else + gear_leftovers += G + +/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + if(visualsOnly) + return + + imprint_idcard(H) + + H.sec_hud_set_ID() + + imprint_pda(H) + + if(gear_leftovers.len) + for(var/datum/gear/G in gear_leftovers) + var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.loadout_gear[G.display_name])) + if(istype(placed_in)) + if(isturf(placed_in)) + to_chat(H, "Placing [G.display_name] on [placed_in]!") + else + to_chat(H, "Placing [G.display_name] in [placed_in.name].") + continue + if(H.equip_to_appropriate_slot(G)) + to_chat(H, "Placing [G.display_name] in your inventory!") + continue + if(H.put_in_hands(G)) + to_chat(H, "Placing [G.display_name] in your hands!") + continue + to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.") + qdel(G) + + qdel(gear_leftovers) + + return 1 + +/datum/outfit/job/proc/imprint_idcard(mob/living/carbon/human/H) + var/datum/job/J = SSjobs.GetJobType(jobtype) + if(!J) + J = SSjobs.GetJob(H.job) + + var/alt_title + if(H.mind) + alt_title = H.mind.role_alt_title + + var/obj/item/card/id/C = H.wear_id + if(istype(C)) + C.access = J.get_access() + C.registered_name = H.real_name + C.rank = J.title + C.assignment = alt_title ? alt_title : J.title + C.sex = capitalize(H.gender) + C.age = H.age + C.name = "[C.registered_name]'s ID Card ([C.assignment])" + C.photo = get_id_photo(H) + + if(H.mind && H.mind.initial_account) + C.associated_account_number = H.mind.initial_account.account_number + C.owner_uid = H.UID() + C.owner_ckey = H.ckey + +/datum/outfit/job/proc/imprint_pda(mob/living/carbon/human/H) + var/obj/item/pda/PDA = H.wear_pda + var/obj/item/card/id/C = H.wear_id + if(istype(PDA) && istype(C)) + PDA.owner = H.real_name + PDA.ownjob = C.assignment + PDA.ownrank = C.rank + PDA.name = "PDA-[H.real_name] ([PDA.ownjob])" + +/datum/job/proc/would_accept_job_transfer_from_player(mob/player) + if(!transfer_allowed) + return FALSE + if(!guest_jobbans(title)) // actually checks if job is a whitelisted position + return TRUE + if(!istype(player)) + return FALSE + return is_job_whitelisted(player, title) diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index 49e6b4486ac..2d1b091a10d 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -1,318 +1,317 @@ -/datum/job/cmo - title = "Chief Medical Officer" - flag = CMO - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the captain" - department_head = list("Captain") - selection_color = "#ffddf0" - req_admin_notify = 1 - access = list(access_medical, access_morgue, access_genetics, access_heads, - access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_paramedic, access_mineral_storeroom) - minimal_access = list(access_eva, access_medical, access_morgue, access_genetics, access_heads, - access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_maint_tunnels, access_paramedic, access_mineral_storeroom) - minimal_player_age = 21 - exp_requirements = 300 - exp_type = EXP_TYPE_MEDICAL - outfit = /datum/outfit/job/cmo - -/datum/outfit/job/cmo - name = "Chief Medical Officer" - jobtype = /datum/job/cmo - - uniform = /obj/item/clothing/under/rank/chief_medical_officer - suit = /obj/item/clothing/suit/storage/labcoat/cmo - shoes = /obj/item/clothing/shoes/brown - l_ear = /obj/item/radio/headset/heads/cmo - id = /obj/item/card/id/cmo - suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/storage/firstaid/adv - pda = /obj/item/pda/heads/cmo - backpack_contents = list( - /obj/item/melee/classic_baton/telescopic = 1 - ) - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - -/datum/job/doctor - title = "Medical Doctor" - flag = DOCTOR - department_flag = MEDSCI - total_positions = 5 - spawn_positions = 3 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_mineral_storeroom) - minimal_access = list(access_medical, access_morgue, access_surgery, access_maint_tunnels) - alt_titles = list("Surgeon","Nurse") - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/doctor - -/datum/outfit/job/doctor - name = "Medical Doctor" - jobtype = /datum/job/doctor - - uniform = /obj/item/clothing/under/rank/medical - suit = /obj/item/clothing/suit/storage/labcoat - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/storage/firstaid/adv - pda = /obj/item/pda/medical - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - -/datum/job/coroner - title = "Coroner" - flag = CORONER - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_mineral_storeroom) - minimal_access = list(access_medical, access_morgue, access_maint_tunnels) - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/coroner - -/datum/outfit/job/coroner - name = "Coroner" - jobtype = /datum/job/coroner - - uniform = /obj/item/clothing/under/rank/medical/mortician - suit = /obj/item/clothing/suit/storage/labcoat/mortician - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/clipboard - pda = /obj/item/pda/medical - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - - backpack_contents = list( - /obj/item/clothing/head/surgery/black = 1, - /obj/item/autopsy_scanner = 1, - /obj/item/reagent_scanner = 1, - /obj/item/storage/box/bodybags = 1) - -/datum/outfit/job/doctor/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(H.mind && H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Surgeon") - uniform = /obj/item/clothing/under/rank/medical/blue - head = /obj/item/clothing/head/surgery/blue - if("Medical Doctor") - uniform = /obj/item/clothing/under/rank/medical - if("Nurse") - if(H.gender == FEMALE) - if(prob(50)) - uniform = /obj/item/clothing/under/rank/nursesuit - else - uniform = /obj/item/clothing/under/rank/nurse - head = /obj/item/clothing/head/nursehat - else - uniform = /obj/item/clothing/under/rank/medical/purple - - - -//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro -/datum/job/chemist - title = "Chemist" - flag = CHEMIST - department_flag = MEDSCI - total_positions = 2 - spawn_positions = 2 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_mineral_storeroom) - minimal_access = list(access_medical, access_chemistry, access_maint_tunnels, access_mineral_storeroom) - alt_titles = list("Pharmacist","Pharmacologist") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/chemist - -/datum/outfit/job/chemist - name = "Chemist" - jobtype = /datum/job/chemist - - uniform = /obj/item/clothing/under/rank/chemist - suit = /obj/item/clothing/suit/storage/labcoat/chemist - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_med - glasses = /obj/item/clothing/glasses/science - id = /obj/item/card/id/medical - pda = /obj/item/pda/chemist - - backpack = /obj/item/storage/backpack/chemistry - satchel = /obj/item/storage/backpack/satchel_chem - dufflebag = /obj/item/storage/backpack/duffel/chemistry - -/datum/job/geneticist - title = "Geneticist" - flag = GENETICIST - department_flag = MEDSCI - total_positions = 2 - spawn_positions = 2 - is_medical = 1 - supervisors = "the chief medical officer and the research director" - department_head = list("Chief Medical Officer", "Research Director") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_research, access_mineral_storeroom) - minimal_access = list(access_medical, access_morgue, access_genetics, access_research, access_maint_tunnels) - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/geneticist - -/datum/outfit/job/geneticist - name = "Geneticist" - jobtype = /datum/job/geneticist - - uniform = /obj/item/clothing/under/rank/geneticist - suit = /obj/item/clothing/suit/storage/labcoat/genetics - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_medsci - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - pda = /obj/item/pda/geneticist - - backpack = /obj/item/storage/backpack/genetics - satchel = /obj/item/storage/backpack/satchel_gen - dufflebag = /obj/item/storage/backpack/duffel/genetics - - -/datum/job/virologist - title = "Virologist" - flag = VIROLOGIST - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) - minimal_access = list(access_medical, access_virology, access_maint_tunnels, access_mineral_storeroom) - alt_titles = list("Pathologist","Microbiologist") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/virologist - -/datum/outfit/job/virologist - name = "Virologist" - jobtype = /datum/job/virologist - - uniform = /obj/item/clothing/under/rank/virologist - suit = /obj/item/clothing/suit/storage/labcoat/virologist - shoes = /obj/item/clothing/shoes/white - mask = /obj/item/clothing/mask/surgical - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - pda = /obj/item/pda/viro - - backpack = /obj/item/storage/backpack/virology - satchel = /obj/item/storage/backpack/satchel_vir - dufflebag = /obj/item/storage/backpack/duffel/virology - -/datum/job/psychiatrist - title = "Psychiatrist" - flag = PSYCHIATRIST - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_psychiatrist) - minimal_access = list(access_medical, access_psychiatrist, access_maint_tunnels) - alt_titles = list("Psychologist","Therapist") - outfit = /datum/outfit/job/psychiatrist - -/datum/outfit/job/psychiatrist - name = "Psychiatrist" - jobtype = /datum/job/psychiatrist - - uniform = /obj/item/clothing/under/rank/medical - suit = /obj/item/clothing/suit/storage/labcoat - shoes = /obj/item/clothing/shoes/laceup - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - pda = /obj/item/pda/medical - -/datum/outfit/job/psychiatrist/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(H.mind && H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Psychiatrist") - uniform = /obj/item/clothing/under/rank/psych - if("Psychologist") - uniform = /obj/item/clothing/under/rank/psych/turtleneck - if("Therapist") - uniform = /obj/item/clothing/under/rank/medical - -/datum/job/paramedic - title = "Paramedic" - flag = PARAMEDIC - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_paramedic, access_medical, access_maint_tunnels, access_external_airlocks, access_morgue) - minimal_access=list(access_paramedic, access_medical, access_maint_tunnels, access_external_airlocks, access_morgue) - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/paramedic - -/datum/outfit/job/paramedic - name = "Paramedic" - jobtype = /datum/job/paramedic - - uniform = /obj/item/clothing/under/rank/medical/paramedic - shoes = /obj/item/clothing/shoes/black - head = /obj/item/clothing/head/soft/blue - mask = /obj/item/clothing/mask/cigarette - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - l_pocket = /obj/item/flashlight/pen - pda = /obj/item/pda/medical - backpack_contents = list( - /obj/item/healthanalyzer = 1 - ) - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - box = /obj/item/storage/box/engineer - +/datum/job/cmo + title = "Chief Medical Officer" + flag = JOB_CMO + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the captain" + department_head = list("Captain") + selection_color = "#ffddf0" + req_admin_notify = 1 + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_EVA, ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) + minimal_player_age = 21 + exp_requirements = 300 + exp_type = EXP_TYPE_MEDICAL + outfit = /datum/outfit/job/cmo + +/datum/outfit/job/cmo + name = "Chief Medical Officer" + jobtype = /datum/job/cmo + + uniform = /obj/item/clothing/under/rank/chief_medical_officer + suit = /obj/item/clothing/suit/storage/labcoat/cmo + shoes = /obj/item/clothing/shoes/brown + l_ear = /obj/item/radio/headset/heads/cmo + id = /obj/item/card/id/cmo + suit_store = /obj/item/flashlight/pen + l_hand = /obj/item/storage/firstaid/doctor + pda = /obj/item/pda/heads/cmo + backpack_contents = list( + /obj/item/melee/classic_baton/telescopic = 1 + ) + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + +/datum/job/doctor + title = "Medical Doctor" + flag = JOB_DOCTOR + department_flag = JOBCAT_MEDSCI + total_positions = 5 + spawn_positions = 3 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_MAINT_TUNNELS) + alt_titles = list("Surgeon","Nurse") + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/doctor + +/datum/outfit/job/doctor + name = "Medical Doctor" + jobtype = /datum/job/doctor + + uniform = /obj/item/clothing/under/rank/medical + suit = /obj/item/clothing/suit/storage/labcoat + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + l_hand = /obj/item/storage/firstaid/doctor + pda = /obj/item/pda/medical + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + +/datum/job/coroner + title = "Coroner" + flag = JOB_CORONER + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS) + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/coroner + +/datum/outfit/job/coroner + name = "Coroner" + jobtype = /datum/job/coroner + + uniform = /obj/item/clothing/under/rank/medical/mortician + suit = /obj/item/clothing/suit/storage/labcoat/mortician + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + l_hand = /obj/item/clipboard + pda = /obj/item/pda/medical + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + + backpack_contents = list( + /obj/item/clothing/head/surgery/black = 1, + /obj/item/autopsy_scanner = 1, + /obj/item/reagent_scanner = 1, + /obj/item/storage/box/bodybags = 1) + +/datum/outfit/job/doctor/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(H.mind && H.mind.role_alt_title) + switch(H.mind.role_alt_title) + if("Surgeon") + uniform = /obj/item/clothing/under/rank/medical/blue + head = /obj/item/clothing/head/surgery/blue + if("Medical Doctor") + uniform = /obj/item/clothing/under/rank/medical + if("Nurse") + if(H.gender == FEMALE) + if(prob(50)) + uniform = /obj/item/clothing/under/rank/nursesuit + else + uniform = /obj/item/clothing/under/rank/nurse + head = /obj/item/clothing/head/nursehat + else + uniform = /obj/item/clothing/under/rank/medical/purple + + + +//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro +/datum/job/chemist + title = "Chemist" + flag = JOB_CHEMIST + department_flag = JOBCAT_MEDSCI + total_positions = 2 + spawn_positions = 2 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_CHEMISTRY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + alt_titles = list("Pharmacist","Pharmacologist") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/chemist + +/datum/outfit/job/chemist + name = "Chemist" + jobtype = /datum/job/chemist + + uniform = /obj/item/clothing/under/rank/chemist + suit = /obj/item/clothing/suit/storage/labcoat/chemist + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_med + glasses = /obj/item/clothing/glasses/science + id = /obj/item/card/id/medical + pda = /obj/item/pda/chemist + + backpack = /obj/item/storage/backpack/chemistry + satchel = /obj/item/storage/backpack/satchel_chem + dufflebag = /obj/item/storage/backpack/duffel/chemistry + +/datum/job/geneticist + title = "Geneticist" + flag = JOB_GENETICIST + department_flag = JOBCAT_MEDSCI + total_positions = 2 + spawn_positions = 2 + is_medical = 1 + supervisors = "the chief medical officer and the research director" + department_head = list("Chief Medical Officer", "Research Director") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_RESEARCH, ACCESS_MAINT_TUNNELS) + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/geneticist + +/datum/outfit/job/geneticist + name = "Geneticist" + jobtype = /datum/job/geneticist + + uniform = /obj/item/clothing/under/rank/geneticist + suit = /obj/item/clothing/suit/storage/labcoat/genetics + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_medsci + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + pda = /obj/item/pda/geneticist + + backpack = /obj/item/storage/backpack/genetics + satchel = /obj/item/storage/backpack/satchel_gen + dufflebag = /obj/item/storage/backpack/duffel/genetics + + +/datum/job/virologist + title = "Virologist" + flag = JOB_VIROLOGIST + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS) + minimal_access = list(ACCESS_MEDICAL, ACCESS_VIROLOGY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + alt_titles = list("Pathologist","Microbiologist") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/virologist + +/datum/outfit/job/virologist + name = "Virologist" + jobtype = /datum/job/virologist + + uniform = /obj/item/clothing/under/rank/virologist + suit = /obj/item/clothing/suit/storage/labcoat/virologist + shoes = /obj/item/clothing/shoes/white + mask = /obj/item/clothing/mask/surgical + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + pda = /obj/item/pda/viro + + backpack = /obj/item/storage/backpack/virology + satchel = /obj/item/storage/backpack/satchel_vir + dufflebag = /obj/item/storage/backpack/duffel/virology + +/datum/job/psychiatrist + title = "Psychiatrist" + flag = JOB_PSYCHIATRIST + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_PSYCHIATRIST) + minimal_access = list(ACCESS_MEDICAL, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS) + alt_titles = list("Psychologist","Therapist") + outfit = /datum/outfit/job/psychiatrist + +/datum/outfit/job/psychiatrist + name = "Psychiatrist" + jobtype = /datum/job/psychiatrist + + uniform = /obj/item/clothing/under/rank/medical + suit = /obj/item/clothing/suit/storage/labcoat + shoes = /obj/item/clothing/shoes/laceup + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + pda = /obj/item/pda/medical + +/datum/outfit/job/psychiatrist/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(H.mind && H.mind.role_alt_title) + switch(H.mind.role_alt_title) + if("Psychiatrist") + uniform = /obj/item/clothing/under/rank/psych + if("Psychologist") + uniform = /obj/item/clothing/under/rank/psych/turtleneck + if("Therapist") + uniform = /obj/item/clothing/under/rank/medical + +/datum/job/paramedic + title = "Paramedic" + flag = JOB_PARAMEDIC + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MORGUE) + minimal_access=list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MORGUE) + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/paramedic + +/datum/outfit/job/paramedic + name = "Paramedic" + jobtype = /datum/job/paramedic + + uniform = /obj/item/clothing/under/rank/medical/paramedic + shoes = /obj/item/clothing/shoes/black + head = /obj/item/clothing/head/soft/blue + mask = /obj/item/clothing/mask/cigarette + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + l_pocket = /obj/item/flashlight/pen + pda = /obj/item/pda/medical + backpack_contents = list( + /obj/item/healthanalyzer = 1 + ) + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + box = /obj/item/storage/box/engineer diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index 435565434c8..684de933832 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -1,7 +1,7 @@ /datum/job/rd title = "Research Director" - flag = RD - department_flag = MEDSCI + flag = JOB_RD + department_flag = JOBCAT_MEDSCI total_positions = 1 spawn_positions = 1 is_science = 1 @@ -9,14 +9,14 @@ department_head = list("Captain") selection_color = "#ffddff" req_admin_notify = 1 - access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, - access_tox_storage, access_tech_storage, access_teleporter, access_sec_doors, - access_research, access_robotics, access_xenobiology, access_ai_upload, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_minisat, access_mineral_storeroom, access_network) - minimal_access = list(access_eva, access_rd, access_heads, access_tox, access_genetics, access_morgue, - access_tox_storage, access_tech_storage, access_teleporter, access_sec_doors, - access_research, access_robotics, access_xenobiology, access_ai_upload, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_minisat, access_maint_tunnels, access_mineral_storeroom, access_network) + access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, + ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, + ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_MINERAL_STOREROOM, ACCESS_NETWORK) + minimal_access = list(ACCESS_EVA, ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, + ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, + ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM, ACCESS_NETWORK) minimal_player_age = 21 exp_requirements = 300 exp_type = EXP_TYPE_SCIENCE @@ -49,16 +49,16 @@ /datum/job/scientist title = "Scientist" - flag = SCIENTIST - department_flag = MEDSCI + flag = JOB_SCIENTIST + department_flag = JOBCAT_MEDSCI total_positions = 6 spawn_positions = 6 is_science = 1 supervisors = "the research director" department_head = list("Research Director") selection_color = "#ffeeff" - access = list(access_robotics, access_tox, access_tox_storage, access_research, access_xenobiology, access_xenoarch, access_mineral_storeroom) - minimal_access = list(access_tox, access_tox_storage, access_research, access_xenobiology, access_xenoarch, access_maint_tunnels, access_mineral_storeroom) + access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) alt_titles = list("Xenoarcheologist", "Anomalist", "Plasma Researcher", "Xenobiologist", "Chemical Researcher") minimal_player_age = 3 exp_requirements = 300 @@ -88,16 +88,16 @@ /datum/job/roboticist title = "Roboticist" - flag = ROBOTICIST - department_flag = MEDSCI + flag = JOB_ROBOTICIST + department_flag = JOBCAT_MEDSCI total_positions = 2 spawn_positions = 2 is_science = 1 supervisors = "the research director" department_head = list("Research Director") selection_color = "#ffeeff" - access = list(access_robotics, access_tox, access_tox_storage, access_tech_storage, access_morgue, access_research, access_mineral_storeroom) //As a job that handles so many corpses, it makes sense for them to have morgue access. - minimal_access = list(access_robotics, access_tech_storage, access_morgue, access_research, access_maint_tunnels, access_mineral_storeroom) //As a job that handles so many corpses, it makes sense for them to have morgue access. + access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) //As a job that handles so many corpses, it makes sense for them to have morgue access. + minimal_access = list(ACCESS_ROBOTICS, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) //As a job that handles so many corpses, it makes sense for them to have morgue access. alt_titles = list("Biomechanical Engineer","Mechatronic Engineer") minimal_player_age = 3 exp_requirements = 180 diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index 0dfd0876817..a32656598df 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -1,7 +1,7 @@ /datum/job/hos title = "Head of Security" - flag = HOS - department_flag = ENGSEC + flag = JOB_HOS + department_flag = JOBCAT_ENGSEC total_positions = 1 spawn_positions = 1 is_security = 1 @@ -9,14 +9,14 @@ department_head = list("Captain") selection_color = "#ffdddd" req_admin_notify = 1 - access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, - access_forensics_lockers, access_pilot, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_weapons) - minimal_access = list(access_eva, access_security, access_sec_doors, access_brig, access_armory, access_court, - access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_pilot, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, + ACCESS_FORENSICS_LOCKERS, ACCESS_PILOT, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, + ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS) + minimal_access = list(ACCESS_EVA, ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, + ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, + ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_PILOT, ACCESS_WEAPONS) minimal_player_age = 21 exp_requirements = 300 exp_type = EXP_TYPE_SECURITY @@ -52,16 +52,16 @@ /datum/job/warden title = "Warden" - flag = WARDEN - department_flag = ENGSEC + flag = JOB_WARDEN + department_flag = JOBCAT_ENGSEC total_positions = 1 spawn_positions = 1 is_security = 1 supervisors = "the head of security" department_head = list("Head of Security") selection_color = "#ffeeee" - access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, access_maint_tunnels, access_morgue, access_weapons) - minimal_access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, access_maint_tunnels, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS) minimal_player_age = 21 exp_requirements = 600 exp_type = EXP_TYPE_CREW @@ -96,8 +96,8 @@ /datum/job/detective title = "Detective" - flag = DETECTIVE - department_flag = ENGSEC + flag = JOB_DETECTIVE + department_flag = JOBCAT_ENGSEC total_positions = 1 spawn_positions = 1 is_security = 1 @@ -105,8 +105,8 @@ department_head = list("Head of Security") selection_color = "#ffeeee" alt_titles = list("Forensic Technician") - access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court, access_weapons) - minimal_access = list(access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_WEAPONS) + minimal_access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_WEAPONS) alt_titles = list("Forensic Technician") minimal_player_age = 14 exp_requirements = 600 @@ -149,22 +149,22 @@ if(visualsOnly) return - H.dna.SetSEState(SOBERBLOCK,1) + H.dna.SetSEState(GLOB.soberblock,1) H.mutations += SOBER H.check_mutations = 1 /datum/job/officer title = "Security Officer" - flag = OFFICER - department_flag = ENGSEC + flag = JOB_OFFICER + department_flag = JOBCAT_ENGSEC total_positions = 7 spawn_positions = 7 is_security = 1 supervisors = "the head of security" department_head = list("Head of Security") selection_color = "#ffeeee" - access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_morgue, access_weapons) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS) minimal_player_age = 14 exp_requirements = 600 exp_type = EXP_TYPE_CREW @@ -195,16 +195,16 @@ /datum/job/brigdoc title = "Brig Physician" - flag = BRIGDOC - department_flag = KARMA + flag = JOB_BRIGDOC + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 is_security = 1 supervisors = "the head of security" department_head = list("Head of Security") selection_color = "#ffeeee" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) - minimal_access = list(access_medical, access_morgue, access_surgery, access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels) + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/brigdoc /datum/outfit/job/brigdoc @@ -217,7 +217,7 @@ glasses = /obj/item/clothing/glasses/hud/health/sunglasses id = /obj/item/card/id/security suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/storage/firstaid/adv + l_hand = /obj/item/storage/firstaid/doctor pda = /obj/item/pda/medical implants = list(/obj/item/implant/mindshield) backpack = /obj/item/storage/backpack/medic @@ -227,16 +227,16 @@ /datum/job/pilot title = "Security Pod Pilot" - flag = PILOT - department_flag = KARMA + flag = JOB_PILOT + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 is_security = 1 supervisors = "the head of security" department_head = list("Head of Security") selection_color = "#ffeeee" - access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_morgue, access_weapons, access_pilot, access_external_airlocks) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_weapons, access_pilot, access_external_airlocks) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_PILOT, ACCESS_EXTERNAL_AIRLOCKS) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS, ACCESS_PILOT, ACCESS_EXTERNAL_AIRLOCKS) minimal_player_age = 7 outfit = /datum/outfit/job/pilot @@ -259,4 +259,4 @@ backpack = /obj/item/storage/backpack/security satchel = /obj/item/storage/backpack/satchel_sec dufflebag = /obj/item/storage/backpack/duffel/security - box = /obj/item/storage/box/engineer \ No newline at end of file + box = /obj/item/storage/box/engineer diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index c239c1d3b3b..b6321765861 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -1,40 +1,40 @@ -/datum/job/ai - title = "AI" - flag = AI - department_flag = ENGSEC - total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm - spawn_positions = 1 - selection_color = "#ccffcc" - supervisors = "your laws" - department_head = list("Captain") - req_admin_notify = 1 - minimal_player_age = 30 - exp_requirements = 300 - exp_type = EXP_TYPE_SILICON - -/datum/job/ai/equip(mob/living/carbon/human/H) - if(!H) - return 0 - -/datum/job/ai/is_position_available() - return (empty_playable_ai_cores.len != 0) - - -/datum/job/cyborg - title = "Cyborg" - flag = CYBORG - department_flag = ENGSEC - total_positions = 1 - spawn_positions = 1 - supervisors = "your laws and the AI" //Nodrak - department_head = list("AI") - selection_color = "#ddffdd" - minimal_player_age = 21 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - alt_titles = list("Android", "Robot") - -/datum/job/cyborg/equip(mob/living/carbon/human/H) - if(!H) - return 0 - return H.Robotize() \ No newline at end of file +/datum/job/ai + title = "AI" + flag = JOB_AI + department_flag = JOBCAT_ENGSEC + total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm + spawn_positions = 1 + selection_color = "#ccffcc" + supervisors = "your laws" + department_head = list("Captain") + req_admin_notify = 1 + minimal_player_age = 30 + exp_requirements = 300 + exp_type = EXP_TYPE_SILICON + +/datum/job/ai/equip(mob/living/carbon/human/H) + if(!H) + return 0 + +/datum/job/ai/is_position_available() + return (GLOB.empty_playable_ai_cores.len != 0) + + +/datum/job/cyborg + title = "Cyborg" + flag = JOB_CYBORG + department_flag = JOBCAT_ENGSEC + total_positions = 1 + spawn_positions = 1 + supervisors = "your laws and the AI" //Nodrak + department_head = list("AI") + selection_color = "#ddffdd" + minimal_player_age = 21 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + alt_titles = list("Android", "Robot") + +/datum/job/cyborg/equip(mob/living/carbon/human/H) + if(!H) + return 0 + return H.Robotize() diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm index 6a22935f7be..c56d27bb408 100644 --- a/code/game/jobs/job/supervisor.dm +++ b/code/game/jobs/job/supervisor.dm @@ -1,8 +1,8 @@ -var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) +GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newscast = 0)) // Why the hell are captain announcements minor /datum/job/captain title = "Captain" - flag = CAPTAIN - department_flag = ENGSEC + flag = JOB_CAPTAIN + department_flag = JOBCAT_ENGSEC total_positions = 1 spawn_positions = 1 supervisors = "Nanotrasen officials" @@ -23,7 +23,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/captain/announce(mob/living/carbon/human/H) . = ..() - captain_announcement.Announce("All hands, Captain [H.real_name] on deck!") + GLOB.captain_announcement.Announce("All hands, Captain [H.real_name] on deck!") /datum/outfit/job/captain name = "Captain" @@ -57,8 +57,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/hop title = "Head of Personnel" - flag = HOP - department_flag = SUPPORT + flag = JOB_HOP + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 supervisors = "the captain" @@ -69,18 +69,18 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) minimal_player_age = 21 exp_requirements = 300 exp_type = EXP_TYPE_COMMAND - access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_hop, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_mineral_storeroom) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_hop, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_mineral_storeroom) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) outfit = /datum/outfit/job/hop /datum/outfit/job/hop @@ -103,8 +103,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/nanotrasenrep title = "Nanotrasen Representative" - flag = NANO - department_flag = KARMA + flag = JOB_NANO + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 supervisors = "the command staff" @@ -112,19 +112,20 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) selection_color = "#ddddff" req_admin_notify = 1 is_command = 1 + transfer_allowed = FALSE minimal_player_age = 21 - access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_hop, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_ntrep) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_ntrep) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_NTREP) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_NTREP) outfit = /datum/outfit/job/nanotrasenrep /datum/outfit/job/nanotrasenrep @@ -146,8 +147,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/blueshield title = "Blueshield" - flag = BLUESHIELD - department_flag = KARMA + flag = JOB_BLUESHIELD + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 supervisors = "the Nanotrasen representative" @@ -155,15 +156,16 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) selection_color = "#ddddff" req_admin_notify = 1 is_command = 1 + transfer_allowed = FALSE minimal_player_age = 21 - access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_hop, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_blueshield) - minimal_access = list(access_forensics_lockers, access_sec_doors, access_medical, access_construction, access_engine, access_maint_tunnels, access_research, - access_RC_announce, access_keycard_auth, access_heads, access_blueshield, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_BLUESHIELD) + minimal_access = list(ACCESS_FORENSICS_LOCKERS, ACCESS_SEC_DOORS, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_ENGINE, ACCESS_MAINT_TUNNELS, ACCESS_RESEARCH, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_HEADS, ACCESS_BLUESHIELD, ACCESS_WEAPONS) outfit = /datum/outfit/job/blueshield /datum/outfit/job/blueshield @@ -189,8 +191,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/judge title = "Magistrate" - flag = JUDGE - department_flag = KARMA + flag = JOB_JUDGE + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 supervisors = "the Nanotrasen Supreme Court" @@ -198,14 +200,15 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) selection_color = "#ddddff" req_admin_notify = 1 is_legal = 1 + transfer_allowed = FALSE minimal_player_age = 30 - access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_RC_announce, access_keycard_auth, access_gateway, access_magistrate) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_lawyer, access_magistrate, access_heads) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAGISTRATE) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_LAWYER, ACCESS_MAGISTRATE, ACCESS_HEADS) outfit = /datum/outfit/job/judge /datum/outfit/job/judge @@ -230,19 +233,19 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) -//var/global/lawyer = 0//Checks for another lawyer //This changed clothes on 2nd lawyer, both IA get the same dreds. +//GLOBAL_VAR_INIT(lawyer, 0) //Checks for another lawyer //This changed clothes on 2nd lawyer, both IA get the same dreds. | This was deprecated back in 2014, and its now 2020 /datum/job/lawyer title = "Internal Affairs Agent" - flag = LAWYER - department_flag = SUPPORT + flag = JOB_LAWYER + department_flag = JOBCAT_SUPPORT total_positions = 2 spawn_positions = 2 is_legal = 1 supervisors = "the magistrate" department_head = list("Captain") selection_color = "#ddddff" - access = list(access_lawyer, access_court, access_sec_doors, access_maint_tunnels, access_research, access_medical, access_construction, access_mailsorting) - minimal_access = list(access_lawyer, access_court, access_sec_doors, access_maint_tunnels, access_research, access_medical, access_construction, access_mailsorting) + access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS, ACCESS_RESEARCH, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING) + minimal_access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS, ACCESS_RESEARCH, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING) alt_titles = list("Human Resources Agent") minimal_player_age = 30 exp_requirements = 600 diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm index 9a70beaae31..d0064300b62 100644 --- a/code/game/jobs/job/support.dm +++ b/code/game/jobs/job/support.dm @@ -1,16 +1,16 @@ //Food /datum/job/bartender title = "Bartender" - flag = BARTENDER - department_flag = SUPPORT + flag = JOB_BARTENDER + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_hydroponics, access_bar, access_kitchen, access_morgue, access_weapons, access_mineral_storeroom) - minimal_access = list(access_bar, access_maint_tunnels, access_weapons, access_mineral_storeroom) + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_BAR, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) outfit = /datum/outfit/job/bartender /datum/outfit/job/bartender @@ -33,25 +33,25 @@ if(visualsOnly) return - H.dna.SetSEState(SOBERBLOCK,1) - genemutcheck(H, SOBERBLOCK, null, MUTCHK_FORCED) - H.dna.default_blocks.Add(SOBERBLOCK) + H.dna.SetSEState(GLOB.soberblock,1) + genemutcheck(H, GLOB.soberblock, null, MUTCHK_FORCED) + H.dna.default_blocks.Add(GLOB.soberblock) H.check_mutations = 1 /datum/job/chef title = "Chef" - flag = CHEF - department_flag = SUPPORT + flag = JOB_CHEF + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_hydroponics, access_bar, access_kitchen, access_morgue) - minimal_access = list(access_kitchen, access_maint_tunnels) + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE) + minimal_access = list(ACCESS_KITCHEN, ACCESS_MAINT_TUNNELS) alt_titles = list("Cook","Culinary Artist","Butcher") outfit = /datum/outfit/job/chef @@ -76,16 +76,16 @@ /datum/job/hydro title = "Botanist" - flag = BOTANIST - department_flag = SUPPORT + flag = JOB_BOTANIST + department_flag = JOBCAT_SUPPORT total_positions = 3 spawn_positions = 2 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_hydroponics, access_bar, access_kitchen, access_morgue) - minimal_access = list(access_hydroponics, access_morgue, access_maint_tunnels) + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE) + minimal_access = list(ACCESS_HYDROPONICS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS) alt_titles = list("Hydroponicist", "Botanical Researcher") outfit = /datum/outfit/job/hydro @@ -110,16 +110,16 @@ //Cargo /datum/job/qm title = "Quartermaster" - flag = QUARTERMASTER - department_flag = SUPPORT + flag = JOB_QUARTERMASTER + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_supply = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mint, access_mining, access_mining_station, access_mineral_storeroom) - minimal_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mint, access_mining, access_mining_station, access_mineral_storeroom) + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) outfit = /datum/outfit/job/qm /datum/outfit/job/qm @@ -138,16 +138,16 @@ /datum/job/cargo_tech title = "Cargo Technician" - flag = CARGOTECH - department_flag = SUPPORT + flag = JOB_CARGOTECH + department_flag = JOBCAT_SUPPORT total_positions = 2 spawn_positions = 2 is_supply = 1 supervisors = "the quartermaster" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mint, access_mining, access_mining_station, access_mineral_storeroom) - minimal_access = list(access_maint_tunnels, access_cargo, access_cargo_bot, access_mailsorting, access_mineral_storeroom) + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) outfit = /datum/outfit/job/cargo_tech /datum/outfit/job/cargo_tech @@ -164,16 +164,16 @@ /datum/job/mining title = "Shaft Miner" - flag = MINER - department_flag = SUPPORT + flag = JOB_MINER + department_flag = JOBCAT_SUPPORT total_positions = 6 spawn_positions = 8 is_supply = 1 supervisors = "the quartermaster" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mint, access_mining, access_mining_station, access_mineral_storeroom) - minimal_access = list(access_mining, access_mint, access_mining_station, access_mailsorting, access_maint_tunnels, access_mineral_storeroom) + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MINING, ACCESS_MINT, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) alt_titles = list("Spelunker") outfit = /datum/outfit/job/mining @@ -234,16 +234,16 @@ /datum/job/clown title = "Clown" - flag = CLOWN - department_flag = SUPPORT + flag = JOB_CLOWN + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_clown, access_theatre, access_maint_tunnels) - minimal_access = list(access_clown, access_theatre, access_maint_tunnels) + access = list(ACCESS_CLOWN, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_CLOWN, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/clown /datum/outfit/job/clown @@ -286,13 +286,13 @@ var/obj/item/organ/internal/cyberimp/brain/clown_voice/implant = new implant.insert(H) - H.dna.SetSEState(CLUMSYBLOCK, TRUE) - genemutcheck(H, CLUMSYBLOCK, null, MUTCHK_FORCED) - H.dna.default_blocks.Add(CLUMSYBLOCK) + H.dna.SetSEState(GLOB.clumsyblock, TRUE) + genemutcheck(H, GLOB.clumsyblock, null, MUTCHK_FORCED) + H.dna.default_blocks.Add(GLOB.clumsyblock) if(!ismachine(H)) - H.dna.SetSEState(COMICBLOCK, TRUE) - genemutcheck(H, COMICBLOCK, null, MUTCHK_FORCED) - H.dna.default_blocks.Add(COMICBLOCK) + H.dna.SetSEState(GLOB.comicblock, TRUE) + genemutcheck(H, GLOB.comicblock, null, MUTCHK_FORCED) + H.dna.default_blocks.Add(GLOB.comicblock) H.check_mutations = TRUE H.add_language("Clownish") @@ -319,16 +319,16 @@ /datum/job/mime title = "Mime" - flag = MIME - department_flag = SUPPORT + flag = JOB_MIME + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_mime, access_theatre, access_maint_tunnels) - minimal_access = list(access_mime, access_theatre, access_maint_tunnels) + access = list(ACCESS_MIME, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_MIME, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/mime /datum/outfit/job/mime @@ -371,16 +371,16 @@ /datum/job/janitor title = "Janitor" - flag = JANITOR - department_flag = SUPPORT + flag = JOB_JANITOR + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_janitor, access_maint_tunnels) - minimal_access = list(access_janitor, access_maint_tunnels) + access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS) alt_titles = list("Custodial Technician") outfit = /datum/outfit/job/janitor @@ -397,16 +397,16 @@ //More or less assistants /datum/job/librarian title = "Librarian" - flag = LIBRARIAN - department_flag = SUPPORT + flag = JOB_LIBRARIAN + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_library, access_maint_tunnels) - minimal_access = list(access_library, access_maint_tunnels) + access = list(ACCESS_LIBRARY, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_LIBRARY, ACCESS_MAINT_TUNNELS) alt_titles = list("Journalist") outfit = /datum/outfit/job/librarian @@ -426,8 +426,8 @@ /datum/job/barber title = "Barber" - flag = BARBER - department_flag = KARMA + flag = JOB_BARBER + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 is_service = 1 @@ -435,8 +435,8 @@ department_head = list("Head of Personnel") selection_color = "#dddddd" alt_titles = list("Hair Stylist","Beautician") - access = list(access_maint_tunnels) - minimal_access = list(access_maint_tunnels) + access = list(ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/barber /datum/outfit/job/barber diff --git a/code/game/jobs/job/support_chaplain.dm b/code/game/jobs/job/support_chaplain.dm index 02e55a325b2..b39eed344ae 100644 --- a/code/game/jobs/job/support_chaplain.dm +++ b/code/game/jobs/job/support_chaplain.dm @@ -1,16 +1,16 @@ //Due to how large this one is it gets its own file /datum/job/chaplain title = "Chaplain" - flag = CHAPLAIN - department_flag = SUPPORT + flag = JOB_CHAPLAIN + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_morgue, access_chapel_office, access_crematorium, access_maint_tunnels) - minimal_access = list(access_morgue, access_chapel_office, access_crematorium, access_maint_tunnels) + access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/chaplain diff --git a/code/game/jobs/job/syndicate.dm b/code/game/jobs/job/syndicate.dm index b5757ffe37c..f1bf52cbc8a 100644 --- a/code/game/jobs/job/syndicate.dm +++ b/code/game/jobs/job/syndicate.dm @@ -1,7 +1,7 @@ /datum/job/syndicateofficer title = "Syndicate Officer" - flag = SYNDICATE - department_flag = SYNDICATE + flag = JOB_SYNDICATE + department_flag = JOB_SYNDICATE // This gets its job as its own flag because admin jobs dont have flags total_positions = 5 spawn_positions = 5 supervisors = "the admins" @@ -51,8 +51,8 @@ U.implant(H) U.hidden_uplink.uses = 500 H.faction += "syndicate" - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] opshud.join_hud(H.mind.current) H.mind.offstation_role = TRUE set_antag_hud(H.mind.current, "hudoperative") - H.regenerate_icons() \ No newline at end of file + H.regenerate_icons() diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm index b69dbfd3ed4..f0e6f9bff42 100644 --- a/code/game/jobs/job_exp.dm +++ b/code/game/jobs/job_exp.dm @@ -1,6 +1,6 @@ // Playtime requirements for special roles (hours) -var/global/list/role_playtime_requirements = list( +GLOBAL_LIST_INIT(role_playtime_requirements, list( // NT ROLES ROLE_PAI = 0, ROLE_POSIBRAIN = 5, // Same as cyborg job. @@ -35,7 +35,7 @@ var/global/list/role_playtime_requirements = list( ROLE_RAIDER = 10, ROLE_ALIEN = 10, ROLE_ABDUCTOR = 10, -) +)) // Client Verbs @@ -113,7 +113,7 @@ var/global/list/role_playtime_requirements = list( var/isexempt = text2num(play_records[EXP_TYPE_EXEMPT]) if(isexempt) return 0 - var/minimal_player_hrs = role_playtime_requirements[role] + var/minimal_player_hrs = GLOB.role_playtime_requirements[role] if(!minimal_player_hrs) return 0 var/req_mins = minimal_player_hrs * 60 @@ -163,7 +163,7 @@ var/global/list/role_playtime_requirements = list( return "[key] has no records." var/return_text = "
        " var/list/exp_data = list() - for(var/category in exp_jobsmap) + for(var/category in GLOB.exp_jobsmap) if(text2num(play_records[category])) exp_data[category] = text2num(play_records[category]) else @@ -238,7 +238,7 @@ var/global/list/role_playtime_requirements = list( /client/proc/update_exp_client(var/minutes, var/announce_changes = 0) if(!src ||!ckey) return - var/DBQuery/exp_read = dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'") + var/DBQuery/exp_read = GLOB.dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'") if(!exp_read.Execute()) var/err = exp_read.ErrorMsg() log_game("SQL ERROR during exp_update_client read. Error : \[[err]\]\n") @@ -252,7 +252,7 @@ var/global/list/role_playtime_requirements = list( if(!hasread) return var/list/play_records = list() - for(var/rtype in exp_jobsmap) + for(var/rtype in GLOB.exp_jobsmap) if(text2num(read_records[rtype])) play_records[rtype] = text2num(read_records[rtype]) else @@ -270,9 +270,9 @@ var/global/list/role_playtime_requirements = list( added_living += minutes if(announce_changes) to_chat(mob,"You got: [minutes] Living EXP!") - for(var/category in exp_jobsmap) - if(exp_jobsmap[category]["titles"]) - if(myrole in exp_jobsmap[category]["titles"]) + for(var/category in GLOB.exp_jobsmap) + if(GLOB.exp_jobsmap[category]["titles"]) + if(myrole in GLOB.exp_jobsmap[category]["titles"]) play_records[category] += minutes if(announce_changes) to_chat(mob,"You got: [minutes] [category] EXP!") @@ -290,15 +290,15 @@ var/global/list/role_playtime_requirements = list( var/new_exp = list2params(play_records) prefs.exp = new_exp new_exp = sanitizeSQL(new_exp) - var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET exp = '[new_exp]',lastseen = Now() WHERE ckey='[ckey]'") + var/DBQuery/update_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET exp = '[new_exp]',lastseen = Now() WHERE ckey='[ckey]'") if(!update_query.Execute()) var/err = update_query.ErrorMsg() log_game("SQL ERROR during exp_update_client write 1. Error : \[[err]\]\n") message_admins("SQL ERROR during exp_update_client write 1. Error : \[[err]\]\n") return - var/DBQuery/update_query_history = dbcon.NewQuery("INSERT INTO [format_table_name("playtime_history")] (ckey, date, time_living, time_ghost) VALUES ('[ckey]',CURDATE(),[added_living],[added_ghost]) ON DUPLICATE KEY UPDATE time_living=time_living+VALUES(time_living),time_ghost=time_ghost+VALUES(time_ghost)") + var/DBQuery/update_query_history = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("playtime_history")] (ckey, date, time_living, time_ghost) VALUES ('[ckey]',CURDATE(),[added_living],[added_ghost]) ON DUPLICATE KEY UPDATE time_living=time_living+VALUES(time_living),time_ghost=time_ghost+VALUES(time_ghost)") if(!update_query_history.Execute()) var/err = update_query_history.ErrorMsg() log_game("SQL ERROR during exp_update_client write 2. Error : \[[err]\]\n") message_admins("SQL ERROR during exp_update_client write 2. Error : \[[err]\]\n") - return \ No newline at end of file + return diff --git a/code/game/jobs/job_objective.dm b/code/game/jobs/job_objective.dm index 48fa911bcf5..11fc2b7181d 100644 --- a/code/game/jobs/job_objective.dm +++ b/code/game/jobs/job_objective.dm @@ -73,4 +73,4 @@ else feedback_add_details("employee_success","FAIL") - return text \ No newline at end of file + return text diff --git a/code/game/jobs/job_objectives/science.dm b/code/game/jobs/job_objectives/science.dm index 0d5b4aebd83..70ff38a6a32 100644 --- a/code/game/jobs/job_objectives/science.dm +++ b/code/game/jobs/job_objectives/science.dm @@ -42,4 +42,4 @@ /datum/job_objective/make_ripley/get_description() var/desc = "Make a Ripley or Firefighter." desc += "([units_completed] created.)" - return desc \ No newline at end of file + return desc diff --git a/code/game/jobs/job_scaling.dm b/code/game/jobs/job_scaling.dm index 90d55b03444..c062fe6c216 100644 --- a/code/game/jobs/job_scaling.dm +++ b/code/game/jobs/job_scaling.dm @@ -8,4 +8,4 @@ SSjobs.LoadJobs("config/jobs_highpop.txt") else log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - keeping standard job config"); - return 1 \ No newline at end of file + return 1 diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index f45f8f5a22b..8ff6b58e5c4 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -1,206 +1,147 @@ - -var/const/ENGSEC =(1<<0) - -var/const/CAPTAIN =(1<<0) -var/const/HOS =(1<<1) -var/const/WARDEN =(1<<2) -var/const/DETECTIVE =(1<<3) -var/const/OFFICER =(1<<4) -var/const/CHIEF =(1<<5) -var/const/ENGINEER =(1<<6) -var/const/ATMOSTECH =(1<<7) -var/const/AI =(1<<8) -var/const/CYBORG =(1<<9) -var/const/CENTCOM =(1<<10) -var/const/SYNDICATE =(1<<11) - -var/const/MEDSCI =(1<<1) - -var/const/RD =(1<<0) -var/const/SCIENTIST =(1<<1) -var/const/CHEMIST =(1<<2) -var/const/CMO =(1<<3) -var/const/DOCTOR =(1<<4) -var/const/GENETICIST =(1<<5) -var/const/VIROLOGIST =(1<<6) -var/const/PSYCHIATRIST =(1<<7) -var/const/ROBOTICIST =(1<<8) -var/const/PARAMEDIC =(1<<9) -var/const/CORONER =(1<<10) - - -var/const/SUPPORT =(1<<2) - -var/const/HOP =(1<<0) -var/const/BARTENDER =(1<<1) -var/const/BOTANIST =(1<<2) -var/const/CHEF =(1<<3) -var/const/JANITOR =(1<<4) -var/const/LIBRARIAN =(1<<5) -var/const/QUARTERMASTER =(1<<6) -var/const/CARGOTECH =(1<<7) -var/const/MINER =(1<<8) -var/const/LAWYER =(1<<9) -var/const/CHAPLAIN =(1<<10) -var/const/CLOWN =(1<<11) -var/const/MIME =(1<<12) -var/const/CIVILIAN =(1<<13) - - -var/const/KARMA =(1<<3) - -var/const/NANO =(1<<0) -var/const/BLUESHIELD =(1<<1) -var/const/BARBER =(1<<3) -var/const/MECHANIC =(1<<4) -var/const/BRIGDOC =(1<<5) -var/const/JUDGE =(1<<6) -var/const/PILOT =(1<<7) - -var/list/assistant_occupations = list( -) - - -var/list/command_positions = list( - "Captain", - "Head of Personnel", - "Head of Security", - "Chief Engineer", - "Research Director", - "Chief Medical Officer", - "Nanotrasen Representative" -) - - -var/list/engineering_positions = list( - "Chief Engineer", - "Station Engineer", - "Life Support Specialist", - "Mechanic" -) - - -var/list/medical_positions = list( - "Chief Medical Officer", - "Medical Doctor", - "Geneticist", - "Psychiatrist", - "Chemist", - "Virologist", - "Paramedic", - "Coroner" -) - - -var/list/science_positions = list( - "Research Director", - "Scientist", - "Geneticist", //Part of both medical and science - "Roboticist", -) - -//BS12 EDIT -var/list/support_positions = list( - "Head of Personnel", - "Bartender", - "Botanist", - "Chef", - "Janitor", - "Librarian", - "Quartermaster", - "Cargo Technician", - "Shaft Miner", - "Internal Affairs Agent", - "Chaplain", - "Clown", - "Mime", - "Barber", - "Magistrate", - "Nanotrasen Representative", - "Blueshield" -) - -var/list/supply_positions = list( - "Head of Personnel", - "Quartermaster", - "Cargo Technician", - "Shaft Miner" -) - -var/list/service_positions = support_positions - supply_positions + list("Head of Personnel") - - -var/list/security_positions = list( - "Head of Security", - "Warden", - "Detective", - "Security Officer", - "Brig Physician", - "Security Pod Pilot", - "Magistrate" -) - - -var/list/civilian_positions = list( - "Civilian" -) - -var/list/nonhuman_positions = list( - "AI", - "Cyborg", - "Drone", - "pAI" -) - -var/list/whitelisted_positions = list( - "Blueshield", - "Nanotrasen Representative", - "Barber", - "Mechanic", - "Brig Physician", - "Magistrate", - "Security Pod Pilot", -) - - -/proc/guest_jobbans(var/job) - return (job in whitelisted_positions) - -/proc/get_job_datums() - var/list/occupations = list() - var/list/all_jobs = typesof(/datum/job) - - for(var/A in all_jobs) - var/datum/job/job = new A() - if(!job) continue - occupations += job - - return occupations - -/proc/get_alternate_titles(var/job) - var/list/jobs = get_job_datums() - var/list/titles = list() - - for(var/datum/job/J in jobs) - if(!J) continue - if(J.title == job) - titles = J.alt_titles - - return titles - -var/global/list/exp_jobsmap = list( - EXP_TYPE_LIVING = list(), // all living mobs - EXP_TYPE_CREW = list(titles = command_positions | engineering_positions | medical_positions | science_positions | support_positions | supply_positions | security_positions | civilian_positions | list("AI","Cyborg") | whitelisted_positions), // crew positions - EXP_TYPE_SPECIAL = list(), // antags, ERT, etc - EXP_TYPE_GHOST = list(), // dead people, observers - EXP_TYPE_EXEMPT = list(), // special grandfather setting - EXP_TYPE_COMMAND = list(titles = command_positions), - EXP_TYPE_ENGINEERING = list(titles = engineering_positions), - EXP_TYPE_MEDICAL = list(titles = medical_positions), - EXP_TYPE_SCIENCE = list(titles = science_positions), - EXP_TYPE_SUPPLY = list(titles = supply_positions), - EXP_TYPE_SECURITY = list(titles = security_positions), - EXP_TYPE_SILICON = list(titles = list("AI","Cyborg")), - EXP_TYPE_SERVICE = list(titles = service_positions), - EXP_TYPE_WHITELIST = list(titles = whitelisted_positions) // karma-locked jobs -) + +GLOBAL_LIST_EMPTY(assistant_occupations) + + +GLOBAL_LIST_INIT(command_positions, list( + "Captain", + "Head of Personnel", + "Head of Security", + "Chief Engineer", + "Research Director", + "Chief Medical Officer", + "Nanotrasen Representative" +)) + + +GLOBAL_LIST_INIT(engineering_positions, list( + "Chief Engineer", + "Station Engineer", + "Life Support Specialist", + "Mechanic" +)) + + +GLOBAL_LIST_INIT(medical_positions, list( + "Chief Medical Officer", + "Medical Doctor", + "Geneticist", + "Psychiatrist", + "Chemist", + "Virologist", + "Paramedic", + "Coroner" +)) + + +GLOBAL_LIST_INIT(science_positions, list( + "Research Director", + "Scientist", + "Geneticist", //Part of both medical and science + "Roboticist", +)) + +//BS12 EDIT +GLOBAL_LIST_INIT(support_positions, list( + "Head of Personnel", + "Bartender", + "Botanist", + "Chef", + "Janitor", + "Librarian", + "Quartermaster", + "Cargo Technician", + "Shaft Miner", + "Internal Affairs Agent", + "Chaplain", + "Clown", + "Mime", + "Barber", + "Magistrate", + "Nanotrasen Representative", + "Blueshield" +)) + +GLOBAL_LIST_INIT(supply_positions, list( + "Head of Personnel", + "Quartermaster", + "Cargo Technician", + "Shaft Miner" +)) + +GLOBAL_LIST_INIT(service_positions, (support_positions - supply_positions + list("Head of Personnel"))) + + +GLOBAL_LIST_INIT(security_positions, list( + "Head of Security", + "Warden", + "Detective", + "Security Officer", + "Brig Physician", + "Security Pod Pilot", + "Magistrate" +)) + + +GLOBAL_LIST_INIT(civilian_positions, list( + "Civilian" +)) + +GLOBAL_LIST_INIT(nonhuman_positions, list( + "AI", + "Cyborg", + "Drone", + "pAI" +)) + +GLOBAL_LIST_INIT(whitelisted_positions, list( + "Blueshield", + "Nanotrasen Representative", + "Barber", + "Mechanic", + "Brig Physician", + "Magistrate", + "Security Pod Pilot", +)) + + +/proc/guest_jobbans(var/job) + return (job in GLOB.whitelisted_positions) + +/proc/get_job_datums() + var/list/occupations = list() + var/list/all_jobs = typesof(/datum/job) + + for(var/A in all_jobs) + var/datum/job/job = new A() + if(!job) continue + occupations += job + + return occupations + +/proc/get_alternate_titles(var/job) + var/list/jobs = get_job_datums() + var/list/titles = list() + + for(var/datum/job/J in jobs) + if(!J) continue + if(J.title == job) + titles = J.alt_titles + + return titles + +GLOBAL_LIST_INIT(exp_jobsmap, list( + EXP_TYPE_LIVING = list(), // all living mobs + EXP_TYPE_CREW = list(titles = command_positions | engineering_positions | medical_positions | science_positions | support_positions | supply_positions | security_positions | civilian_positions | list("AI","Cyborg") | whitelisted_positions), // crew positions + EXP_TYPE_SPECIAL = list(), // antags, ERT, etc + EXP_TYPE_GHOST = list(), // dead people, observers + EXP_TYPE_EXEMPT = list(), // special grandfather setting + EXP_TYPE_COMMAND = list(titles = command_positions), + EXP_TYPE_ENGINEERING = list(titles = engineering_positions), + EXP_TYPE_MEDICAL = list(titles = medical_positions), + EXP_TYPE_SCIENCE = list(titles = science_positions), + EXP_TYPE_SUPPLY = list(titles = supply_positions), + EXP_TYPE_SECURITY = list(titles = security_positions), + EXP_TYPE_SILICON = list(titles = list("AI","Cyborg")), + EXP_TYPE_SERVICE = list(titles = service_positions), + EXP_TYPE_WHITELIST = list(titles = whitelisted_positions) // karma-locked jobs +)) diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm index a09eaed6c02..ab1bd3b8067 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/whitelist.dm @@ -1,99 +1,99 @@ -#define WHITELISTFILE "data/whitelist.txt" - -var/list/whitelist = list() - -/hook/startup/proc/loadWhitelist() - if(config.usewhitelist) - load_whitelist() - return 1 - -/proc/load_whitelist() - whitelist = file2list(WHITELISTFILE) - if(!whitelist.len) whitelist = null -/* -/proc/check_whitelist(mob/M, var/rank) - if(!whitelist) - return 0 - return ("[M.ckey]" in whitelist) -*/ - -/proc/is_job_whitelisted(mob/M, var/rank) - if(guest_jobbans(rank)) - if(!config.usewhitelist) - return 1 - if(config.disable_karma) - return 1 - if(check_rights(R_ADMIN, 0, M)) - return 1 - if(!dbcon.IsConnected()) - to_chat(usr, "Unable to connect to whitelist database. Please try again later.
        ") - return 0 - else - var/DBQuery/query = dbcon.NewQuery("SELECT job FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") - query.Execute() - - - while(query.NextRow()) - var/joblist = query.item[1] - if(joblist!="*") - var/allowed_jobs = splittext(joblist,",") - if(rank in allowed_jobs) return 1 - else return 1 - return 0 - else - return 1 - - - - -/var/list/alien_whitelist = list() - -/hook/startup/proc/loadAlienWhitelist() - if(config.usealienwhitelist) - load_alienwhitelist() - return 1 - -/proc/load_alienwhitelist() - var/text = file2text("config/alienwhitelist.txt") - if(!text) - log_config("Failed to load config/alienwhitelist.txt\n") - else - alien_whitelist = splittext(text, "\n") - -//todo: admin aliens -/proc/is_alien_whitelisted(mob/M, var/species) - if(!config.usealienwhitelist) - return 1 - if(config.disable_karma) - return 1 - if(species == "human" || species == "Human") - return 1 - if(check_rights(R_ADMIN, 0)) - return 1 - if(!alien_whitelist) - return 0 - if(!dbcon.IsConnected()) - to_chat(usr, "Unable to connect to whitelist database. Please try again later.
        ") - return 0 - else - var/DBQuery/query = dbcon.NewQuery("SELECT species FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") - query.Execute() - - while(query.NextRow()) - var/specieslist = query.item[1] - if(specieslist!="*") - var/allowed_species = splittext(specieslist,",") - if(species in allowed_species) return 1 - else return 1 - return 0 -/* - if(M && species) - for(var/s in alien_whitelist) - if(findtext(s,"[M.ckey] - [species]")) - return 1 - if(findtext(s,"[M.ckey] - All")) - return 1 -*/ - - -#undef WHITELISTFILE \ No newline at end of file +#define WHITELISTFILE "data/whitelist.txt" + +GLOBAL_LIST_EMPTY(whitelist) + +/hook/startup/proc/loadWhitelist() + if(config.usewhitelist) + load_whitelist() + return 1 + +/proc/load_whitelist() + GLOB.whitelist = file2list(WHITELISTFILE) + if(!GLOB.whitelist.len) GLOB.whitelist = null +/* +/proc/check_whitelist(mob/M, var/rank) + if(!whitelist) + return 0 + return ("[M.ckey]" in whitelist) +*/ + +/proc/is_job_whitelisted(mob/M, var/rank) + if(guest_jobbans(rank)) + if(!config.usewhitelist) + return 1 + if(config.disable_karma) + return 1 + if(check_rights(R_ADMIN, 0, M)) + return 1 + if(!GLOB.dbcon.IsConnected()) + to_chat(usr, "Unable to connect to whitelist database. Please try again later.
        ") + return 0 + else + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT job FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") + query.Execute() + + + while(query.NextRow()) + var/joblist = query.item[1] + if(joblist!="*") + var/allowed_jobs = splittext(joblist,",") + if(rank in allowed_jobs) return 1 + else return 1 + return 0 + else + return 1 + + + + +GLOBAL_LIST_EMPTY(alien_whitelist) + +/hook/startup/proc/loadAlienWhitelist() + if(config.usealienwhitelist) + load_alienwhitelist() + return 1 + +/proc/load_alienwhitelist() + var/text = file2text("config/alienwhitelist.txt") + if(!text) + log_config("Failed to load config/alienwhitelist.txt\n") + else + GLOB.alien_whitelist = splittext(text, "\n") + +//todo: admin aliens +/proc/is_alien_whitelisted(mob/M, var/species) + if(!config.usealienwhitelist) + return 1 + if(config.disable_karma) + return 1 + if(species == "human" || species == "Human") + return 1 + if(check_rights(R_ADMIN, 0)) + return 1 + if(!GLOB.alien_whitelist) + return 0 + if(!GLOB.dbcon.IsConnected()) + to_chat(usr, "Unable to connect to whitelist database. Please try again later.
        ") + return 0 + else + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT species FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") + query.Execute() + + while(query.NextRow()) + var/specieslist = query.item[1] + if(specieslist!="*") + var/allowed_species = splittext(specieslist,",") + if(species in allowed_species) return 1 + else return 1 + return 0 +/* + if(M && species) + for(var/s in alien_whitelist) + if(findtext(s,"[M.ckey] - [species]")) + return 1 + if(findtext(s,"[M.ckey] - All")) + return 1 +*/ + + +#undef WHITELISTFILE diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm index b5b2413fb96..d3b2114f801 100644 --- a/code/game/machinery/Beacon.dm +++ b/code/game/machinery/Beacon.dm @@ -86,4 +86,4 @@ /obj/machinery/bluespace_beacon/syndicate/infiltrator/New() ..() - enabled = FALSE \ No newline at end of file + enabled = FALSE diff --git a/code/game/machinery/Freezer.dm b/code/game/machinery/Freezer.dm index b6c6121cfd6..d9243005ead 100644 --- a/code/game/machinery/Freezer.dm +++ b/code/game/machinery/Freezer.dm @@ -1,332 +1,332 @@ -/obj/machinery/atmospherics/unary/cold_sink/freezer - name = "freezer" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "freezer" - density = 1 - var/min_temperature = 0 - anchored = 1.0 - use_power = IDLE_POWER_USE - current_heat_capacity = 1000 - layer = 3 - plane = GAME_PLANE - max_integrity = 300 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/New() - ..() - initialize_directions = dir - component_parts = list() - component_parts += new /obj/item/circuitboard/thermomachine(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/thermomachine(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/RefreshParts() - var/H - var/T - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - H += M.rating - for(var/obj/item/stock_parts/micro_laser/M in component_parts) - T += M.rating - min_temperature = max(0,T0C - (170 + (T*15))) - current_heat_capacity = 1000 * ((H - 1) ** 2) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/on_construction() - ..(dir,dir) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attackby(obj/item/I, mob/user, params) - if(exchange_parts(user, I)) - return - return ..() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/crowbar_act(mob/user, obj/item/I) - if(default_deconstruction_crowbar(user, I)) - return TRUE - -/obj/machinery/atmospherics/unary/cold_sink/freezer/screwdriver_act(mob/user, obj/item/I) - if(default_deconstruction_screwdriver(user, "freezer-o", "freezer", I)) - on = FALSE - update_icon() - return TRUE - -/obj/machinery/atmospherics/unary/cold_sink/freezer/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!panel_open) - to_chat(user, "Open the maintenance panel first.") - return - var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) - var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices - dir = choices[selected] - var/node_connect = dir - initialize_directions = dir - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(target.initialize_directions & get_dir(target,src)) - node = target - break - build_network() - update_icon() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/update_icon() - if(panel_open) - icon_state = "freezer-o" - else if(src.on) - icon_state = "freezer_1" - else - icon_state = "freezer" - return - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ai(mob/user as mob) - attack_hand(user) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ghost(mob/user as mob) - attack_hand(user) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_hand(mob/user as mob) - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - - src.ui_interact(user) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 540, 300) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["on"] = on ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) - if(air_contents.total_moles() == 0 && air_contents.temperature == 0) - data["gasTemperatureCelsius"] = 0 - data["minGasTemperature"] = round(min_temperature) - data["maxGasTemperature"] = round(T20C) - data["targetGasTemperature"] = round(current_temperature) - data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) - - var/temp_class = "good" - if(air_contents.temperature > (T0C - 20)) - temp_class = "bad" - else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) - temp_class = "average" - data["gasTemperatureClass"] = temp_class - return data - -/obj/machinery/atmospherics/unary/cold_sink/freezer/Topic(href, href_list) - if(..()) - return 1 - if(href_list["toggleStatus"]) - src.on = !src.on - update_icon() - else if(href_list["minimum"]) - current_temperature = min_temperature - else if(href_list["maximum"]) - current_temperature = T20C - else if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - src.current_temperature = min(T20C, src.current_temperature+amount) - else - src.current_temperature = max(min_temperature, src.current_temperature+amount) - src.add_fingerprint(usr) - return 1 - -/obj/machinery/atmospherics/unary/cold_sink/freezer/power_change() - ..() - if(stat & NOPOWER) - on = 0 - update_icon() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ - name = "heater" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "heater" - density = 1 - var/max_temperature = 0 - anchored = 1.0 - layer = 3 - current_heat_capacity = 1000 - max_integrity = 300 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/New() - ..() - initialize_directions = dir - var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) - H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater - H.name = "circuit board (Heater)" - component_parts = list() - component_parts += H - component_parts += new /obj/item/stock_parts/matter_bin(src) - component_parts += new /obj/item/stock_parts/matter_bin(src) - component_parts += new /obj/item/stock_parts/micro_laser(src) - component_parts += new /obj/item/stock_parts/micro_laser(src) - component_parts += new /obj/item/stack/sheet/glass(src) - component_parts += new /obj/item/stack/cable_coil(src, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/upgraded/New() - ..() - var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) - H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater - H.name = "circuit board (Heater)" - component_parts = list() - component_parts += H - component_parts += new /obj/item/stock_parts/matter_bin/super(src) - component_parts += new /obj/item/stock_parts/matter_bin/super(src) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) - component_parts += new /obj/item/stack/sheet/glass(src) - component_parts += new /obj/item/stack/cable_coil(src, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/on_construction() - ..(dir,dir) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/RefreshParts() - var/H - var/T - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - H += M.rating - for(var/obj/item/stock_parts/micro_laser/M in component_parts) - T += M.rating - max_temperature = T20C + (140 * T) - current_heat_capacity = 1000 * ((H - 1) ** 2) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attackby(obj/item/I, mob/user, params) - if(exchange_parts(user, I)) - return - return ..() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/crowbar_act(mob/user, obj/item/I) - if(default_deconstruction_crowbar(user, I)) - return TRUE - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/screwdriver_act(mob/user, obj/item/I) - if(default_deconstruction_screwdriver(user, "heater-o", "heater", I)) - on = 0 - update_icon() - return TRUE - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!panel_open) - to_chat(user, "Open the maintenance panel first.") - return - var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) - var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices - dir = choices[selected] - var/node_connect = dir - initialize_directions = dir - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(target.initialize_directions & get_dir(target,src)) - node = target - break - build_network() - update_icon() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/update_icon() - if(panel_open) - icon_state = "heater-o" - else if(src.on) - icon_state = "heater_1" - else - icon_state = "heater" - return - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ai(mob/user as mob) - attack_hand(user) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ghost(mob/user as mob) - src.attack_hand(user) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_hand(mob/user as mob) - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - src.ui_interact(user) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 540, 300) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["on"] = on ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) - if(air_contents.total_moles() == 0 && air_contents.temperature == 0) - data["gasTemperatureCelsius"] = 0 - data["minGasTemperature"] = round(T20C) - data["maxGasTemperature"] = round(T20C+max_temperature) - data["targetGasTemperature"] = round(current_temperature) - data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) - - var/temp_class = "normal" - if(air_contents.temperature > (T20C+40)) - temp_class = "bad" - data["gasTemperatureClass"] = temp_class - return data - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/Topic(href, href_list) - if(..()) - return 1 - if(href_list["toggleStatus"]) - src.on = !src.on - update_icon() - else if(href_list["minimum"]) - current_temperature = T20C - else if(href_list["maximum"]) - current_temperature = max_temperature + T20C - else if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - src.current_temperature = min((T20C+max_temperature), src.current_temperature+amount) - else - src.current_temperature = max(T20C, src.current_temperature+amount) - src.add_fingerprint(usr) - return 1 - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/power_change() - ..() - if(stat & NOPOWER) - on = 0 - update_icon() +/obj/machinery/atmospherics/unary/cold_sink/freezer + name = "freezer" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "freezer" + density = 1 + var/min_temperature = 0 + anchored = 1.0 + use_power = IDLE_POWER_USE + current_heat_capacity = 1000 + layer = 3 + plane = GAME_PLANE + max_integrity = 300 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/New() + ..() + initialize_directions = dir + component_parts = list() + component_parts += new /obj/item/circuitboard/thermomachine(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/thermomachine(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/RefreshParts() + var/H + var/T + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + H += M.rating + for(var/obj/item/stock_parts/micro_laser/M in component_parts) + T += M.rating + min_temperature = max(0,T0C - (170 + (T*15))) + current_heat_capacity = 1000 * ((H - 1) ** 2) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/on_construction() + ..(dir,dir) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + return ..() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/atmospherics/unary/cold_sink/freezer/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, "freezer-o", "freezer", I)) + on = FALSE + update_icon() + return TRUE + +/obj/machinery/atmospherics/unary/cold_sink/freezer/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!panel_open) + to_chat(user, "Open the maintenance panel first.") + return + var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) + var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices + dir = choices[selected] + var/node_connect = dir + initialize_directions = dir + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(target.initialize_directions & get_dir(target,src)) + node = target + break + build_network() + update_icon() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/update_icon() + if(panel_open) + icon_state = "freezer-o" + else if(src.on) + icon_state = "freezer_1" + else + icon_state = "freezer" + return + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ai(mob/user as mob) + attack_hand(user) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ghost(mob/user as mob) + attack_hand(user) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_hand(mob/user as mob) + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + + src.ui_interact(user) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 540, 300) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["on"] = on ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) + if(air_contents.total_moles() == 0 && air_contents.temperature == 0) + data["gasTemperatureCelsius"] = 0 + data["minGasTemperature"] = round(min_temperature) + data["maxGasTemperature"] = round(T20C) + data["targetGasTemperature"] = round(current_temperature) + data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) + + var/temp_class = "good" + if(air_contents.temperature > (T0C - 20)) + temp_class = "bad" + else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) + temp_class = "average" + data["gasTemperatureClass"] = temp_class + return data + +/obj/machinery/atmospherics/unary/cold_sink/freezer/Topic(href, href_list) + if(..()) + return 1 + if(href_list["toggleStatus"]) + src.on = !src.on + update_icon() + else if(href_list["minimum"]) + current_temperature = min_temperature + else if(href_list["maximum"]) + current_temperature = T20C + else if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + src.current_temperature = min(T20C, src.current_temperature+amount) + else + src.current_temperature = max(min_temperature, src.current_temperature+amount) + src.add_fingerprint(usr) + return 1 + +/obj/machinery/atmospherics/unary/cold_sink/freezer/power_change() + ..() + if(stat & NOPOWER) + on = 0 + update_icon() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/ + name = "heater" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "heater" + density = 1 + var/max_temperature = 0 + anchored = 1.0 + layer = 3 + current_heat_capacity = 1000 + max_integrity = 300 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/New() + ..() + initialize_directions = dir + var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) + H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater + H.name = "circuit board (Heater)" + component_parts = list() + component_parts += H + component_parts += new /obj/item/stock_parts/matter_bin(src) + component_parts += new /obj/item/stock_parts/matter_bin(src) + component_parts += new /obj/item/stock_parts/micro_laser(src) + component_parts += new /obj/item/stock_parts/micro_laser(src) + component_parts += new /obj/item/stack/sheet/glass(src) + component_parts += new /obj/item/stack/cable_coil(src, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/upgraded/New() + ..() + var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) + H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater + H.name = "circuit board (Heater)" + component_parts = list() + component_parts += H + component_parts += new /obj/item/stock_parts/matter_bin/super(src) + component_parts += new /obj/item/stock_parts/matter_bin/super(src) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) + component_parts += new /obj/item/stack/sheet/glass(src) + component_parts += new /obj/item/stack/cable_coil(src, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/on_construction() + ..(dir,dir) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/RefreshParts() + var/H + var/T + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + H += M.rating + for(var/obj/item/stock_parts/micro_laser/M in component_parts) + T += M.rating + max_temperature = T20C + (140 * T) + current_heat_capacity = 1000 * ((H - 1) ** 2) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + return ..() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, "heater-o", "heater", I)) + on = 0 + update_icon() + return TRUE + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!panel_open) + to_chat(user, "Open the maintenance panel first.") + return + var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) + var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices + dir = choices[selected] + var/node_connect = dir + initialize_directions = dir + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(target.initialize_directions & get_dir(target,src)) + node = target + break + build_network() + update_icon() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/update_icon() + if(panel_open) + icon_state = "heater-o" + else if(src.on) + icon_state = "heater_1" + else + icon_state = "heater" + return + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ai(mob/user as mob) + attack_hand(user) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ghost(mob/user as mob) + src.attack_hand(user) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_hand(mob/user as mob) + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + src.ui_interact(user) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 540, 300) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["on"] = on ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) + if(air_contents.total_moles() == 0 && air_contents.temperature == 0) + data["gasTemperatureCelsius"] = 0 + data["minGasTemperature"] = round(T20C) + data["maxGasTemperature"] = round(T20C+max_temperature) + data["targetGasTemperature"] = round(current_temperature) + data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) + + var/temp_class = "normal" + if(air_contents.temperature > (T20C+40)) + temp_class = "bad" + data["gasTemperatureClass"] = temp_class + return data + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/Topic(href, href_list) + if(..()) + return 1 + if(href_list["toggleStatus"]) + src.on = !src.on + update_icon() + else if(href_list["minimum"]) + current_temperature = T20C + else if(href_list["maximum"]) + current_temperature = max_temperature + T20C + else if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + src.current_temperature = min((T20C+max_temperature), src.current_temperature+amount) + else + src.current_temperature = max(T20C, src.current_temperature+amount) + src.add_fingerprint(usr) + return 1 + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/power_change() + ..() + if(stat & NOPOWER) + on = 0 + update_icon() diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 8c93028649b..7317099b8dc 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -1,155 +1,155 @@ -/obj/machinery/optable - name = "Operating Table" - desc = "Used for advanced medical procedures." - icon = 'icons/obj/surgery.dmi' - icon_state = "table2-idle" - density = 1 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 1 - active_power_usage = 5 - var/mob/living/carbon/human/victim = null - var/strapped = 0.0 - - var/obj/machinery/computer/operating/computer = null - buckle_lying = -1 - var/no_icon_updates = 0 //set this to 1 if you don't want the icons ever changing - var/list/injected_reagents = list() - var/reagent_target_amount = 1 - var/inject_amount = 1 - -/obj/machinery/optable/New() - ..() - for(dir in list(NORTH,EAST,SOUTH,WEST)) - computer = locate(/obj/machinery/computer/operating, get_step(src, dir)) - if(computer) - computer.table = src - break - -/obj/machinery/optable/Destroy() - if(computer) - computer.table = null - computer.victim = null - computer = null - if(victim) - victim = null - return ..() - -/obj/machinery/optable/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - if(user.a_intent == INTENT_HARM) - ..(user, TRUE) - visible_message("[user] destroys the operating table!") - qdel(src) - return TRUE - -/obj/machinery/optable/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) return 1 - - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - else - return 0 - - -/obj/machinery/optable/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) - if(usr.stat || (!ishuman(user) && !isrobot(user)) || user.restrained() || !check_table(user) || user.IsWeakened() || user.stunned) - return - - if(!ismob(O)) //humans only - return - - if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit - return - - var/mob/living/L = O - take_victim(L,usr) - return - -/obj/machinery/optable/proc/check_victim() - if(locate(/mob/living/carbon/human, src.loc)) - var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) - if(M.lying) - src.victim = M - if(!no_icon_updates) - icon_state = M.pulse ? "table2-active" : "table2-idle" - return 1 - src.victim = null - if(!no_icon_updates) - icon_state = "table2-idle" - return 0 - -/obj/machinery/optable/Crossed(atom/movable/AM, oldloc) - . = ..() - if(iscarbon(AM) && LAZYLEN(injected_reagents)) - to_chat(AM, "You feel a series of tiny pricks!") - -/obj/machinery/optable/process() - check_victim() - if(LAZYLEN(injected_reagents)) - for(var/mob/living/carbon/C in get_turf(src)) - var/datum/reagents/R = C.reagents - for(var/chemical in injected_reagents) - R.check_and_add(chemical,reagent_target_amount,inject_amount) - -/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) - if(C == user) - user.visible_message("[user] climbs on the operating table.","You climb on the operating table.") - else - visible_message("[C] has been laid on the operating table by [user].") - C.resting = 1 - C.update_canmove() - C.forceMove(loc) - if(user.pulling == C) - user.stop_pulling() - if(C.s_active) //Close the container opened - C.s_active.close(C) - for(var/obj/O in src) - O.loc = src.loc - src.add_fingerprint(user) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - src.victim = H - if(!no_icon_updates) - icon_state = H.pulse ? "table2-active" : "table2-idle" - else - if(!no_icon_updates) - icon_state = "table2-idle" - -/obj/machinery/optable/verb/climb_on() - set name = "Climb On Table" - set category = "Object" - set src in oview(1) - - if(usr.stat || !ishuman(usr) || usr.restrained() || !check_table(usr)) - return - - take_victim(usr,usr) - -/obj/machinery/optable/attackby(obj/item/I, mob/living/carbon/user, params) - if(istype(I, /obj/item/grab)) - var/obj/item/grab/G = I - if(iscarbon(G.affecting)) - take_victim(G.affecting, user) - qdel(G) - else - return ..() - -/obj/machinery/optable/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_start_check(user, 0)) - return - if(I.use_tool(src, user, 20, volume = I.tool_volume)) - to_chat(user, "You deconstruct the table.") - new /obj/item/stack/sheet/plasteel(loc, 5) - qdel(src) - -/obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) - if(src.victim && get_turf(victim) == get_turf(src) && victim.lying) - to_chat(usr, "The table is already occupied!") - return 0 - - if(patient.buckled) - to_chat(usr, "Unbuckle first!") - return 0 - - return 1 +/obj/machinery/optable + name = "Operating Table" + desc = "Used for advanced medical procedures." + icon = 'icons/obj/surgery.dmi' + icon_state = "table2-idle" + density = 1 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 1 + active_power_usage = 5 + var/mob/living/carbon/human/victim = null + var/strapped = 0.0 + + var/obj/machinery/computer/operating/computer = null + buckle_lying = -1 + var/no_icon_updates = 0 //set this to 1 if you don't want the icons ever changing + var/list/injected_reagents = list() + var/reagent_target_amount = 1 + var/inject_amount = 1 + +/obj/machinery/optable/New() + ..() + for(dir in list(NORTH,EAST,SOUTH,WEST)) + computer = locate(/obj/machinery/computer/operating, get_step(src, dir)) + if(computer) + computer.table = src + break + +/obj/machinery/optable/Destroy() + if(computer) + computer.table = null + computer.victim = null + computer = null + if(victim) + victim = null + return ..() + +/obj/machinery/optable/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + if(user.a_intent == INTENT_HARM) + ..(user, TRUE) + visible_message("[user] destroys the operating table!") + qdel(src) + return TRUE + +/obj/machinery/optable/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) return 1 + + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + else + return 0 + + +/obj/machinery/optable/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) + if(usr.stat || (!ishuman(user) && !isrobot(user)) || user.restrained() || !check_table(user) || user.IsWeakened() || user.stunned) + return + + if(!ismob(O)) //humans only + return + + if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit + return + + var/mob/living/L = O + take_victim(L,usr) + return + +/obj/machinery/optable/proc/check_victim() + if(locate(/mob/living/carbon/human, src.loc)) + var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) + if(M.lying) + src.victim = M + if(!no_icon_updates) + icon_state = M.pulse ? "table2-active" : "table2-idle" + return 1 + src.victim = null + if(!no_icon_updates) + icon_state = "table2-idle" + return 0 + +/obj/machinery/optable/Crossed(atom/movable/AM, oldloc) + . = ..() + if(iscarbon(AM) && LAZYLEN(injected_reagents)) + to_chat(AM, "You feel a series of tiny pricks!") + +/obj/machinery/optable/process() + check_victim() + if(LAZYLEN(injected_reagents)) + for(var/mob/living/carbon/C in get_turf(src)) + var/datum/reagents/R = C.reagents + for(var/chemical in injected_reagents) + R.check_and_add(chemical,reagent_target_amount,inject_amount) + +/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) + if(C == user) + user.visible_message("[user] climbs on the operating table.","You climb on the operating table.") + else + visible_message("[C] has been laid on the operating table by [user].") + C.resting = 1 + C.update_canmove() + C.forceMove(loc) + if(user.pulling == C) + user.stop_pulling() + if(C.s_active) //Close the container opened + C.s_active.close(C) + for(var/obj/O in src) + O.loc = src.loc + src.add_fingerprint(user) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + src.victim = H + if(!no_icon_updates) + icon_state = H.pulse ? "table2-active" : "table2-idle" + else + if(!no_icon_updates) + icon_state = "table2-idle" + +/obj/machinery/optable/verb/climb_on() + set name = "Climb On Table" + set category = "Object" + set src in oview(1) + + if(usr.stat || !ishuman(usr) || usr.restrained() || !check_table(usr)) + return + + take_victim(usr,usr) + +/obj/machinery/optable/attackby(obj/item/I, mob/living/carbon/user, params) + if(istype(I, /obj/item/grab)) + var/obj/item/grab/G = I + if(iscarbon(G.affecting)) + take_victim(G.affecting, user) + qdel(G) + else + return ..() + +/obj/machinery/optable/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_start_check(user, 0)) + return + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + to_chat(user, "You deconstruct the table.") + new /obj/item/stack/sheet/plasteel(loc, 5) + qdel(src) + +/obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) + if(src.victim && get_turf(victim) == get_turf(src) && victim.lying) + to_chat(usr, "The table is already occupied!") + return 0 + + if(patient.buckled) + to_chat(usr, "Unbuckle first!") + return 0 + + return 1 diff --git a/code/game/machinery/PDApainter.dm b/code/game/machinery/PDApainter.dm index 83778a99c26..3185cdcddd4 100644 --- a/code/game/machinery/PDApainter.dm +++ b/code/game/machinery/PDApainter.dm @@ -1,132 +1,132 @@ -/obj/machinery/pdapainter - name = "PDA painter" - desc = "A PDA painting machine. To use, simply insert your PDA and choose the desired preset paint scheme." - icon = 'icons/obj/pda.dmi' - icon_state = "pdapainter" - density = 1 - anchored = 1 - max_integrity = 200 - var/obj/item/pda/storedpda = null - var/list/colorlist = list() - - -/obj/machinery/pdapainter/update_icon() - cut_overlays() - - if(stat & BROKEN) - icon_state = "[initial(icon_state)]-broken" - return - - if(storedpda) - add_overlay("[initial(icon_state)]-closed") - - if(powered()) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]-off" - - return - -/obj/machinery/pdapainter/New() - ..() - var/blocked = list(/obj/item/pda/silicon/ai, /obj/item/pda/silicon/robot, /obj/item/pda/silicon/pai, /obj/item/pda/heads, - /obj/item/pda/clear, /obj/item/pda/syndicate) - - for(var/P in typesof(/obj/item/pda)-blocked) - var/obj/item/pda/D = new P - - //D.name = "PDA Style [colorlist.len+1]" //Gotta set the name, otherwise it all comes up as "PDA" - D.name = D.icon_state //PDAs don't have unique names, but using the sprite names works. - - src.colorlist += D - -/obj/machinery/pdapainter/Destroy() - QDEL_NULL(storedpda) - return ..() - -/obj/machinery/pdapainter/on_deconstruction() - if(storedpda) - storedpda.forceMove(loc) - storedpda = null - -/obj/machinery/pdapainter/ex_act(severity) - if(storedpda) - storedpda.ex_act(severity) - ..() - -/obj/machinery/pdapainter/handle_atom_del(atom/A) - if(A == storedpda) - storedpda = null - update_icon() - -/obj/machinery/pdapainter/attackby(obj/item/I, mob/user, params) - if(default_unfasten_wrench(user, I)) - power_change() - return - if(istype(I, /obj/item/pda)) - if(storedpda) - to_chat(user, "There is already a PDA inside.") - return - else - var/obj/item/pda/P = user.get_active_hand() - if(istype(P)) - if(user.drop_item()) - storedpda = P - P.forceMove(src) - P.add_fingerprint(user) - update_icon() - else - return ..() - -/obj/machinery/pdapainter/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - default_welder_repair(user, I) - -/obj/machinery/pdapainter/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!(stat & BROKEN)) - stat |= BROKEN - update_icon() - -/obj/machinery/pdapainter/attack_hand(mob/user as mob) - if(..()) - return 1 - - src.add_fingerprint(user) - - if(storedpda) - var/obj/item/pda/P - P = input(user, "Select your color!", "PDA Painting") as null|anything in colorlist - if(!P) - return - if(!in_range(src, user)) - return - - storedpda.icon_state = P.icon_state - storedpda.desc = P.desc - - else - to_chat(user, "The [src] is empty.") - - -/obj/machinery/pdapainter/verb/ejectpda() - set name = "Eject PDA" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return - - if(storedpda) - storedpda.loc = get_turf(src.loc) - storedpda = null - update_icon() - else - to_chat(usr, "The [src] is empty.") - - -/obj/machinery/pdapainter/power_change() - ..() - update_icon() \ No newline at end of file +/obj/machinery/pdapainter + name = "PDA painter" + desc = "A PDA painting machine. To use, simply insert your PDA and choose the desired preset paint scheme." + icon = 'icons/obj/pda.dmi' + icon_state = "pdapainter" + density = 1 + anchored = 1 + max_integrity = 200 + var/obj/item/pda/storedpda = null + var/list/colorlist = list() + + +/obj/machinery/pdapainter/update_icon() + cut_overlays() + + if(stat & BROKEN) + icon_state = "[initial(icon_state)]-broken" + return + + if(storedpda) + add_overlay("[initial(icon_state)]-closed") + + if(powered()) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]-off" + + return + +/obj/machinery/pdapainter/New() + ..() + var/blocked = list(/obj/item/pda/silicon/ai, /obj/item/pda/silicon/robot, /obj/item/pda/silicon/pai, /obj/item/pda/heads, + /obj/item/pda/clear, /obj/item/pda/syndicate) + + for(var/P in typesof(/obj/item/pda)-blocked) + var/obj/item/pda/D = new P + + //D.name = "PDA Style [colorlist.len+1]" //Gotta set the name, otherwise it all comes up as "PDA" + D.name = D.icon_state //PDAs don't have unique names, but using the sprite names works. + + src.colorlist += D + +/obj/machinery/pdapainter/Destroy() + QDEL_NULL(storedpda) + return ..() + +/obj/machinery/pdapainter/on_deconstruction() + if(storedpda) + storedpda.forceMove(loc) + storedpda = null + +/obj/machinery/pdapainter/ex_act(severity) + if(storedpda) + storedpda.ex_act(severity) + ..() + +/obj/machinery/pdapainter/handle_atom_del(atom/A) + if(A == storedpda) + storedpda = null + update_icon() + +/obj/machinery/pdapainter/attackby(obj/item/I, mob/user, params) + if(default_unfasten_wrench(user, I)) + power_change() + return + if(istype(I, /obj/item/pda)) + if(storedpda) + to_chat(user, "There is already a PDA inside.") + return + else + var/obj/item/pda/P = user.get_active_hand() + if(istype(P)) + if(user.drop_item()) + storedpda = P + P.forceMove(src) + P.add_fingerprint(user) + update_icon() + else + return ..() + +/obj/machinery/pdapainter/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_welder_repair(user, I) + +/obj/machinery/pdapainter/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!(stat & BROKEN)) + stat |= BROKEN + update_icon() + +/obj/machinery/pdapainter/attack_hand(mob/user as mob) + if(..()) + return 1 + + src.add_fingerprint(user) + + if(storedpda) + var/obj/item/pda/P + P = input(user, "Select your color!", "PDA Painting") as null|anything in colorlist + if(!P) + return + if(!in_range(src, user)) + return + + storedpda.icon_state = P.icon_state + storedpda.desc = P.desc + + else + to_chat(user, "The [src] is empty.") + + +/obj/machinery/pdapainter/verb/ejectpda() + set name = "Eject PDA" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return + + if(storedpda) + storedpda.loc = get_turf(src.loc) + storedpda = null + update_icon() + else + to_chat(usr, "The [src] is empty.") + + +/obj/machinery/pdapainter/power_change() + ..() + update_icon() diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index f1adf1a5f11..70d88e92b71 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -1,556 +1,572 @@ -#define ADDICTION_SPEEDUP_TIME 1500 // 2.5 minutes - -///////////// -// SLEEPER // -//////////// - -/obj/machinery/sleeper - name = "Sleeper" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "sleeper-open" - var/base_icon = "sleeper" - density = 1 - anchored = 1 - dir = WEST - var/orient = "LEFT" // "RIGHT" changes the dir suffix to "-r" - var/mob/living/carbon/human/occupant = null - var/possible_chems = list("ephedrine", "salglu_solution", "salbutamol", "charcoal") - var/emergency_chems = list("ephedrine") // Desnowflaking - var/amounts = list(5, 10) - var/obj/item/reagent_containers/glass/beaker = null - var/filtering = 0 - var/max_chem - var/initial_bin_rating = 1 - var/min_health = -25 - var/controls_inside = FALSE - idle_power_usage = 1250 - active_power_usage = 2500 - - light_color = LIGHT_COLOR_CYAN - -/obj/machinery/sleeper/power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) - -/obj/machinery/sleeper/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/sleeper(null) - - // Customizable bin rating, used by the labor camp to stop people filling themselves with chemicals and escaping. - var/obj/item/stock_parts/matter_bin/B = new(null) - B.rating = initial_bin_rating - component_parts += B - - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/sleeper/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/sleeper(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/sleeper/RefreshParts() - var/E - for(var/obj/item/stock_parts/matter_bin/B in component_parts) - E += B.rating - - max_chem = E * 20 - min_health = -E * 25 - -/obj/machinery/sleeper/Destroy() - for(var/mob/M in contents) - M.forceMove(get_turf(src)) - return ..() - -/obj/machinery/sleeper/relaymove(mob/user as mob) - if(user.incapacitated()) - return 0 //maybe they should be able to get out with cuffs, but whatever - go_out() - -/obj/machinery/sleeper/process() - if(filtering > 0) - if(beaker) - // To prevent runtimes from drawing blood from runtime, and to prevent getting IPC blood. - if(!istype(occupant) || !occupant.dna || (NO_BLOOD in occupant.dna.species.species_traits)) - filtering = 0 - return - - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - occupant.transfer_blood_to(beaker, 1) - for(var/datum/reagent/x in occupant.reagents.reagent_list) - occupant.reagents.trans_to(beaker, 3) - occupant.transfer_blood_to(beaker, 1) - - if(occupant) - for(var/A in occupant.reagents.addiction_list) - var/datum/reagent/R = A - - var/addiction_removal_chance = 5 - if(world.timeofday > (R.last_addiction_dose + ADDICTION_SPEEDUP_TIME)) // 2.5 minutes - addiction_removal_chance = 10 - if(prob(addiction_removal_chance)) - to_chat(occupant, "You no longer feel reliant on [R.name]!") - occupant.reagents.addiction_list.Remove(R) - qdel(R) - - for(var/mob/M as mob in src) // makes sure that simple mobs don't get stuck inside a sleeper when they resist out of occupant's grasp - if(M == occupant) - continue - else - M.forceMove(loc) - - updateDialog() - return - - -/obj/machinery/sleeper/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/sleeper/attack_ghost(mob/user) - return attack_hand(user) - -/obj/machinery/sleeper/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - - ui_interact(user) - -/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state) - var/data[0] - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - var/crisis = 0 - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.dna.species) - // I wanna do something where the bar gets bluer as the temperature gets lower - // For now, I'll just use the standard format for the temperature status - var/datum/species/sp = occupant.dna.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(istype(occupant, /mob/living/simple_animal)) - var/mob/living/simple_animal/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - - crisis = (occupant.health < min_health) - // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent - // Runtime is aptly named, isn't she? - if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - occupantData["bloodLevel"] = round(occupant.blood_volume) - occupantData["bloodMax"] = occupant.max_blood - occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) - - data["occupant"] = occupantData - data["maxchem"] = max_chem - data["minhealth"] = min_health - data["dialysis"] = filtering - if(beaker) - data["isBeakerLoaded"] = 1 - if(beaker.reagents) - data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume) - else - data["beakerFreeSpace"] = 0 - - var/chemicals[0] - for(var/re in possible_chems) - var/datum/reagent/temp = GLOB.chemical_reagents_list[re] - if(temp) - var/reagent_amount = 0 - var/pretty_amount - var/injectable = occupant ? 1 : 0 - var/overdosing = 0 - var/caution = 0 // To make things clear that you're coming close to an overdose - if(crisis && !(temp.id in emergency_chems)) - injectable = 0 - - if(occupant && occupant.reagents) - reagent_amount = occupant.reagents.get_reagent_amount(temp.id) - // If they're mashing the highest concentration, they get one warning - if(temp.overdose_threshold && reagent_amount + 10 > temp.overdose_threshold) - caution = 1 - if(temp.id in occupant.reagents.overdose_list()) - overdosing = 1 - - // Because I don't know how to do this on the nano side - pretty_amount = round(reagent_amount, 0.05) - - chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) - data["chemicals"] = chemicals - return data - -/obj/machinery/sleeper/Topic(href, href_list) - if(!controls_inside && usr == occupant) - return 0 - - if(..()) - return 1 - - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return 0 - - if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) - if(href_list["chemical"]) - if(occupant) - if(occupant.stat == DEAD) - to_chat(usr, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") - else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems)) - inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"])) - else - to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") - if(href_list["removebeaker"]) - remove_beaker() - if(href_list["togglefilter"]) - toggle_filter() - if(href_list["ejectify"]) - eject() - add_fingerprint(usr) - return 1 - -/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers/glass)) - if(!beaker) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to you!") - return - - beaker = I - I.forceMove(src) - user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!") - return - - else - to_chat(user, "The sleeper has a beaker already.") - return - - if(exchange_parts(user, I)) - return - - if(istype(I, /obj/item/grab)) - var/obj/item/grab/G = I - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(!ismob(G.affecting)) - return - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - if(G.affecting.has_buckled_mobs()) //mob attached to us - to_chat(user, "[G.affecting] will not fit into [src] because [G.affecting.p_they()] [G.affecting.p_have()] a slime latched onto [G.affecting.p_their()] head.") - return - - visible_message("[user] starts putting [G.affecting.name] into the sleeper.") - - if(do_after(user, 20, target = G.affecting)) - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - if(!G || !G.affecting) - return - var/mob/M = G.affecting - M.forceMove(src) - occupant = M - icon_state = "[base_icon]" - to_chat(M, "You feel cool air surround you. You go numb as your senses turn inward.") - add_fingerprint(user) - qdel(G) - return - - return ..() - - -/obj/machinery/sleeper/crowbar_act(mob/user, obj/item/I) - if(default_deconstruction_crowbar(user, I)) - return TRUE - -/obj/machinery/sleeper/screwdriver_act(mob/user, obj/item/I) - if(occupant) - to_chat(user, "The maintenance panel is locked.") - return TRUE - if(default_deconstruction_screwdriver(user, "[base_icon]-o", "[base_icon]-open", I)) - return TRUE - -/obj/machinery/sleeper/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(occupant) - to_chat(user, "The scanner is occupied.") - return - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(dir == EAST) - orient = "LEFT" - setDir(WEST) - else - orient = "RIGHT" - setDir(EAST) - -/obj/machinery/sleeper/ex_act(severity) - if(filtering) - toggle_filter() - if(occupant) - occupant.ex_act(severity) - ..() - -/obj/machinery/sleeper/handle_atom_del(atom/A) - ..() - if(A == occupant) - occupant = null - updateUsrDialog() - update_icon() - if(A == beaker) - beaker = null - updateUsrDialog() - -/obj/machinery/sleeper/emp_act(severity) - if(filtering) - toggle_filter() - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - if(occupant) - go_out() - ..(severity) - -/obj/machinery/sleeper/narsie_act() - go_out() - new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! - qdel(src) - -/obj/machinery/sleeper/proc/toggle_filter() - if(filtering) - filtering = 0 - else - filtering = 1 - -/obj/machinery/sleeper/proc/go_out() - if(filtering) - toggle_filter() - if(!occupant) - return - occupant.forceMove(loc) - occupant = null - icon_state = "[base_icon]-open" - // eject trash the occupant dropped - for(var/atom/movable/A in contents - component_parts - list(beaker)) - A.forceMove(loc) - -/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount) - if(!(chemical in possible_chems)) - to_chat(user, "The sleeper does not offer that chemical!") - return - - if(occupant) - if(occupant.reagents) - if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) - occupant.reagents.add_reagent(chemical, amount) - return - else - to_chat(user, "You can not inject any more of this chemical.") - return - else - to_chat(user, "The patient rejects the chemicals!") - return - else - to_chat(user, "There's no occupant in the sleeper!") - return - -/obj/machinery/sleeper/verb/eject() - set name = "Eject Sleeper" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - - icon_state = "[base_icon]-open" - go_out() - add_fingerprint(usr) - return - -/obj/machinery/sleeper/verb/remove_beaker() - set name = "Remove Beaker" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - - if(beaker) - filtering = 0 - beaker.forceMove(usr.loc) - beaker = null - add_fingerprint(usr) - return - -/obj/machinery/sleeper/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) - if(O.loc == user) //no you can't pull things out of your ass - return - if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source - return - if(!ismob(O)) //humans only - return - if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit - return - if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper - return - if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems - return - if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? - return - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - var/mob/living/L = O - if(!istype(L) || L.buckled) - return - if(L.abiotic()) - to_chat(user, "Subject cannot have abiotic items on.") - return - if(L.has_buckled_mobs()) //mob attached to us - to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") - return - if(L == user) - visible_message("[user] starts climbing into the sleeper.") - else - visible_message("[user] starts putting [L.name] into the sleeper.") - - if(do_after(user, 20, target = L)) - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - if(!L) return - L.forceMove(src) - occupant = L - icon_state = "[base_icon]" - to_chat(L, "You feel cool air surround you. You go numb as your senses turn inward.") - add_fingerprint(user) - if(user.pulling == L) - user.stop_pulling() - return - return - -/obj/machinery/sleeper/AllowDrop() - return FALSE - -/obj/machinery/sleeper/verb/move_inside() - set name = "Enter Sleeper" - set category = "Object" - set src in oview(1) - if(usr.stat != 0 || !(ishuman(usr))) - return - if(occupant) - to_chat(usr, "The sleeper is already occupied!") - return - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - if(usr.has_buckled_mobs()) //mob attached to us - to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") - return - visible_message("[usr] starts climbing into the sleeper.") - if(do_after(usr, 20, target = usr)) - if(occupant) - to_chat(usr, "The sleeper is already occupied!") - return - usr.stop_pulling() - usr.forceMove(src) - occupant = usr - icon_state = "[base_icon]" - - for(var/obj/O in src) - qdel(O) - add_fingerprint(usr) - return - return - -/obj/machinery/sleeper/syndie - icon_state = "sleeper_s-open" - base_icon = "sleeper_s" - possible_chems = list("epinephrine", "ether", "salbutamol", "styptic_powder", "silver_sulfadiazine") - emergency_chems = list("epinephrine") - controls_inside = TRUE - - light_color = LIGHT_COLOR_DARKRED - -/obj/machinery/sleeper/syndie/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/sleeper/syndicate(null) - var/obj/item/stock_parts/matter_bin/B = new(null) - B.rating = initial_bin_rating - component_parts += B - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -#undef ADDICTION_SPEEDUP_TIME +#define ADDICTION_SPEEDUP_TIME 1500 // 2.5 minutes + +///////////// +// SLEEPER // +//////////// + +/obj/machinery/sleeper + name = "Sleeper" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper-open" + var/base_icon = "sleeper" + density = 1 + anchored = 1 + dir = WEST + var/orient = "LEFT" // "RIGHT" changes the dir suffix to "-r" + var/mob/living/carbon/human/occupant = null + var/possible_chems = list("ephedrine", "salglu_solution", "salbutamol", "charcoal") + var/emergency_chems = list("ephedrine") // Desnowflaking + var/amounts = list(5, 10) + var/obj/item/reagent_containers/glass/beaker = null + var/filtering = 0 + var/max_chem + var/initial_bin_rating = 1 + var/min_health = -25 + var/controls_inside = FALSE + var/auto_eject_dead = FALSE + idle_power_usage = 1250 + active_power_usage = 2500 + + light_color = LIGHT_COLOR_CYAN + +/obj/machinery/sleeper/power_change() + ..() + if(!(stat & (BROKEN|NOPOWER))) + set_light(2) + else + set_light(0) + +/obj/machinery/sleeper/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/sleeper(null) + + // Customizable bin rating, used by the labor camp to stop people filling themselves with chemicals and escaping. + var/obj/item/stock_parts/matter_bin/B = new(null) + B.rating = initial_bin_rating + component_parts += B + + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/sleeper/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/sleeper(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/sleeper/RefreshParts() + var/E + for(var/obj/item/stock_parts/matter_bin/B in component_parts) + E += B.rating + + max_chem = E * 20 + min_health = -E * 25 + +/obj/machinery/sleeper/Destroy() + for(var/mob/M in contents) + M.forceMove(get_turf(src)) + return ..() + +/obj/machinery/sleeper/relaymove(mob/user as mob) + if(user.incapacitated()) + return 0 //maybe they should be able to get out with cuffs, but whatever + go_out() + +/obj/machinery/sleeper/process() + for(var/mob/M as mob in src) // makes sure that simple mobs don't get stuck inside a sleeper when they resist out of occupant's grasp + if(M == occupant) + continue + else + M.forceMove(loc) + + if(occupant) + if(auto_eject_dead && occupant.stat == DEAD) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 40) + go_out() + return + + if(filtering > 0 && beaker) + // To prevent runtimes from drawing blood from runtime, and to prevent getting IPC blood. + if(!istype(occupant) || !occupant.dna || (NO_BLOOD in occupant.dna.species.species_traits)) + filtering = 0 + return + + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) + occupant.transfer_blood_to(beaker, 1) + for(var/datum/reagent/x in occupant.reagents.reagent_list) + occupant.reagents.trans_to(beaker, 3) + occupant.transfer_blood_to(beaker, 1) + + for(var/A in occupant.reagents.addiction_list) + var/datum/reagent/R = A + + var/addiction_removal_chance = 5 + if(world.timeofday > (R.last_addiction_dose + ADDICTION_SPEEDUP_TIME)) // 2.5 minutes + addiction_removal_chance = 10 + if(prob(addiction_removal_chance)) + to_chat(occupant, "You no longer feel reliant on [R.name]!") + occupant.reagents.addiction_list.Remove(R) + qdel(R) + + updateDialog() + return + + +/obj/machinery/sleeper/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/sleeper/attack_ghost(mob/user) + return attack_hand(user) + +/obj/machinery/sleeper/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + + ui_interact(user) + +/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state) + var/data[0] + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + var/crisis = 0 + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.dna.species) + // I wanna do something where the bar gets bluer as the temperature gets lower + // For now, I'll just use the standard format for the temperature status + var/datum/species/sp = occupant.dna.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(istype(occupant, /mob/living/simple_animal)) + var/mob/living/simple_animal/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + + crisis = (occupant.health < min_health) + // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent + // Runtime is aptly named, isn't she? + if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + occupantData["bloodLevel"] = round(occupant.blood_volume) + occupantData["bloodMax"] = occupant.max_blood + occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) + + data["occupant"] = occupantData + data["maxchem"] = max_chem + data["minhealth"] = min_health + data["dialysis"] = filtering + data["auto_eject_dead"] = auto_eject_dead + if(beaker) + data["isBeakerLoaded"] = 1 + if(beaker.reagents) + data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume) + else + data["beakerFreeSpace"] = 0 + + var/chemicals[0] + for(var/re in possible_chems) + var/datum/reagent/temp = GLOB.chemical_reagents_list[re] + if(temp) + var/reagent_amount = 0 + var/pretty_amount + var/injectable = occupant ? 1 : 0 + var/overdosing = 0 + var/caution = 0 // To make things clear that you're coming close to an overdose + if(crisis && !(temp.id in emergency_chems)) + injectable = 0 + + if(occupant && occupant.reagents) + reagent_amount = occupant.reagents.get_reagent_amount(temp.id) + // If they're mashing the highest concentration, they get one warning + if(temp.overdose_threshold && reagent_amount + 10 > temp.overdose_threshold) + caution = 1 + if(temp.id in occupant.reagents.overdose_list()) + overdosing = 1 + + // Because I don't know how to do this on the nano side + pretty_amount = round(reagent_amount, 0.05) + + chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) + data["chemicals"] = chemicals + return data + +/obj/machinery/sleeper/Topic(href, href_list) + if(!controls_inside && usr == occupant) + return 0 + + if(..()) + return 1 + + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return 0 + + if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) + if(href_list["chemical"]) + if(occupant) + if(occupant.stat == DEAD) + to_chat(usr, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") + else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems)) + inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"])) + else + to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") + + if(href_list["removebeaker"]) + remove_beaker() + + if(href_list["togglefilter"]) + toggle_filter() + + if(href_list["ejectify"]) + eject() + + if(href_list["auto_eject_dead_on"]) + auto_eject_dead = TRUE + + if(href_list["auto_eject_dead_off"]) + auto_eject_dead = FALSE + + add_fingerprint(usr) + return 1 + +/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/reagent_containers/glass)) + if(!beaker) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to you!") + return + + beaker = I + I.forceMove(src) + user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!") + return + + else + to_chat(user, "The sleeper has a beaker already.") + return + + if(exchange_parts(user, I)) + return + + if(istype(I, /obj/item/grab)) + var/obj/item/grab/G = I + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(!ismob(G.affecting)) + return + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + if(G.affecting.has_buckled_mobs()) //mob attached to us + to_chat(user, "[G.affecting] will not fit into [src] because [G.affecting.p_they()] [G.affecting.p_have()] a slime latched onto [G.affecting.p_their()] head.") + return + + visible_message("[user] starts putting [G.affecting.name] into the sleeper.") + + if(do_after(user, 20, target = G.affecting)) + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + if(!G || !G.affecting) + return + var/mob/M = G.affecting + M.forceMove(src) + occupant = M + icon_state = "[base_icon]" + to_chat(M, "You feel cool air surround you. You go numb as your senses turn inward.") + add_fingerprint(user) + qdel(G) + return + + return ..() + + +/obj/machinery/sleeper/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/sleeper/screwdriver_act(mob/user, obj/item/I) + if(occupant) + to_chat(user, "The maintenance panel is locked.") + return TRUE + if(default_deconstruction_screwdriver(user, "[base_icon]-o", "[base_icon]-open", I)) + return TRUE + +/obj/machinery/sleeper/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(occupant) + to_chat(user, "The scanner is occupied.") + return + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(dir == EAST) + orient = "LEFT" + setDir(WEST) + else + orient = "RIGHT" + setDir(EAST) + +/obj/machinery/sleeper/ex_act(severity) + if(filtering) + toggle_filter() + if(occupant) + occupant.ex_act(severity) + ..() + +/obj/machinery/sleeper/handle_atom_del(atom/A) + ..() + if(A == occupant) + occupant = null + updateUsrDialog() + update_icon() + if(A == beaker) + beaker = null + updateUsrDialog() + +/obj/machinery/sleeper/emp_act(severity) + if(filtering) + toggle_filter() + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + if(occupant) + go_out() + ..(severity) + +/obj/machinery/sleeper/narsie_act() + go_out() + new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! + qdel(src) + +/obj/machinery/sleeper/proc/toggle_filter() + if(filtering) + filtering = 0 + else + filtering = 1 + +/obj/machinery/sleeper/proc/go_out() + if(filtering) + toggle_filter() + if(!occupant) + return + occupant.forceMove(loc) + occupant = null + icon_state = "[base_icon]-open" + // eject trash the occupant dropped + for(var/atom/movable/A in contents - component_parts - list(beaker)) + A.forceMove(loc) + +/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount) + if(!(chemical in possible_chems)) + to_chat(user, "The sleeper does not offer that chemical!") + return + + if(occupant) + if(occupant.reagents) + if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) + occupant.reagents.add_reagent(chemical, amount) + return + else + to_chat(user, "You can not inject any more of this chemical.") + return + else + to_chat(user, "The patient rejects the chemicals!") + return + else + to_chat(user, "There's no occupant in the sleeper!") + return + +/obj/machinery/sleeper/verb/eject() + set name = "Eject Sleeper" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + + icon_state = "[base_icon]-open" + go_out() + add_fingerprint(usr) + return + +/obj/machinery/sleeper/verb/remove_beaker() + set name = "Remove Beaker" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + + if(beaker) + filtering = 0 + beaker.forceMove(usr.loc) + beaker = null + add_fingerprint(usr) + return + +/obj/machinery/sleeper/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) + if(O.loc == user) //no you can't pull things out of your ass + return + if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source + return + if(!ismob(O)) //humans only + return + if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit + return + if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper + return + if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems + return + if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? + return + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + var/mob/living/L = O + if(!istype(L) || L.buckled) + return + if(L.abiotic()) + to_chat(user, "Subject cannot have abiotic items on.") + return + if(L.has_buckled_mobs()) //mob attached to us + to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") + return + if(L == user) + visible_message("[user] starts climbing into the sleeper.") + else + visible_message("[user] starts putting [L.name] into the sleeper.") + + if(do_after(user, 20, target = L)) + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + if(!L) return + L.forceMove(src) + occupant = L + icon_state = "[base_icon]" + to_chat(L, "You feel cool air surround you. You go numb as your senses turn inward.") + add_fingerprint(user) + if(user.pulling == L) + user.stop_pulling() + return + return + +/obj/machinery/sleeper/AllowDrop() + return FALSE + +/obj/machinery/sleeper/verb/move_inside() + set name = "Enter Sleeper" + set category = "Object" + set src in oview(1) + if(usr.stat != 0 || !(ishuman(usr))) + return + if(occupant) + to_chat(usr, "The sleeper is already occupied!") + return + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + if(usr.has_buckled_mobs()) //mob attached to us + to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") + return + visible_message("[usr] starts climbing into the sleeper.") + if(do_after(usr, 20, target = usr)) + if(occupant) + to_chat(usr, "The sleeper is already occupied!") + return + usr.stop_pulling() + usr.forceMove(src) + occupant = usr + icon_state = "[base_icon]" + + for(var/obj/O in src) + qdel(O) + add_fingerprint(usr) + return + return + +/obj/machinery/sleeper/syndie + icon_state = "sleeper_s-open" + base_icon = "sleeper_s" + possible_chems = list("epinephrine", "ether", "salbutamol", "styptic_powder", "silver_sulfadiazine") + emergency_chems = list("epinephrine") + controls_inside = TRUE + + light_color = LIGHT_COLOR_DARKRED + +/obj/machinery/sleeper/syndie/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/sleeper/syndicate(null) + var/obj/item/stock_parts/matter_bin/B = new(null) + B.rating = initial_bin_rating + component_parts += B + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +#undef ADDICTION_SPEEDUP_TIME diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index edb12487a68..9d96ec94b99 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -425,6 +425,7 @@ var/AN = "" var/open = "" var/infected = "" + var/dead = "" var/robot = "" var/imp = "" var/bled = "" @@ -439,6 +440,8 @@ splint = "Splinted:" if(e.status & ORGAN_BROKEN) AN = "[e.broken_description]:" + if(e.status & ORGAN_DEAD) + dead = "DEAD:" if(e.is_robotic()) robot = "Robotic:" if(e.open) @@ -454,9 +457,9 @@ infected = "Acute Infection:" if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) infected = "Acute Infection+:" - if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 400) + if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 399) infected = "Acute Infection++:" - if(INFECTION_LEVEL_THREE to INFINITY) + if(INFECTION_LEVEL_TWO + 400 to INFINITY) infected = "Septic:" var/unknown_body = 0 @@ -467,11 +470,14 @@ imp += "Unknown body present:" if(!AN && !open && !infected & !imp) AN = "None:" - dat += "
    " + dat += "" dat += "" for(var/obj/item/organ/internal/i in occupant.internal_organs) var/mech = i.desc var/infection = "None" + var/dead = "" + if(i.status & ORGAN_DEAD) + dead = "DEAD:" switch(i.germ_level) if(1 to INFECTION_LEVEL_ONE + 200) infection = "Mild Infection:" @@ -483,11 +489,13 @@ infection = "Acute Infection:" if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) infection = "Acute Infection+:" - if(INFECTION_LEVEL_TWO + 300 to INFINITY) + if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 399) infection = "Acute Infection++:" + if(INFECTION_LEVEL_TWO + 400 to INFINITY) + infection = "Septic:" dat += "" - dat += "" + dat += "" dat += "" dat += "
    [e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured][e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured][dead]
    [i.name]N/A[i.damage][infection]:[mech][i.name]N/A[i.damage][infection]:[mech][dead]
    " if(occupant.disabilities & BLIND) @@ -499,4 +507,4 @@ else dat += "[src] is empty." - return dat \ No newline at end of file + return dat diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index 12ad51c8b28..eaa1120ab04 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -14,7 +14,7 @@ var/cooldown_time = 0 var/cooldown_timeleft = 0 var/cooldown_on = FALSE - req_access = list(access_ai_upload) + req_access = list(ACCESS_AI_UPLOAD) /obj/machinery/ai_slipper/power_change() if(stat & BROKEN) @@ -133,4 +133,4 @@ return if(uses >= 0) cooldown_on = FALSE - power_change() \ No newline at end of file + power_change() diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 5d286c0a367..02d95278e30 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -1,1110 +1,1110 @@ -// A datum for dealing with threshold limit values -// used in /obj/machinery/alarm -/datum/tlv - var/min2 - var/min1 - var/max1 - var/max2 - -/datum/tlv/New(_min2 as num, _min1 as num, _max1 as num, _max2 as num) - min2 = _min2 - min1 = _min1 - max1 = _max1 - max2 = _max2 - -/datum/tlv/proc/get_danger_level(curval as num) - if(max2 >=0 && curval>max2) - return ATMOS_ALARM_DANGER - if(min2 >=0 && curval=0 && curval>max1) - return ATMOS_ALARM_WARNING - if(min1 >=0 && curval target_temperature + 2 || regulating_temperature) - //If it goes too far, we should adjust ourselves back before stopping. - if(!cur_tlv.get_danger_level(target_temperature)) - if(!regulating_temperature) - regulating_temperature = 1 - visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click and a faint electronic hum.") - - if(target_temperature > MAX_TEMPERATURE) - target_temperature = MAX_TEMPERATURE - - if(target_temperature < MIN_TEMPERATURE) - target_temperature = MIN_TEMPERATURE - - var/datum/gas_mixture/gas = location.remove_air(0.25*environment.total_moles()) - var/heat_capacity = gas.heat_capacity() - var/energy_used = max( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE) - - //Use power. Assuming that each power unit represents 1000 watts.... - use_power(energy_used/1000, ENVIRON) - - //We need to cool ourselves. - if(heat_capacity) - if(environment.temperature > target_temperature) - gas.temperature -= energy_used/heat_capacity - else - gas.temperature += energy_used/heat_capacity - - environment.merge(gas) - - if(abs(environment.temperature - target_temperature) <= 0.5) - regulating_temperature = 0 - visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click as a faint electronic humming stops.") - -/obj/machinery/alarm/update_icon() - if(wiresexposed) - icon_state = "alarmx" - return - if((stat & (NOPOWER|BROKEN)) || shorted) - icon_state = "alarmp" - return - - switch(max(danger_level, alarm_area.atmosalm-1)) - if(ATMOS_ALARM_NONE) - icon_state = "alarm0" - if(ATMOS_ALARM_WARNING) - icon_state = "alarm2" //yes, alarm2 is yellow alarm - if(ATMOS_ALARM_DANGER) - icon_state = "alarm1" - -/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) - var/new_name - if(device_type=="AVP") - new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" - alarm_area.air_vent_names[m_id] = new_name - else if(device_type=="AScr") - new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" - alarm_area.air_scrub_names[m_id] = new_name - else - return - spawn (10) - send_signal(m_id, list("init" = new_name) ) - -/obj/machinery/alarm/proc/refresh_all() - for(var/id_tag in alarm_area.air_vent_names) - var/list/I = alarm_area.air_vent_info[id_tag] - if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) - continue - send_signal(id_tag, list("status") ) - for(var/id_tag in alarm_area.air_scrub_names) - var/list/I = alarm_area.air_scrub_info[id_tag] - if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) - continue - send_signal(id_tag, list("status") ) - -/obj/machinery/alarm/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) - -/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = command - signal.data["tag"] = target - signal.data["sigtype"] = "command" - - radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) -// to_chat(world, text("Signal [] Broadcasted to []", command, target)) - - return 1 - -/obj/machinery/alarm/proc/apply_mode() - switch(mode) - if(AALARM_MODE_SCRUBBING) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "o2_scrub" = (preset==AALARM_PRESET_VOX), - "n2_scrub" = 0, - "co2_scrub"= 1, - "scrubbing"= 1, - "widenet"= 0, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure"= ONE_ATMOSPHERE - )) - if(AALARM_MODE_CONTAMINATED) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "co2_scrub"= 1, - "tox_scrub"= 1, - "n2o_scrub"= 1, - "scrubbing"= 1, - "widenet"= 1, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure"= ONE_ATMOSPHERE - )) - if(AALARM_MODE_VENTING) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 0, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure" = ONE_ATMOSPHERE*2 - )) - if(AALARM_MODE_REFILL) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "co2_scrub"= 1, - "tox_scrub"= 0, - "n2o_scrub"= 0, - "scrubbing"= 1, - "widenet"= 0, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure" = ONE_ATMOSPHERE*3 - )) - if( - AALARM_MODE_PANIC, - AALARM_MODE_REPLACEMENT - ) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 1, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - if( - AALARM_MODE_SIPHON - ) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 0, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - - if(AALARM_MODE_OFF) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - if(AALARM_MODE_FLOOD) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"=0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 0, - )) - -/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) - if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) - post_alert(new_danger_level) - - update_icon() - -/obj/machinery/alarm/proc/post_alert(alert_level) - var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency) - if(!frequency) - return - - var/datum/signal/alert_signal = new - alert_signal.source = src - alert_signal.transmission_method = 1 - alert_signal.data["zone"] = alarm_area.name - alert_signal.data["type"] = "Atmospheric" - - if(alert_level==2) - alert_signal.data["alert"] = "severe" - else if(alert_level==1) - alert_signal.data["alert"] = "minor" - else if(alert_level==0) - alert_signal.data["alert"] = "clear" - - frequency.post_signal(src, alert_signal) - -/////////////// -//END HACKING// -/////////////// - -/obj/machinery/alarm/attack_ai(mob/user) - src.add_hiddenprint(user) - return ui_interact(user) - -/obj/machinery/alarm/attack_ghost(user as mob) - return interact(user) - -/obj/machinery/alarm/attack_hand(mob/user) - . = ..() - if(.) - return - return interact(user) - -/obj/machinery/alarm/interact(mob/user) - if(buildstage != 2) - return - - if(wiresexposed) - wires.Interact(user) - - if(!shorted) - ui_interact(user) - -/obj/machinery/alarm/proc/ui_air_status() - var/turf/location = get_turf(src) - if(!istype(location)) - return - - var/datum/gas_mixture/environment = location.return_air() - var/total = environment.oxygen + environment.nitrogen + environment.carbon_dioxide + environment.toxins - if(total==0) - return null - - var/datum/tlv/cur_tlv - var/GET_PP = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume - - cur_tlv = TLV["pressure"] - var/environment_pressure = environment.return_pressure() - var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) - - cur_tlv = TLV["oxygen"] - var/oxygen_dangerlevel = cur_tlv.get_danger_level(environment.oxygen*GET_PP) - var/oxygen_percent = round(environment.oxygen / total * 100, 2) - - cur_tlv = TLV["nitrogen"] - var/nitrogen_dangerlevel = cur_tlv.get_danger_level(environment.nitrogen*GET_PP) - var/nitrogen_percent = round(environment.nitrogen / total * 100, 2) - - cur_tlv = TLV["carbon dioxide"] - var/co2_dangerlevel = cur_tlv.get_danger_level(environment.carbon_dioxide*GET_PP) - var/co2_percent = round(environment.carbon_dioxide / total * 100, 2) - - cur_tlv = TLV["plasma"] - var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP) - var/plasma_percent = round(environment.toxins / total * 100, 2) - - cur_tlv = TLV["other"] - var/other_moles = 0.0 - for(var/datum/gas/G in environment.trace_gases) - other_moles+=G.moles - var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP) - - cur_tlv = TLV["temperature"] - var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) - - var/data[0] - data["pressure"] = environment_pressure - data["temperature"] = environment.temperature - data["temperature_c"] = round(environment.temperature - T0C, 0.1) - - var/percentages[0] - percentages["oxygen"] = oxygen_percent - percentages["nitrogen"] = nitrogen_percent - percentages["co2"] = co2_percent - percentages["plasma"] = plasma_percent - percentages["other"] = other_moles - data["contents"] = percentages - - var/danger[0] - danger["pressure"] = pressure_dangerlevel - danger["temperature"] = temperature_dangerlevel - danger["oxygen"] = oxygen_dangerlevel - danger["nitrogen"] = nitrogen_dangerlevel - danger["co2"] = co2_dangerlevel - danger["plasma"] = plasma_dangerlevel - danger["other"] = other_dangerlevel - danger["overall"] = max(pressure_dangerlevel,oxygen_dangerlevel,nitrogen_dangerlevel,co2_dangerlevel,plasma_dangerlevel,other_dangerlevel,temperature_dangerlevel) - data["danger"] = danger - return data - -/obj/machinery/alarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - var/list/href_list = state.href_list(user) - if(href_list) - data["remote_connection"] = href_list["remote_connection"] - data["remote_access"] = href_list["remote_access"] - - data["name"] = sanitize(name) - data["air"] = ui_air_status() - data["alarmActivated"] = alarmActivated || danger_level == ATMOS_ALARM_DANGER - data["thresholds"] = generate_thresholds_menu() - - // Locked when: - // Not sent from atmos console AND - // Not silicon AND locked. - data["locked"] = !is_authenticated(user, href_list) - data["rcon"] = rcon_setting - data["target_temp"] = target_temperature - T0C - data["atmos_alarm"] = alarm_area.atmosalm - data["emagged"] = emagged - data["modes"] = list( - AALARM_MODE_SCRUBBING = list("name"="Filtering", "desc"="Scrubs out contaminants"),\ - AALARM_MODE_VENTING = list("name"="Draught", "desc"="Siphons out air while replacing"),\ - AALARM_MODE_PANIC = list("name"="Panic Siphon","desc"="Siphons air out of the room quickly"),\ - AALARM_MODE_REPLACEMENT = list("name"="Cycle", "desc"="Siphons air before replacing"),\ - AALARM_MODE_SIPHON = list("name"="Siphon", "desc"="Siphons air out of the room"),\ - AALARM_MODE_CONTAMINATED= list("name"="Contaminated","desc"="Scrubs out all contaminants quickly"),\ - AALARM_MODE_REFILL = list("name"="Refill", "desc"="Triples vent output"),\ - AALARM_MODE_OFF = list("name"="Off", "desc"="Shuts off vents and scrubbers"),\ - AALARM_MODE_FLOOD = list("name"="Flood", "desc"="Shuts off scrubbers and opens vents", "emagonly" = 1) - ) - data["mode"] = mode - data["presets"] = list( - AALARM_PRESET_HUMAN = list("name"="Human", "desc"="Checks for oxygen and nitrogen"),\ - AALARM_PRESET_VOX = list("name"="Vox", "desc"="Checks for nitrogen only"),\ - AALARM_PRESET_COLDROOM = list("name"="Coldroom", "desc"="For freezers"),\ - AALARM_PRESET_SERVER = list("name"="Server Room", "desc"="For server rooms") - ) - data["preset"] = preset - data["screen"] = screen - - var/list/vents=list() - if(alarm_area.air_vent_names.len) - for(var/id_tag in alarm_area.air_vent_names) - var/vent_info[0] - var/long_name = alarm_area.air_vent_names[id_tag] - var/list/vent_data = alarm_area.air_vent_info[id_tag] - if(!vent_data) - continue - vent_info["id_tag"]=id_tag - vent_info["name"]=sanitize(long_name) - vent_info += vent_data - vents+=list(vent_info) - data["vents"]=vents - - var/list/scrubbers=list() - if(alarm_area.air_scrub_names.len) - for(var/id_tag in alarm_area.air_scrub_names) - var/long_name = alarm_area.air_scrub_names[id_tag] - var/list/scrubber_data = alarm_area.air_scrub_info[id_tag] - if(!scrubber_data) - continue - scrubber_data["id_tag"]=id_tag - scrubber_data["name"]=sanitize(long_name) - scrubbers+=list(scrubber_data) - data["scrubbers"]=scrubbers - return data - -/obj/machinery/alarm/proc/get_nano_data_console(mob/user) - var/data[0] - data["name"] = sanitize(name) - data["ref"] = "\ref[src]" - data["danger"] = max(danger_level, alarm_area.atmosalm) - var/area/Area = get_area(src) - data["area"] = sanitize(Area.name) - var/turf/pos = get_turf(src) - data["x"] = pos.x - data["y"] = pos.y - data["z"] = pos.z - return data - -/obj/machinery/alarm/proc/generate_thresholds_menu() - var/datum/tlv/selected - var/list/thresholds = list() - - var/list/gas_names = list( - "oxygen" = "O2", - "nitrogen" = "N2", - "carbon dioxide" = "CO2", - "plasma" = "Toxin", - "other" = "Other") - for(var/g in gas_names) - thresholds += list(list("name" = gas_names[g], "settings" = list())) - selected = TLV[g] - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max2", "selected" = selected.max2)) - - selected = TLV["pressure"] - thresholds += list(list("name" = "Pressure", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) - - selected = TLV["temperature"] - thresholds += list(list("name" = "Temperature", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) - - return thresholds - -/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "air_alarm.tmpl", name, 570, 410, state = state) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/alarm/proc/is_authenticated(var/mob/user, href_list) - if(user.can_admin_interact()) - return 1 - else if(isAI(user) || isrobot(user) || emagged || is_auth_rcon(href_list)) - return 1 - else - return !locked - -/obj/machinery/alarm/proc/is_auth_rcon(href_list) - if(href_list && href_list["remote_connection"] && href_list["remote_access"]) - return 1 - else - return 0 - -/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list()) - if(buildstage != 2) - return STATUS_CLOSE - - if(aidisabled && (isAI(user) || isrobot(user))) - to_chat(user, "AI control for \the [src] interface has been disabled.") - return STATUS_CLOSE - - . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE - - if(. == STATUS_INTERACTIVE) - var/extra_href = state.href_list(usr) - // Prevent remote users from altering RCON settings or activating atmos alarms unless they already have access - if((href_list["atmos_alarm"] || href_list["rcon"]) && extra_href["remote_connection"] && !extra_href["remote_access"]) - . = STATUS_UPDATE - return min(..(), .) - -/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state) - if(..(href, href_list, nowindow, state)) - return 1 - - var/state_href = state.href_list(usr) - - if(href_list["rcon"]) - var/attempted_rcon_setting = text2num(href_list["rcon"]) - switch(attempted_rcon_setting) - if(RCON_NO) - rcon_setting = RCON_NO - if(RCON_AUTO) - rcon_setting = RCON_AUTO - if(RCON_YES) - rcon_setting = RCON_YES - return 1 - - add_fingerprint(usr) - - if(href_list["command"]) - if(!is_authenticated(usr, state_href)) - return - - var/device_id = href_list["id_tag"] - switch(href_list["command"]) - if( "power", - "adjust_external_pressure", - "set_external_pressure", - "checks", - "co2_scrub", - "tox_scrub", - "n2o_scrub", - "n2_scrub", - "o2_scrub", - "widenet", - "scrubbing", - "direction") - var/val - if(href_list["val"]) - val=text2num(href_list["val"]) - else - var/newval = input("Enter new value") as num|null - if(isnull(newval)) - return - if(href_list["command"]=="set_external_pressure") - if(newval>1000+ONE_ATMOSPHERE) - newval = 1000+ONE_ATMOSPHERE - if(newval<0) - newval = 0 - val = newval - - send_signal(device_id, list(href_list["command"] = val ) ) - waiting_on_device=device_id - - if("set_threshold") - var/env = href_list["env"] - var/varname = href_list["var"] - var/datum/tlv/tlv = TLV[env] - var/newval = input("Enter [varname] for [env]", "Alarm triggers", tlv.vars[varname]) as num|null - - if(isnull(newval) || ..(href, href_list, nowindow, state)) - return - if(newval<0) - tlv.vars[varname] = -1.0 - else if(env=="temperature" && newval>5000) - tlv.vars[varname] = 5000 - else if(env=="pressure" && newval>50*ONE_ATMOSPHERE) - tlv.vars[varname] = 50*ONE_ATMOSPHERE - else if(env!="temperature" && env!="pressure" && newval>200) - tlv.vars[varname] = 200 - else - newval = round(newval,0.01) - tlv.vars[varname] = newval - - if(href_list["screen"]) - if(!is_authenticated(usr, state_href)) - return - - screen = text2num(href_list["screen"]) - return 1 - - if(href_list["atmos_alarm"]) - if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src)) - apply_danger_level(ATMOS_ALARM_DANGER) - alarmActivated = 1 - update_icon() - return 1 - - if(href_list["atmos_reset"]) - if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE)) - apply_danger_level(ATMOS_ALARM_NONE) - alarmActivated = 0 - update_icon() - return 1 - - if(href_list["mode"]) - if(!is_authenticated(usr, state_href)) - return - - mode = text2num(href_list["mode"]) - apply_mode() - return 1 - - if(href_list["preset"]) - if(!is_authenticated(usr, state_href)) - return - - preset = text2num(href_list["preset"]) - apply_preset() - return 1 - - if(href_list["temperature"]) - var/datum/tlv/selected = TLV["temperature"] - var/max_temperature = selected.max1 >= 0 ? min(selected.max1, MAX_TEMPERATURE) : max(selected.max1, MAX_TEMPERATURE) - var/min_temperature = max(selected.min1, MIN_TEMPERATURE) - var/max_temperature_c = max_temperature - T0C - var/min_temperature_c = min_temperature - T0C - var/input_temperature = input("What temperature would you like the system to maintain? (Capped between [min_temperature_c]C and [max_temperature_c]C)", "Thermostat Controls") as num|null - if(isnull(input_temperature) || ..(href, href_list, nowindow, state)) - return - input_temperature = input_temperature + T0C - if(input_temperature > max_temperature || input_temperature < min_temperature) - to_chat(usr, "Temperature must be between [min_temperature_c]C and [max_temperature_c]C") - else - target_temperature = input_temperature - return 1 - -/obj/machinery/alarm/emag_act(mob/user) - if(!emagged) - src.emagged = 1 - if(user) - user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its safeties.") - playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) - return - -/obj/machinery/alarm/attackby(obj/item/I, mob/user, params) - add_fingerprint(user) - - switch(buildstage) - if(2) - if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda))// trying to unlock the interface with an ID card - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "It does nothing") - return - else - if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.") - updateUsrDialog() - else - to_chat(user, "Access denied.") - return - - if(1) - if(iscoil(I)) - var/obj/item/stack/cable_coil/coil = I - if(coil.amount < 5) - to_chat(user, "You need more cable for this!") - return - - to_chat(user, "You wire \the [src]!") - playsound(get_turf(src), coil.usesound, 50, 1) - coil.amount -= 5 - if(!coil.amount) - qdel(coil) - - buildstage = 2 - update_icon() - first_run() - return - if(0) - if(istype(I, /obj/item/airalarm_electronics)) - to_chat(user, "You insert the circuit!") - playsound(get_turf(src), I.usesound, 50, 1) - qdel(I) - buildstage = 1 - update_icon() - return - return ..() - -/obj/machinery/alarm/crowbar_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_BUILDING) - return - . = TRUE - if(!I.tool_start_check(user, 0)) - return - to_chat(user, "You start prying out the circuit.") - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) - return - if(buildstage != AIR_ALARM_BUILDING) - return - to_chat(user, "You pry out the circuit!") - new /obj/item/airalarm_electronics(user.drop_location()) - buildstage = AIR_ALARM_FRAME - update_icon() - -/obj/machinery/alarm/multitool_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_READY) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(wiresexposed) - attack_hand(user) - -/obj/machinery/alarm/screwdriver_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_READY) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - wiresexposed = !wiresexposed - update_icon() - if(wiresexposed) - SCREWDRIVER_OPEN_PANEL_MESSAGE - else - SCREWDRIVER_CLOSE_PANEL_MESSAGE - -/obj/machinery/alarm/wirecutter_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_READY) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(wires.wires_status == 31) // all wires cut - var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil(user.drop_location()) - new_coil.amount = 5 - buildstage = AIR_ALARM_BUILDING - update_icon() - if(wiresexposed) - wires.Interact(user) - -/obj/machinery/alarm/wrench_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_FRAME) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - new /obj/item/mounted/frame/alarm_frame(get_turf(user)) - WRENCH_UNANCHOR_WALL_MESSAGE - qdel(src) - -/obj/machinery/alarm/power_change() - if(powered(power_channel)) - stat &= ~NOPOWER - else - stat |= NOPOWER - spawn(rand(0,15)) - update_icon() - -/obj/machinery/alarm/obj_break(damage_flag) - ..() - update_icon() - -/obj/machinery/alarm/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc, 2) - var/obj/item/I = new /obj/item/airalarm_electronics(loc) - if(!disassembled) - I.obj_integrity = I.max_integrity * 0.5 - new /obj/item/stack/cable_coil(loc, 3) - qdel(src) - -/obj/machinery/alarm/examine(mob/user) - . = ..() - if(buildstage < 2) - . += "It is not wired." - if(buildstage < 1) - . += "The circuit is missing." - -/obj/machinery/alarm/all_access - name = "all-access air alarm" - desc = "This particular atmos control unit appears to have no access restrictions." - locked = FALSE - req_access = null - req_one_access = null - -/* -AIR ALARM CIRCUIT -Just an object used in constructing air alarms -*/ -/obj/item/airalarm_electronics - name = "air alarm electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - desc = "Looks like a circuit. Probably is." - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50, MAT_GLASS=50) - origin_tech = "engineering=2;programming=1" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - - -#undef AIR_ALARM_FRAME -#undef AIR_ALARM_BUILDING -#undef AIR_ALARM_READY +// A datum for dealing with threshold limit values +// used in /obj/machinery/alarm +/datum/tlv + var/min2 + var/min1 + var/max1 + var/max2 + +/datum/tlv/New(_min2 as num, _min1 as num, _max1 as num, _max2 as num) + min2 = _min2 + min1 = _min1 + max1 = _max1 + max2 = _max2 + +/datum/tlv/proc/get_danger_level(curval as num) + if(max2 >=0 && curval>max2) + return ATMOS_ALARM_DANGER + if(min2 >=0 && curval=0 && curval>max1) + return ATMOS_ALARM_WARNING + if(min1 >=0 && curval target_temperature + 2 || regulating_temperature) + //If it goes too far, we should adjust ourselves back before stopping. + if(!cur_tlv.get_danger_level(target_temperature)) + if(!regulating_temperature) + regulating_temperature = 1 + visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click and a faint electronic hum.") + + if(target_temperature > MAX_TEMPERATURE) + target_temperature = MAX_TEMPERATURE + + if(target_temperature < MIN_TEMPERATURE) + target_temperature = MIN_TEMPERATURE + + var/datum/gas_mixture/gas = location.remove_air(0.25*environment.total_moles()) + var/heat_capacity = gas.heat_capacity() + var/energy_used = max( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE) + + //Use power. Assuming that each power unit represents 1000 watts.... + use_power(energy_used/1000, ENVIRON) + + //We need to cool ourselves. + if(heat_capacity) + if(environment.temperature > target_temperature) + gas.temperature -= energy_used/heat_capacity + else + gas.temperature += energy_used/heat_capacity + + environment.merge(gas) + + if(abs(environment.temperature - target_temperature) <= 0.5) + regulating_temperature = 0 + visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click as a faint electronic humming stops.") + +/obj/machinery/alarm/update_icon() + if(wiresexposed) + icon_state = "alarmx" + return + if((stat & (NOPOWER|BROKEN)) || shorted) + icon_state = "alarmp" + return + + switch(max(danger_level, alarm_area.atmosalm-1)) + if(ATMOS_ALARM_NONE) + icon_state = "alarm0" + if(ATMOS_ALARM_WARNING) + icon_state = "alarm2" //yes, alarm2 is yellow alarm + if(ATMOS_ALARM_DANGER) + icon_state = "alarm1" + +/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) + var/new_name + if(device_type=="AVP") + new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" + alarm_area.air_vent_names[m_id] = new_name + else if(device_type=="AScr") + new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" + alarm_area.air_scrub_names[m_id] = new_name + else + return + spawn (10) + send_signal(m_id, list("init" = new_name) ) + +/obj/machinery/alarm/proc/refresh_all() + for(var/id_tag in alarm_area.air_vent_names) + var/list/I = alarm_area.air_vent_info[id_tag] + if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) + continue + send_signal(id_tag, list("status") ) + for(var/id_tag in alarm_area.air_scrub_names) + var/list/I = alarm_area.air_scrub_info[id_tag] + if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) + continue + send_signal(id_tag, list("status") ) + +/obj/machinery/alarm/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) + +/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = command + signal.data["tag"] = target + signal.data["sigtype"] = "command" + + radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) +// to_chat(world, text("Signal [] Broadcasted to []", command, target)) + + return 1 + +/obj/machinery/alarm/proc/apply_mode() + switch(mode) + if(AALARM_MODE_SCRUBBING) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "o2_scrub" = (preset==AALARM_PRESET_VOX), + "n2_scrub" = 0, + "co2_scrub"= 1, + "scrubbing"= 1, + "widenet"= 0, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure"= ONE_ATMOSPHERE + )) + if(AALARM_MODE_CONTAMINATED) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "co2_scrub"= 1, + "tox_scrub"= 1, + "n2o_scrub"= 1, + "scrubbing"= 1, + "widenet"= 1, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure"= ONE_ATMOSPHERE + )) + if(AALARM_MODE_VENTING) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 0, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure" = ONE_ATMOSPHERE*2 + )) + if(AALARM_MODE_REFILL) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "co2_scrub"= 1, + "tox_scrub"= 0, + "n2o_scrub"= 0, + "scrubbing"= 1, + "widenet"= 0, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure" = ONE_ATMOSPHERE*3 + )) + if( + AALARM_MODE_PANIC, + AALARM_MODE_REPLACEMENT + ) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 1, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + if( + AALARM_MODE_SIPHON + ) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 0, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + + if(AALARM_MODE_OFF) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + if(AALARM_MODE_FLOOD) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"=0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 0, + )) + +/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) + if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) + post_alert(new_danger_level) + + update_icon() + +/obj/machinery/alarm/proc/post_alert(alert_level) + var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency) + if(!frequency) + return + + var/datum/signal/alert_signal = new + alert_signal.source = src + alert_signal.transmission_method = 1 + alert_signal.data["zone"] = alarm_area.name + alert_signal.data["type"] = "Atmospheric" + + if(alert_level==2) + alert_signal.data["alert"] = "severe" + else if(alert_level==1) + alert_signal.data["alert"] = "minor" + else if(alert_level==0) + alert_signal.data["alert"] = "clear" + + frequency.post_signal(src, alert_signal) + +/////////////// +//END HACKING// +/////////////// + +/obj/machinery/alarm/attack_ai(mob/user) + src.add_hiddenprint(user) + return ui_interact(user) + +/obj/machinery/alarm/attack_ghost(user as mob) + return interact(user) + +/obj/machinery/alarm/attack_hand(mob/user) + . = ..() + if(.) + return + return interact(user) + +/obj/machinery/alarm/interact(mob/user) + if(buildstage != 2) + return + + if(wiresexposed) + wires.Interact(user) + + if(!shorted) + ui_interact(user) + +/obj/machinery/alarm/proc/ui_air_status() + var/turf/location = get_turf(src) + if(!istype(location)) + return + + var/datum/gas_mixture/environment = location.return_air() + var/total = environment.oxygen + environment.nitrogen + environment.carbon_dioxide + environment.toxins + if(total==0) + return null + + var/datum/tlv/cur_tlv + var/GET_PP = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume + + cur_tlv = TLV["pressure"] + var/environment_pressure = environment.return_pressure() + var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) + + cur_tlv = TLV["oxygen"] + var/oxygen_dangerlevel = cur_tlv.get_danger_level(environment.oxygen*GET_PP) + var/oxygen_percent = round(environment.oxygen / total * 100, 2) + + cur_tlv = TLV["nitrogen"] + var/nitrogen_dangerlevel = cur_tlv.get_danger_level(environment.nitrogen*GET_PP) + var/nitrogen_percent = round(environment.nitrogen / total * 100, 2) + + cur_tlv = TLV["carbon dioxide"] + var/co2_dangerlevel = cur_tlv.get_danger_level(environment.carbon_dioxide*GET_PP) + var/co2_percent = round(environment.carbon_dioxide / total * 100, 2) + + cur_tlv = TLV["plasma"] + var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP) + var/plasma_percent = round(environment.toxins / total * 100, 2) + + cur_tlv = TLV["other"] + var/other_moles = 0.0 + for(var/datum/gas/G in environment.trace_gases) + other_moles+=G.moles + var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP) + + cur_tlv = TLV["temperature"] + var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) + + var/data[0] + data["pressure"] = environment_pressure + data["temperature"] = environment.temperature + data["temperature_c"] = round(environment.temperature - T0C, 0.1) + + var/percentages[0] + percentages["oxygen"] = oxygen_percent + percentages["nitrogen"] = nitrogen_percent + percentages["co2"] = co2_percent + percentages["plasma"] = plasma_percent + percentages["other"] = other_moles + data["contents"] = percentages + + var/danger[0] + danger["pressure"] = pressure_dangerlevel + danger["temperature"] = temperature_dangerlevel + danger["oxygen"] = oxygen_dangerlevel + danger["nitrogen"] = nitrogen_dangerlevel + danger["co2"] = co2_dangerlevel + danger["plasma"] = plasma_dangerlevel + danger["other"] = other_dangerlevel + danger["overall"] = max(pressure_dangerlevel,oxygen_dangerlevel,nitrogen_dangerlevel,co2_dangerlevel,plasma_dangerlevel,other_dangerlevel,temperature_dangerlevel) + data["danger"] = danger + return data + +/obj/machinery/alarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + var/list/href_list = state.href_list(user) + if(href_list) + data["remote_connection"] = href_list["remote_connection"] + data["remote_access"] = href_list["remote_access"] + + data["name"] = sanitize(name) + data["air"] = ui_air_status() + data["alarmActivated"] = alarmActivated || danger_level == ATMOS_ALARM_DANGER + data["thresholds"] = generate_thresholds_menu() + + // Locked when: + // Not sent from atmos console AND + // Not silicon AND locked. + data["locked"] = !is_authenticated(user, href_list) + data["rcon"] = rcon_setting + data["target_temp"] = target_temperature - T0C + data["atmos_alarm"] = alarm_area.atmosalm + data["emagged"] = emagged + data["modes"] = list( + AALARM_MODE_SCRUBBING = list("name"="Filtering", "desc"="Scrubs out contaminants"),\ + AALARM_MODE_VENTING = list("name"="Draught", "desc"="Siphons out air while replacing"),\ + AALARM_MODE_PANIC = list("name"="Panic Siphon","desc"="Siphons air out of the room quickly"),\ + AALARM_MODE_REPLACEMENT = list("name"="Cycle", "desc"="Siphons air before replacing"),\ + AALARM_MODE_SIPHON = list("name"="Siphon", "desc"="Siphons air out of the room"),\ + AALARM_MODE_CONTAMINATED= list("name"="Contaminated","desc"="Scrubs out all contaminants quickly"),\ + AALARM_MODE_REFILL = list("name"="Refill", "desc"="Triples vent output"),\ + AALARM_MODE_OFF = list("name"="Off", "desc"="Shuts off vents and scrubbers"),\ + AALARM_MODE_FLOOD = list("name"="Flood", "desc"="Shuts off scrubbers and opens vents", "emagonly" = 1) + ) + data["mode"] = mode + data["presets"] = list( + AALARM_PRESET_HUMAN = list("name"="Human", "desc"="Checks for oxygen and nitrogen"),\ + AALARM_PRESET_VOX = list("name"="Vox", "desc"="Checks for nitrogen only"),\ + AALARM_PRESET_COLDROOM = list("name"="Coldroom", "desc"="For freezers"),\ + AALARM_PRESET_SERVER = list("name"="Server Room", "desc"="For server rooms") + ) + data["preset"] = preset + data["screen"] = screen + + var/list/vents=list() + if(alarm_area.air_vent_names.len) + for(var/id_tag in alarm_area.air_vent_names) + var/vent_info[0] + var/long_name = alarm_area.air_vent_names[id_tag] + var/list/vent_data = alarm_area.air_vent_info[id_tag] + if(!vent_data) + continue + vent_info["id_tag"]=id_tag + vent_info["name"]=sanitize(long_name) + vent_info += vent_data + vents+=list(vent_info) + data["vents"]=vents + + var/list/scrubbers=list() + if(alarm_area.air_scrub_names.len) + for(var/id_tag in alarm_area.air_scrub_names) + var/long_name = alarm_area.air_scrub_names[id_tag] + var/list/scrubber_data = alarm_area.air_scrub_info[id_tag] + if(!scrubber_data) + continue + scrubber_data["id_tag"]=id_tag + scrubber_data["name"]=sanitize(long_name) + scrubbers+=list(scrubber_data) + data["scrubbers"]=scrubbers + return data + +/obj/machinery/alarm/proc/get_nano_data_console(mob/user) + var/data[0] + data["name"] = sanitize(name) + data["ref"] = "\ref[src]" + data["danger"] = max(danger_level, alarm_area.atmosalm) + var/area/Area = get_area(src) + data["area"] = sanitize(Area.name) + var/turf/pos = get_turf(src) + data["x"] = pos.x + data["y"] = pos.y + data["z"] = pos.z + return data + +/obj/machinery/alarm/proc/generate_thresholds_menu() + var/datum/tlv/selected + var/list/thresholds = list() + + var/list/gas_names = list( + "oxygen" = "O2", + "nitrogen" = "N2", + "carbon dioxide" = "CO2", + "plasma" = "Toxin", + "other" = "Other") + for(var/g in gas_names) + thresholds += list(list("name" = gas_names[g], "settings" = list())) + selected = TLV[g] + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max2", "selected" = selected.max2)) + + selected = TLV["pressure"] + thresholds += list(list("name" = "Pressure", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) + + selected = TLV["temperature"] + thresholds += list(list("name" = "Temperature", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) + + return thresholds + +/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "air_alarm.tmpl", name, 570, 410, state = state) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/alarm/proc/is_authenticated(var/mob/user, href_list) + if(user.can_admin_interact()) + return 1 + else if(isAI(user) || isrobot(user) || emagged || is_auth_rcon(href_list)) + return 1 + else + return !locked + +/obj/machinery/alarm/proc/is_auth_rcon(href_list) + if(href_list && href_list["remote_connection"] && href_list["remote_access"]) + return 1 + else + return 0 + +/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list()) + if(buildstage != 2) + return STATUS_CLOSE + + if(aidisabled && (isAI(user) || isrobot(user))) + to_chat(user, "AI control for \the [src] interface has been disabled.") + return STATUS_CLOSE + + . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE + + if(. == STATUS_INTERACTIVE) + var/extra_href = state.href_list(usr) + // Prevent remote users from altering RCON settings or activating atmos alarms unless they already have access + if((href_list["atmos_alarm"] || href_list["rcon"]) && extra_href["remote_connection"] && !extra_href["remote_access"]) + . = STATUS_UPDATE + return min(..(), .) + +/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state) + if(..(href, href_list, nowindow, state)) + return 1 + + var/state_href = state.href_list(usr) + + if(href_list["rcon"]) + var/attempted_rcon_setting = text2num(href_list["rcon"]) + switch(attempted_rcon_setting) + if(RCON_NO) + rcon_setting = RCON_NO + if(RCON_AUTO) + rcon_setting = RCON_AUTO + if(RCON_YES) + rcon_setting = RCON_YES + return 1 + + add_fingerprint(usr) + + if(href_list["command"]) + if(!is_authenticated(usr, state_href)) + return + + var/device_id = href_list["id_tag"] + switch(href_list["command"]) + if( "power", + "adjust_external_pressure", + "set_external_pressure", + "checks", + "co2_scrub", + "tox_scrub", + "n2o_scrub", + "n2_scrub", + "o2_scrub", + "widenet", + "scrubbing", + "direction") + var/val + if(href_list["val"]) + val=text2num(href_list["val"]) + else + var/newval = input("Enter new value") as num|null + if(isnull(newval)) + return + if(href_list["command"]=="set_external_pressure") + if(newval>1000+ONE_ATMOSPHERE) + newval = 1000+ONE_ATMOSPHERE + if(newval<0) + newval = 0 + val = newval + + send_signal(device_id, list(href_list["command"] = val ) ) + waiting_on_device=device_id + + if("set_threshold") + var/env = href_list["env"] + var/varname = href_list["var"] + var/datum/tlv/tlv = TLV[env] + var/newval = input("Enter [varname] for [env]", "Alarm triggers", tlv.vars[varname]) as num|null + + if(isnull(newval) || ..(href, href_list, nowindow, state)) + return + if(newval<0) + tlv.vars[varname] = -1.0 + else if(env=="temperature" && newval>5000) + tlv.vars[varname] = 5000 + else if(env=="pressure" && newval>50*ONE_ATMOSPHERE) + tlv.vars[varname] = 50*ONE_ATMOSPHERE + else if(env!="temperature" && env!="pressure" && newval>200) + tlv.vars[varname] = 200 + else + newval = round(newval,0.01) + tlv.vars[varname] = newval + + if(href_list["screen"]) + if(!is_authenticated(usr, state_href)) + return + + screen = text2num(href_list["screen"]) + return 1 + + if(href_list["atmos_alarm"]) + if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src)) + apply_danger_level(ATMOS_ALARM_DANGER) + alarmActivated = 1 + update_icon() + return 1 + + if(href_list["atmos_reset"]) + if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE)) + apply_danger_level(ATMOS_ALARM_NONE) + alarmActivated = 0 + update_icon() + return 1 + + if(href_list["mode"]) + if(!is_authenticated(usr, state_href)) + return + + mode = text2num(href_list["mode"]) + apply_mode() + return 1 + + if(href_list["preset"]) + if(!is_authenticated(usr, state_href)) + return + + preset = text2num(href_list["preset"]) + apply_preset() + return 1 + + if(href_list["temperature"]) + var/datum/tlv/selected = TLV["temperature"] + var/max_temperature = selected.max1 >= 0 ? min(selected.max1, MAX_TEMPERATURE) : max(selected.max1, MAX_TEMPERATURE) + var/min_temperature = max(selected.min1, MIN_TEMPERATURE) + var/max_temperature_c = max_temperature - T0C + var/min_temperature_c = min_temperature - T0C + var/input_temperature = input("What temperature would you like the system to maintain? (Capped between [min_temperature_c]C and [max_temperature_c]C)", "Thermostat Controls") as num|null + if(isnull(input_temperature) || ..(href, href_list, nowindow, state)) + return + input_temperature = input_temperature + T0C + if(input_temperature > max_temperature || input_temperature < min_temperature) + to_chat(usr, "Temperature must be between [min_temperature_c]C and [max_temperature_c]C") + else + target_temperature = input_temperature + return 1 + +/obj/machinery/alarm/emag_act(mob/user) + if(!emagged) + src.emagged = 1 + if(user) + user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its safeties.") + playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) + return + +/obj/machinery/alarm/attackby(obj/item/I, mob/user, params) + add_fingerprint(user) + + switch(buildstage) + if(2) + if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda))// trying to unlock the interface with an ID card + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "It does nothing") + return + else + if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.") + updateUsrDialog() + else + to_chat(user, "Access denied.") + return + + if(1) + if(iscoil(I)) + var/obj/item/stack/cable_coil/coil = I + if(coil.amount < 5) + to_chat(user, "You need more cable for this!") + return + + to_chat(user, "You wire \the [src]!") + playsound(get_turf(src), coil.usesound, 50, 1) + coil.amount -= 5 + if(!coil.amount) + qdel(coil) + + buildstage = 2 + update_icon() + first_run() + return + if(0) + if(istype(I, /obj/item/airalarm_electronics)) + to_chat(user, "You insert the circuit!") + playsound(get_turf(src), I.usesound, 50, 1) + qdel(I) + buildstage = 1 + update_icon() + return + return ..() + +/obj/machinery/alarm/crowbar_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_BUILDING) + return + . = TRUE + if(!I.tool_start_check(user, 0)) + return + to_chat(user, "You start prying out the circuit.") + if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + return + if(buildstage != AIR_ALARM_BUILDING) + return + to_chat(user, "You pry out the circuit!") + new /obj/item/airalarm_electronics(user.drop_location()) + buildstage = AIR_ALARM_FRAME + update_icon() + +/obj/machinery/alarm/multitool_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(wiresexposed) + attack_hand(user) + +/obj/machinery/alarm/screwdriver_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + wiresexposed = !wiresexposed + update_icon() + if(wiresexposed) + SCREWDRIVER_OPEN_PANEL_MESSAGE + else + SCREWDRIVER_CLOSE_PANEL_MESSAGE + +/obj/machinery/alarm/wirecutter_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(wires.wires_status == 31) // all wires cut + var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil(user.drop_location()) + new_coil.amount = 5 + buildstage = AIR_ALARM_BUILDING + update_icon() + if(wiresexposed) + wires.Interact(user) + +/obj/machinery/alarm/wrench_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_FRAME) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + new /obj/item/mounted/frame/alarm_frame(get_turf(user)) + WRENCH_UNANCHOR_WALL_MESSAGE + qdel(src) + +/obj/machinery/alarm/power_change() + if(powered(power_channel)) + stat &= ~NOPOWER + else + stat |= NOPOWER + spawn(rand(0,15)) + update_icon() + +/obj/machinery/alarm/obj_break(damage_flag) + ..() + update_icon() + +/obj/machinery/alarm/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc, 2) + var/obj/item/I = new /obj/item/airalarm_electronics(loc) + if(!disassembled) + I.obj_integrity = I.max_integrity * 0.5 + new /obj/item/stack/cable_coil(loc, 3) + qdel(src) + +/obj/machinery/alarm/examine(mob/user) + . = ..() + if(buildstage < 2) + . += "It is not wired." + if(buildstage < 1) + . += "The circuit is missing." + +/obj/machinery/alarm/all_access + name = "all-access air alarm" + desc = "This particular atmos control unit appears to have no access restrictions." + locked = FALSE + req_access = null + req_one_access = null + +/* +AIR ALARM CIRCUIT +Just an object used in constructing air alarms +*/ +/obj/item/airalarm_electronics + name = "air alarm electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + desc = "Looks like a circuit. Probably is." + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50, MAT_GLASS=50) + origin_tech = "engineering=2;programming=1" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + + +#undef AIR_ALARM_FRAME +#undef AIR_ALARM_BUILDING +#undef AIR_ALARM_READY diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index f0c6a81820f..3f5fbc374a6 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -357,7 +357,7 @@ if(istype(O,/obj/machinery/air_sensor) || istype(O, /obj/machinery/meter)) return O:id_tag in sensors -/obj/machinery/computer/general_air_control/linkWith(mob/user, obj/O, link/context) +/obj/machinery/computer/general_air_control/linkWith(mob/user, obj/O, context) sensors[O:id_tag] = reject_bad_name(clean_input(user, "Choose a sensor label:", "Sensor Label"), allow_numbers=1) return 1 diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 230401b673b..d91fba5fa5c 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -1,606 +1,605 @@ -/datum/canister_icons - var - possiblemaincolor = list( //these lists contain the possible colors of a canister - list("name" = "\[N2O\]", "icon" = "redws"), - list("name" = "\[N2\]", "icon" = "red"), - list("name" = "\[O2\]", "icon" = "blue"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange"), - list("name" = "\[CO2\]", "icon" = "black"), - list("name" = "\[Air\]", "icon" = "grey"), - list("name" = "\[CAUTION\]", "icon" = "yellow"), - list("name" = "\[SPECIAL\]", "icon" = "whiters") - ) - possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists - list("name" = "\[N2\]", "icon" = "red-c"), - list("name" = "\[O2\]", "icon" = "blue-c"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"), - list("name" = "\[CO2\]", "icon" = "black-c"), - list("name" = "\[Air\]", "icon" = "grey-c"), - list("name" = "\[CAUTION\]", "icon" = "yellow-c") - ) - possibletertcolor = list( - list("name" = "\[N2\]", "icon" = "red-c-1"), - list("name" = "\[O2\]", "icon" = "blue-c-1"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"), - list("name" = "\[CO2\]", "icon" = "black-c-1"), - list("name" = "\[Air\]", "icon" = "grey-c-1"), - list("name" = "\[CAUTION\]", "icon" = "yellow-c-1") - ) - possiblequartcolor = list( - list("name" = "\[N2\]", "icon" = "red-c-2"), - list("name" = "\[O2\]", "icon" = "blue-c-2"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"), - list("name" = "\[CO2\]", "icon" = "black-c-2"), - list("name" = "\[Air\]", "icon" = "grey-c-2"), - list("name" = "\[CAUTION\]", "icon" = "yellow-c-2") - ) - - possibledecals = list( //var that stores all possible decals, used by ui - list("name" = "Low temperature canister", "icon" = "cold"), - list("name" = "High temperature canister", "icon" = "hot"), - list("name" = "Plasma containing canister", "icon" = "plasma") - ) - -var/datum/canister_icons/canister_icon_container = new() - -/obj/machinery/portable_atmospherics/canister - name = "canister" - icon = 'icons/obj/atmos.dmi' - icon_state = "yellow" - density = 1 - flags = CONDUCT - armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 50) - max_integrity = 250 - integrity_failure = 100 - - var/menu = 0 - //used by nanoui: 0 = main menu, 1 = relabel - - var/valve_open = 0 - var/release_pressure = ONE_ATMOSPHERE - - var/list/canister_color //variable that stores colours - var/list/decals //list that stores the decals - - //lists for check_change() - var/list/oldcolor - var/list/olddecals - - //passed to the ui to render the color lists - var/list/colorcontainer - var/list/possibledecals - - var/can_label = 1 - var/filled = 0.5 - pressure_resistance = 7 * ONE_ATMOSPHERE - var/temperature_resistance = 1000 + T0C - volume = 1000 - use_power = NO_POWER_USE - interact_offline = 1 - var/release_log = "" - var/update_flag = 0 - -/obj/machinery/portable_atmospherics/canister/New() - ..() - canister_color = list( - "prim" = "yellow", - "sec" = "none", - "ter" = "none", - "quart" = "none") - oldcolor = new /list() - decals = list("cold" = 0, "hot" = 0, "plasma" = 0) - colorcontainer = list() - possibledecals = list() - update_icon() - -/obj/machinery/portable_atmospherics/canister/proc/init_data_vars() - //passed to the ui to render the color lists - colorcontainer = list( - "prim" = list( - "options" = canister_icon_container.possiblemaincolor, - "name" = "Primary color", - ), - "sec" = list( - "options" = canister_icon_container.possibleseccolor, - "name" = "Secondary color", - ), - "ter" = list( - "options" = canister_icon_container.possibletertcolor, - "name" = "Tertiary color", - ), - "quart" = list( - "options" = canister_icon_container.possiblequartcolor, - "name" = "Quaternary color", - ) - ) - - //var/anycolor used by the nanoUI, 0: no color applied. 1: color applied - for(var/C in colorcontainer) - if(C == "prim") continue - var/list/L = colorcontainer[C] - if(!(canister_color[C]) || (canister_color[C] == "none")) - L.Add(list("anycolor" = 0)) - else - L.Add(list("anycolor" = 1)) - colorcontainer[C] = L - - possibledecals = list() - - var/i - var/list/L = canister_icon_container.possibledecals - for(i=1;i<=L.len;i++) - var/list/LL = L[i] - LL = LL.Copy() //make sure we don't edit the datum list - LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI - possibledecals.Add(LL) - -/obj/machinery/portable_atmospherics/canister/proc/check_change() - var/old_flag = update_flag - update_flag = 0 - if(holding) - update_flag |= 1 - if(connected_port) - update_flag |= 2 - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < 10) - update_flag |= 4 - else if(tank_pressure < ONE_ATMOSPHERE) - update_flag |= 8 - else if(tank_pressure < 15*ONE_ATMOSPHERE) - update_flag |= 16 - else - update_flag |= 32 - - if(list2params(oldcolor) != list2params(canister_color)) - update_flag |= 64 - oldcolor = canister_color.Copy() - - if(list2params(olddecals) != list2params(decals)) - update_flag |= 128 - olddecals = decals.Copy() - - if(update_flag == old_flag) - return 1 - else - return 0 - -/obj/machinery/portable_atmospherics/canister/update_icon() -/* -update_flag -1 = holding -2 = connected_port -4 = tank_pressure < 10 -8 = tank_pressure < ONE_ATMOS -16 = tank_pressure < 15*ONE_ATMOS -32 = tank_pressure go boom. -64 = colors -128 = decals -(note: colors and decals has to be applied every icon update) -*/ - - if(src.destroyed) - src.overlays = 0 - src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. - return - - if(icon_state != src.canister_color["prim"]) - icon_state = src.canister_color["prim"] - - if(check_change()) //Returns 1 if no change needed to icons. - return - - overlays.Cut() - - for(var/C in canister_color) - if(C == "prim") continue - if(canister_color[C] == "none") continue - overlays.Add(canister_color[C]) - - for(var/D in decals) - if(decals[D]) - overlays.Add("decal-" + D) - - if(update_flag & 1) - overlays += "can-open" - if(update_flag & 2) - overlays += "can-connector" - if(update_flag & 4) - overlays += "can-o0" - if(update_flag & 8) - overlays += "can-o1" - else if(update_flag & 16) - overlays += "can-o2" - else if(update_flag & 32) - overlays += "can-o3" - - update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go. - return - -//template modification exploit prevention, used in Topic() -/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all") - if(checkColor == "prim" || checkColor == "all") - for(var/list/L in canister_icon_container.possiblemaincolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "sec" || checkColor == "all") - for(var/list/L in canister_icon_container.possibleseccolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "ter" || checkColor == "all") - for(var/list/L in canister_icon_container.possibletertcolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "quart" || checkColor == "all") - for(var/list/L in canister_icon_container.possiblequartcolor) - if(L["icon"] == inputVar) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar) - for(var/list/L in canister_icon_container.possibledecals) - if(L["icon"] == inputVar) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > temperature_resistance) - take_damage(5, BURN, 0) - -/obj/machinery/portable_atmospherics/canister/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!(stat & BROKEN)) - canister_break() - if(disassembled) - new /obj/item/stack/sheet/metal (loc, 10) - else - new /obj/item/stack/sheet/metal (loc, 5) - qdel(src) - -/obj/machinery/portable_atmospherics/canister/obj_break(damage_flag) - if((stat & BROKEN) || (flags & NODECONSTRUCT)) - return - canister_break() - -/obj/machinery/portable_atmospherics/canister/proc/canister_break() - disconnect() - var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) - var/turf/T = get_turf(src) - T.assume_air(expelled_gas) - air_update_turf() - - stat |= BROKEN - density = FALSE - playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) - update_icon() - - if(holding) - holding.forceMove(T) - holding = null - -/obj/machinery/portable_atmospherics/canister/process_atmos() - if(destroyed) - return - - ..() - - if(valve_open) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/env_pressure = environment.return_pressure() - var/pressure_delta = min(release_pressure - env_pressure, (air_contents.return_pressure() - env_pressure)/2) - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - - if(holding) - environment.merge(removed) - else - loc.assume_air(removed) - air_update_turf() - src.update_icon() - - - if(air_contents.return_pressure() < 1) - can_label = 1 - else - can_label = 0 - - src.updateDialog() - return - -/obj/machinery/portable_atmospherics/canister/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/canister/proc/return_temperature() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.temperature - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/return_pressure() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.return_pressure() - return 0 - -/obj/machinery/portable_atmospherics/canister/replace_tank(mob/living/user, close_valve) - . = ..() - if(.) - if(close_valve) - valve_open = FALSE - update_icon() - investigate_log("Valve was closed by [key_name(user)].
    ", "atmos") - else if(valve_open && holding) - investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") - -/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob) - return src.ui_interact(user) - -/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) - return src.ui_interact(user) - -/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) - if(src.destroyed) - return - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400, state = state) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - - -/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state) - init_data_vars() //set up var/colorcontainer and var/possibledecals - - // this is the data which will be sent to the ui - var/data[0] - data["name"] = name - data["menu"] = menu ? 1 : 0 - data["canLabel"] = can_label ? 1 : 0 - data["canister_color"] = canister_color - data["colorContainer"] = colorcontainer.Copy() - colorcontainer.Cut() - data["possibleDecals"] = possibledecals.Copy() - possibledecals.Cut() - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(release_pressure ? release_pressure : 0) - data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) - data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) - data["valveOpen"] = valve_open ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) - - return data - -/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["choice"] == "menu") - menu = text2num(href_list["mode_target"]) - - if(href_list["toggle"]) - var/logmsg - if(valve_open) - if(holding) - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " - else - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
    " - else - if(holding) - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
    " - else - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
    " - if(air_contents.toxins > 0) - message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") - var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases - if(sleeping_agent && (sleeping_agent.moles > 1)) - message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") - investigate_log(logmsg, "atmos") - release_log += logmsg - valve_open = !valve_open - - if(href_list["remove_tank"]) - if(holding) - if(valve_open) - valve_open = 0 - release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " - holding.loc = loc - holding = null - - if(href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - if(diff > 0) - release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff) - else - release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff) - - if(href_list["rename"]) - if(can_label) - var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN)) - if(can_label) //Exploit prevention - if(T) - name = T - else - name = "canister" - else - to_chat(usr, "As you attempted to rename it the pressure rose!") - - if(href_list["choice"] == "Primary color") - if(is_a_color(href_list["icon"],"prim")) - canister_color["prim"] = href_list["icon"] - if(href_list["choice"] == "Secondary color") - if(href_list["icon"] == "none") - canister_color["sec"] = "none" - else if(is_a_color(href_list["icon"],"sec")) - canister_color["sec"] = href_list["icon"] - if(href_list["choice"] == "Tertiary color") - if(href_list["icon"] == "none") - canister_color["ter"] = "none" - else if(is_a_color(href_list["icon"],"ter")) - canister_color["ter"] = href_list["icon"] - if(href_list["choice"] == "Quaternary color") - if(href_list["icon"] == "none") - canister_color["quart"] = "none" - else if(is_a_color(href_list["icon"],"quart")) - canister_color["quart"] = href_list["icon"] - - if(href_list["choice"] == "decals") - if(is_a_decal(href_list["icon"])) - decals[href_list["icon"]] = (decals[href_list["icon"]] == 0) - - src.add_fingerprint(usr) - update_icon() - - return 1 - - -/obj/machinery/portable_atmospherics/canister/toxins - name = "Canister \[Toxin (Plasma)\]" - icon_state = "orange" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/oxygen - name = "Canister: \[O2\]" - icon_state = "blue" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/sleeping_agent - name = "Canister: \[N2O\]" - icon_state = "redws" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/nitrogen - name = "Canister: \[N2\]" - icon_state = "red" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/carbon_dioxide - name = "Canister \[CO2\]" - icon_state = "black" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/air - name = "Canister \[Air\]" - icon_state = "grey" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/custom_mix - name = "Canister \[Custom\]" - icon_state = "whiters" //See New() - can_label = 0 - - -/obj/machinery/portable_atmospherics/canister/toxins/New() - ..() - - canister_color["prim"] = "orange" - decals["plasma"] = 1 - src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/oxygen/New() - ..() - - canister_color["prim"] = "blue" - src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/sleeping_agent/New() - ..() - - canister_color["prim"] = "redws" - var/datum/gas/sleeping_agent/trace_gas = new - air_contents.trace_gases += trace_gas - trace_gas.moles = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - - -//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 -/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New() - ..() - var/datum/gas/sleeping_agent/trace_gas = air_contents.trace_gases[1] - trace_gas.moles = 9*4000 - spawn(100) - var/turf/simulated/location = src.loc - if(istype(src.loc)) - while(!location.air) - sleep(1000) - location.assume_air(air_contents) - air_contents = new - return 1 - - -/obj/machinery/portable_atmospherics/canister/nitrogen/New() - ..() - - canister_color["prim"] = "red" - src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() - ..() - - canister_color["prim"] = "black" - src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - - -/obj/machinery/portable_atmospherics/canister/air/New() - ..() - - canister_color["prim"] = "grey" - src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/custom_mix/New() - ..() - - canister_color["prim"] = "whiters" - src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD - return 1 - -/obj/machinery/portable_atmospherics/canister/welder_act(mob/user, obj/item/I) - if(!(stat & BROKEN)) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_SLICING_MESSAGE - if(I.use_tool(src, user, 50, volume = I.tool_volume)) - to_chat(user, "You salvage whats left of [src]!") - new /obj/item/stack/sheet/metal(drop_location(), 3) - qdel(src) +/datum/canister_icons + var + possiblemaincolor = list( //these lists contain the possible colors of a canister + list("name" = "\[N2O\]", "icon" = "redws"), + list("name" = "\[N2\]", "icon" = "red"), + list("name" = "\[O2\]", "icon" = "blue"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange"), + list("name" = "\[CO2\]", "icon" = "black"), + list("name" = "\[Air\]", "icon" = "grey"), + list("name" = "\[CAUTION\]", "icon" = "yellow"), + list("name" = "\[SPECIAL\]", "icon" = "whiters") + ) + possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists + list("name" = "\[N2\]", "icon" = "red-c"), + list("name" = "\[O2\]", "icon" = "blue-c"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"), + list("name" = "\[CO2\]", "icon" = "black-c"), + list("name" = "\[Air\]", "icon" = "grey-c"), + list("name" = "\[CAUTION\]", "icon" = "yellow-c") + ) + possibletertcolor = list( + list("name" = "\[N2\]", "icon" = "red-c-1"), + list("name" = "\[O2\]", "icon" = "blue-c-1"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"), + list("name" = "\[CO2\]", "icon" = "black-c-1"), + list("name" = "\[Air\]", "icon" = "grey-c-1"), + list("name" = "\[CAUTION\]", "icon" = "yellow-c-1") + ) + possiblequartcolor = list( + list("name" = "\[N2\]", "icon" = "red-c-2"), + list("name" = "\[O2\]", "icon" = "blue-c-2"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"), + list("name" = "\[CO2\]", "icon" = "black-c-2"), + list("name" = "\[Air\]", "icon" = "grey-c-2"), + list("name" = "\[CAUTION\]", "icon" = "yellow-c-2") + ) + + possibledecals = list( //var that stores all possible decals, used by ui + list("name" = "Low temperature canister", "icon" = "cold"), + list("name" = "High temperature canister", "icon" = "hot"), + list("name" = "Plasma containing canister", "icon" = "plasma") + ) +GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) + +/obj/machinery/portable_atmospherics/canister + name = "canister" + icon = 'icons/obj/atmos.dmi' + icon_state = "yellow" + density = 1 + flags = CONDUCT + armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 50) + max_integrity = 250 + integrity_failure = 100 + + var/menu = 0 + //used by nanoui: 0 = main menu, 1 = relabel + + var/valve_open = 0 + var/release_pressure = ONE_ATMOSPHERE + + var/list/canister_color //variable that stores colours + var/list/decals //list that stores the decals + + //lists for check_change() + var/list/oldcolor + var/list/olddecals + + //passed to the ui to render the color lists + var/list/colorcontainer + var/list/possibledecals + + var/can_label = 1 + var/filled = 0.5 + pressure_resistance = 7 * ONE_ATMOSPHERE + var/temperature_resistance = 1000 + T0C + volume = 1000 + use_power = NO_POWER_USE + interact_offline = 1 + var/release_log = "" + var/update_flag = 0 + +/obj/machinery/portable_atmospherics/canister/New() + ..() + canister_color = list( + "prim" = "yellow", + "sec" = "none", + "ter" = "none", + "quart" = "none") + oldcolor = new /list() + decals = list("cold" = 0, "hot" = 0, "plasma" = 0) + colorcontainer = list() + possibledecals = list() + update_icon() + +/obj/machinery/portable_atmospherics/canister/proc/init_data_vars() + //passed to the ui to render the color lists + colorcontainer = list( + "prim" = list( + "options" = GLOB.canister_icon_container.possiblemaincolor, + "name" = "Primary color", + ), + "sec" = list( + "options" = GLOB.canister_icon_container.possibleseccolor, + "name" = "Secondary color", + ), + "ter" = list( + "options" = GLOB.canister_icon_container.possibletertcolor, + "name" = "Tertiary color", + ), + "quart" = list( + "options" = GLOB.canister_icon_container.possiblequartcolor, + "name" = "Quaternary color", + ) + ) + + //var/anycolor used by the nanoUI, 0: no color applied. 1: color applied + for(var/C in colorcontainer) + if(C == "prim") continue + var/list/L = colorcontainer[C] + if(!(canister_color[C]) || (canister_color[C] == "none")) + L.Add(list("anycolor" = 0)) + else + L.Add(list("anycolor" = 1)) + colorcontainer[C] = L + + possibledecals = list() + + var/i + var/list/L = GLOB.canister_icon_container.possibledecals + for(i=1;i<=L.len;i++) + var/list/LL = L[i] + LL = LL.Copy() //make sure we don't edit the datum list + LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI + possibledecals.Add(LL) + +/obj/machinery/portable_atmospherics/canister/proc/check_change() + var/old_flag = update_flag + update_flag = 0 + if(holding) + update_flag |= 1 + if(connected_port) + update_flag |= 2 + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < 10) + update_flag |= 4 + else if(tank_pressure < ONE_ATMOSPHERE) + update_flag |= 8 + else if(tank_pressure < 15*ONE_ATMOSPHERE) + update_flag |= 16 + else + update_flag |= 32 + + if(list2params(oldcolor) != list2params(canister_color)) + update_flag |= 64 + oldcolor = canister_color.Copy() + + if(list2params(olddecals) != list2params(decals)) + update_flag |= 128 + olddecals = decals.Copy() + + if(update_flag == old_flag) + return 1 + else + return 0 + +/obj/machinery/portable_atmospherics/canister/update_icon() +/* +update_flag +1 = holding +2 = connected_port +4 = tank_pressure < 10 +8 = tank_pressure < ONE_ATMOS +16 = tank_pressure < 15*ONE_ATMOS +32 = tank_pressure go boom. +64 = colors +128 = decals +(note: colors and decals has to be applied every icon update) +*/ + + if(src.destroyed) + src.overlays = 0 + src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. + return + + if(icon_state != src.canister_color["prim"]) + icon_state = src.canister_color["prim"] + + if(check_change()) //Returns 1 if no change needed to icons. + return + + overlays.Cut() + + for(var/C in canister_color) + if(C == "prim") continue + if(canister_color[C] == "none") continue + overlays.Add(canister_color[C]) + + for(var/D in decals) + if(decals[D]) + overlays.Add("decal-" + D) + + if(update_flag & 1) + overlays += "can-open" + if(update_flag & 2) + overlays += "can-connector" + if(update_flag & 4) + overlays += "can-o0" + if(update_flag & 8) + overlays += "can-o1" + else if(update_flag & 16) + overlays += "can-o2" + else if(update_flag & 32) + overlays += "can-o3" + + update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go. + return + +//template modification exploit prevention, used in Topic() +/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all") + if(checkColor == "prim" || checkColor == "all") + for(var/list/L in GLOB.canister_icon_container.possiblemaincolor) + if(L["icon"] == inputVar) + return 1 + if(checkColor == "sec" || checkColor == "all") + for(var/list/L in GLOB.canister_icon_container.possibleseccolor) + if(L["icon"] == inputVar) + return 1 + if(checkColor == "ter" || checkColor == "all") + for(var/list/L in GLOB.canister_icon_container.possibletertcolor) + if(L["icon"] == inputVar) + return 1 + if(checkColor == "quart" || checkColor == "all") + for(var/list/L in GLOB.canister_icon_container.possiblequartcolor) + if(L["icon"] == inputVar) + return 1 + return 0 + +/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar) + for(var/list/L in GLOB.canister_icon_container.possibledecals) + if(L["icon"] == inputVar) + return 1 + return 0 + +/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > temperature_resistance) + take_damage(5, BURN, 0) + +/obj/machinery/portable_atmospherics/canister/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!(stat & BROKEN)) + canister_break() + if(disassembled) + new /obj/item/stack/sheet/metal (loc, 10) + else + new /obj/item/stack/sheet/metal (loc, 5) + qdel(src) + +/obj/machinery/portable_atmospherics/canister/obj_break(damage_flag) + if((stat & BROKEN) || (flags & NODECONSTRUCT)) + return + canister_break() + +/obj/machinery/portable_atmospherics/canister/proc/canister_break() + disconnect() + var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) + var/turf/T = get_turf(src) + T.assume_air(expelled_gas) + air_update_turf() + + stat |= BROKEN + density = FALSE + playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) + update_icon() + + if(holding) + holding.forceMove(T) + holding = null + +/obj/machinery/portable_atmospherics/canister/process_atmos() + if(destroyed) + return + + ..() + + if(valve_open) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/env_pressure = environment.return_pressure() + var/pressure_delta = min(release_pressure - env_pressure, (air_contents.return_pressure() - env_pressure)/2) + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if((air_contents.temperature > 0) && (pressure_delta > 0)) + transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + + if(holding) + environment.merge(removed) + else + loc.assume_air(removed) + air_update_turf() + src.update_icon() + + + if(air_contents.return_pressure() < 1) + can_label = 1 + else + can_label = 0 + + src.updateDialog() + return + +/obj/machinery/portable_atmospherics/canister/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/canister/proc/return_temperature() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.temperature + return 0 + +/obj/machinery/portable_atmospherics/canister/proc/return_pressure() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.return_pressure() + return 0 + +/obj/machinery/portable_atmospherics/canister/replace_tank(mob/living/user, close_valve) + . = ..() + if(.) + if(close_valve) + valve_open = FALSE + update_icon() + investigate_log("Valve was closed by [key_name(user)].
    ", "atmos") + else if(valve_open && holding) + investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") + +/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob) + return src.ui_interact(user) + +/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) + return src.ui_interact(user) + +/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) + if(src.destroyed) + return + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400, state = state) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + + +/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state) + init_data_vars() //set up var/colorcontainer and var/possibledecals + + // this is the data which will be sent to the ui + var/data[0] + data["name"] = name + data["menu"] = menu ? 1 : 0 + data["canLabel"] = can_label ? 1 : 0 + data["canister_color"] = canister_color + data["colorContainer"] = colorcontainer.Copy() + colorcontainer.Cut() + data["possibleDecals"] = possibledecals.Copy() + possibledecals.Cut() + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(release_pressure ? release_pressure : 0) + data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) + data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) + data["valveOpen"] = valve_open ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if(holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) + + return data + +/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["choice"] == "menu") + menu = text2num(href_list["mode_target"]) + + if(href_list["toggle"]) + var/logmsg + if(valve_open) + if(holding) + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " + else + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
    " + else + if(holding) + logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
    " + else + logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
    " + if(air_contents.toxins > 0) + message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") + var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases + if(sleeping_agent && (sleeping_agent.moles > 1)) + message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") + investigate_log(logmsg, "atmos") + release_log += logmsg + valve_open = !valve_open + + if(href_list["remove_tank"]) + if(holding) + if(valve_open) + valve_open = 0 + release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " + holding.loc = loc + holding = null + + if(href_list["pressure_adj"]) + var/diff = text2num(href_list["pressure_adj"]) + if(diff > 0) + release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff) + else + release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff) + + if(href_list["rename"]) + if(can_label) + var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN)) + if(can_label) //Exploit prevention + if(T) + name = T + else + name = "canister" + else + to_chat(usr, "As you attempted to rename it the pressure rose!") + + if(href_list["choice"] == "Primary color") + if(is_a_color(href_list["icon"],"prim")) + canister_color["prim"] = href_list["icon"] + if(href_list["choice"] == "Secondary color") + if(href_list["icon"] == "none") + canister_color["sec"] = "none" + else if(is_a_color(href_list["icon"],"sec")) + canister_color["sec"] = href_list["icon"] + if(href_list["choice"] == "Tertiary color") + if(href_list["icon"] == "none") + canister_color["ter"] = "none" + else if(is_a_color(href_list["icon"],"ter")) + canister_color["ter"] = href_list["icon"] + if(href_list["choice"] == "Quaternary color") + if(href_list["icon"] == "none") + canister_color["quart"] = "none" + else if(is_a_color(href_list["icon"],"quart")) + canister_color["quart"] = href_list["icon"] + + if(href_list["choice"] == "decals") + if(is_a_decal(href_list["icon"])) + decals[href_list["icon"]] = (decals[href_list["icon"]] == 0) + + src.add_fingerprint(usr) + update_icon() + + return 1 + + +/obj/machinery/portable_atmospherics/canister/toxins + name = "Canister \[Toxin (Plasma)\]" + icon_state = "orange" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/oxygen + name = "Canister: \[O2\]" + icon_state = "blue" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/sleeping_agent + name = "Canister: \[N2O\]" + icon_state = "redws" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/nitrogen + name = "Canister: \[N2\]" + icon_state = "red" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/carbon_dioxide + name = "Canister \[CO2\]" + icon_state = "black" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/air + name = "Canister \[Air\]" + icon_state = "grey" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/custom_mix + name = "Canister \[Custom\]" + icon_state = "whiters" //See New() + can_label = 0 + + +/obj/machinery/portable_atmospherics/canister/toxins/New() + ..() + + canister_color["prim"] = "orange" + decals["plasma"] = 1 + src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/oxygen/New() + ..() + + canister_color["prim"] = "blue" + src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/sleeping_agent/New() + ..() + + canister_color["prim"] = "redws" + var/datum/gas/sleeping_agent/trace_gas = new + air_contents.trace_gases += trace_gas + trace_gas.moles = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + + +//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 +/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New() + ..() + var/datum/gas/sleeping_agent/trace_gas = air_contents.trace_gases[1] + trace_gas.moles = 9*4000 + spawn(100) + var/turf/simulated/location = src.loc + if(istype(src.loc)) + while(!location.air) + sleep(1000) + location.assume_air(air_contents) + air_contents = new + return 1 + + +/obj/machinery/portable_atmospherics/canister/nitrogen/New() + ..() + + canister_color["prim"] = "red" + src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() + ..() + + canister_color["prim"] = "black" + src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + + +/obj/machinery/portable_atmospherics/canister/air/New() + ..() + + canister_color["prim"] = "grey" + src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/custom_mix/New() + ..() + + canister_color["prim"] = "whiters" + src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD + return 1 + +/obj/machinery/portable_atmospherics/canister/welder_act(mob/user, obj/item/I) + if(!(stat & BROKEN)) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + to_chat(user, "You salvage whats left of [src]!") + new /obj/item/stack/sheet/metal(drop_location(), 3) + qdel(src) diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index 90601f61bfb..a20b47b30db 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -1,174 +1,174 @@ -/obj/machinery/meter - name = "gas flow meter" - desc = "It measures something." - icon = 'icons/obj/meter.dmi' - icon_state = "meterX" - - layer = GAS_PUMP_LAYER - - var/obj/machinery/atmospherics/pipe/target = null - anchored = TRUE - max_integrity = 150 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 0) - power_channel = ENVIRON - var/frequency = ATMOS_DISTRO_FREQ - var/id - var/id_tag - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 5 - req_one_access_txt = "24;10" - Mtoollink = TRUE - settagwhitelist = list("id_tag") - -/obj/machinery/meter/New() - ..() - SSair.atmos_machinery += src - target = locate(/obj/machinery/atmospherics/pipe) in loc - if(id && !id_tag)//i'm not dealing with further merge conflicts, fuck it - id_tag = id - return 1 - -/obj/machinery/meter/Destroy() - SSair.atmos_machinery -= src - target = null - return ..() - -/obj/machinery/meter/Initialize() - ..() - if(!target) - target = locate(/obj/machinery/atmospherics/pipe) in loc - -/obj/machinery/meter/process_atmos() - if(!target) - icon_state = "meterX" - return 0 - - if(stat & (BROKEN|NOPOWER)) - icon_state = "meter0" - return 0 - - var/datum/gas_mixture/environment = target.return_air() - if(!environment) - icon_state = "meterX" - return 0 - - var/env_pressure = environment.return_pressure() - if(env_pressure <= 0.15*ONE_ATMOSPHERE) - icon_state = "meter0" - else if(env_pressure <= 1.8*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) - icon_state = "meter1_[val]" - else if(env_pressure <= 30*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 - icon_state = "meter2_[val]" - else if(env_pressure <= 59*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 - icon_state = "meter3_[val]" - else - icon_state = "meter4" - - if(frequency) - var/datum/radio_frequency/radio_connection = SSradio.return_frequency(frequency) - - if(!radio_connection) return - - var/datum/signal/signal = new - signal.source = src - signal.transmission_method = 1 - signal.data = list( - "tag" = id_tag, - "device" = "AM", - "pressure" = round(env_pressure), - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal) - -/obj/machinery/meter/proc/status() - var/t = "" - if(target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]°K ([round(environment.temperature-T0C,0.01)]°C)" - else - t += "The sensor error light is blinking." - else - t += "The connect error light is blinking." - return t - -/obj/machinery/meter/examine(mob/user) - var/t = "A gas flow meter. " - - if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/dead))) - t += "You are too far away to read it." - - else if(stat & (NOPOWER|BROKEN)) - t += "The display is off." - - else if(target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" - else - t += "The sensor error light is blinking." - else - t += "The connect error light is blinking." - - . = list(t) - -/obj/machinery/meter/Click() - if(istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine - usr.examinate(src) - return 1 - - return ..() - -/obj/machinery/meter/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(!istype(W, /obj/item/wrench)) - return ..() - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if(do_after(user, 40 * W.toolspeed, target = src)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - deconstruct(TRUE) - -/obj/machinery/meter/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/pipe_meter(loc) - qdel(src) - -/obj/machinery/meter/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct() - -// TURF METER - REPORTS A TILE'S AIR CONTENTS - -/obj/machinery/meter/turf/New() - ..() - target = loc - return 1 - - -/obj/machinery/meter/turf/Initialize() - if(!target) - target = loc - ..() - -/obj/machinery/meter/turf/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - return - -/obj/machinery/meter/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" - Main - "} +/obj/machinery/meter + name = "gas flow meter" + desc = "It measures something." + icon = 'icons/obj/meter.dmi' + icon_state = "meterX" + + layer = GAS_PUMP_LAYER + + var/obj/machinery/atmospherics/pipe/target = null + anchored = TRUE + max_integrity = 150 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 0) + power_channel = ENVIRON + var/frequency = ATMOS_DISTRO_FREQ + var/id + var/id_tag + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 5 + req_one_access_txt = "24;10" + Mtoollink = TRUE + settagwhitelist = list("id_tag") + +/obj/machinery/meter/New() + ..() + SSair.atmos_machinery += src + target = locate(/obj/machinery/atmospherics/pipe) in loc + if(id && !id_tag)//i'm not dealing with further merge conflicts, fuck it + id_tag = id + return 1 + +/obj/machinery/meter/Destroy() + SSair.atmos_machinery -= src + target = null + return ..() + +/obj/machinery/meter/Initialize() + ..() + if(!target) + target = locate(/obj/machinery/atmospherics/pipe) in loc + +/obj/machinery/meter/process_atmos() + if(!target) + icon_state = "meterX" + return 0 + + if(stat & (BROKEN|NOPOWER)) + icon_state = "meter0" + return 0 + + var/datum/gas_mixture/environment = target.return_air() + if(!environment) + icon_state = "meterX" + return 0 + + var/env_pressure = environment.return_pressure() + if(env_pressure <= 0.15*ONE_ATMOSPHERE) + icon_state = "meter0" + else if(env_pressure <= 1.8*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) + icon_state = "meter1_[val]" + else if(env_pressure <= 30*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 + icon_state = "meter2_[val]" + else if(env_pressure <= 59*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 + icon_state = "meter3_[val]" + else + icon_state = "meter4" + + if(frequency) + var/datum/radio_frequency/radio_connection = SSradio.return_frequency(frequency) + + if(!radio_connection) return + + var/datum/signal/signal = new + signal.source = src + signal.transmission_method = 1 + signal.data = list( + "tag" = id_tag, + "device" = "AM", + "pressure" = round(env_pressure), + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal) + +/obj/machinery/meter/proc/status() + var/t = "" + if(target) + var/datum/gas_mixture/environment = target.return_air() + if(environment) + t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]°K ([round(environment.temperature-T0C,0.01)]°C)" + else + t += "The sensor error light is blinking." + else + t += "The connect error light is blinking." + return t + +/obj/machinery/meter/examine(mob/user) + var/t = "A gas flow meter. " + + if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/dead))) + t += "You are too far away to read it." + + else if(stat & (NOPOWER|BROKEN)) + t += "The display is off." + + else if(target) + var/datum/gas_mixture/environment = target.return_air() + if(environment) + t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" + else + t += "The sensor error light is blinking." + else + t += "The connect error light is blinking." + + . = list(t) + +/obj/machinery/meter/Click() + if(istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine + usr.examinate(src) + return 1 + + return ..() + +/obj/machinery/meter/attackby(var/obj/item/W as obj, var/mob/user as mob, params) + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(!istype(W, /obj/item/wrench)) + return ..() + playsound(loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if(do_after(user, 40 * W.toolspeed, target = src)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + deconstruct(TRUE) + +/obj/machinery/meter/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/pipe_meter(loc) + qdel(src) + +/obj/machinery/meter/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct() + +// TURF METER - REPORTS A TILE'S AIR CONTENTS + +/obj/machinery/meter/turf/New() + ..() + target = loc + return 1 + + +/obj/machinery/meter/turf/Initialize() + if(!target) + target = loc + ..() + +/obj/machinery/meter/turf/attackby(var/obj/item/W as obj, var/mob/user as mob, params) + return + +/obj/machinery/meter/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" + Main + "} diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 5008f4791ba..cec04bf4f6f 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -1,160 +1,160 @@ -/obj/machinery/portable_atmospherics - name = "atmoalter" - use_power = NO_POWER_USE - max_integrity = 250 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 60, "acid" = 30) - var/datum/gas_mixture/air_contents = new - - var/obj/machinery/atmospherics/unary/portables_connector/connected_port - var/obj/item/tank/holding - - var/volume = 0 - var/destroyed = 0 - - var/maximum_pressure = 90*ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/New() - ..() - SSair.atmos_machinery += src - - air_contents.volume = volume - air_contents.temperature = T20C - - return 1 - -/obj/machinery/portable_atmospherics/Initialize() - . = ..() - spawn() - var/obj/machinery/atmospherics/unary/portables_connector/port = locate() in loc - if(port) - connect(port) - update_icon() - -/obj/machinery/portable_atmospherics/process_atmos() - if(!connected_port) //only react when pipe_network will ont it do it for you - //Allow for reactions - air_contents.react() - else - update_icon() - -/obj/machinery/portable_atmospherics/Destroy() - SSair.atmos_machinery -= src - disconnect() - QDEL_NULL(air_contents) - QDEL_NULL(holding) - return ..() - -/obj/machinery/portable_atmospherics/update_icon() - return null - -/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !new_port || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - // To avoid a chicken-egg thing where pipes need to - // be initialized before the atmos cans are - if(!connected_port.parent) - connected_port.build_network() - connected_port.parent.reconcile_air() - - anchored = 1 //Prevent movement - - return 1 - -/obj/machinery/portable_atmospherics/proc/disconnect() - if(!connected_port) - return 0 - - anchored = 0 - - connected_port.connected_device = null - connected_port = null - - return 1 - -/obj/machinery/portable_atmospherics/portableConnectorReturnAir() - return air_contents - -/obj/machinery/portable_atmospherics/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - if(holding) - to_chat(user, "You remove [holding] from [src].") - replace_tank(user, TRUE) - -/obj/machinery/portable_atmospherics/examine(mob/user) - . = ..() - if(holding) - . += "\The [src] contains [holding]. Alt-click [src] to remove it." - -/obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank) - if(holding) - holding.forceMove(drop_location()) - if(Adjacent(user) && !issilicon(user)) - user.put_in_hands(holding) - if(new_tank) - holding = new_tank - else - holding = null - update_icon() - return TRUE - -/obj/machinery/portable_atmospherics/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/tank)) - if(!(stat & BROKEN)) - if(!user.drop_item()) - return - var/obj/item/tank/T = W - user.drop_item() - if(src.holding) - to_chat(user, "[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [T]" : "You insert [T] into [src]"].") - replace_tank(user, FALSE) - T.loc = src - src.holding = T - update_icon() - return - if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) - atmosanalyzer_scan(air_contents, user) - return - return ..() - -/obj/machinery/portable_atmospherics/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(connected_port) - disconnect() - to_chat(user, "You disconnect [name] from the port.") - update_icon() - else - var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector/) in loc - if(possible_port) - if(connect(possible_port)) - to_chat(user, "You connect [src] to the port.") - update_icon() - return - else - to_chat(user, "[src] failed to connect to the port.") - return - else - to_chat(user, "Nothing happens.") - -/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user) - if(I.force < 10 && !(stat & BROKEN)) - take_damage(0) - else - add_fingerprint(user) - ..() \ No newline at end of file +/obj/machinery/portable_atmospherics + name = "atmoalter" + use_power = NO_POWER_USE + max_integrity = 250 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 60, "acid" = 30) + var/datum/gas_mixture/air_contents = new + + var/obj/machinery/atmospherics/unary/portables_connector/connected_port + var/obj/item/tank/holding + + var/volume = 0 + var/destroyed = 0 + + var/maximum_pressure = 90*ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/New() + ..() + SSair.atmos_machinery += src + + air_contents.volume = volume + air_contents.temperature = T20C + + return 1 + +/obj/machinery/portable_atmospherics/Initialize() + . = ..() + spawn() + var/obj/machinery/atmospherics/unary/portables_connector/port = locate() in loc + if(port) + connect(port) + update_icon() + +/obj/machinery/portable_atmospherics/process_atmos() + if(!connected_port) //only react when pipe_network will ont it do it for you + //Allow for reactions + air_contents.react() + else + update_icon() + +/obj/machinery/portable_atmospherics/Destroy() + SSair.atmos_machinery -= src + disconnect() + QDEL_NULL(air_contents) + QDEL_NULL(holding) + return ..() + +/obj/machinery/portable_atmospherics/update_icon() + return null + +/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !new_port || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + // To avoid a chicken-egg thing where pipes need to + // be initialized before the atmos cans are + if(!connected_port.parent) + connected_port.build_network() + connected_port.parent.reconcile_air() + + anchored = 1 //Prevent movement + + return 1 + +/obj/machinery/portable_atmospherics/proc/disconnect() + if(!connected_port) + return 0 + + anchored = 0 + + connected_port.connected_device = null + connected_port = null + + return 1 + +/obj/machinery/portable_atmospherics/portableConnectorReturnAir() + return air_contents + +/obj/machinery/portable_atmospherics/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + if(holding) + to_chat(user, "You remove [holding] from [src].") + replace_tank(user, TRUE) + +/obj/machinery/portable_atmospherics/examine(mob/user) + . = ..() + if(holding) + . += "\The [src] contains [holding]. Alt-click [src] to remove it." + +/obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank) + if(holding) + holding.forceMove(drop_location()) + if(Adjacent(user) && !issilicon(user)) + user.put_in_hands(holding) + if(new_tank) + holding = new_tank + else + holding = null + update_icon() + return TRUE + +/obj/machinery/portable_atmospherics/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/tank)) + if(!(stat & BROKEN)) + if(!user.drop_item()) + return + var/obj/item/tank/T = W + user.drop_item() + if(src.holding) + to_chat(user, "[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [T]" : "You insert [T] into [src]"].") + replace_tank(user, FALSE) + T.loc = src + src.holding = T + update_icon() + return + if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) + atmosanalyzer_scan(air_contents, user) + return + return ..() + +/obj/machinery/portable_atmospherics/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(connected_port) + disconnect() + to_chat(user, "You disconnect [name] from the port.") + update_icon() + else + var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector/) in loc + if(possible_port) + if(connect(possible_port)) + to_chat(user, "You connect [src] to the port.") + update_icon() + return + else + to_chat(user, "[src] failed to connect to the port.") + return + else + to_chat(user, "Nothing happens.") + +/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user) + if(I.force < 10 && !(stat & BROKEN)) + take_damage(0) + else + add_fingerprint(user) + ..() diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 5bd441f6748..0144a7f93c2 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -1,173 +1,173 @@ -/obj/machinery/portable_atmospherics/pump - name = "Portable Air Pump" - - icon = 'icons/obj/atmos.dmi' - icon_state = "psiphon:0" - density = 1 - - var/on = 0 - var/direction_out = 0 //0 = siphoning, 1 = releasing - var/target_pressure = 100 - - var/pressuremin = 0 - var/pressuremax = 10 * ONE_ATMOSPHERE - - volume = 1000 - -/obj/machinery/portable_atmospherics/pump/update_icon() - src.overlays = 0 - - if(on) - icon_state = "psiphon:1" - else - icon_state = "psiphon:0" - - if(holding) - overlays += "siphon-open" - - if(connected_port) - overlays += "siphon-connector" - - return - -/obj/machinery/portable_atmospherics/pump/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - - if(prob(100/severity)) - direction_out = !direction_out - - target_pressure = rand(0,1300) - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/pump/process_atmos() - ..() - if(on) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - if(direction_out) - var/pressure_delta = target_pressure - environment.return_pressure() - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(air_contents.temperature > 0) - transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - - if(holding) - environment.merge(removed) - else - loc.assume_air(removed) - air_update_turf() - else - var/pressure_delta = target_pressure - air_contents.return_pressure() - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(environment.temperature > 0) - transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed - if(holding) - removed = environment.remove(transfer_moles) - else - removed = loc.remove_air(transfer_moles) - air_update_turf() - - air_contents.merge(removed) - //src.update_icon() - - src.updateDialog() - return - -/obj/machinery/portable_atmospherics/pump/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/pump/replace_tank(mob/living/user, close_valve) - . = ..() - if(.) - if(close_valve) - if(on) - on = FALSE - update_icon() - else if(on && holding && direction_out) - investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") - -/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/pump/attack_ghost(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 400, state = state) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) - var/data[0] - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - data["targetpressure"] = round(target_pressure) - data["pump_dir"] = direction_out - data["minpressure"] = round(pressuremin) - data["maxpressure"] = round(pressuremax) - data["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) - - return data - -/obj/machinery/portable_atmospherics/pump/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - if(on && direction_out) - investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") - update_icon() - - if(href_list["direction"]) - direction_out = !direction_out - if(on && holding) - investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") - - if(href_list["remove_tank"]) - if(holding) - on = FALSE - holding.loc = loc - holding = null - update_icon() - - if(href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - target_pressure = Clamp(target_pressure+diff, pressuremin, pressuremax) - update_icon() - - src.add_fingerprint(usr) +/obj/machinery/portable_atmospherics/pump + name = "Portable Air Pump" + + icon = 'icons/obj/atmos.dmi' + icon_state = "psiphon:0" + density = 1 + + var/on = 0 + var/direction_out = 0 //0 = siphoning, 1 = releasing + var/target_pressure = 100 + + var/pressuremin = 0 + var/pressuremax = 10 * ONE_ATMOSPHERE + + volume = 1000 + +/obj/machinery/portable_atmospherics/pump/update_icon() + src.overlays = 0 + + if(on) + icon_state = "psiphon:1" + else + icon_state = "psiphon:0" + + if(holding) + overlays += "siphon-open" + + if(connected_port) + overlays += "siphon-connector" + + return + +/obj/machinery/portable_atmospherics/pump/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + + if(prob(100/severity)) + direction_out = !direction_out + + target_pressure = rand(0,1300) + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/pump/process_atmos() + ..() + if(on) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + if(direction_out) + var/pressure_delta = target_pressure - environment.return_pressure() + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if(air_contents.temperature > 0) + transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + + if(holding) + environment.merge(removed) + else + loc.assume_air(removed) + air_update_turf() + else + var/pressure_delta = target_pressure - air_contents.return_pressure() + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if(environment.temperature > 0) + transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed + if(holding) + removed = environment.remove(transfer_moles) + else + removed = loc.remove_air(transfer_moles) + air_update_turf() + + air_contents.merge(removed) + //src.update_icon() + + src.updateDialog() + return + +/obj/machinery/portable_atmospherics/pump/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/pump/replace_tank(mob/living/user, close_valve) + . = ..() + if(.) + if(close_valve) + if(on) + on = FALSE + update_icon() + else if(on && holding && direction_out) + investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") + +/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/pump/attack_ghost(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 400, state = state) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) + var/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["targetpressure"] = round(target_pressure) + data["pump_dir"] = direction_out + data["minpressure"] = round(pressuremin) + data["maxpressure"] = round(pressuremax) + data["on"] = on ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if(holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + + return data + +/obj/machinery/portable_atmospherics/pump/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + if(on && direction_out) + investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") + update_icon() + + if(href_list["direction"]) + direction_out = !direction_out + if(on && holding) + investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") + + if(href_list["remove_tank"]) + if(holding) + on = FALSE + holding.loc = loc + holding = null + update_icon() + + if(href_list["pressure_adj"]) + var/diff = text2num(href_list["pressure_adj"]) + target_pressure = Clamp(target_pressure+diff, pressuremin, pressuremax) + update_icon() + + src.add_fingerprint(usr) diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 2079b8cf915..36e340d1a12 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -1,216 +1,216 @@ -/obj/machinery/portable_atmospherics/scrubber - name = "Portable Air Scrubber" - - icon = 'icons/obj/atmos.dmi' - icon_state = "pscrubber:0" - density = 1 - - var/on = 0 - var/volume_rate = 800 - var/widenet = 0 //is this scrubber acting on the 3x3 area around it. - - volume = 750 - - var/minrate = 0//probably useless, but whatever - var/maxrate = 10 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/scrubber/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/scrubber/update_icon() - src.overlays = 0 - - if(on) - icon_state = "pscrubber:1" - else - icon_state = "pscrubber:0" - - if(holding) - overlays += "scrubber-open" - - if(connected_port) - overlays += "scrubber-connector" - - return - -/obj/machinery/portable_atmospherics/scrubber/process_atmos() - ..() - - if(!on) - return - scrub(loc) - if(widenet) - var/turf/T = loc - if(istype(T)) - for(var/turf/simulated/tile in T.GetAtmosAdjacentTurfs(alldir=1)) - scrub(tile) - -/obj/machinery/portable_atmospherics/scrubber/proc/scrub(var/turf/simulated/tile) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = tile.return_air() - var/transfer_moles = min(1,volume_rate/environment.volume)*environment.total_moles() - - //Take a gas sample - var/datum/gas_mixture/removed - if(holding) - removed = environment.remove(transfer_moles) - else - removed = loc.remove_air(transfer_moles) - - //Filter it - if(removed) - var/datum/gas_mixture/filtered_out = new - - filtered_out.temperature = removed.temperature - - - filtered_out.toxins = removed.toxins - removed.toxins = 0 - - filtered_out.carbon_dioxide = removed.carbon_dioxide - removed.carbon_dioxide = 0 - - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/sleeping_agent)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/oxygen_agent_b)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - //Remix the resulting gases - air_contents.merge(filtered_out) - - if(holding) - environment.merge(removed) - else - tile.assume_air(removed) - tile.air_update_turf() - -/obj/machinery/portable_atmospherics/scrubber/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/scrubber/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/scrubber/attack_ghost(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/scrubber/attack_hand(var/mob/user as mob) - ui_interact(user) - return - -/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = state) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/scrubber/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) - var/data[0] - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - data["rate"] = round(volume_rate) - data["minrate"] = round(minrate) - data["maxrate"] = round(maxrate) - data["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) - - return data - -/obj/machinery/portable_atmospherics/scrubber/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - update_icon() - - if(href_list["remove_tank"]) - if(holding) - holding.loc = loc - holding = null - - if(href_list["volume_adj"]) - var/diff = text2num(href_list["volume_adj"]) - volume_rate = Clamp(volume_rate+diff, minrate, maxrate) - - src.add_fingerprint(usr) - -/obj/machinery/portable_atmospherics/scrubber/huge - name = "Huge Air Scrubber" - icon_state = "scrubber:0" - anchored = 1 - volume = 50000 - volume_rate = 5000 - widenet = 1 - - var/global/gid = 1 - var/id = 0 - var/stationary = 0 - -/obj/machinery/portable_atmospherics/scrubber/huge/New() - ..() - id = gid - gid++ - - name = "[name] (ID [id])" - -/obj/machinery/portable_atmospherics/scrubber/huge/attack_hand(var/mob/user as mob) - to_chat(usr, "You can't directly interact with this machine. Use the area atmos computer.") - -/obj/machinery/portable_atmospherics/scrubber/huge/update_icon() - src.overlays = 0 - - if(on) - icon_state = "scrubber:1" - else - icon_state = "scrubber:0" - -/obj/machinery/portable_atmospherics/scrubber/huge/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - if(istype(W, /obj/item/wrench)) - if(stationary) - to_chat(user, "The bolts are too tight for you to unscrew!") - return - if(on) - to_chat(user, "Turn it off first!") - return - - anchored = !anchored - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") - return - - if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) - atmosanalyzer_scan(air_contents, user) - return - return ..() - -/obj/machinery/portable_atmospherics/scrubber/huge/stationary - name = "Stationary Air Scrubber" - stationary = 1 +/obj/machinery/portable_atmospherics/scrubber + name = "Portable Air Scrubber" + + icon = 'icons/obj/atmos.dmi' + icon_state = "pscrubber:0" + density = 1 + + var/on = 0 + var/volume_rate = 800 + var/widenet = 0 //is this scrubber acting on the 3x3 area around it. + + volume = 750 + + var/minrate = 0//probably useless, but whatever + var/maxrate = 10 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/scrubber/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/scrubber/update_icon() + src.overlays = 0 + + if(on) + icon_state = "pscrubber:1" + else + icon_state = "pscrubber:0" + + if(holding) + overlays += "scrubber-open" + + if(connected_port) + overlays += "scrubber-connector" + + return + +/obj/machinery/portable_atmospherics/scrubber/process_atmos() + ..() + + if(!on) + return + scrub(loc) + if(widenet) + var/turf/T = loc + if(istype(T)) + for(var/turf/simulated/tile in T.GetAtmosAdjacentTurfs(alldir=1)) + scrub(tile) + +/obj/machinery/portable_atmospherics/scrubber/proc/scrub(var/turf/simulated/tile) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = tile.return_air() + var/transfer_moles = min(1,volume_rate/environment.volume)*environment.total_moles() + + //Take a gas sample + var/datum/gas_mixture/removed + if(holding) + removed = environment.remove(transfer_moles) + else + removed = loc.remove_air(transfer_moles) + + //Filter it + if(removed) + var/datum/gas_mixture/filtered_out = new + + filtered_out.temperature = removed.temperature + + + filtered_out.toxins = removed.toxins + removed.toxins = 0 + + filtered_out.carbon_dioxide = removed.carbon_dioxide + removed.carbon_dioxide = 0 + + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/sleeping_agent)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/oxygen_agent_b)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + //Remix the resulting gases + air_contents.merge(filtered_out) + + if(holding) + environment.merge(removed) + else + tile.assume_air(removed) + tile.air_update_turf() + +/obj/machinery/portable_atmospherics/scrubber/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/scrubber/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/scrubber/attack_ghost(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/scrubber/attack_hand(var/mob/user as mob) + ui_interact(user) + return + +/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = state) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/portable_atmospherics/scrubber/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) + var/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["rate"] = round(volume_rate) + data["minrate"] = round(minrate) + data["maxrate"] = round(maxrate) + data["on"] = on ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if(holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + + return data + +/obj/machinery/portable_atmospherics/scrubber/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + update_icon() + + if(href_list["remove_tank"]) + if(holding) + holding.loc = loc + holding = null + + if(href_list["volume_adj"]) + var/diff = text2num(href_list["volume_adj"]) + volume_rate = Clamp(volume_rate+diff, minrate, maxrate) + + src.add_fingerprint(usr) + +/obj/machinery/portable_atmospherics/scrubber/huge + name = "Huge Air Scrubber" + icon_state = "scrubber:0" + anchored = 1 + volume = 50000 + volume_rate = 5000 + widenet = 1 + + var/global/gid = 1 + var/id = 0 + var/stationary = 0 + +/obj/machinery/portable_atmospherics/scrubber/huge/New() + ..() + id = gid + gid++ + + name = "[name] (ID [id])" + +/obj/machinery/portable_atmospherics/scrubber/huge/attack_hand(var/mob/user as mob) + to_chat(usr, "You can't directly interact with this machine. Use the area atmos computer.") + +/obj/machinery/portable_atmospherics/scrubber/huge/update_icon() + src.overlays = 0 + + if(on) + icon_state = "scrubber:1" + else + icon_state = "scrubber:0" + +/obj/machinery/portable_atmospherics/scrubber/huge/attackby(var/obj/item/W as obj, var/mob/user as mob, params) + if(istype(W, /obj/item/wrench)) + if(stationary) + to_chat(user, "The bolts are too tight for you to unscrew!") + return + if(on) + to_chat(user, "Turn it off first!") + return + + anchored = !anchored + playsound(loc, W.usesound, 50, 1) + to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") + return + + if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) + atmosanalyzer_scan(air_contents, user) + return + return ..() + +/obj/machinery/portable_atmospherics/scrubber/huge/stationary + name = "Stationary Air Scrubber" + stationary = 1 diff --git a/code/game/machinery/atmoalter/zvent.dm b/code/game/machinery/atmoalter/zvent.dm index 18af424c4d6..773432133e8 100644 --- a/code/game/machinery/atmoalter/zvent.dm +++ b/code/game/machinery/atmoalter/zvent.dm @@ -1,35 +1,35 @@ -/obj/machinery/zvent - name = "Interfloor Air Transfer System" - - icon = 'icons/obj/pipes.dmi' - icon_state = "vent-db" - density = 0 - anchored=1 - - var/on = 0 - var/volume_rate = 800 - -/obj/machinery/zvent/New() - ..() - SSair.atmos_machinery += src - -/obj/machinery/zvent/Destroy() - SSair.atmos_machinery -= src - return ..() - -/obj/machinery/zvent/process_atmos() - - //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. - if(istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it - for(var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented - var/turf/simulated/zturf_conn = locate(x,y,z+new_z) - if(istype(zturf_conn)) - var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn - if(istype(zvent_conn)) - //both floors have simulated turfs, share() - var/turf/simulated/myturf = loc - var/datum/gas_mixture/conn_air = zturf_conn.air //TODO: pop culture reference - var/datum/gas_mixture/my_air = myturf.air - if(istype(conn_air) && istype(my_air)) - my_air.share(conn_air) - air_update_turf() +/obj/machinery/zvent + name = "Interfloor Air Transfer System" + + icon = 'icons/obj/pipes.dmi' + icon_state = "vent-db" + density = 0 + anchored=1 + + var/on = 0 + var/volume_rate = 800 + +/obj/machinery/zvent/New() + ..() + SSair.atmos_machinery += src + +/obj/machinery/zvent/Destroy() + SSair.atmos_machinery -= src + return ..() + +/obj/machinery/zvent/process_atmos() + + //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. + if(istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it + for(var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented + var/turf/simulated/zturf_conn = locate(x,y,z+new_z) + if(istype(zturf_conn)) + var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn + if(istype(zvent_conn)) + //both floors have simulated turfs, share() + var/turf/simulated/myturf = loc + var/datum/gas_mixture/conn_air = zturf_conn.air //TODO: pop culture reference + var/datum/gas_mixture/my_air = myturf.air + if(istype(conn_air) && istype(my_air)) + my_air.share(conn_air) + air_update_turf() diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 52ee48f067b..78367327a0c 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -1,469 +1,469 @@ -#define AUTOLATHE_MAIN_MENU 1 -#define AUTOLATHE_CATEGORY_MENU 2 -#define AUTOLATHE_SEARCH_MENU 3 - -/obj/machinery/autolathe - name = "autolathe" - desc = "It produces items using metal and glass." - icon_state = "autolathe" - density = 1 - - var/operating = 0.0 - var/list/queue = list() - var/queue_max_len = 12 - var/turf/BuildTurf - anchored = 1.0 - var/list/L = list() - var/list/LL = list() - var/hacked = 0 - var/disabled = 0 - var/shocked = 0 - var/hack_wire - var/disable_wire - var/shock_wire - use_power = IDLE_POWER_USE - idle_power_usage = 10 - active_power_usage = 100 - var/busy = 0 - var/prod_coeff - var/datum/wires/autolathe/wires = null - - var/list/being_built = list() - var/datum/research/files - var/list/datum/design/matching_designs - var/temp_search - var/selected_category - var/screen = 1 - - var/list/categories = list("Tools", "Electronics", "Construction", "Communication", "Security", "Machinery", "Medical", "Miscellaneous", "Dinnerware", "Imported") - -/obj/machinery/autolathe/New() - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert)) - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/autolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - RefreshParts() - - wires = new(src) - files = new /datum/research/autolathe(src) - matching_designs = list() - -/obj/machinery/autolathe/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/autolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stack/sheet/glass(null) - RefreshParts() - -/obj/machinery/autolathe/Destroy() - QDEL_NULL(wires) - GET_COMPONENT(materials, /datum/component/material_container) - materials.retrieve_all() - return ..() - -/obj/machinery/autolathe/interact(mob/user) - if(shocked && !(stat & NOPOWER)) - if(shock(user, 50)) - return - - if(panel_open) - wires.Interact(user) - else - ui_interact(user) - -/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550) - ui.open() - -/obj/machinery/autolathe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - GET_COMPONENT(materials, /datum/component/material_container) - var/data[0] - data["screen"] = screen - data["total_amount"] = materials.total_amount - data["max_amount"] = materials.max_amount - data["metal_amount"] = materials.amount(MAT_METAL) - data["glass_amount"] = materials.amount(MAT_GLASS) - switch(screen) - if(AUTOLATHE_MAIN_MENU) - data["uid"] = UID() - data["categories"] = categories - if(AUTOLATHE_CATEGORY_MENU) - data["selected_category"] = selected_category - var/list/designs = list() - data["designs"] = designs - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(!(selected_category in D.category)) - continue - var/list/design = list() - designs[++designs.len] = design - design["name"] = D.name - design["id"] = D.id - design["disabled"] = disabled || !can_build(D) ? "disabled" : null - if(ispath(D.build_path, /obj/item/stack)) - design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) - else - design["max_multiplier"] = null - design["materials"] = design_cost_data(D) - if(AUTOLATHE_SEARCH_MENU) - data["search"] = temp_search - var/list/designs = list() - data["designs"] = designs - for(var/datum/design/D in matching_designs) - var/list/design = list() - designs[++designs.len] = design - design["name"] = D.name - design["id"] = D.id - design["disabled"] = disabled || !can_build(D) ? "disabled" : null - if(ispath(D.build_path, /obj/item/stack)) - design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) - else - design["max_multiplier"] = null - design["materials"] = design_cost_data(D) - - data = queue_data(data) - return data - -/obj/machinery/autolathe/proc/design_cost_data(datum/design/D) - var/list/data = list() - var/coeff = get_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) - var/has_metal = 1 - if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] / coeff))) - has_metal = 0 - var/has_glass = 1 - if(D.materials[MAT_GLASS] && (materials.amount(MAT_GLASS) < (D.materials[MAT_GLASS] / coeff))) - has_glass = 0 - - data[++data.len] = list("name" = "metal", "amount" = D.materials[MAT_METAL] / coeff, "is_red" = !has_metal) - data[++data.len] = list("name" = "glass", "amount" = D.materials[MAT_GLASS] / coeff, "is_red" = !has_glass) - - return data - -/obj/machinery/autolathe/proc/queue_data(list/data) - GET_COMPONENT(materials, /datum/component/material_container) - var/temp_metal = materials.amount(MAT_METAL) - var/temp_glass = materials.amount(MAT_GLASS) - data["processing"] = being_built.len ? get_processing_line() : null - if(istype(queue) && queue.len) - var/list/data_queue = list() - for(var/list/L in queue) - var/datum/design/D = L[1] - var/list/LL = get_design_cost_as_list(D, L[2]) - data_queue[++data_queue.len] = list("name" = initial(D.name), "can_build" = can_build(D, L[2], temp_metal, temp_glass), "multiplier" = L[2]) - temp_metal = max(temp_metal - LL[1], 1) - temp_glass = max(temp_glass - LL[2], 1) - data["queue"] = data_queue - data["queue_len"] = data_queue.len - else - data["queue"] = null - return data - -/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params) - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return 1 - if(exchange_parts(user, O)) - return - if(stat) - return 1 - - // Disks in general - if(istype(O, /obj/item/disk)) - if(istype(O, /obj/item/disk/design_disk)) - var/obj/item/disk/design_disk/D = O - if(D.blueprint) - 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.") - playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - busy = 1 - if(do_after(user, 14.4, target = src)) - files.AddDesign2Known(D.blueprint) - busy = 0 - else - to_chat(user, "That disk does not have a design on it!") - return 1 - else - // So that people who are bad at computers don't shred their disks - to_chat(user, "This is not the correct type of disk for the autolathe!") - return 1 - - return ..() - -/obj/machinery/autolathe/crowbar_act(mob/user, obj/item/I) - if(!I.use_tool(src, user, 0, volume = 0)) - return - . = TRUE - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return - if(panel_open) - default_deconstruction_crowbar(user, I) - I.play_tool_sound(user, I.tool_volume) - -/obj/machinery/autolathe/screwdriver_act(mob/user, obj/item/I) - if(!I.use_tool(src, user, 0, volume = 0)) - return - . = TRUE - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return - if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I)) - SSnanoui.update_uis(src) - I.play_tool_sound(user, I.tool_volume) - -/obj/machinery/autolathe/wirecutter_act(mob/user, obj/item/I) - if(!panel_open) - return - if(!I.use_tool(src, user, 0, volume = 0)) - return - . = TRUE - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return - interact(user) - -/obj/machinery/autolathe/multitool_act(mob/user, obj/item/I) - if(!panel_open) - return - if(!I.use_tool(src, user, 0, volume = 0)) - return - . = TRUE - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return - interact(user) - -/obj/machinery/autolathe/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) - switch(id_inserted) - if(MAT_METAL) - flick("autolathe_o", src)//plays metal insertion animation - if(MAT_GLASS) - flick("autolathe_r", src)//plays glass insertion animation - use_power(min(1000, amount_inserted / 100)) - SSnanoui.update_uis(src) - -/obj/machinery/autolathe/attack_ghost(mob/user) - interact(user) - -/obj/machinery/autolathe/attack_hand(mob/user) - if(..(user, 0)) - return - interact(user) - -/obj/machinery/autolathe/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["menu"]) - screen = text2num(href_list["menu"]) - - if(href_list["category"]) - selected_category = href_list["category"] - screen = AUTOLATHE_CATEGORY_MENU - - if(href_list["make"]) - BuildTurf = loc - - ///////////////// - //href protection - var/datum/design/design_last_ordered - design_last_ordered = files.FindDesignByID(href_list["make"]) //check if it's a valid design - if(!design_last_ordered) - return - if(!(design_last_ordered.build_type & AUTOLATHE)) - return - - //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier - var/multiplier = text2num(href_list["multiplier"]) - GET_COMPONENT(materials, /datum/component/material_container) - var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY) - var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) - - if(!is_stack && (multiplier > 1)) - return - if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc - return - ///////////////// - - if((queue.len + 1) < queue_max_len) - add_to_queue(design_last_ordered,multiplier) - else - to_chat(usr, "The autolathe queue is full!") - if(!busy) - busy = 1 - process_queue() - busy = 0 - - if(href_list["remove_from_queue"]) - var/index = text2num(href_list["remove_from_queue"]) - if(isnum(index) && IsInRange(index, 1, queue.len)) - remove_from_queue(index) - if(href_list["queue_move"] && href_list["index"]) - var/index = text2num(href_list["index"]) - var/new_index = index + text2num(href_list["queue_move"]) - if(isnum(index) && isnum(new_index)) - if(IsInRange(new_index, 1, queue.len)) - queue.Swap(index,new_index) - if(href_list["clear_queue"]) - queue = list() - if(href_list["search"]) - if(href_list["to_search"]) - temp_search = href_list["to_search"] - if(!temp_search) - return - matching_designs.Cut() - - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(findtext(D.name, temp_search)) - matching_designs.Add(D) - - screen = AUTOLATHE_SEARCH_MENU - - SSnanoui.update_uis(src) - return 1 - -/obj/machinery/autolathe/RefreshParts() - var/tot_rating = 0 - prod_coeff = 0 - for(var/obj/item/stock_parts/matter_bin/MB in component_parts) - tot_rating += MB.rating - tot_rating *= 25000 - GET_COMPONENT(materials, /datum/component/material_container) - materials.max_amount = tot_rating * 3 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - prod_coeff += M.rating - 1 - -/obj/machinery/autolathe/proc/get_coeff(datum/design/D) - var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)//stacks are unaffected by production coefficient - return coeff - -/obj/machinery/autolathe/proc/build_item(datum/design/D, multiplier) - desc = initial(desc)+"\nIt's building \a [initial(D.name)]." - var/is_stack = ispath(D.build_path, /obj/item/stack) - var/coeff = get_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) - var/metal_cost = D.materials[MAT_METAL] - var/glass_cost = D.materials[MAT_GLASS] - var/power = max(2000, (metal_cost+glass_cost)*multiplier/5) - if(can_build(D, multiplier)) - being_built = list(D, multiplier) - use_power(power) - icon_state = "autolathe" - flick("autolathe_n",src) - if(is_stack) - var/list/materials_used = list(MAT_METAL=metal_cost*multiplier, MAT_GLASS=glass_cost*multiplier) - materials.use_amount(materials_used) - else - var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff) - materials.use_amount(materials_used) - SSnanoui.update_uis(src) - sleep(32/coeff) - if(is_stack) - var/obj/item/stack/S = new D.build_path(BuildTurf) - S.amount = multiplier - else - var/obj/item/new_item = new D.build_path(BuildTurf) - new_item.materials[MAT_METAL] /= coeff - new_item.materials[MAT_GLASS] /= coeff - SSnanoui.update_uis(src) - desc = initial(desc) - -/obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass) - if(D.make_reagents.len) - return 0 - - var/coeff = get_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) - var/metal_amount = materials.amount(MAT_METAL) - if(custom_metal) - metal_amount = custom_metal - var/glass_amount = materials.amount(MAT_GLASS) - if(custom_glass) - glass_amount = custom_glass - - if(D.materials[MAT_METAL] && (metal_amount < (multiplier*D.materials[MAT_METAL] / coeff))) - return 0 - if(D.materials[MAT_GLASS] && (glass_amount < (multiplier*D.materials[MAT_GLASS] / coeff))) - return 0 - return 1 - -/obj/machinery/autolathe/proc/get_design_cost_as_list(datum/design/D, multiplier = 1) - var/list/OutputList = list(0,0) - var/coeff = get_coeff(D) - if(D.materials[MAT_METAL]) - OutputList[1] = (D.materials[MAT_METAL] / coeff)*multiplier - if(D.materials[MAT_GLASS]) - OutputList[2] = (D.materials[MAT_GLASS] / coeff)*multiplier - return OutputList - -/obj/machinery/autolathe/proc/get_processing_line() - var/datum/design/D = being_built[1] - var/multiplier = being_built[2] - var/is_stack = (multiplier>1) - var/output = "PROCESSING: [initial(D.name)][is_stack?" (x[multiplier])":null]" - return output - -/obj/machinery/autolathe/proc/add_to_queue(D, multiplier) - if(!istype(queue)) - queue = list() - if(D) - queue.Add(list(list(D,multiplier))) - return queue.len - -/obj/machinery/autolathe/proc/remove_from_queue(index) - if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len)) - return 0 - queue.Cut(index,++index) - return 1 - -/obj/machinery/autolathe/proc/process_queue() - var/datum/design/D = queue[1][1] - var/multiplier = queue[1][2] - if(!D) - remove_from_queue(1) - if(queue.len) - return process_queue() - else - return - while(D) - if(stat & (NOPOWER|BROKEN)) - being_built = new /list() - return 0 - if(!can_build(D, multiplier)) - visible_message("[bicon(src)] \The [src] beeps, \"Not enough resources. Queue processing terminated.\"") - queue = list() - being_built = new /list() - return 0 - - remove_from_queue(1) - build_item(D,multiplier) - D = listgetindex(listgetindex(queue, 1),1) - multiplier = listgetindex(listgetindex(queue,1),2) - being_built = new /list() - //visible_message("[bicon(src)] \The [src] beeps, \"Queue processing finished successfully.\"") - -/obj/machinery/autolathe/proc/adjust_hacked(hack) - hacked = hack - - if(hack) - for(var/datum/design/D in files.possible_designs) - if((D.build_type & AUTOLATHE) && ("hacked" in D.category)) - files.AddDesign2Known(D) - else - for(var/datum/design/D in files.known_designs) - if("hacked" in D.category) - files.known_designs -= D.id +#define AUTOLATHE_MAIN_MENU 1 +#define AUTOLATHE_CATEGORY_MENU 2 +#define AUTOLATHE_SEARCH_MENU 3 + +/obj/machinery/autolathe + name = "autolathe" + desc = "It produces items using metal and glass." + icon_state = "autolathe" + density = 1 + + var/operating = 0.0 + var/list/queue = list() + var/queue_max_len = 12 + var/turf/BuildTurf + anchored = 1.0 + var/list/L = list() + var/list/LL = list() + var/hacked = 0 + var/disabled = 0 + var/shocked = 0 + var/hack_wire + var/disable_wire + var/shock_wire + use_power = IDLE_POWER_USE + idle_power_usage = 10 + active_power_usage = 100 + var/busy = 0 + var/prod_coeff + var/datum/wires/autolathe/wires = null + + var/list/being_built = list() + var/datum/research/files + var/list/datum/design/matching_designs + var/temp_search + var/selected_category + var/screen = 1 + + var/list/categories = list("Tools", "Electronics", "Construction", "Communication", "Security", "Machinery", "Medical", "Miscellaneous", "Dinnerware", "Imported") + +/obj/machinery/autolathe/New() + AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert)) + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/autolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + + wires = new(src) + files = new /datum/research/autolathe(src) + matching_designs = list() + +/obj/machinery/autolathe/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/autolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + +/obj/machinery/autolathe/Destroy() + QDEL_NULL(wires) + GET_COMPONENT(materials, /datum/component/material_container) + materials.retrieve_all() + return ..() + +/obj/machinery/autolathe/interact(mob/user) + if(shocked && !(stat & NOPOWER)) + if(shock(user, 50)) + return + + if(panel_open) + wires.Interact(user) + else + ui_interact(user) + +/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550) + ui.open() + +/obj/machinery/autolathe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + GET_COMPONENT(materials, /datum/component/material_container) + var/data[0] + data["screen"] = screen + data["total_amount"] = materials.total_amount + data["max_amount"] = materials.max_amount + data["metal_amount"] = materials.amount(MAT_METAL) + data["glass_amount"] = materials.amount(MAT_GLASS) + switch(screen) + if(AUTOLATHE_MAIN_MENU) + data["uid"] = UID() + data["categories"] = categories + if(AUTOLATHE_CATEGORY_MENU) + data["selected_category"] = selected_category + var/list/designs = list() + data["designs"] = designs + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(!(selected_category in D.category)) + continue + var/list/design = list() + designs[++designs.len] = design + design["name"] = D.name + design["id"] = D.id + design["disabled"] = disabled || !can_build(D) ? "disabled" : null + if(ispath(D.build_path, /obj/item/stack)) + design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) + else + design["max_multiplier"] = null + design["materials"] = design_cost_data(D) + if(AUTOLATHE_SEARCH_MENU) + data["search"] = temp_search + var/list/designs = list() + data["designs"] = designs + for(var/datum/design/D in matching_designs) + var/list/design = list() + designs[++designs.len] = design + design["name"] = D.name + design["id"] = D.id + design["disabled"] = disabled || !can_build(D) ? "disabled" : null + if(ispath(D.build_path, /obj/item/stack)) + design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) + else + design["max_multiplier"] = null + design["materials"] = design_cost_data(D) + + data = queue_data(data) + return data + +/obj/machinery/autolathe/proc/design_cost_data(datum/design/D) + var/list/data = list() + var/coeff = get_coeff(D) + GET_COMPONENT(materials, /datum/component/material_container) + var/has_metal = 1 + if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] / coeff))) + has_metal = 0 + var/has_glass = 1 + if(D.materials[MAT_GLASS] && (materials.amount(MAT_GLASS) < (D.materials[MAT_GLASS] / coeff))) + has_glass = 0 + + data[++data.len] = list("name" = "metal", "amount" = D.materials[MAT_METAL] / coeff, "is_red" = !has_metal) + data[++data.len] = list("name" = "glass", "amount" = D.materials[MAT_GLASS] / coeff, "is_red" = !has_glass) + + return data + +/obj/machinery/autolathe/proc/queue_data(list/data) + GET_COMPONENT(materials, /datum/component/material_container) + var/temp_metal = materials.amount(MAT_METAL) + var/temp_glass = materials.amount(MAT_GLASS) + data["processing"] = being_built.len ? get_processing_line() : null + if(istype(queue) && queue.len) + var/list/data_queue = list() + for(var/list/L in queue) + var/datum/design/D = L[1] + var/list/LL = get_design_cost_as_list(D, L[2]) + data_queue[++data_queue.len] = list("name" = initial(D.name), "can_build" = can_build(D, L[2], temp_metal, temp_glass), "multiplier" = L[2]) + temp_metal = max(temp_metal - LL[1], 1) + temp_glass = max(temp_glass - LL[2], 1) + data["queue"] = data_queue + data["queue_len"] = data_queue.len + else + data["queue"] = null + return data + +/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params) + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return 1 + if(exchange_parts(user, O)) + return + if(stat) + return 1 + + // Disks in general + if(istype(O, /obj/item/disk)) + if(istype(O, /obj/item/disk/design_disk)) + var/obj/item/disk/design_disk/D = O + if(D.blueprint) + 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.") + playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + busy = 1 + if(do_after(user, 14.4, target = src)) + files.AddDesign2Known(D.blueprint) + busy = 0 + else + to_chat(user, "That disk does not have a design on it!") + return 1 + else + // So that people who are bad at computers don't shred their disks + to_chat(user, "This is not the correct type of disk for the autolathe!") + return 1 + + return ..() + +/obj/machinery/autolathe/crowbar_act(mob/user, obj/item/I) + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + if(panel_open) + default_deconstruction_crowbar(user, I) + I.play_tool_sound(user, I.tool_volume) + +/obj/machinery/autolathe/screwdriver_act(mob/user, obj/item/I) + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I)) + SSnanoui.update_uis(src) + I.play_tool_sound(user, I.tool_volume) + +/obj/machinery/autolathe/wirecutter_act(mob/user, obj/item/I) + if(!panel_open) + return + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + interact(user) + +/obj/machinery/autolathe/multitool_act(mob/user, obj/item/I) + if(!panel_open) + return + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + interact(user) + +/obj/machinery/autolathe/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) + switch(id_inserted) + if(MAT_METAL) + flick("autolathe_o", src)//plays metal insertion animation + if(MAT_GLASS) + flick("autolathe_r", src)//plays glass insertion animation + use_power(min(1000, amount_inserted / 100)) + SSnanoui.update_uis(src) + +/obj/machinery/autolathe/attack_ghost(mob/user) + interact(user) + +/obj/machinery/autolathe/attack_hand(mob/user) + if(..(user, 0)) + return + interact(user) + +/obj/machinery/autolathe/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["menu"]) + screen = text2num(href_list["menu"]) + + if(href_list["category"]) + selected_category = href_list["category"] + screen = AUTOLATHE_CATEGORY_MENU + + if(href_list["make"]) + BuildTurf = loc + + ///////////////// + //href protection + var/datum/design/design_last_ordered + design_last_ordered = files.FindDesignByID(href_list["make"]) //check if it's a valid design + if(!design_last_ordered) + return + if(!(design_last_ordered.build_type & AUTOLATHE)) + return + + //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier + var/multiplier = text2num(href_list["multiplier"]) + GET_COMPONENT(materials, /datum/component/material_container) + var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY) + var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) + + if(!is_stack && (multiplier > 1)) + return + if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc + return + ///////////////// + + if((queue.len + 1) < queue_max_len) + add_to_queue(design_last_ordered,multiplier) + else + to_chat(usr, "The autolathe queue is full!") + if(!busy) + busy = 1 + process_queue() + busy = 0 + + if(href_list["remove_from_queue"]) + var/index = text2num(href_list["remove_from_queue"]) + if(isnum(index) && IsInRange(index, 1, queue.len)) + remove_from_queue(index) + if(href_list["queue_move"] && href_list["index"]) + var/index = text2num(href_list["index"]) + var/new_index = index + text2num(href_list["queue_move"]) + if(isnum(index) && isnum(new_index)) + if(IsInRange(new_index, 1, queue.len)) + queue.Swap(index,new_index) + if(href_list["clear_queue"]) + queue = list() + if(href_list["search"]) + if(href_list["to_search"]) + temp_search = href_list["to_search"] + if(!temp_search) + return + matching_designs.Cut() + + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(findtext(D.name, temp_search)) + matching_designs.Add(D) + + screen = AUTOLATHE_SEARCH_MENU + + SSnanoui.update_uis(src) + return 1 + +/obj/machinery/autolathe/RefreshParts() + var/tot_rating = 0 + prod_coeff = 0 + for(var/obj/item/stock_parts/matter_bin/MB in component_parts) + tot_rating += MB.rating + tot_rating *= 25000 + GET_COMPONENT(materials, /datum/component/material_container) + materials.max_amount = tot_rating * 3 + for(var/obj/item/stock_parts/manipulator/M in component_parts) + prod_coeff += M.rating - 1 + +/obj/machinery/autolathe/proc/get_coeff(datum/design/D) + var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)//stacks are unaffected by production coefficient + return coeff + +/obj/machinery/autolathe/proc/build_item(datum/design/D, multiplier) + desc = initial(desc)+"\nIt's building \a [initial(D.name)]." + var/is_stack = ispath(D.build_path, /obj/item/stack) + var/coeff = get_coeff(D) + GET_COMPONENT(materials, /datum/component/material_container) + var/metal_cost = D.materials[MAT_METAL] + var/glass_cost = D.materials[MAT_GLASS] + var/power = max(2000, (metal_cost+glass_cost)*multiplier/5) + if(can_build(D, multiplier)) + being_built = list(D, multiplier) + use_power(power) + icon_state = "autolathe" + flick("autolathe_n",src) + if(is_stack) + var/list/materials_used = list(MAT_METAL=metal_cost*multiplier, MAT_GLASS=glass_cost*multiplier) + materials.use_amount(materials_used) + else + var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff) + materials.use_amount(materials_used) + SSnanoui.update_uis(src) + sleep(32/coeff) + if(is_stack) + var/obj/item/stack/S = new D.build_path(BuildTurf) + S.amount = multiplier + else + var/obj/item/new_item = new D.build_path(BuildTurf) + new_item.materials[MAT_METAL] /= coeff + new_item.materials[MAT_GLASS] /= coeff + SSnanoui.update_uis(src) + desc = initial(desc) + +/obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass) + if(D.make_reagents.len) + return 0 + + var/coeff = get_coeff(D) + GET_COMPONENT(materials, /datum/component/material_container) + var/metal_amount = materials.amount(MAT_METAL) + if(custom_metal) + metal_amount = custom_metal + var/glass_amount = materials.amount(MAT_GLASS) + if(custom_glass) + glass_amount = custom_glass + + if(D.materials[MAT_METAL] && (metal_amount < (multiplier*D.materials[MAT_METAL] / coeff))) + return 0 + if(D.materials[MAT_GLASS] && (glass_amount < (multiplier*D.materials[MAT_GLASS] / coeff))) + return 0 + return 1 + +/obj/machinery/autolathe/proc/get_design_cost_as_list(datum/design/D, multiplier = 1) + var/list/OutputList = list(0,0) + var/coeff = get_coeff(D) + if(D.materials[MAT_METAL]) + OutputList[1] = (D.materials[MAT_METAL] / coeff)*multiplier + if(D.materials[MAT_GLASS]) + OutputList[2] = (D.materials[MAT_GLASS] / coeff)*multiplier + return OutputList + +/obj/machinery/autolathe/proc/get_processing_line() + var/datum/design/D = being_built[1] + var/multiplier = being_built[2] + var/is_stack = (multiplier>1) + var/output = "PROCESSING: [initial(D.name)][is_stack?" (x[multiplier])":null]" + return output + +/obj/machinery/autolathe/proc/add_to_queue(D, multiplier) + if(!istype(queue)) + queue = list() + if(D) + queue.Add(list(list(D,multiplier))) + return queue.len + +/obj/machinery/autolathe/proc/remove_from_queue(index) + if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len)) + return 0 + queue.Cut(index,++index) + return 1 + +/obj/machinery/autolathe/proc/process_queue() + var/datum/design/D = queue[1][1] + var/multiplier = queue[1][2] + if(!D) + remove_from_queue(1) + if(queue.len) + return process_queue() + else + return + while(D) + if(stat & (NOPOWER|BROKEN)) + being_built = new /list() + return 0 + if(!can_build(D, multiplier)) + visible_message("[bicon(src)] \The [src] beeps, \"Not enough resources. Queue processing terminated.\"") + queue = list() + being_built = new /list() + return 0 + + remove_from_queue(1) + build_item(D,multiplier) + D = listgetindex(listgetindex(queue, 1),1) + multiplier = listgetindex(listgetindex(queue,1),2) + being_built = new /list() + //visible_message("[bicon(src)] \The [src] beeps, \"Queue processing finished successfully.\"") + +/obj/machinery/autolathe/proc/adjust_hacked(hack) + hacked = hack + + if(hack) + for(var/datum/design/D in files.possible_designs) + if((D.build_type & AUTOLATHE) && ("hacked" in D.category)) + files.AddDesign2Known(D) + else + for(var/datum/design/D in files.known_designs) + if("hacked" in D.category) + files.known_designs -= D.id diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index f91152b47e4..796d5c552dd 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -1,206 +1,206 @@ - -////////////////////////////////////// -// Driver Button // -////////////////////////////////////// - -/obj/machinery/driver_button - name = "mass driver button" - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - desc = "A remote control switch for a mass driver." - var/id_tag = "default" - var/active = 0 - settagwhitelist = list("id_tag", "logic_id_tag") - anchored = 1.0 - armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 90, acid = 70) - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - resistance_flags = LAVA_PROOF | FIRE_PROOF - var/range = 7 - - var/datum/radio_frequency/radio_connection - var/frequency = 0 - var/logic_id_tag = "default" //Defines the ID tag to send logic signals to, so you don't have to unlink from doors and stuff - var/logic_connect = 0 //Set this to allow the button to send out logic signals when pressed in addition to normal stuff - -/obj/machinery/button/indestructible - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/machinery/driver_button/New(turf/loc, var/w_dir=null) - ..() - switch(w_dir) - if(NORTH) - pixel_y = 25 - if(SOUTH) - pixel_y = -25 - if(EAST) - pixel_x = 25 - if(WEST) - pixel_x = -25 - if(SSradio) - set_frequency(frequency) - -/obj/machinery/driver_button/Initialize() - ..() - set_frequency(frequency) - -/obj/machinery/driver_button/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) - return - -/obj/machinery/driver_button/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - - -/obj/machinery/driver_button/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/driver_button/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/driver_button/attackby(obj/item/W, mob/user as mob, params) - - if(istype(W, /obj/item/detective_scanner)) - return - - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(istype(W, /obj/item/wrench)) - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 30 * W.toolspeed, target = src)) - to_chat(user, "You detach \the [src] from the wall.") - new/obj/item/mounted/frame/driver_button(get_turf(src)) - qdel(src) - return 1 - - return ..() - -/obj/machinery/driver_button/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" -
      -
    • ID Tag: [format_tag("ID Tag","id_tag")]
    • -
    • Logic Connection: [logic_connect ? "On" : "Off"]
    • -
    • Logic ID Tag: [format_tag("Logic ID Tag", "logic_id_tag")]
    • -
    "} - -/obj/machinery/driver_button/attack_hand(mob/user as mob) - - add_fingerprint(usr) - if(stat & (NOPOWER|BROKEN)) - return - if(active) - return - add_fingerprint(user) - - use_power(5) - - launch_sequence() - -/obj/machinery/driver_button/proc/launch_sequence() - active = 1 - icon_state = "launcheract" - - if(logic_connect) - if(!radio_connection) //can't output without this - return - - if(logic_id_tag == null) //Don't output to an undefined id_tag - return - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = logic_id_tag, - "sigtype" = "logic", - "state" = LOGIC_FLICKER, //Buttons are a FLICKER source, since they only register as ON when you press it, then turn OFF after you release - ) - - radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) - - for(var/obj/machinery/door/poddoor/M in range(src,range)) - if(M.id_tag == id_tag && !M.protected) - spawn() - M.open() - - sleep(20) - - for(var/obj/machinery/mass_driver/M in range(src,range)) - if(M.id_tag == id_tag) - M.drive() - - sleep(50) - - for(var/obj/machinery/door/poddoor/M in range(src,range)) - if(M.id_tag == id_tag && !M.protected) - spawn() - M.close() - return - - icon_state = "launcherbtt" - active = 0 - -/obj/machinery/driver_button/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) - ..() - if("toggle_logic" in href_list) - logic_connect = !logic_connect - -////////////////////////////////////// -// Ignition Switch // -////////////////////////////////////// - -/obj/machinery/ignition_switch - name = "ignition switch" - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - desc = "A remote control switch for a mounted igniter." - var/id = null - var/active = 0 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/ignition_switch/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/ignition_switch/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/ignition_switch/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - if(active) - return - - use_power(5) - - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/sparker/M in world) - if(M.id == id) - spawn( 0 ) - M.spark() - - for(var/obj/machinery/igniter/M in world) - if(M.id == id) - use_power(50) - M.on = !( M.on ) - M.icon_state = text("igniter[]", M.on) - - sleep(50) - - icon_state = "launcherbtt" - active = 0 + +////////////////////////////////////// +// Driver Button // +////////////////////////////////////// + +/obj/machinery/driver_button + name = "mass driver button" + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + desc = "A remote control switch for a mass driver." + var/id_tag = "default" + var/active = 0 + settagwhitelist = list("id_tag", "logic_id_tag") + anchored = 1.0 + armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 90, acid = 70) + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + resistance_flags = LAVA_PROOF | FIRE_PROOF + var/range = 7 + + var/datum/radio_frequency/radio_connection + var/frequency = 0 + var/logic_id_tag = "default" //Defines the ID tag to send logic signals to, so you don't have to unlink from doors and stuff + var/logic_connect = 0 //Set this to allow the button to send out logic signals when pressed in addition to normal stuff + +/obj/machinery/button/indestructible + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/machinery/driver_button/New(turf/loc, var/w_dir=null) + ..() + switch(w_dir) + if(NORTH) + pixel_y = 25 + if(SOUTH) + pixel_y = -25 + if(EAST) + pixel_x = 25 + if(WEST) + pixel_x = -25 + if(SSradio) + set_frequency(frequency) + +/obj/machinery/driver_button/Initialize() + ..() + set_frequency(frequency) + +/obj/machinery/driver_button/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) + return + +/obj/machinery/driver_button/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + + +/obj/machinery/driver_button/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/driver_button/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/driver_button/attackby(obj/item/W, mob/user as mob, params) + + if(istype(W, /obj/item/detective_scanner)) + return + + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(istype(W, /obj/item/wrench)) + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 30 * W.toolspeed, target = src)) + to_chat(user, "You detach \the [src] from the wall.") + new/obj/item/mounted/frame/driver_button(get_turf(src)) + qdel(src) + return 1 + + return ..() + +/obj/machinery/driver_button/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" +
      +
    • ID Tag: [format_tag("ID Tag","id_tag")]
    • +
    • Logic Connection: [logic_connect ? "On" : "Off"]
    • +
    • Logic ID Tag: [format_tag("Logic ID Tag", "logic_id_tag")]
    • +
    "} + +/obj/machinery/driver_button/attack_hand(mob/user as mob) + + add_fingerprint(usr) + if(stat & (NOPOWER|BROKEN)) + return + if(active) + return + add_fingerprint(user) + + use_power(5) + + launch_sequence() + +/obj/machinery/driver_button/proc/launch_sequence() + active = 1 + icon_state = "launcheract" + + if(logic_connect) + if(!radio_connection) //can't output without this + return + + if(logic_id_tag == null) //Don't output to an undefined id_tag + return + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = logic_id_tag, + "sigtype" = "logic", + "state" = LOGIC_FLICKER, //Buttons are a FLICKER source, since they only register as ON when you press it, then turn OFF after you release + ) + + radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) + + for(var/obj/machinery/door/poddoor/M in range(src,range)) + if(M.id_tag == id_tag && !M.protected) + spawn() + M.open() + + sleep(20) + + for(var/obj/machinery/mass_driver/M in range(src,range)) + if(M.id_tag == id_tag) + M.drive() + + sleep(50) + + for(var/obj/machinery/door/poddoor/M in range(src,range)) + if(M.id_tag == id_tag && !M.protected) + spawn() + M.close() + return + + icon_state = "launcherbtt" + active = 0 + +/obj/machinery/driver_button/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) + ..() + if("toggle_logic" in href_list) + logic_connect = !logic_connect + +////////////////////////////////////// +// Ignition Switch // +////////////////////////////////////// + +/obj/machinery/ignition_switch + name = "ignition switch" + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + desc = "A remote control switch for a mounted igniter." + var/id = null + var/active = 0 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/ignition_switch/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/ignition_switch/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/ignition_switch/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + if(active) + return + + use_power(5) + + active = 1 + icon_state = "launcheract" + + for(var/obj/machinery/sparker/M in world) + if(M.id == id) + spawn( 0 ) + M.spark() + + for(var/obj/machinery/igniter/M in world) + if(M.id == id) + use_power(50) + M.on = !( M.on ) + M.icon_state = text("igniter[]", M.on) + + sleep(50) + + icon_state = "launcherbtt" + active = 0 diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index bb09c247bca..24ffb494422 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -1,433 +1,429 @@ -/obj/machinery/camera - name = "security camera" - desc = "It's used to monitor rooms." - icon = 'icons/obj/monitors.dmi' - icon_state = "camera" - use_power = ACTIVE_POWER_USE - idle_power_usage = 5 - active_power_usage = 10 - layer = WALL_OBJ_LAYER - resistance_flags = FIRE_PROOF - damage_deflection = 12 - armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 50) - var/datum/wires/camera/wires = null // Wires datum - max_integrity = 100 - integrity_failure = 50 - var/list/network = list("SS13") - var/c_tag = null - var/c_tag_order = 999 - var/status = 1 - anchored = TRUE - var/start_active = FALSE //If it ignores the random chance to start broken on round start - var/invuln = null - var/obj/item/camera_bug/bug = null - var/obj/item/camera_assembly/assembly = null - - //OTHER - - var/view_range = 7 - var/short_range = 2 - - var/busy = FALSE - var/emped = FALSE //Number of consecutive EMP's on this camera - - var/in_use_lights = 0 // TO BE IMPLEMENTED - var/toggle_sound = 'sound/items/wirecutter.ogg' - - -/obj/machinery/camera/New() - ..() - wires = new(src) - assembly = new(src) - assembly.state = 4 - assembly.anchored = 1 - assembly.update_icon() - - cameranet.cameras += src - cameranet.addCamera(src) - -/obj/machinery/camera/Initialize() - ..() - if(is_station_level(z) && prob(3) && !start_active) - toggle_cam(null, FALSE) - wires.CutAll() - -/obj/machinery/camera/Destroy() - toggle_cam(null, FALSE) //kick anyone viewing out - QDEL_NULL(assembly) - if(istype(bug)) - bug.bugged_cameras -= c_tag - if(bug.current == src) - bug.current = null - bug = null - QDEL_NULL(wires) - cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that - cameranet.cameras -= src - var/area/ai_monitored/A = get_area(src) - if(istype(A)) - A.motioncamera = null - area_motion = null - return ..() - -/obj/machinery/camera/emp_act(severity) - if(!status) - return - if(!isEmpProof()) - if(prob(150/severity)) - update_icon() - var/list/previous_network = network - network = list() - cameranet.removeCamera(src) - stat |= EMPED - set_light(0) - emped = emped+1 //Increase the number of consecutive EMP's - update_icon() - var/thisemp = emped //Take note of which EMP this proc is for - spawn(900) - if(!QDELETED(src)) - triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect. - if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again - network = previous_network - stat &= ~EMPED - update_icon() - if(can_use()) - cameranet.addCamera(src) - emped = 0 //Resets the consecutive EMP count - spawn(100) - if(!QDELETED(src)) - cancelCameraAlarm() - for(var/mob/M in GLOB.player_list) - if(M.client && M.client.eye == src) - M.unset_machine() - M.reset_perspective(null) - to_chat(M, "The screen bursts into static.") - ..() - -/obj/machinery/camera/tesla_act(power)//EMP proof upgrade also makes it tesla immune - if(isEmpProof()) - return - ..() - qdel(src)//to prevent bomb testing camera from exploding over and over forever - -/obj/machinery/camera/ex_act(severity) - if(invuln) - return - ..() - -/obj/machinery/camera/proc/setViewRange(num = 7) - view_range = num - cameranet.updateVisibility(src, 0) - -/obj/machinery/camera/singularity_pull(S, current_size) - if (status && current_size >= STAGE_FIVE) // If the singulo is strong enough to pull anchored objects and the camera is still active, turn off the camera as it gets ripped off the wall. - toggle_cam(null, 0) - ..() - -/obj/machinery/camera/attackby(obj/item/I, mob/living/user, params) - var/msg = "You attach [I] into the assembly inner circuits." - var/msg2 = "The camera already has that upgrade!" - - if(istype(I, /obj/item/analyzer) && panel_open) //XRay - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand!") - return - if(!isXRay()) - upgradeXRay() - qdel(I) - to_chat(user, "[msg]") - else - to_chat(user, "[msg2]") - - else if(istype(I, /obj/item/stack/sheet/mineral/plasma) && panel_open) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand!") - return - if(!isEmpProof()) - var/obj/item/stack/sheet/mineral/plasma/P = I - upgradeEmpProof() - to_chat(user, "[msg]") - P.use(1) - else - to_chat(user, "[msg2]") - else if(istype(I, /obj/item/assembly/prox_sensor) && panel_open) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand!") - return - if(!isMotion()) - upgradeMotion() - to_chat(user, "[msg]") - qdel(I) - else - to_chat(user, "[msg2]") - - // OTHER - else if((istype(I, /obj/item/paper) || istype(I, /obj/item/pda)) && isliving(user)) - var/mob/living/U = user - var/obj/item/paper/X = null - var/obj/item/pda/PDA = null - - var/itemname = "" - var/info = "" - if(istype(I, /obj/item/paper)) - X = I - itemname = X.name - info = X.info - else - PDA = I - var/datum/data/pda/app/notekeeper/N = PDA.find_program(/datum/data/pda/app/notekeeper) - if(N) - itemname = PDA.name - info = N.notehtml - to_chat(U, "You hold \the [itemname] up to the camera ...") - U.changeNext_move(CLICK_CD_MELEE) - for(var/mob/O in GLOB.player_list) - if(istype(O, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = O - if(AI.control_disabled || (AI.stat == DEAD)) - return - if(U.name == "Unknown") - to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") - else - to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") - AI.last_paper_seen = "[itemname][info]" - else if(O.client && O.client.eye == src) - to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") - O << browse(text("[][]", itemname, info), text("window=[]", itemname)) - - else if(istype(I, /obj/item/camera_bug)) - if(!can_use()) - to_chat(user, "Camera non-functional.") - return - if(istype(bug)) - to_chat(user, "Camera bug removed.") - bug.bugged_cameras -= c_tag - bug = null - else - to_chat(user, "Camera bugged.") - bug = I - bug.bugged_cameras[c_tag] = src - - else if(istype(I, /obj/item/laser_pointer)) - var/obj/item/laser_pointer/L = I - L.laser_act(src, user) - else - return ..() - - -/obj/machinery/camera/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - panel_open = !panel_open - to_chat(user, "You screw [src]'s panel [panel_open ? "open" : "closed"].") - -/obj/machinery/camera/wirecutter_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(panel_open) - wires.Interact(user) - -/obj/machinery/camera/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(panel_open) - wires.Interact(user) - -/obj/machinery/camera/welder_act(mob/user, obj/item/I) - if(!panel_open || !wires.CanDeconstruct()) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_WELD_MESSAGE - if(I.use_tool(src, user, 100, volume = I.tool_volume)) - visible_message("[user] unwelds [src], leaving it as just a frame bolted to the wall.", - "You unweld [src], leaving it as just a frame bolted to the wall") - deconstruct(TRUE) - -/obj/machinery/camera/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - if(stat & BROKEN) - return damage_amount - . = ..() - -/obj/machinery/camera/obj_break(damage_flag) - if(status && !(flags & NODECONSTRUCT)) - triggerCameraAlarm() - toggle_cam(null, FALSE) - wires.CutAll() - -/obj/machinery/camera/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(disassembled) - if(!assembly) - assembly = new() - assembly.forceMove(drop_location()) - assembly.state = 1 - assembly.setDir(dir) - assembly.update_icon() - assembly = null - else - var/obj/item/I = new /obj/item/camera_assembly(loc) - I.obj_integrity = I.max_integrity * 0.5 - new /obj/item/stack/cable_coil(loc, 2) - qdel(src) - -/obj/machinery/camera/update_icon() - if(!status) - icon_state = "[initial(icon_state)]1" - else if(stat & EMPED) - icon_state = "[initial(icon_state)]emp" - else - icon_state = "[initial(icon_state)]" - -/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE) - status = !status - if(can_use()) - cameranet.addCamera(src) - else - set_light(0) - cameranet.removeCamera(src) - cameranet.updateChunk(x, y, z) - var/change_msg = "deactivates" - if(status) - change_msg = "reactivates" - triggerCameraAlarm() - spawn(100) - if(!QDELETED(src)) - cancelCameraAlarm() - if(displaymessage) - if(user) - visible_message("[user] [change_msg] [src]!") - add_hiddenprint(user) - else - visible_message("\The [src] [change_msg]!") - - playsound(loc, toggle_sound, 100, 1) - update_icon() - - // now disconnect anyone using the camera - //Apparently, this will disconnect anyone even if the camera was re-activated. - //I guess that doesn't matter since they can't use it anyway? - for(var/mob/O in GLOB.player_list) - if(O.client && O.client.eye == src) - O.unset_machine() - O.reset_perspective(null) - to_chat(O, "The screen bursts into static.") - -/obj/machinery/camera/proc/triggerCameraAlarm() - if(is_station_contact(z)) - SSalarms.camera_alarm.triggerAlarm(loc, src) - -/obj/machinery/camera/proc/cancelCameraAlarm() - if(is_station_contact(z)) - SSalarms.camera_alarm.clearAlarm(loc, src) - -/obj/machinery/camera/proc/can_use() - if(!status) - return 0 - if(stat & EMPED) - return 0 - return 1 - -/obj/machinery/camera/proc/can_see() - var/list/see = null - var/turf/pos = get_turf(src) - if(isXRay()) - see = range(view_range, pos) - else - see = hear(view_range, pos) - return see - -/atom/proc/auto_turn() - //Automatically turns based on nearby walls. - var/turf/simulated/wall/T = null - for(var/i = 1, i <= 8; i += i) - T = get_ranged_target_turf(src, i, 1) - if(istype(T)) - //If someone knows a better way to do this, let me know. -Giacom - switch(i) - if(NORTH) - setDir(SOUTH) - if(SOUTH) - setDir(NORTH) - if(WEST) - setDir(EAST) - if(EAST) - setDir(WEST) - break - -//Return a working camera that can see a given mob -//or null if none -/proc/seen_by_camera(mob/M) - for(var/obj/machinery/camera/C in oview(4, M)) - if(C.can_use()) // check if camera disabled - return C - break - return null - -/proc/near_range_camera(mob/M) - for(var/obj/machinery/camera/C in range(4, M)) - if(C.can_use()) // check if camera disabled - return C - break - - return null - -/obj/machinery/camera/proc/Togglelight(on = FALSE) - for(var/mob/living/silicon/ai/A in ai_list) - for(var/obj/machinery/camera/cam in A.lit_cameras) - if(cam == src) - return - if(on) - set_light(AI_CAMERA_LUMINOSITY) - else - set_light(0) - -/obj/machinery/camera/proc/nano_structure() - var/cam[0] - var/turf/T = get_turf(src) - cam["name"] = sanitize(c_tag) - cam["deact"] = !can_use() - cam["camera"] = "\ref[src]" - if(T) - cam["x"] = T.x - cam["y"] = T.y - cam["z"] = T.z - else - cam["x"] = 0 - cam["y"] = 0 - cam["z"] = 0 - return cam - -/obj/machinery/camera/get_remote_view_fullscreens(mob/user) - if(view_range == short_range) //unfocused - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) - -/obj/machinery/camera/update_remote_sight(mob/living/user) - if(isXRay()) - user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - user.see_in_dark = max(user.see_in_dark, 8) - user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - else - user.sight = initial(user.sight) - user.see_in_dark = initial(user.see_in_dark) - user.lighting_alpha = initial(user.lighting_alpha) - - ..() - return TRUE - -/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets. - var/turf/prev_turf - -/obj/machinery/camera/portable/New() - ..() - assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed. - assembly.anchored = 0 - assembly.update_icon() - -/obj/machinery/camera/portable/process() //Updates whenever the camera is moved. - if(cameranet && get_turf(src) != prev_turf) - cameranet.updatePortableCamera(src) - prev_turf = get_turf(src) +/obj/machinery/camera + name = "security camera" + desc = "It's used to monitor rooms." + icon = 'icons/obj/monitors.dmi' + icon_state = "camera" + use_power = ACTIVE_POWER_USE + idle_power_usage = 5 + active_power_usage = 10 + layer = WALL_OBJ_LAYER + resistance_flags = FIRE_PROOF + damage_deflection = 12 + armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 50) + var/datum/wires/camera/wires = null // Wires datum + max_integrity = 100 + integrity_failure = 50 + var/list/network = list("SS13") + var/c_tag = null + var/c_tag_order = 999 + var/status = 1 + anchored = TRUE + var/start_active = FALSE //If it ignores the random chance to start broken on round start + var/invuln = null + var/obj/item/camera_bug/bug = null + var/obj/item/camera_assembly/assembly = null + + //OTHER + + var/view_range = 7 + var/short_range = 2 + + var/busy = FALSE + var/emped = FALSE //Number of consecutive EMP's on this camera + + var/in_use_lights = 0 // TO BE IMPLEMENTED + var/toggle_sound = 'sound/items/wirecutter.ogg' + +/obj/machinery/camera/Initialize() + . = ..() + wires = new(src) + assembly = new(src) + assembly.state = 4 + assembly.anchored = 1 + assembly.update_icon() + + GLOB.cameranet.cameras += src + GLOB.cameranet.addCamera(src) + if(is_station_level(z) && prob(3) && !start_active) + toggle_cam(null, FALSE) + wires.CutAll() + +/obj/machinery/camera/Destroy() + toggle_cam(null, FALSE) //kick anyone viewing out + QDEL_NULL(assembly) + if(istype(bug)) + bug.bugged_cameras -= c_tag + if(bug.current == src) + bug.current = null + bug = null + QDEL_NULL(wires) + GLOB.cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that + GLOB.cameranet.cameras -= src + var/area/ai_monitored/A = get_area(src) + if(istype(A)) + A.motioncamera = null + area_motion = null + return ..() + +/obj/machinery/camera/emp_act(severity) + if(!status) + return + if(!isEmpProof()) + if(prob(150/severity)) + update_icon() + var/list/previous_network = network + network = list() + GLOB.cameranet.removeCamera(src) + stat |= EMPED + set_light(0) + emped = emped+1 //Increase the number of consecutive EMP's + update_icon() + var/thisemp = emped //Take note of which EMP this proc is for + spawn(900) + if(!QDELETED(src)) + triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect. + if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again + network = previous_network + stat &= ~EMPED + update_icon() + if(can_use()) + GLOB.cameranet.addCamera(src) + emped = 0 //Resets the consecutive EMP count + spawn(100) + if(!QDELETED(src)) + cancelCameraAlarm() + for(var/mob/M in GLOB.player_list) + if(M.client && M.client.eye == src) + M.unset_machine() + M.reset_perspective(null) + to_chat(M, "The screen bursts into static.") + ..() + +/obj/machinery/camera/tesla_act(power)//EMP proof upgrade also makes it tesla immune + if(isEmpProof()) + return + ..() + qdel(src)//to prevent bomb testing camera from exploding over and over forever + +/obj/machinery/camera/ex_act(severity) + if(invuln) + return + ..() + +/obj/machinery/camera/proc/setViewRange(num = 7) + view_range = num + GLOB.cameranet.updateVisibility(src, 0) + +/obj/machinery/camera/singularity_pull(S, current_size) + if (status && current_size >= STAGE_FIVE) // If the singulo is strong enough to pull anchored objects and the camera is still active, turn off the camera as it gets ripped off the wall. + toggle_cam(null, 0) + ..() + +/obj/machinery/camera/attackby(obj/item/I, mob/living/user, params) + var/msg = "You attach [I] into the assembly inner circuits." + var/msg2 = "The camera already has that upgrade!" + + if(istype(I, /obj/item/analyzer) && panel_open) //XRay + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand!") + return + if(!isXRay()) + upgradeXRay() + qdel(I) + to_chat(user, "[msg]") + else + to_chat(user, "[msg2]") + + else if(istype(I, /obj/item/stack/sheet/mineral/plasma) && panel_open) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand!") + return + if(!isEmpProof()) + var/obj/item/stack/sheet/mineral/plasma/P = I + upgradeEmpProof() + to_chat(user, "[msg]") + P.use(1) + else + to_chat(user, "[msg2]") + else if(istype(I, /obj/item/assembly/prox_sensor) && panel_open) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand!") + return + if(!isMotion()) + upgradeMotion() + to_chat(user, "[msg]") + qdel(I) + else + to_chat(user, "[msg2]") + + // OTHER + else if((istype(I, /obj/item/paper) || istype(I, /obj/item/pda)) && isliving(user)) + var/mob/living/U = user + var/obj/item/paper/X = null + var/obj/item/pda/PDA = null + + var/itemname = "" + var/info = "" + if(istype(I, /obj/item/paper)) + X = I + itemname = X.name + info = X.info + else + PDA = I + var/datum/data/pda/app/notekeeper/N = PDA.find_program(/datum/data/pda/app/notekeeper) + if(N) + itemname = PDA.name + info = N.notehtml + to_chat(U, "You hold \the [itemname] up to the camera ...") + U.changeNext_move(CLICK_CD_MELEE) + for(var/mob/O in GLOB.player_list) + if(istype(O, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = O + if(AI.control_disabled || (AI.stat == DEAD)) + return + if(U.name == "Unknown") + to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") + else + to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") + AI.last_paper_seen = "[itemname][info]" + else if(O.client && O.client.eye == src) + to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") + O << browse(text("[][]", itemname, info), text("window=[]", itemname)) + + else if(istype(I, /obj/item/camera_bug)) + if(!can_use()) + to_chat(user, "Camera non-functional.") + return + if(istype(bug)) + to_chat(user, "Camera bug removed.") + bug.bugged_cameras -= c_tag + bug = null + else + to_chat(user, "Camera bugged.") + bug = I + bug.bugged_cameras[c_tag] = src + + else if(istype(I, /obj/item/laser_pointer)) + var/obj/item/laser_pointer/L = I + L.laser_act(src, user) + else + return ..() + + +/obj/machinery/camera/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + panel_open = !panel_open + to_chat(user, "You screw [src]'s panel [panel_open ? "open" : "closed"].") + +/obj/machinery/camera/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(panel_open) + wires.Interact(user) + +/obj/machinery/camera/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(panel_open) + wires.Interact(user) + +/obj/machinery/camera/welder_act(mob/user, obj/item/I) + if(!panel_open || !wires.CanDeconstruct()) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(I.use_tool(src, user, 100, volume = I.tool_volume)) + visible_message("[user] unwelds [src], leaving it as just a frame bolted to the wall.", + "You unweld [src], leaving it as just a frame bolted to the wall") + deconstruct(TRUE) + +/obj/machinery/camera/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + if(stat & BROKEN) + return damage_amount + . = ..() + +/obj/machinery/camera/obj_break(damage_flag) + if(status && !(flags & NODECONSTRUCT)) + triggerCameraAlarm() + toggle_cam(null, FALSE) + wires.CutAll() + +/obj/machinery/camera/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(disassembled) + if(!assembly) + assembly = new() + assembly.forceMove(drop_location()) + assembly.state = 1 + assembly.setDir(dir) + assembly.update_icon() + assembly = null + else + var/obj/item/I = new /obj/item/camera_assembly(loc) + I.obj_integrity = I.max_integrity * 0.5 + new /obj/item/stack/cable_coil(loc, 2) + qdel(src) + +/obj/machinery/camera/update_icon() + if(!status) + icon_state = "[initial(icon_state)]1" + else if(stat & EMPED) + icon_state = "[initial(icon_state)]emp" + else + icon_state = "[initial(icon_state)]" + +/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE) + status = !status + if(can_use()) + GLOB.cameranet.addCamera(src) + else + set_light(0) + GLOB.cameranet.removeCamera(src) + GLOB.cameranet.updateChunk(x, y, z) + var/change_msg = "deactivates" + if(status) + change_msg = "reactivates" + triggerCameraAlarm() + spawn(100) + if(!QDELETED(src)) + cancelCameraAlarm() + if(displaymessage) + if(user) + visible_message("[user] [change_msg] [src]!") + add_hiddenprint(user) + else + visible_message("\The [src] [change_msg]!") + + playsound(loc, toggle_sound, 100, 1) + update_icon() + + // now disconnect anyone using the camera + //Apparently, this will disconnect anyone even if the camera was re-activated. + //I guess that doesn't matter since they can't use it anyway? + for(var/mob/O in GLOB.player_list) + if(O.client && O.client.eye == src) + O.unset_machine() + O.reset_perspective(null) + to_chat(O, "The screen bursts into static.") + +/obj/machinery/camera/proc/triggerCameraAlarm() + if(is_station_contact(z)) + SSalarms.camera_alarm.triggerAlarm(loc, src) + +/obj/machinery/camera/proc/cancelCameraAlarm() + if(is_station_contact(z)) + SSalarms.camera_alarm.clearAlarm(loc, src) + +/obj/machinery/camera/proc/can_use() + if(!status) + return 0 + if(stat & EMPED) + return 0 + return 1 + +/obj/machinery/camera/proc/can_see() + var/list/see = null + var/turf/pos = get_turf(src) + if(isXRay()) + see = range(view_range, pos) + else + see = hear(view_range, pos) + return see + +/atom/proc/auto_turn() + //Automatically turns based on nearby walls. + var/turf/simulated/wall/T = null + for(var/i = 1, i <= 8; i += i) + T = get_ranged_target_turf(src, i, 1) + if(istype(T)) + //If someone knows a better way to do this, let me know. -Giacom + switch(i) + if(NORTH) + setDir(SOUTH) + if(SOUTH) + setDir(NORTH) + if(WEST) + setDir(EAST) + if(EAST) + setDir(WEST) + break + +//Return a working camera that can see a given mob +//or null if none +/proc/seen_by_camera(mob/M) + for(var/obj/machinery/camera/C in oview(4, M)) + if(C.can_use()) // check if camera disabled + return C + break + return null + +/proc/near_range_camera(mob/M) + for(var/obj/machinery/camera/C in range(4, M)) + if(C.can_use()) // check if camera disabled + return C + break + + return null + +/obj/machinery/camera/proc/Togglelight(on = FALSE) + for(var/mob/living/silicon/ai/A in GLOB.ai_list) + for(var/obj/machinery/camera/cam in A.lit_cameras) + if(cam == src) + return + if(on) + set_light(AI_CAMERA_LUMINOSITY) + else + set_light(0) + +/obj/machinery/camera/proc/nano_structure() + var/cam[0] + var/turf/T = get_turf(src) + cam["name"] = sanitize(c_tag) + cam["deact"] = !can_use() + cam["camera"] = "\ref[src]" + if(T) + cam["x"] = T.x + cam["y"] = T.y + cam["z"] = T.z + else + cam["x"] = 0 + cam["y"] = 0 + cam["z"] = 0 + return cam + +/obj/machinery/camera/get_remote_view_fullscreens(mob/user) + if(view_range == short_range) //unfocused + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) + +/obj/machinery/camera/update_remote_sight(mob/living/user) + if(isXRay()) + user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + user.see_in_dark = max(user.see_in_dark, 8) + user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + else + user.sight = initial(user.sight) + user.see_in_dark = initial(user.see_in_dark) + user.lighting_alpha = initial(user.lighting_alpha) + + ..() + return TRUE + +/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets. + var/turf/prev_turf + +/obj/machinery/camera/portable/New() + ..() + assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed. + assembly.anchored = 0 + assembly.update_icon() + +/obj/machinery/camera/portable/process() //Updates whenever the camera is moved. + if(GLOB.cameranet && get_turf(src) != prev_turf) + GLOB.cameranet.updatePortableCamera(src) + prev_turf = get_turf(src) diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index bd795ab340b..c7d95370613 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -1,179 +1,179 @@ -#define ASSEMBLY_UNBUILT 0 // Nothing done to it -#define ASSEMBLY_WRENCHED 1 // Wrenched in place -#define ASSEMBLY_WELDED 2 // Welded in place -#define ASSEMBLY_WIRED 3 // Wires attached (Upgradable now) -#define ASSEMBLY_BUILT 4 // Fully built (incl panel closed) -#define HEY_IM_WORKING_HERE 666 //So nobody can mess with the camera while we're inputting settings - -/obj/item/camera_assembly - name = "camera assembly" - desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." - icon = 'icons/obj/monitors.dmi' - icon_state = "cameracase" - w_class = WEIGHT_CLASS_SMALL - anchored = FALSE - materials = list(MAT_METAL=400, MAT_GLASS=250) - // Motion, EMP-Proof, X-Ray - var/list/obj/item/possible_upgrades = list(/obj/item/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/analyzer) - var/list/upgrades = list() - var/state = ASSEMBLY_UNBUILT - - -/obj/item/camera_assembly/Destroy() - QDEL_LIST(upgrades) - return ..() - -/obj/item/camera_assembly/attackby(obj/item/I, mob/living/user, params) - if(state == ASSEMBLY_WELDED && iscoil(I)) - var/obj/item/stack/cable_coil/C = I - if(C.use(2)) - to_chat(user, "You add wires to the assembly.") - playsound(loc, I.usesound, 50, 1) - state = ASSEMBLY_WIRED - else - to_chat(user, "You need 2 coils of wire to wire the assembly.") - return - - // Upgrades! - else if(is_type_in_list(I, possible_upgrades) && !is_type_in_list(I, upgrades)) // Is a possible upgrade and isn't in the camera already. - if(!user.unEquip(I)) - to_chat(user, "[I] is stuck!") - return - to_chat(user, "You attach [I] into the assembly inner circuits.") - upgrades += I - user.drop_item() - I.loc = src - return - else - return ..() - -/obj/item/camera_assembly/crowbar_act(mob/user, obj/item/I) - if(!upgrades.len) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/obj/U = locate(/obj) in upgrades - if(U) - to_chat(user, "You unattach an upgrade from the assembly.") - playsound(loc, I.usesound, 50, 1) - U.loc = get_turf(src) - upgrades -= U - -/obj/item/camera_assembly/screwdriver_act(mob/user, obj/item/I) - if(state != ASSEMBLY_WIRED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - state = HEY_IM_WORKING_HERE - var/input = strip_html(input(usr, "Which networks would you like to connect this camera to? Seperate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret ", "Set Network", "SS13")) - if(!input) - state = ASSEMBLY_WIRED - to_chat(usr, "No input found please hang up and try your call again.") - return - - var/list/tempnetwork = splittext(input, ",") - if(tempnetwork.len < 1) - state = ASSEMBLY_WIRED - to_chat(usr, "No network found please hang up and try your call again.") - return - - var/area/camera_area = get_area(src) - var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" - input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag)) - state = ASSEMBLY_BUILT - var/obj/machinery/camera/C = new(loc) - loc = C - C.assembly = src - - C.auto_turn() - - C.network = uniquelist(tempnetwork) - tempnetwork = difflist(C.network,GLOB.restricted_camera_networks) - if(!tempnetwork.len) // Camera isn't on any open network - remove its chunk from AI visibility. - cameranet.removeCamera(C) - - C.c_tag = input - - for(var/i = 5; i >= 0; i -= 1) - var/direct = input(user, "Direction?", "Assembling Camera", null) in list("LEAVE IT", "NORTH", "EAST", "SOUTH", "WEST" ) - if(direct != "LEAVE IT") - C.dir = text2dir(direct) - if(i != 0) - var/confirm = alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", "Yes", "No") - if(confirm == "Yes") - break - - -/obj/item/camera_assembly/wirecutter_act(mob/user, obj/item/I) - if(state != ASSEMBLY_WIRED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - new/obj/item/stack/cable_coil(get_turf(src), 2) - WIRECUTTER_SNIP_MESSAGE - state = ASSEMBLY_WELDED - return - -/obj/item/camera_assembly/wrench_act(mob/user, obj/item/I) - if(state != ASSEMBLY_UNBUILT && state != ASSEMBLY_WRENCHED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(state == ASSEMBLY_UNBUILT && isturf(loc)) - WRENCH_ANCHOR_TO_WALL_MESSAGE - anchored = TRUE - state = ASSEMBLY_WRENCHED - update_icon() - auto_turn() - else if(state == ASSEMBLY_WRENCHED) - WRENCH_UNANCHOR_WALL_MESSAGE - anchored = FALSE - update_icon() - state = ASSEMBLY_UNBUILT - else - to_chat(user, "[src] can't fit here!") - -/obj/item/camera_assembly/welder_act(mob/user, obj/item/I) - if(state == ASSEMBLY_UNBUILT) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_WELD_MESSAGE - if(state == ASSEMBLY_WRENCHED) - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - to_chat(user, "You weld [src] into place.") - state = ASSEMBLY_WELDED - else if(state == ASSEMBLY_WELDED) - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - to_chat(user, "You unweld [src] from its place.") - state = ASSEMBLY_WRENCHED - -/obj/item/camera_assembly/update_icon() - if(anchored) - icon_state = "camera1" - else - icon_state = "cameracase" - -/obj/item/camera_assembly/attack_hand(mob/user as mob) - if(!anchored) - ..() - -/obj/item/camera_assembly/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc) - qdel(src) - - -#undef ASSEMBLY_UNBUILT -#undef ASSEMBLY_WRENCHED -#undef ASSEMBLY_WELDED -#undef ASSEMBLY_WIRED -#undef ASSEMBLY_BUILT -#undef HEY_IM_WORKING_HERE +#define ASSEMBLY_UNBUILT 0 // Nothing done to it +#define ASSEMBLY_WRENCHED 1 // Wrenched in place +#define ASSEMBLY_WELDED 2 // Welded in place +#define ASSEMBLY_WIRED 3 // Wires attached (Upgradable now) +#define ASSEMBLY_BUILT 4 // Fully built (incl panel closed) +#define HEY_IM_WORKING_HERE 666 //So nobody can mess with the camera while we're inputting settings + +/obj/item/camera_assembly + name = "camera assembly" + desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." + icon = 'icons/obj/monitors.dmi' + icon_state = "cameracase" + w_class = WEIGHT_CLASS_SMALL + anchored = FALSE + materials = list(MAT_METAL=400, MAT_GLASS=250) + // Motion, EMP-Proof, X-Ray + var/list/obj/item/possible_upgrades = list(/obj/item/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/analyzer) + var/list/upgrades = list() + var/state = ASSEMBLY_UNBUILT + + +/obj/item/camera_assembly/Destroy() + QDEL_LIST(upgrades) + return ..() + +/obj/item/camera_assembly/attackby(obj/item/I, mob/living/user, params) + if(state == ASSEMBLY_WELDED && iscoil(I)) + var/obj/item/stack/cable_coil/C = I + if(C.use(2)) + to_chat(user, "You add wires to the assembly.") + playsound(loc, I.usesound, 50, 1) + state = ASSEMBLY_WIRED + else + to_chat(user, "You need 2 coils of wire to wire the assembly.") + return + + // Upgrades! + else if(is_type_in_list(I, possible_upgrades) && !is_type_in_list(I, upgrades)) // Is a possible upgrade and isn't in the camera already. + if(!user.unEquip(I)) + to_chat(user, "[I] is stuck!") + return + to_chat(user, "You attach [I] into the assembly inner circuits.") + upgrades += I + user.drop_item() + I.loc = src + return + else + return ..() + +/obj/item/camera_assembly/crowbar_act(mob/user, obj/item/I) + if(!upgrades.len) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/obj/U = locate(/obj) in upgrades + if(U) + to_chat(user, "You unattach an upgrade from the assembly.") + playsound(loc, I.usesound, 50, 1) + U.loc = get_turf(src) + upgrades -= U + +/obj/item/camera_assembly/screwdriver_act(mob/user, obj/item/I) + if(state != ASSEMBLY_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + state = HEY_IM_WORKING_HERE + var/input = strip_html(input(usr, "Which networks would you like to connect this camera to? Seperate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret ", "Set Network", "SS13")) + if(!input) + state = ASSEMBLY_WIRED + to_chat(usr, "No input found please hang up and try your call again.") + return + + var/list/tempnetwork = splittext(input, ",") + if(tempnetwork.len < 1) + state = ASSEMBLY_WIRED + to_chat(usr, "No network found please hang up and try your call again.") + return + + var/area/camera_area = get_area(src) + var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" + input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag)) + state = ASSEMBLY_BUILT + var/obj/machinery/camera/C = new(loc) + loc = C + C.assembly = src + + C.auto_turn() + + C.network = uniquelist(tempnetwork) + tempnetwork = difflist(C.network,GLOB.restricted_camera_networks) + if(!tempnetwork.len) // Camera isn't on any open network - remove its chunk from AI visibility. + GLOB.cameranet.removeCamera(C) + + C.c_tag = input + + for(var/i = 5; i >= 0; i -= 1) + var/direct = input(user, "Direction?", "Assembling Camera", null) in list("LEAVE IT", "NORTH", "EAST", "SOUTH", "WEST" ) + if(direct != "LEAVE IT") + C.dir = text2dir(direct) + if(i != 0) + var/confirm = alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", "Yes", "No") + if(confirm == "Yes") + break + + +/obj/item/camera_assembly/wirecutter_act(mob/user, obj/item/I) + if(state != ASSEMBLY_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + new/obj/item/stack/cable_coil(get_turf(src), 2) + WIRECUTTER_SNIP_MESSAGE + state = ASSEMBLY_WELDED + return + +/obj/item/camera_assembly/wrench_act(mob/user, obj/item/I) + if(state != ASSEMBLY_UNBUILT && state != ASSEMBLY_WRENCHED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(state == ASSEMBLY_UNBUILT && isturf(loc)) + WRENCH_ANCHOR_TO_WALL_MESSAGE + anchored = TRUE + state = ASSEMBLY_WRENCHED + update_icon() + auto_turn() + else if(state == ASSEMBLY_WRENCHED) + WRENCH_UNANCHOR_WALL_MESSAGE + anchored = FALSE + update_icon() + state = ASSEMBLY_UNBUILT + else + to_chat(user, "[src] can't fit here!") + +/obj/item/camera_assembly/welder_act(mob/user, obj/item/I) + if(state == ASSEMBLY_UNBUILT) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(state == ASSEMBLY_WRENCHED) + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + to_chat(user, "You weld [src] into place.") + state = ASSEMBLY_WELDED + else if(state == ASSEMBLY_WELDED) + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + to_chat(user, "You unweld [src] from its place.") + state = ASSEMBLY_WRENCHED + +/obj/item/camera_assembly/update_icon() + if(anchored) + icon_state = "camera1" + else + icon_state = "cameracase" + +/obj/item/camera_assembly/attack_hand(mob/user as mob) + if(!anchored) + ..() + +/obj/item/camera_assembly/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc) + qdel(src) + + +#undef ASSEMBLY_UNBUILT +#undef ASSEMBLY_WRENCHED +#undef ASSEMBLY_WELDED +#undef ASSEMBLY_WIRED +#undef ASSEMBLY_BUILT +#undef HEY_IM_WORKING_HERE diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index 312e4cf577e..16fb6eca204 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -1,66 +1,66 @@ -/obj/machinery/camera - - var/list/motionTargets = list() - var/detectTime = 0 - var/area/ai_monitored/area_motion = null - var/alarm_delay = 100 - - -/obj/machinery/camera/process() - // motion camera event loop - if(stat & (EMPED|NOPOWER)) - return - if(!isMotion()) - . = PROCESS_KILL - return - if(detectTime > 0) - var/elapsed = world.time - detectTime - if(elapsed > alarm_delay) - triggerAlarm() - else if(detectTime == -1) - for(var/mob/target in motionTargets) - if(target.stat == 2) lostTarget(target) - // If not detecting with motion camera... - if(!area_motion) - // See if the camera is still in range - if(!in_range(src, target)) - // If they aren't in range, lose the target. - lostTarget(target) - -/obj/machinery/camera/proc/newTarget(var/mob/target) - if(istype(target, /mob/living/silicon/ai)) return 0 - if(detectTime == 0) - detectTime = world.time // start the clock - if(!(target in motionTargets)) - motionTargets += target - return 1 - -/obj/machinery/camera/proc/lostTarget(var/mob/target) - if(target in motionTargets) - motionTargets -= target - if(motionTargets.len == 0) - cancelAlarm() - -/obj/machinery/camera/proc/cancelAlarm() - if(!status || (stat & NOPOWER)) - return FALSE - if(detectTime == -1 && is_station_contact(z)) - SSalarms.motion_alarm.clearAlarm(loc, src) - detectTime = 0 - return TRUE - -/obj/machinery/camera/proc/triggerAlarm() - if(!status || (stat & NOPOWER)) - return FALSE - if(!detectTime || !is_station_contact(z)) - return FALSE - SSalarms.motion_alarm.triggerAlarm(loc, src) - detectTime = -1 - return TRUE - -/obj/machinery/camera/HasProximity(atom/movable/AM as mob|obj) - // Motion cameras outside of an "ai monitored" area will use this to detect stuff. - if(!area_motion) - if(isliving(AM)) - newTarget(AM) - +/obj/machinery/camera + + var/list/motionTargets = list() + var/detectTime = 0 + var/area/ai_monitored/area_motion = null + var/alarm_delay = 100 + + +/obj/machinery/camera/process() + // motion camera event loop + if(stat & (EMPED|NOPOWER)) + return + if(!isMotion()) + . = PROCESS_KILL + return + if(detectTime > 0) + var/elapsed = world.time - detectTime + if(elapsed > alarm_delay) + triggerAlarm() + else if(detectTime == -1) + for(var/mob/target in motionTargets) + if(target.stat == 2) lostTarget(target) + // If not detecting with motion camera... + if(!area_motion) + // See if the camera is still in range + if(!in_range(src, target)) + // If they aren't in range, lose the target. + lostTarget(target) + +/obj/machinery/camera/proc/newTarget(var/mob/target) + if(istype(target, /mob/living/silicon/ai)) return 0 + if(detectTime == 0) + detectTime = world.time // start the clock + if(!(target in motionTargets)) + motionTargets += target + return 1 + +/obj/machinery/camera/proc/lostTarget(var/mob/target) + if(target in motionTargets) + motionTargets -= target + if(motionTargets.len == 0) + cancelAlarm() + +/obj/machinery/camera/proc/cancelAlarm() + if(!status || (stat & NOPOWER)) + return FALSE + if(detectTime == -1 && is_station_contact(z)) + SSalarms.motion_alarm.clearAlarm(loc, src) + detectTime = 0 + return TRUE + +/obj/machinery/camera/proc/triggerAlarm() + if(!status || (stat & NOPOWER)) + return FALSE + if(!detectTime || !is_station_contact(z)) + return FALSE + SSalarms.motion_alarm.triggerAlarm(loc, src) + detectTime = -1 + return TRUE + +/obj/machinery/camera/HasProximity(atom/movable/AM as mob|obj) + // Motion cameras outside of an "ai monitored" area will use this to detect stuff. + if(!area_motion) + if(isliving(AM)) + newTarget(AM) + diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index ac70a7c11ba..c0949fb3c9d 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -1,92 +1,92 @@ -// PRESETS - -// EMP - -/obj/machinery/camera/emp_proof/New() - ..() - upgradeEmpProof() - -// X-RAY - -/obj/machinery/camera/xray - icon_state = "xraycam" // Thanks to Krutchen for the icons. - -/obj/machinery/camera/xray/New() - ..() - upgradeXRay() - -// MOTION - -/obj/machinery/camera/motion/New() - ..() - upgradeMotion() - -// ALL UPGRADES - -/obj/machinery/camera/all/New() - ..() - upgradeEmpProof() - upgradeXRay() - upgradeMotion() - -// AUTONAME - -/obj/machinery/camera/autoname - var/number = 0 //camera number in area - -//This camera type automatically sets it's name to whatever the area that it's in is called. -/obj/machinery/camera/autoname/New() - ..() - spawn(10) - number = 1 - var/area/A = get_area(src) - if(A) - for(var/obj/machinery/camera/autoname/C in world) - if(C == src) continue - var/area/CA = get_area(C) - if(CA.type == A.type) - if(C.number) - number = max(number, C.number+1) - c_tag = "[A.name] #[number]" - - -// CHECKS - -/obj/machinery/camera/proc/isEmpProof() - var/O = locate(/obj/item/stack/sheet/mineral/plasma) in assembly.upgrades - return O - -/obj/machinery/camera/proc/isXRay() - var/O = locate(/obj/item/analyzer) in assembly.upgrades - return O - -/obj/machinery/camera/proc/isMotion() - var/O = locate(/obj/item/assembly/prox_sensor) in assembly.upgrades - return O - -// UPGRADE PROCS - -/obj/machinery/camera/proc/upgradeEmpProof() - assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly)) - setPowerUsage() - -/obj/machinery/camera/proc/upgradeXRay() - assembly.upgrades.Add(new /obj/item/analyzer(assembly)) - setPowerUsage() - //Update what it can see. - cameranet.updateVisibility(src, 0) - -// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list. -/obj/machinery/camera/proc/upgradeMotion() - assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly)) - setPowerUsage() - // Add it to machines that process - START_PROCESSING(SSmachines, src) - -/obj/machinery/camera/proc/setPowerUsage() - var/mult = 1 - if(isXRay()) - mult++ - if(isMotion()) - mult++ - active_power_usage = mult*initial(active_power_usage) +// PRESETS + +// EMP + +/obj/machinery/camera/emp_proof/Initialize() + . = ..() + upgradeEmpProof() + +// X-RAY + +/obj/machinery/camera/xray + icon_state = "xraycam" // Thanks to Krutchen for the icons. + +/obj/machinery/camera/xray/Initialize() + . = ..() + upgradeXRay() + +// MOTION + +/obj/machinery/camera/motion/Initialize() + . = ..() + upgradeMotion() + +// ALL UPGRADES + +/obj/machinery/camera/all/Initialize() + . = ..() + upgradeEmpProof() + upgradeXRay() + upgradeMotion() + +// AUTONAME + +/obj/machinery/camera/autoname + var/number = 0 //camera number in area + +//This camera type automatically sets it's name to whatever the area that it's in is called. +/obj/machinery/camera/autoname/New() + ..() + spawn(10) + number = 1 + var/area/A = get_area(src) + if(A) + for(var/obj/machinery/camera/autoname/C in world) + if(C == src) continue + var/area/CA = get_area(C) + if(CA.type == A.type) + if(C.number) + number = max(number, C.number+1) + c_tag = "[A.name] #[number]" + + +// CHECKS + +/obj/machinery/camera/proc/isEmpProof() + var/O = locate(/obj/item/stack/sheet/mineral/plasma) in assembly.upgrades + return O + +/obj/machinery/camera/proc/isXRay() + var/O = locate(/obj/item/analyzer) in assembly.upgrades + return O + +/obj/machinery/camera/proc/isMotion() + var/O = locate(/obj/item/assembly/prox_sensor) in assembly.upgrades + return O + +// UPGRADE PROCS + +/obj/machinery/camera/proc/upgradeEmpProof() + assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly)) + setPowerUsage() + +/obj/machinery/camera/proc/upgradeXRay() + assembly.upgrades.Add(new /obj/item/analyzer(assembly)) + setPowerUsage() + //Update what it can see. + GLOB.cameranet.updateVisibility(src, 0) + +// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list. +/obj/machinery/camera/proc/upgradeMotion() + assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly)) + setPowerUsage() + // Add it to machines that process + START_PROCESSING(SSmachines, src) + +/obj/machinery/camera/proc/setPowerUsage() + var/mult = 1 + if(isXRay()) + mult++ + if(isMotion()) + mult++ + active_power_usage = mult*initial(active_power_usage) diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 277cee5beb3..1c247b23c03 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -1,260 +1,260 @@ -/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf) - if(!T) - return 1 - if(!is_level_reachable(T.z)) - return 1 - return 0 - - -/mob/living/silicon/ai/var/max_locations = 10 -/mob/living/silicon/ai/var/stored_locations[0] - -/mob/living/silicon/ai/proc/get_camera_list() - - track.cameras.Cut() - - if(src.stat == 2) - return - - var/list/L = list() - for(var/obj/machinery/camera/C in cameranet.cameras) - L.Add(C) - - camera_sort(L) - - var/list/T = list() - - for(var/obj/machinery/camera/C in L) - var/list/tempnetwork = C.network & src.network - if(tempnetwork.len) - T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C - - track.cameras = T - return T - - -/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) - set category = "AI Commands" - set name = "Show Camera List" - - if(src.stat == 2) - to_chat(src, "You can't list the cameras because you are dead!") - return - - if(!camera || camera == "Cancel") - return 0 - - var/obj/machinery/camera/C = track.cameras[camera] - src.eyeobj.setLoc(C) - - return - -/mob/living/silicon/ai/proc/ai_store_location(loc as text) - set category = "AI Commands" - set name = "Store Camera Location" - set desc = "Stores your current camera location by the given name" - - loc = sanitize(copytext(loc, 1, MAX_MESSAGE_LEN)) - if(!loc) - to_chat(src, "Must supply a location name") - return - - if(stored_locations.len >= max_locations) - to_chat(src, "Cannot store additional locations. Remove one first") - return - - if(loc in stored_locations) - to_chat(src, "There is already a stored location by this name") - return - - var/L = get_turf(eyeobj) - if(InvalidTurf(get_turf(L))) - to_chat(src, "Unable to store this location") - return - - stored_locations[loc] = L - to_chat(src, "Location '[loc]' stored") - -/mob/living/silicon/ai/proc/sorted_stored_locations() - return sortList(stored_locations) - -/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Goto Camera Location" - set desc = "Returns to the selected camera location" - - if(!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - var/L = stored_locations[loc] - src.eyeobj.setLoc(L) - -/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Delete Camera Location" - set desc = "Deletes the selected camera location" - - if(!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - stored_locations.Remove(loc) - to_chat(src, "Location [loc] removed") - -// Used to allow the AI is write in mob names/camera name from the CMD line. -/datum/trackable - var/list/names = list() - var/list/namecounts = list() - var/list/humans = list() - var/list/others = list() - var/list/cameras = list() - -/mob/living/silicon/ai/proc/trackable_mobs() - - track.names.Cut() - track.namecounts.Cut() - track.humans.Cut() - track.others.Cut() - - if(usr.stat == 2) - return list() - - for(var/mob/living/M in GLOB.mob_list) - if(!M.can_track(usr)) - continue - - // Human check - var/human = 0 - if(istype(M, /mob/living/carbon/human)) - human = 1 - - var/name = M.name - if(name in track.names) - track.namecounts[name]++ - name = text("[] ([])", name, track.namecounts[name]) - else - track.names.Add(name) - track.namecounts[name] = 1 - if(human) - track.humans[name] = M - else - track.others[name] = M - - var/list/targets = sortList(track.humans) + sortList(track.others) - - return targets - -/mob/living/silicon/ai/proc/ai_camera_track(target_name in trackable_mobs()) - set category = "AI Commands" - set name = "Track With Camera" - set desc = "Select who you would like to track." - - if(src.stat == DEAD) - to_chat(src, "You can't track with camera because you are dead!") - return - if(!target_name) - return - - var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) - - ai_actual_track(target) - -/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) - if(!cameraFollow) - return - - to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") - cameraFollow = null - -/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target) - if(!istype(target)) - return - var/mob/living/silicon/ai/U = usr - - U.cameraFollow = target - U.tracking = 1 - - to_chat(U, "Attempting to track [target.get_visible_name()]...") - sleep(min(30, get_dist(target, U.eyeobj) / 4)) - spawn(15) //give the AI a grace period to stop moving. - U.tracking = 0 - - if(!target || !target.can_track(usr)) - to_chat(U, "Target is not near any active cameras.") - U.cameraFollow = null - return - - to_chat(U, "Now tracking [target.get_visible_name()] on camera.") - - var/cameraticks = 0 - spawn(0) - while(U.cameraFollow == target) - if(U.cameraFollow == null) - return - - if(!target.can_track(usr)) - U.tracking = 1 - if(!cameraticks) - to_chat(U, "Target is not near any active cameras. Attempting to reacquire...") - cameraticks++ - if(cameraticks > 9) - U.cameraFollow = null - to_chat(U, "Unable to reacquire, cancelling track...") - U.tracking = 0 - return - else - sleep(10) - continue - - else - cameraticks = 0 - U.tracking = 0 - - if(U.eyeobj) - U.eyeobj.setLoc(get_turf(target)) - - else - view_core() - U.cameraFollow = null - return - - sleep(10) - -/proc/near_camera(mob/living/M) - if(!isturf(M.loc)) - return 0 - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(!(R.camera && R.camera.can_use()) && !cameranet.checkCameraVis(M)) - return 0 - else if(!cameranet.checkCameraVis(M)) - return 0 - return 1 - -/obj/machinery/camera/attack_ai(mob/living/silicon/ai/user) - if(!istype(user)) - return - if(!src.can_use()) - return - user.eyeobj.setLoc(get_turf(src)) - - -/mob/living/silicon/ai/attack_ai(mob/user) - ai_camera_list() - -/proc/camera_sort(list/L) - var/obj/machinery/camera/a - var/obj/machinery/camera/b - - for(var/i = L.len, i > 0, i--) - for(var/j = 1 to i - 1) - a = L[j] - b = L[j + 1] - if(a.c_tag_order != b.c_tag_order) - if(a.c_tag_order > b.c_tag_order) - L.Swap(j, j + 1) - else - if(sorttext(a.c_tag, b.c_tag) < 0) - L.Swap(j, j + 1) - return L +/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf) + if(!T) + return 1 + if(!is_level_reachable(T.z)) + return 1 + return 0 + + +/mob/living/silicon/ai/var/max_locations = 10 +/mob/living/silicon/ai/var/stored_locations[0] + +/mob/living/silicon/ai/proc/get_camera_list() + + track.cameras.Cut() + + if(src.stat == 2) + return + + var/list/L = list() + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + L.Add(C) + + camera_sort(L) + + var/list/T = list() + + for(var/obj/machinery/camera/C in L) + var/list/tempnetwork = C.network & src.network + if(tempnetwork.len) + T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C + + track.cameras = T + return T + + +/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) + set category = "AI Commands" + set name = "Show Camera List" + + if(src.stat == 2) + to_chat(src, "You can't list the cameras because you are dead!") + return + + if(!camera || camera == "Cancel") + return 0 + + var/obj/machinery/camera/C = track.cameras[camera] + src.eyeobj.setLoc(C) + + return + +/mob/living/silicon/ai/proc/ai_store_location(loc as text) + set category = "AI Commands" + set name = "Store Camera Location" + set desc = "Stores your current camera location by the given name" + + loc = sanitize(copytext(loc, 1, MAX_MESSAGE_LEN)) + if(!loc) + to_chat(src, "Must supply a location name") + return + + if(stored_locations.len >= max_locations) + to_chat(src, "Cannot store additional locations. Remove one first") + return + + if(loc in stored_locations) + to_chat(src, "There is already a stored location by this name") + return + + var/L = get_turf(eyeobj) + if(InvalidTurf(get_turf(L))) + to_chat(src, "Unable to store this location") + return + + stored_locations[loc] = L + to_chat(src, "Location '[loc]' stored") + +/mob/living/silicon/ai/proc/sorted_stored_locations() + return sortList(stored_locations) + +/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Goto Camera Location" + set desc = "Returns to the selected camera location" + + if(!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + var/L = stored_locations[loc] + src.eyeobj.setLoc(L) + +/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Delete Camera Location" + set desc = "Deletes the selected camera location" + + if(!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + stored_locations.Remove(loc) + to_chat(src, "Location [loc] removed") + +// Used to allow the AI is write in mob names/camera name from the CMD line. +/datum/trackable + var/list/names = list() + var/list/namecounts = list() + var/list/humans = list() + var/list/others = list() + var/list/cameras = list() + +/mob/living/silicon/ai/proc/trackable_mobs() + + track.names.Cut() + track.namecounts.Cut() + track.humans.Cut() + track.others.Cut() + + if(usr.stat == 2) + return list() + + for(var/mob/living/M in GLOB.mob_list) + if(!M.can_track(usr)) + continue + + // Human check + var/human = 0 + if(istype(M, /mob/living/carbon/human)) + human = 1 + + var/name = M.name + if(name in track.names) + track.namecounts[name]++ + name = text("[] ([])", name, track.namecounts[name]) + else + track.names.Add(name) + track.namecounts[name] = 1 + if(human) + track.humans[name] = M + else + track.others[name] = M + + var/list/targets = sortList(track.humans) + sortList(track.others) + + return targets + +/mob/living/silicon/ai/proc/ai_camera_track(target_name in trackable_mobs()) + set category = "AI Commands" + set name = "Track With Camera" + set desc = "Select who you would like to track." + + if(src.stat == DEAD) + to_chat(src, "You can't track with camera because you are dead!") + return + if(!target_name) + return + + var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) + + ai_actual_track(target) + +/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) + if(!cameraFollow) + return + + to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") + cameraFollow = null + +/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target) + if(!istype(target)) + return + var/mob/living/silicon/ai/U = usr + + U.cameraFollow = target + U.tracking = 1 + + to_chat(U, "Attempting to track [target.get_visible_name()]...") + sleep(min(30, get_dist(target, U.eyeobj) / 4)) + spawn(15) //give the AI a grace period to stop moving. + U.tracking = 0 + + if(!target || !target.can_track(usr)) + to_chat(U, "Target is not near any active cameras.") + U.cameraFollow = null + return + + to_chat(U, "Now tracking [target.get_visible_name()] on camera.") + + var/cameraticks = 0 + spawn(0) + while(U.cameraFollow == target) + if(U.cameraFollow == null) + return + + if(!target.can_track(usr)) + U.tracking = 1 + if(!cameraticks) + to_chat(U, "Target is not near any active cameras. Attempting to reacquire...") + cameraticks++ + if(cameraticks > 9) + U.cameraFollow = null + to_chat(U, "Unable to reacquire, cancelling track...") + U.tracking = 0 + return + else + sleep(10) + continue + + else + cameraticks = 0 + U.tracking = 0 + + if(U.eyeobj) + U.eyeobj.setLoc(get_turf(target)) + + else + view_core() + U.cameraFollow = null + return + + sleep(10) + +/proc/near_camera(mob/living/M) + if(!isturf(M.loc)) + return 0 + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(!(R.camera && R.camera.can_use()) && !GLOB.cameranet.checkCameraVis(M)) + return 0 + else if(!GLOB.cameranet.checkCameraVis(M)) + return 0 + return 1 + +/obj/machinery/camera/attack_ai(mob/living/silicon/ai/user) + if(!istype(user)) + return + if(!src.can_use()) + return + user.eyeobj.setLoc(get_turf(src)) + + +/mob/living/silicon/ai/attack_ai(mob/user) + ai_camera_list() + +/proc/camera_sort(list/L) + var/obj/machinery/camera/a + var/obj/machinery/camera/b + + for(var/i = L.len, i > 0, i--) + for(var/j = 1 to i - 1) + a = L[j] + b = L[j + 1] + if(a.c_tag_order != b.c_tag_order) + if(a.c_tag_order > b.c_tag_order) + L.Swap(j, j + 1) + else + if(sorttext(a.c_tag, b.c_tag) < 0) + L.Swap(j, j + 1) + return L diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index fb87d91e3f0..675d18ebb32 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -1,137 +1,137 @@ -/obj/machinery/cell_charger - name = "cell charger" - desc = "It charges power cells." - icon = 'icons/obj/power.dmi' - icon_state = "ccharger0" - anchored = 1 - use_power = IDLE_POWER_USE - idle_power_usage = 5 - active_power_usage = 60 - power_channel = EQUIP - pass_flags = PASSTABLE - var/obj/item/stock_parts/cell/charging = null - var/chargelevel = -1 - -/obj/machinery/cell_charger/deconstruct() - if(charging) - charging.forceMove(drop_location()) - return ..() - -/obj/machinery/cell_charger/Destroy() - QDEL_NULL(charging) - return ..() - -/obj/machinery/cell_charger/proc/updateicon() - icon_state = "ccharger[charging ? 1 : 0]" - - if(charging && !(stat & (BROKEN|NOPOWER))) - var/newlevel = round(charging.percent() * 4 / 100) - - if(chargelevel != newlevel) - chargelevel = newlevel - - overlays.Cut() - overlays += "ccharger-o[newlevel]" - - else - overlays.Cut() - -/obj/machinery/cell_charger/examine(mob/user) - . = ..() - . += "There's [charging ? "a" : "no"] cell in the charger." - if(charging) - . += "Current charge: [round(charging.percent(), 1)]%" - -/obj/machinery/cell_charger/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stock_parts/cell)) - if(stat & BROKEN) - to_chat(user, "[src] is broken!") - return - if(!anchored) - to_chat(user, "[src] isn't attached to the ground!") - return - if(charging) - to_chat(user, "There is already a cell in the charger!") - return - else - var/area/a = loc.loc // Gets our locations location, like a dream within a dream - if(!isarea(a)) - return - if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! - to_chat(user, "[src] blinks red as you try to insert the cell!") - return - if(!user.drop_item()) - return - - I.forceMove(src) - charging = I - user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") - chargelevel = -1 - updateicon() - else - return ..() - -/obj/machinery/cell_charger/wrench_act(mob/user, obj/item/I) - . = TRUE - if(charging) - to_chat(user, "Remove the cell first!") - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - anchored = !anchored - if(anchored) - WRENCH_ANCHOR_MESSAGE - else - WRENCH_UNANCHOR_MESSAGE - - -/obj/machinery/cell_charger/proc/removecell() - charging.update_icon() - charging = null - chargelevel = -1 - updateicon() - -/obj/machinery/cell_charger/attack_hand(mob/user) - if(!charging) - return - - user.put_in_hands(charging) - charging.add_fingerprint(user) - - user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") - - removecell() - -/obj/machinery/cell_charger/attack_tk(mob/user) - if(!charging) - return - - charging.forceMove(loc) - to_chat(user, "You telekinetically remove [charging] from [src].") - - removecell() - -/obj/machinery/cell_charger/attack_ai(mob/user) - return - -/obj/machinery/cell_charger/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - - if(charging) - charging.emp_act(severity) - - ..(severity) - - -/obj/machinery/cell_charger/process() - if(!charging || !anchored || (stat & (BROKEN|NOPOWER))) - return - - if(charging.percent() >= 100) - return - - use_power(200) //this used to use CELLRATE, but CELLRATE is fucking awful. feel free to fix this properly! - charging.give(175) //inefficiency. - - updateicon() +/obj/machinery/cell_charger + name = "cell charger" + desc = "It charges power cells." + icon = 'icons/obj/power.dmi' + icon_state = "ccharger0" + anchored = 1 + use_power = IDLE_POWER_USE + idle_power_usage = 5 + active_power_usage = 60 + power_channel = EQUIP + pass_flags = PASSTABLE + var/obj/item/stock_parts/cell/charging = null + var/chargelevel = -1 + +/obj/machinery/cell_charger/deconstruct() + if(charging) + charging.forceMove(drop_location()) + return ..() + +/obj/machinery/cell_charger/Destroy() + QDEL_NULL(charging) + return ..() + +/obj/machinery/cell_charger/proc/updateicon() + icon_state = "ccharger[charging ? 1 : 0]" + + if(charging && !(stat & (BROKEN|NOPOWER))) + var/newlevel = round(charging.percent() * 4 / 100) + + if(chargelevel != newlevel) + chargelevel = newlevel + + overlays.Cut() + overlays += "ccharger-o[newlevel]" + + else + overlays.Cut() + +/obj/machinery/cell_charger/examine(mob/user) + . = ..() + . += "There's [charging ? "a" : "no"] cell in the charger." + if(charging) + . += "Current charge: [round(charging.percent(), 1)]%" + +/obj/machinery/cell_charger/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stock_parts/cell)) + if(stat & BROKEN) + to_chat(user, "[src] is broken!") + return + if(!anchored) + to_chat(user, "[src] isn't attached to the ground!") + return + if(charging) + to_chat(user, "There is already a cell in the charger!") + return + else + var/area/a = loc.loc // Gets our locations location, like a dream within a dream + if(!isarea(a)) + return + if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! + to_chat(user, "[src] blinks red as you try to insert the cell!") + return + if(!user.drop_item()) + return + + I.forceMove(src) + charging = I + user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") + chargelevel = -1 + updateicon() + else + return ..() + +/obj/machinery/cell_charger/wrench_act(mob/user, obj/item/I) + . = TRUE + if(charging) + to_chat(user, "Remove the cell first!") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + anchored = !anchored + if(anchored) + WRENCH_ANCHOR_MESSAGE + else + WRENCH_UNANCHOR_MESSAGE + + +/obj/machinery/cell_charger/proc/removecell() + charging.update_icon() + charging = null + chargelevel = -1 + updateicon() + +/obj/machinery/cell_charger/attack_hand(mob/user) + if(!charging) + return + + user.put_in_hands(charging) + charging.add_fingerprint(user) + + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") + + removecell() + +/obj/machinery/cell_charger/attack_tk(mob/user) + if(!charging) + return + + charging.forceMove(loc) + to_chat(user, "You telekinetically remove [charging] from [src].") + + removecell() + +/obj/machinery/cell_charger/attack_ai(mob/user) + return + +/obj/machinery/cell_charger/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + + if(charging) + charging.emp_act(severity) + + ..(severity) + + +/obj/machinery/cell_charger/process() + if(!charging || !anchored || (stat & (BROKEN|NOPOWER))) + return + + if(charging.percent() >= 100) + return + + use_power(200) //this used to use CELLRATE, but CELLRATE is fucking awful. feel free to fix this properly! + charging.give(175) //inefficiency. + + updateicon() diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index b810c3a3b73..a98d7b08c13 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -1,658 +1,672 @@ -//Cloning revival method. -//The pod handles the actual cloning while the computer manages the clone profiles - -//Potential replacement for genetics revives or something I dunno (?) - -#define CLONE_BIOMASS 150 -#define BIOMASS_MEAT_AMOUNT 50 -#define MINIMUM_HEAL_LEVEL 40 -#define CLONE_INITIAL_DAMAGE 190 -#define BRAIN_INITIAL_DAMAGE 90 // our minds are too feeble for 190 - -/obj/machinery/clonepod - anchored = 1 - name = "cloning pod" - desc = "An electronically-lockable pod for growing organic tissue." - density = 1 - icon = 'icons/obj/cloning.dmi' - icon_state = "pod_0" - req_access = list(access_genetics) //For premature unlocking. - var/mob/living/carbon/human/occupant - var/heal_level //The clone is released once its health reaches this level. - var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. - var/mess = 0 //Need to clean out it if it's full of exploded clone. - var/attempting = 0 //One clone attempt at a time thanks - var/biomass = 0 - var/speed_coeff - var/efficiency - - var/datum/mind/clonemind - var/grab_ghost_when = CLONER_MATURE_CLONE - - var/obj/item/radio/Radio - var/radio_announce = 0 - - var/obj/effect/countdown/clonepod/countdown - - var/list/brine_types = list("corazone", "perfluorodecalin", "epinephrine", "salglu_solution") //stops heart attacks, heart failure, shock, and keeps their O2 levels normal - var/list/missing_organs - var/organs_number = 0 - - light_color = LIGHT_COLOR_PURE_GREEN - -/obj/machinery/clonepod/power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) - -/obj/machinery/clonepod/biomass - biomass = CLONE_BIOMASS - -/obj/machinery/clonepod/New() - ..() - countdown = new(src) - - Radio = new /obj/item/radio(src) - Radio.listening = 0 - Radio.config(list("Medical" = 0)) - - component_parts = list() - component_parts += new /obj/item/circuitboard/clonepod(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - update_icon() - -/obj/machinery/clonepod/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/clonepod(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - biomass = CLONE_BIOMASS - RefreshParts() - -/obj/machinery/clonepod/Destroy() - if(connected) - connected.pods -= src - for(var/s in sharedSoulhooks) - var/datum/soullink/S = s - S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed - sharedSoulhooks = null - QDEL_NULL(Radio) - QDEL_NULL(countdown) - QDEL_LIST(missing_organs) - return ..() - -/obj/machinery/clonepod/RefreshParts() - speed_coeff = 0 - efficiency = 0 - for(var/obj/item/stock_parts/scanning_module/S in component_parts) - efficiency += S.rating - for(var/obj/item/stock_parts/manipulator/P in component_parts) - speed_coeff += P.rating - heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) - -//The return of data disks?? Just for transferring between genetics machine/cloning machine. -//TO-DO: Make the genetics machine accept them. -/obj/item/disk/data - name = "Cloning Data Disk" - icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. - var/datum/dna2/record/buf = null - var/read_only = 0 //Well,it's still a floppy disk - -/obj/item/disk/data/proc/initialize() - buf = new - buf.dna=new - -/obj/item/disk/data/Destroy() - QDEL_NULL(buf) - return ..() - -/obj/item/disk/data/demo - name = "data disk - 'God Emperor of Mankind'" - read_only = 1 - -/obj/item/disk/data/demo/New() - ..() - initialize() - buf.types=DNA2_BUF_UE|DNA2_BUF_UI - //data = "066000033000000000AF00330660FF4DB002690" - //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff - buf.dna.real_name="God Emperor of Mankind" - buf.dna.unique_enzymes = md5(buf.dna.real_name) - buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0xAF0,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x033,0x066,0x0FF,0x4DB,0x002,0x690,0x000,0x000,0x000,0x328,0x045,0x5FC,0x053,0x035,0x035,0x035) - //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff - if(buf.dna.UI.len != DNA_UI_LENGTH) //If there's a disparity b/w the dna UI string lengths, 0-fill the extra blocks in this UI. - for(var/i in buf.dna.UI.len to DNA_UI_LENGTH) - buf.dna.UI += 0x000 - buf.dna.ResetSE() - buf.dna.UpdateUI() - -/obj/item/disk/data/monkey - name = "data disk - 'Mr. Muggles'" - read_only = 1 - -/obj/item/disk/data/monkey/New() - ..() - initialize() - buf.types=DNA2_BUF_SE - var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) - for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) - new_SE += rand(1,1024) - buf.dna.SE=new_SE - buf.dna.SetSEValueRange(MONKEYBLOCK,0xDAC, 0xFFF) - -//Disk stuff. -/obj/item/disk/data/New() - ..() - var/diskcolor = pick(0,1,2) - icon_state = "datadisk[diskcolor]" - -/obj/item/disk/data/attack_self(mob/user as mob) - read_only = !read_only - to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") - -/obj/item/disk/data/examine(mob/user) - . = ..() - . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." - -//Clonepod - -/obj/machinery/clonepod/examine(mob/user) - . = ..() - if(mess) - . += "It's filled with blood and viscera. You swear you can see it moving..." - if(!occupant || stat & (NOPOWER|BROKEN)) - return - if(occupant && occupant.stat != DEAD) - . += "Current clone cycle is [round(get_completion())]% complete." - -/obj/machinery/clonepod/return_air() //non-reactive air - var/datum/gas_mixture/GM = new - GM.nitrogen = MOLES_O2STANDARD + MOLES_N2STANDARD - GM.temperature = T20C - return GM - -/obj/machinery/clonepod/proc/get_completion() - . = (100 * ((occupant.health + 100) / (heal_level + 100))) - -/obj/machinery/clonepod/attack_ai(mob/user) - return examine(user) - -//Radio Announcement - -/obj/machinery/clonepod/proc/announce_radio_message(message) - if(radio_announce) - Radio.autosay(message, name, "Medical", list(z)) - -/obj/machinery/clonepod/proc/spooky_devil_flavor() - playsound(loc, pick('sound/goonstation/voice/male_scream.ogg', 'sound/goonstation/voice/female_scream.ogg'), 100, 1) - mess = 1 - update_icon() - connected_message("If you keep trying to steal from me, you'll end up with me.") - -//Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(datum/dna2/record/R) - if(mess || attempting || panel_open || stat & (NOPOWER|BROKEN)) - return 0 - clonemind = locate(R.mind) - if(!istype(clonemind)) //not a mind - return 0 - if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body - return 0 - if(clonemind.damnation_type) - spooky_devil_flavor() - return 0 - if(!clonemind.is_revivable()) //Other reasons for being unrevivable - return 0 - if(clonemind.active) //somebody is using that mind - if(ckey(clonemind.key) != R.ckey ) - return 0 - if(clonemind.suicided) // and stay out! - malfunction(go_easy = 0) - return -1 // Flush the record - else - // get_ghost() will fail if they're unable to reenter their body - var/mob/dead/observer/G = clonemind.get_ghost() - if(!G) - return 0 - -/* - if(clonemind.damnation_type) //Can't clone the damned. - playsound('sound/hallucinations/veryfar_noise.ogg', 50, 0) - malfunction() - return -1 // so that the record gets flushed out - */ - - if(biomass >= CLONE_BIOMASS) - biomass -= CLONE_BIOMASS - else - return 0 - - attempting = 1 //One at a time!! - countdown.start() - - if(!R.dna) - R.dna = new /datum/dna() - - var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) - H.set_species(R.dna.species.type) - occupant = H - - if(!R.dna.real_name) //to prevent null names - R.dna.real_name = H.real_name - else - H.real_name = R.dna.real_name - - H.dna = R.dna.Clone() - - for(var/datum/language/L in R.languages) - H.add_language(L.name) - - domutcheck(H, null, MUTCHK_FORCED) //Ensures species that get powers by the species proc handle_dna keep them - - if(efficiency > 2 && efficiency < 5 && prob(25)) - randmutb(H) - if(efficiency > 5 && prob(20)) - randmutg(H) - if(efficiency < 3 && prob(50)) - randmutb(H) - - H.dna.UpdateSE() - H.dna.UpdateUI() - - H.sync_organ_dna(1) // It's literally a fresh body as you can get, so all organs properly belong to it - H.UpdateAppearance() - - check_brine() - //Get the clone body ready - maim_clone(H) - H.Paralyse(4) - - if(grab_ghost_when == CLONER_FRESH_CLONE) - clonemind.transfer_to(H) - H.ckey = R.ckey - update_clone_antag(H) //Since the body's got the mind, update their antag stuff right now. Otherwise, wait until they get kicked out (as per the CLONER_MATURE_CLONE business) to do it. - to_chat(H, {"Consciousness slowly creeps over you - as your body regenerates.
    So this is what cloning - feels like?
    "}) - else if(grab_ghost_when == CLONER_MATURE_CLONE) - to_chat(clonemind.current, {"Your body is - beginning to regenerate in a cloning pod. You will - become conscious when it is complete."}) - // Set up a soul link with the dead body to catch a revival - soullink(/datum/soullink/soulhook, clonemind.current, src) - - update_icon() - - H.suiciding = 0 - attempting = 0 - return 1 - -//Grow clones to maturity then kick them out. FREELOADERS -/obj/machinery/clonepod/process() - var/show_message = 0 - for(var/obj/item/reagent_containers/food/snacks/meat/meat in range(1, src)) - qdel(meat) - biomass += BIOMASS_MEAT_AMOUNT - show_message = 1 - if(show_message) - visible_message("[src] sucks in and processes the nearby biomass.") - - if(stat & NOPOWER) //Autoeject if power is lost - if(occupant) - go_out() - connected_message("Clone Ejected: Loss of power.") - - else if((occupant) && (occupant.loc == src)) - if((occupant.stat == DEAD) || (occupant.suiciding) || (occupant.mind && !occupant.mind.is_revivable())) //Autoeject corpses and suiciding dudes. - announce_radio_message("The cloning of [occupant] has been aborted due to unrecoverable tissue failure.") - go_out() - connected_message("Clone Rejected: Deceased.") - - else if(occupant.cloneloss > (100 - heal_level)) - occupant.Paralyse(4) - - //Slowly get that clone healed and finished. - occupant.adjustCloneLoss(-((speed_coeff/2))) - - // For human species that lack non-vital parts for some weird reason - if(organs_number) - var/progress = CLONE_INITIAL_DAMAGE - occupant.getCloneLoss() - progress += (100 - MINIMUM_HEAL_LEVEL) - var/milestone = CLONE_INITIAL_DAMAGE / organs_number -// Doing this as a #define so that the value can change when evaluated multiple times -#define INSTALLED (organs_number - LAZYLEN(missing_organs)) - - while((progress / milestone) > INSTALLED && LAZYLEN(missing_organs)) - var/obj/item/organ/I = pick_n_take(missing_organs) - I.safe_replace(occupant) - -#undef INSTALLED - - //Premature clones may have brain damage. - occupant.adjustBrainLoss(-((speed_coeff/20)*efficiency)) - - check_brine() - - //Also heal some oxyloss ourselves just in case!! - occupant.adjustOxyLoss(-10) - - use_power(7500) //This might need tweaking. - - else if((occupant.cloneloss <= (100 - heal_level))) - connected_message("Cloning Process Complete.") - announce_radio_message("The cloning cycle of [occupant] is complete.") - go_out() - - else if((!occupant) || (occupant.loc != src)) - occupant = null - update_icon() - use_power(200) - -//Let's unlock this early I guess. Might be too early, needs tweaking. -/obj/machinery/clonepod/attackby(obj/item/I, mob/user, params) - if(exchange_parts(user, I)) - return - - if(I.GetID()) - if(!check_access(I)) - to_chat(user, "Access Denied.") - return - if(!(occupant || mess)) - to_chat(user, "Error: Pod has no occupant.") - return - else - connected_message("Authorized Ejection") - announce_radio_message("An authorized ejection of [(occupant) ? occupant.real_name : "the malfunctioning pod"] has occured") - to_chat(user, "You force an emergency ejection.") - go_out() - -//Removing cloning pod biomass - else if(istype(I, /obj/item/reagent_containers/food/snacks/meat)) - if(user.drop_item()) - to_chat(user, "[src] processes [I].") - biomass += BIOMASS_MEAT_AMOUNT - qdel(I) - else - return ..() - -/obj/machinery/clonepod/crowbar_act(mob/user, obj/item/I) - . = TRUE - default_deconstruction_crowbar(user, I) - -/obj/machinery/clonepod/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!I.multitool_check_buffer(user)) - return - var/obj/item/multitool/M = I - M.set_multitool_buffer(user, src) - -/obj/machinery/clonepod/screwdriver_act(mob/user, obj/item/I) - . = TRUE - default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]", I) - -/obj/machinery/clonepod/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(occupant) - to_chat(user, "Can not do that while [src] is in use.") - return - if(anchored) - WRENCH_UNANCHOR_MESSAGE - anchored = FALSE - connected.pods -= src - connected = null - else - WRENCH_ANCHOR_MESSAGE - anchored = TRUE - -/obj/machinery/clonepod/emag_act(user) - if(isnull(occupant)) - return - go_out() - -/obj/machinery/clonepod/proc/update_clone_antag(var/mob/living/carbon/human/H) - // Check to see if the clone's mind is an antagonist of any kind and handle them accordingly to make sure they get their spells, HUD/whatever else back. - if((H.mind in SSticker.mode:revolutionaries) || (H.mind in SSticker.mode:head_revolutionaries)) - SSticker.mode.update_rev_icons_added() //So the icon actually appears - if(H.mind in SSticker.mode.syndicates) - SSticker.mode.update_synd_icons_added() - if(H.mind in SSticker.mode.cult) - SSticker.mode.add_cultist(occupant.mind) - SSticker.mode.update_cult_icons_added() //So the icon actually appears - SSticker.mode.update_cult_comms_added(H.mind) //So the comms actually appears - if(H.mind.vampire) - H.mind.vampire.update_owner(H) - if((H.mind in SSticker.mode.vampire_thralls) || (H.mind in SSticker.mode.vampire_enthralled)) - SSticker.mode.update_vampire_icons_added(H.mind) - if(H.mind in SSticker.mode.changelings) - SSticker.mode.update_change_icons_added(H.mind) - if((H.mind in SSticker.mode.shadowling_thralls) || (H.mind in SSticker.mode.shadows)) - SSticker.mode.update_shadow_icons_added(H.mind) - -//Put messages in the connected computer's temp var for display. -/obj/machinery/clonepod/proc/connected_message(message) - if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) - return 0 - if(!message) - return 0 - - connected.temp = "[name] : [message]" - connected.updateUsrDialog() - return 1 - -/obj/machinery/clonepod/proc/go_out() - countdown.stop() - var/turf/T = get_turf(src) - if(mess) //Clean that mess and dump those gibs! - for(var/i in missing_organs) - var/obj/I = i - I.forceMove(T) - missing_organs.Cut() - mess = FALSE - new /obj/effect/gibspawner/generic(get_turf(src), occupant) - playsound(loc, 'sound/effects/splat.ogg', 50, 1) - update_icon() - return - - if(!occupant) - return - - if(grab_ghost_when == CLONER_MATURE_CLONE) - clonemind.transfer_to(occupant) - occupant.grab_ghost() - update_clone_antag(occupant) - to_chat(occupant, "There is a bright flash!
    \ - You feel like a new being.
    ") - occupant.flash_eyes(visual = 1) - for(var/s in sharedSoulhooks) - var/datum/soullink/S = s - S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed - sharedSoulhooks = null - - - for(var/i in missing_organs) - qdel(i) - missing_organs.Cut() - occupant.SetLoseBreath(0) // Stop friggin' dying, gosh damn - occupant.setOxyLoss(0) - for(var/datum/disease/critical/crit in occupant.viruses) - crit.cure() - occupant.forceMove(T) - occupant.update_body() - domutcheck(occupant) //Waiting until they're out before possible notransform. - occupant.special_post_clone_handling() - occupant = null - update_icon() - -/obj/machinery/clonepod/proc/malfunction(go_easy = FALSE) - if(occupant) - connected_message("Critical Error!") - announce_radio_message("Critical error! Please contact a Thinktronic Systems technician, as your warranty may be affected.") - for(var/s in sharedSoulhooks) - var/datum/soullink/S = s - S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed - sharedSoulhooks = null - if(!go_easy) - if(occupant.mind != clonemind) - clonemind.transfer_to(occupant) - occupant.grab_ghost() // We really just want to make you suffer. - to_chat(occupant, {"Agony blazes across your - consciousness as your body is torn apart.
    - Is this what dying is like? Yes it is.
    "}) - occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50) - for(var/i in missing_organs) - qdel(i) - missing_organs.Cut() - spawn(40) - qdel(occupant) - - - playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, 0) - mess = TRUE - update_icon() - -/obj/machinery/clonepod/update_icon() - ..() - icon_state = "pod_0" - if(occupant && !(stat & NOPOWER)) - icon_state = "pod_1" - else if(mess && !panel_open) - icon_state = "pod_g" - -/obj/machinery/clonepod/relaymove(mob/user) - if(user.stat == CONSCIOUS) - go_out() - -/obj/machinery/clonepod/emp_act(severity) - if(prob(100/(severity*efficiency))) malfunction() - ..() - -/obj/machinery/clonepod/ex_act(severity) - ..() - if(!QDELETED(src) && occupant) - go_out() - -/obj/machinery/clonepod/handle_atom_del(atom/A) - if(A == occupant) - occupant = null - countdown.stop() - -/obj/machinery/clonepod/deconstruct(disassembled = TRUE) - if(occupant) - go_out() - ..() - -/obj/machinery/clonepod/onSoullinkRevive(mob/living/L) - if(occupant && L == clonemind.current) - // The old body's back in shape, time to ditch the cloning one - malfunction(go_easy = TRUE) - -/obj/machinery/clonepod/proc/maim_clone(mob/living/carbon/human/H) - LAZYINITLIST(missing_organs) - for(var/i in missing_organs) - qdel(i) - missing_organs.Cut() - - H.setCloneLoss(CLONE_INITIAL_DAMAGE, FALSE) - H.setBrainLoss(BRAIN_INITIAL_DAMAGE) - - for(var/o in H.internal_organs) - var/obj/item/organ/O = o - if(!istype(O) || O.vital) - continue - - // Let's non-specially remove all non-vital organs - // What could possibly go wrong - var/obj/item/I = O.remove(H) - // Make this support stuff that turns into items when removed - I.forceMove(src) - missing_organs += I - - var/static/list/zones = list("r_arm", "l_arm", "r_leg", "l_leg") - for(var/zone in zones) - var/obj/item/organ/external/E = H.get_organ(zone) - var/obj/item/I = E.remove(H) - I.forceMove(src) - missing_organs += I - - organs_number = LAZYLEN(missing_organs) - H.updatehealth() - -/obj/machinery/clonepod/proc/check_brine() - // Clones are in a pickled bath of mild chemicals, keeping - // them alive, despite their lack of internal organs - for(var/bt in brine_types) - if(occupant.reagents.get_reagent_amount(bt) < 1) - occupant.reagents.add_reagent(bt, 1) - -/* - * Diskette Box - */ - -/obj/item/storage/box/disks - name = "Diskette Box" - icon_state = "disk_kit" - -/obj/item/storage/box/disks/New() - ..() - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - -/* - * Manual -- A big ol' manual. - */ - -/obj/item/paper/Cloning - name = "paper - 'H-87 Cloning Apparatus Manual" - info = {"

    Getting Started

    - Congratulations, your station has purchased the H-87 industrial cloning device!
    - Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
    - That's all there is to it!
    - Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
    -

    Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. - Profile Deletion has been restricted to \[Station Head\] level access.

    -

    Cloning from a profile

    - Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
    - Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
    -
    -

    The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. - The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


    - Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
    -

    Profile Management

    -

    The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. - These diskettes are used to transfer genetic information between machines and profiles. - A load/save dialog will become available in each profile if a disk is inserted.


    - A good diskette is a great way to counter aforementioned genetic drift!
    -
    - This technology produced under license from Thinktronic Systems, LTD."} - -//SOME SCRAPS I GUESS -/* EMP grenade/spell effect - if(istype(A, /obj/machinery/clonepod)) - A:malfunction() -*/ - -#undef MINIMUM_HEAL_LEVEL +//Cloning revival method. +//The pod handles the actual cloning while the computer manages the clone profiles + +//Potential replacement for genetics revives or something I dunno (?) + +#define CLONE_BIOMASS 150 + +#define BIOMASS_BASE_AMOUNT 50 // How much biomass a BIOMASSABLE item gives the cloning pod + +// Not a comprehensive list: Further PRs should add appropriate items here. +// Meat as usual, monstermeat covers goliath, xeno, spider, bear meat +GLOBAL_LIST_INIT(cloner_biomass_items, list(\ +/obj/item/reagent_containers/food/snacks/meat,\ +/obj/item/reagent_containers/food/snacks/monstermeat, +/obj/item/reagent_containers/food/snacks/carpmeat, +/obj/item/reagent_containers/food/snacks/salmonmeat, +/obj/item/reagent_containers/food/snacks/catfishmeat, +/obj/item/reagent_containers/food/snacks/tofurkey)) + +#define MINIMUM_HEAL_LEVEL 40 +#define CLONE_INITIAL_DAMAGE 190 +#define BRAIN_INITIAL_DAMAGE 90 // our minds are too feeble for 190 + +/obj/machinery/clonepod + anchored = 1 + name = "cloning pod" + desc = "An electronically-lockable pod for growing organic tissue." + density = 1 + icon = 'icons/obj/cloning.dmi' + icon_state = "pod_0" + req_access = list(ACCESS_GENETICS) //For premature unlocking. + + var/mob/living/carbon/human/occupant + var/heal_level //The clone is released once its health reaches this level. + var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. + var/mess = 0 //Need to clean out it if it's full of exploded clone. + var/attempting = 0 //One clone attempt at a time thanks + var/biomass = 0 + var/speed_coeff + var/efficiency + + var/datum/mind/clonemind + var/grab_ghost_when = CLONER_MATURE_CLONE + + var/obj/item/radio/Radio + var/radio_announce = 0 + + var/obj/effect/countdown/clonepod/countdown + + var/list/brine_types = list("corazone", "perfluorodecalin", "epinephrine", "salglu_solution") //stops heart attacks, heart failure, shock, and keeps their O2 levels normal + var/list/missing_organs + var/organs_number = 0 + + light_color = LIGHT_COLOR_PURE_GREEN + +/obj/machinery/clonepod/power_change() + ..() + if(!(stat & (BROKEN|NOPOWER))) + set_light(2) + else + set_light(0) + +/obj/machinery/clonepod/biomass + biomass = CLONE_BIOMASS + +/obj/machinery/clonepod/New() + ..() + countdown = new(src) + + Radio = new /obj/item/radio(src) + Radio.listening = 0 + Radio.config(list("Medical" = 0)) + + component_parts = list() + component_parts += new /obj/item/circuitboard/clonepod(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + update_icon() + +/obj/machinery/clonepod/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/clonepod(null) + component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) + component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + biomass = CLONE_BIOMASS + RefreshParts() + +/obj/machinery/clonepod/Destroy() + if(connected) + connected.pods -= src + for(var/s in sharedSoulhooks) + var/datum/soullink/S = s + S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed + sharedSoulhooks = null + QDEL_NULL(Radio) + QDEL_NULL(countdown) + QDEL_LIST(missing_organs) + return ..() + +/obj/machinery/clonepod/RefreshParts() + speed_coeff = 0 + efficiency = 0 + for(var/obj/item/stock_parts/scanning_module/S in component_parts) + efficiency += S.rating + for(var/obj/item/stock_parts/manipulator/P in component_parts) + speed_coeff += P.rating + heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) + +//The return of data disks?? Just for transferring between genetics machine/cloning machine. +//TO-DO: Make the genetics machine accept them. +/obj/item/disk/data + name = "Cloning Data Disk" + icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. + var/datum/dna2/record/buf = null + var/read_only = 0 //Well,it's still a floppy disk + +/obj/item/disk/data/proc/initialize() + buf = new + buf.dna=new + +/obj/item/disk/data/Destroy() + QDEL_NULL(buf) + return ..() + +/obj/item/disk/data/demo + name = "data disk - 'God Emperor of Mankind'" + read_only = 1 + +/obj/item/disk/data/demo/New() + ..() + initialize() + buf.types=DNA2_BUF_UE|DNA2_BUF_UI + //data = "066000033000000000AF00330660FF4DB002690" + //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff + buf.dna.real_name="God Emperor of Mankind" + buf.dna.unique_enzymes = md5(buf.dna.real_name) + buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0xAF0,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x033,0x066,0x0FF,0x4DB,0x002,0x690,0x000,0x000,0x000,0x328,0x045,0x5FC,0x053,0x035,0x035,0x035) + //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff + if(buf.dna.UI.len != DNA_UI_LENGTH) //If there's a disparity b/w the dna UI string lengths, 0-fill the extra blocks in this UI. + for(var/i in buf.dna.UI.len to DNA_UI_LENGTH) + buf.dna.UI += 0x000 + buf.dna.ResetSE() + buf.dna.UpdateUI() + +/obj/item/disk/data/monkey + name = "data disk - 'Mr. Muggles'" + read_only = 1 + +/obj/item/disk/data/monkey/New() + ..() + initialize() + buf.types=DNA2_BUF_SE + var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) + for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) + new_SE += rand(1,1024) + buf.dna.SE=new_SE + buf.dna.SetSEValueRange(GLOB.monkeyblock,0xDAC, 0xFFF) + +//Disk stuff. +/obj/item/disk/data/New() + ..() + var/diskcolor = pick(0,1,2) + icon_state = "datadisk[diskcolor]" + +/obj/item/disk/data/attack_self(mob/user as mob) + read_only = !read_only + to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") + +/obj/item/disk/data/examine(mob/user) + . = ..() + . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." + +//Clonepod + +/obj/machinery/clonepod/examine(mob/user) + . = ..() + if(mess) + . += "It's filled with blood and viscera. You swear you can see it moving..." + if(!occupant || stat & (NOPOWER|BROKEN)) + return + if(occupant && occupant.stat != DEAD) + . += "Current clone cycle is [round(get_completion())]% complete." + +/obj/machinery/clonepod/return_air() //non-reactive air + var/datum/gas_mixture/GM = new + GM.nitrogen = MOLES_O2STANDARD + MOLES_N2STANDARD + GM.temperature = T20C + return GM + +/obj/machinery/clonepod/proc/get_completion() + . = (100 * ((occupant.health + 100) / (heal_level + 100))) + +/obj/machinery/clonepod/attack_ai(mob/user) + return examine(user) + +//Radio Announcement + +/obj/machinery/clonepod/proc/announce_radio_message(message) + if(radio_announce) + Radio.autosay(message, name, "Medical", list(z)) + +/obj/machinery/clonepod/proc/spooky_devil_flavor() + playsound(loc, pick('sound/goonstation/voice/male_scream.ogg', 'sound/goonstation/voice/female_scream.ogg'), 100, 1) + mess = 1 + update_icon() + connected_message("If you keep trying to steal from me, you'll end up with me.") + +//Start growing a human clone in the pod! +/obj/machinery/clonepod/proc/growclone(datum/dna2/record/R) + if(mess || attempting || panel_open || stat & (NOPOWER|BROKEN)) + return 0 + clonemind = locate(R.mind) + if(!istype(clonemind)) //not a mind + return 0 + if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body + return 0 + if(clonemind.damnation_type) + spooky_devil_flavor() + return 0 + if(!clonemind.is_revivable()) //Other reasons for being unrevivable + return 0 + if(clonemind.active) //somebody is using that mind + if(ckey(clonemind.key) != R.ckey ) + return 0 + if(clonemind.suicided) // and stay out! + malfunction(go_easy = 0) + return -1 // Flush the record + else + // get_ghost() will fail if they're unable to reenter their body + var/mob/dead/observer/G = clonemind.get_ghost() + if(!G) + return 0 + +/* + if(clonemind.damnation_type) //Can't clone the damned. + playsound('sound/hallucinations/veryfar_noise.ogg', 50, 0) + malfunction() + return -1 // so that the record gets flushed out + */ + + if(biomass >= CLONE_BIOMASS) + biomass -= CLONE_BIOMASS + else + return 0 + + attempting = 1 //One at a time!! + countdown.start() + + if(!R.dna) + R.dna = new /datum/dna() + + var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) + H.set_species(R.dna.species.type) + occupant = H + + if(!R.dna.real_name) //to prevent null names + R.dna.real_name = H.real_name + else + H.real_name = R.dna.real_name + + H.dna = R.dna.Clone() + + for(var/datum/language/L in R.languages) + H.add_language(L.name) + + domutcheck(H, null, MUTCHK_FORCED) //Ensures species that get powers by the species proc handle_dna keep them + + if(efficiency > 2 && efficiency < 5 && prob(25)) + randmutb(H) + if(efficiency > 5 && prob(20)) + randmutg(H) + if(efficiency < 3 && prob(50)) + randmutb(H) + + H.dna.UpdateSE() + H.dna.UpdateUI() + + H.sync_organ_dna(1) // It's literally a fresh body as you can get, so all organs properly belong to it + H.UpdateAppearance() + + check_brine() + //Get the clone body ready + maim_clone(H) + H.Paralyse(4) + + if(grab_ghost_when == CLONER_FRESH_CLONE) + clonemind.transfer_to(H) + H.ckey = R.ckey + update_clone_antag(H) //Since the body's got the mind, update their antag stuff right now. Otherwise, wait until they get kicked out (as per the CLONER_MATURE_CLONE business) to do it. + to_chat(H, {"Consciousness slowly creeps over you + as your body regenerates.
    So this is what cloning + feels like?
    "}) + else if(grab_ghost_when == CLONER_MATURE_CLONE) + to_chat(clonemind.current, {"Your body is + beginning to regenerate in a cloning pod. You will + become conscious when it is complete."}) + // Set up a soul link with the dead body to catch a revival + soullink(/datum/soullink/soulhook, clonemind.current, src) + + update_icon() + + H.suiciding = 0 + attempting = 0 + return 1 + +//Grow clones to maturity then kick them out. FREELOADERS +/obj/machinery/clonepod/process() + var/show_message = FALSE + for(var/obj/item/item in range(1, src)) + if(is_type_in_list(item, GLOB.cloner_biomass_items)) + qdel(item) + biomass += BIOMASS_BASE_AMOUNT + show_message = TRUE + if(show_message) + visible_message("[src] sucks in and processes the nearby biomass.") + + if(stat & NOPOWER) //Autoeject if power is lost + if(occupant) + go_out() + connected_message("Clone Ejected: Loss of power.") + + else if((occupant) && (occupant.loc == src)) + if((occupant.stat == DEAD) || (occupant.suiciding) || (occupant.mind && !occupant.mind.is_revivable())) //Autoeject corpses and suiciding dudes. + announce_radio_message("The cloning of [occupant] has been aborted due to unrecoverable tissue failure.") + go_out() + connected_message("Clone Rejected: Deceased.") + + else if(occupant.cloneloss > (100 - heal_level)) + occupant.Paralyse(4) + + //Slowly get that clone healed and finished. + occupant.adjustCloneLoss(-((speed_coeff/2))) + + // For human species that lack non-vital parts for some weird reason + if(organs_number) + var/progress = CLONE_INITIAL_DAMAGE - occupant.getCloneLoss() + progress += (100 - MINIMUM_HEAL_LEVEL) + var/milestone = CLONE_INITIAL_DAMAGE / organs_number +// Doing this as a #define so that the value can change when evaluated multiple times +#define INSTALLED (organs_number - LAZYLEN(missing_organs)) + + while((progress / milestone) > INSTALLED && LAZYLEN(missing_organs)) + var/obj/item/organ/I = pick_n_take(missing_organs) + I.safe_replace(occupant) + +#undef INSTALLED + + //Premature clones may have brain damage. + occupant.adjustBrainLoss(-((speed_coeff/20)*efficiency)) + + check_brine() + + //Also heal some oxyloss ourselves just in case!! + occupant.adjustOxyLoss(-10) + + use_power(7500) //This might need tweaking. + + else if((occupant.cloneloss <= (100 - heal_level))) + connected_message("Cloning Process Complete.") + announce_radio_message("The cloning cycle of [occupant] is complete.") + go_out() + + else if((!occupant) || (occupant.loc != src)) + occupant = null + update_icon() + use_power(200) + +//Let's unlock this early I guess. Might be too early, needs tweaking. +/obj/machinery/clonepod/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + + if(I.GetID()) + if(!check_access(I)) + to_chat(user, "Access Denied.") + return + if(!(occupant || mess)) + to_chat(user, "Error: Pod has no occupant.") + return + else + connected_message("Authorized Ejection") + announce_radio_message("An authorized ejection of [(occupant) ? occupant.real_name : "the malfunctioning pod"] has occured") + to_chat(user, "You force an emergency ejection.") + go_out() + +// A user can feed in biomass sources manually. + else if(is_type_in_list(I, GLOB.cloner_biomass_items)) + if(user.drop_item()) + to_chat(user, "[src] processes [I].") + biomass += BIOMASS_BASE_AMOUNT + qdel(I) + else + return ..() + +/obj/machinery/clonepod/crowbar_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_crowbar(user, I) + +/obj/machinery/clonepod/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!I.multitool_check_buffer(user)) + return + var/obj/item/multitool/M = I + M.set_multitool_buffer(user, src) + +/obj/machinery/clonepod/screwdriver_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]", I) + +/obj/machinery/clonepod/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(occupant) + to_chat(user, "Can not do that while [src] is in use.") + return + if(anchored) + WRENCH_UNANCHOR_MESSAGE + anchored = FALSE + connected.pods -= src + connected = null + else + WRENCH_ANCHOR_MESSAGE + anchored = TRUE + +/obj/machinery/clonepod/emag_act(user) + if(isnull(occupant)) + return + go_out() + +/obj/machinery/clonepod/proc/update_clone_antag(var/mob/living/carbon/human/H) + // Check to see if the clone's mind is an antagonist of any kind and handle them accordingly to make sure they get their spells, HUD/whatever else back. + if((H.mind in SSticker.mode:revolutionaries) || (H.mind in SSticker.mode:head_revolutionaries)) + SSticker.mode.update_rev_icons_added() //So the icon actually appears + if(H.mind in SSticker.mode.syndicates) + SSticker.mode.update_synd_icons_added() + if(H.mind in SSticker.mode.cult) + SSticker.mode.add_cultist(occupant.mind) + SSticker.mode.update_cult_icons_added() //So the icon actually appears + SSticker.mode.update_cult_comms_added(H.mind) //So the comms actually appears + if(H.mind.vampire) + H.mind.vampire.update_owner(H) + if((H.mind in SSticker.mode.vampire_thralls) || (H.mind in SSticker.mode.vampire_enthralled)) + SSticker.mode.update_vampire_icons_added(H.mind) + if(H.mind in SSticker.mode.changelings) + SSticker.mode.update_change_icons_added(H.mind) + if((H.mind in SSticker.mode.shadowling_thralls) || (H.mind in SSticker.mode.shadows)) + SSticker.mode.update_shadow_icons_added(H.mind) + +//Put messages in the connected computer's temp var for display. +/obj/machinery/clonepod/proc/connected_message(message) + if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) + return 0 + if(!message) + return 0 + + connected.temp = "[name] : [message]" + connected.updateUsrDialog() + return 1 + +/obj/machinery/clonepod/proc/go_out() + countdown.stop() + var/turf/T = get_turf(src) + if(mess) //Clean that mess and dump those gibs! + for(var/i in missing_organs) + var/obj/I = i + I.forceMove(T) + missing_organs.Cut() + mess = FALSE + new /obj/effect/gibspawner/generic(get_turf(src), occupant) + playsound(loc, 'sound/effects/splat.ogg', 50, 1) + update_icon() + return + + if(!occupant) + return + + if(grab_ghost_when == CLONER_MATURE_CLONE) + clonemind.transfer_to(occupant) + occupant.grab_ghost() + update_clone_antag(occupant) + to_chat(occupant, "There is a bright flash!
    \ + You feel like a new being.
    ") + occupant.flash_eyes(visual = 1) + for(var/s in sharedSoulhooks) + var/datum/soullink/S = s + S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed + sharedSoulhooks = null + + + for(var/i in missing_organs) + qdel(i) + missing_organs.Cut() + occupant.SetLoseBreath(0) // Stop friggin' dying, gosh damn + occupant.setOxyLoss(0) + for(var/datum/disease/critical/crit in occupant.viruses) + crit.cure() + occupant.forceMove(T) + occupant.update_body() + domutcheck(occupant) //Waiting until they're out before possible notransform. + occupant.special_post_clone_handling() + occupant = null + update_icon() + +/obj/machinery/clonepod/proc/malfunction(go_easy = FALSE) + if(occupant) + connected_message("Critical Error!") + announce_radio_message("Critical error! Please contact a Thinktronic Systems technician, as your warranty may be affected.") + for(var/s in sharedSoulhooks) + var/datum/soullink/S = s + S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed + sharedSoulhooks = null + if(!go_easy) + if(occupant.mind != clonemind) + clonemind.transfer_to(occupant) + occupant.grab_ghost() // We really just want to make you suffer. + to_chat(occupant, {"Agony blazes across your + consciousness as your body is torn apart.
    + Is this what dying is like? Yes it is.
    "}) + occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50) + for(var/i in missing_organs) + qdel(i) + missing_organs.Cut() + spawn(40) + qdel(occupant) + + + playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, 0) + mess = TRUE + update_icon() + +/obj/machinery/clonepod/update_icon() + ..() + icon_state = "pod_0" + if(occupant && !(stat & NOPOWER)) + icon_state = "pod_1" + else if(mess && !panel_open) + icon_state = "pod_g" + +/obj/machinery/clonepod/relaymove(mob/user) + if(user.stat == CONSCIOUS) + go_out() + +/obj/machinery/clonepod/emp_act(severity) + if(prob(100/(severity*efficiency))) malfunction() + ..() + +/obj/machinery/clonepod/ex_act(severity) + ..() + if(!QDELETED(src) && occupant) + go_out() + +/obj/machinery/clonepod/handle_atom_del(atom/A) + if(A == occupant) + occupant = null + countdown.stop() + +/obj/machinery/clonepod/deconstruct(disassembled = TRUE) + if(occupant) + go_out() + ..() + +/obj/machinery/clonepod/onSoullinkRevive(mob/living/L) + if(occupant && L == clonemind.current) + // The old body's back in shape, time to ditch the cloning one + malfunction(go_easy = TRUE) + +/obj/machinery/clonepod/proc/maim_clone(mob/living/carbon/human/H) + LAZYINITLIST(missing_organs) + for(var/i in missing_organs) + qdel(i) + missing_organs.Cut() + + H.setCloneLoss(CLONE_INITIAL_DAMAGE, FALSE) + H.setBrainLoss(BRAIN_INITIAL_DAMAGE) + + for(var/o in H.internal_organs) + var/obj/item/organ/O = o + if(!istype(O) || O.vital) + continue + + // Let's non-specially remove all non-vital organs + // What could possibly go wrong + var/obj/item/I = O.remove(H) + // Make this support stuff that turns into items when removed + I.forceMove(src) + missing_organs += I + + var/static/list/zones = list("r_arm", "l_arm", "r_leg", "l_leg") + for(var/zone in zones) + var/obj/item/organ/external/E = H.get_organ(zone) + var/obj/item/I = E.remove(H) + I.forceMove(src) + missing_organs += I + + organs_number = LAZYLEN(missing_organs) + H.updatehealth() + +/obj/machinery/clonepod/proc/check_brine() + // Clones are in a pickled bath of mild chemicals, keeping + // them alive, despite their lack of internal organs + for(var/bt in brine_types) + if(occupant.reagents.get_reagent_amount(bt) < 1) + occupant.reagents.add_reagent(bt, 1) + +/* + * Diskette Box + */ + +/obj/item/storage/box/disks + name = "Diskette Box" + icon_state = "disk_kit" + +/obj/item/storage/box/disks/New() + ..() + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + +/* + * Manual -- A big ol' manual. + */ + +/obj/item/paper/Cloning + name = "paper - 'H-87 Cloning Apparatus Manual" + info = {"

    Getting Started

    + Congratulations, your station has purchased the H-87 industrial cloning device!
    + Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
    + That's all there is to it!
    + Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
    +

    Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. + Profile Deletion has been restricted to \[Station Head\] level access.

    +

    Cloning from a profile

    + Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
    + Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
    +
    +

    The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. + The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


    + Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
    +

    Profile Management

    +

    The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. + These diskettes are used to transfer genetic information between machines and profiles. + A load/save dialog will become available in each profile if a disk is inserted.


    + A good diskette is a great way to counter aforementioned genetic drift!
    +
    + This technology produced under license from Thinktronic Systems, LTD."} + +//SOME SCRAPS I GUESS +/* EMP grenade/spell effect + if(istype(A, /obj/machinery/clonepod)) + A:malfunction() +*/ + +#undef MINIMUM_HEAL_LEVEL diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index bf019593065..21fddce64b7 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -1,188 +1,188 @@ -#define OP_COMPUTER_COOLDOWN 60 - -/obj/machinery/computer/operating - name = "operating computer" - density = 1 - anchored = 1.0 - icon_keyboard = "med_key" - icon_screen = "crew" - circuit = /obj/item/circuitboard/operating - var/obj/machinery/optable/table = null - var/mob/living/carbon/human/victim = null - light_color = LIGHT_COLOR_PURE_BLUE - var/verbose = 1 //general speaker toggle - var/patientName = null - var/oxyAlarm = 30 //oxy damage at which the computer will beep - var/choice = 0 //just for going into and out of the options menu - var/healthAnnounce = 1 //healther announcer toggle - var/crit = 1 //crit beeping toggle - var/nextTick = OP_COMPUTER_COOLDOWN - var/healthAlarm = 50 - var/oxy = 1 //oxygen beeping toggle - -/obj/machinery/computer/operating/New() - ..() - for(dir in list(NORTH,EAST,SOUTH,WEST)) - table = locate(/obj/machinery/optable, get_step(src, dir)) - if(table) - table.computer = src - break - -/obj/machinery/computer/operating/Destroy() - if(table) - table.computer = null - table = null - if(victim) - victim = null - return ..() - -/obj/machinery/computer/operating/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - ui_interact(user) - - -/obj/machinery/computer/operating/attack_hand(mob/user) - if(..(user)) - return - - if(stat & (NOPOWER|BROKEN)) - return - - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - var/mob/living/carbon/human/occupant - if(table) - occupant = table.victim - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.dna.species) - var/datum/species/sp = occupant.dna.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(istype(occupant, /mob/living/simple_animal)) - var/mob/living/simple_animal/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - occupantData["bloodLevel"] = round(occupant.blood_volume) - occupantData["bloodMax"] = occupant.max_blood - occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) //copy pasta ends here - - occupantData["bloodType"] = occupant.dna.blood_type - if(occupant.surgeries.len) - occupantData["inSurgery"] = 1 - for(var/datum/surgery/procedure in occupant.surgeries) - occupantData["surgeryName"] = "[capitalize(procedure.name)]" - var/datum/surgery_step/surgery_step = procedure.get_surgery_step() - occupantData["stepName"] = "[capitalize(surgery_step.name)]" - - data["occupant"] = occupantData - data["verbose"]=verbose - data["oxyAlarm"]=oxyAlarm - data["choice"]=choice - data["health"]=healthAnnounce - data["crit"]=crit - data["healthAlarm"]=healthAlarm - data["oxy"]=oxy - - return data - - -/obj/machinery/computer/operating/Topic(href, href_list) - if(..()) - return 1 - if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - if(href_list["verboseOn"]) - verbose=1 - if(href_list["verboseOff"]) - verbose=0 - if(href_list["healthOn"]) - healthAnnounce=1 - if(href_list["healthOff"]) - healthAnnounce=0 - if(href_list["critOn"]) - crit=1 - if(href_list["critOff"]) - crit=0 - if(href_list["oxyOn"]) - oxy=1 - if(href_list["oxyOff"]) - oxy=0 - if(href_list["oxy_adj"]!=0) - oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"]) - if(href_list["choiceOn"]) - choice=1 - if(href_list["choiceOff"]) - choice=0 - if(href_list["health_adj"]!=0) - healthAlarm=healthAlarm+text2num(href_list["health_adj"]) - return - - -/obj/machinery/computer/operating/process() - - if(table && table.check_victim()) - if(verbose) - if(patientName!=table.victim.name) - patientName=table.victim.name - atom_say("New patient detected, loading stats") - victim = table.victim - atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") - if(nextTick < world.time) - nextTick=world.time + OP_COMPUTER_COOLDOWN - if(crit && victim.health <= -50 ) - playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) - if(oxy && victim.getOxyLoss()>oxyAlarm) - playsound(src.loc, 'sound/machines/defib_saftyoff.ogg', 50, 0) - if(healthAnnounce && victim.health <= healthAlarm) - atom_say("[round(victim.health)]") +#define OP_COMPUTER_COOLDOWN 60 + +/obj/machinery/computer/operating + name = "operating computer" + density = 1 + anchored = 1.0 + icon_keyboard = "med_key" + icon_screen = "crew" + circuit = /obj/item/circuitboard/operating + var/obj/machinery/optable/table = null + var/mob/living/carbon/human/victim = null + light_color = LIGHT_COLOR_PURE_BLUE + var/verbose = 1 //general speaker toggle + var/patientName = null + var/oxyAlarm = 30 //oxy damage at which the computer will beep + var/choice = 0 //just for going into and out of the options menu + var/healthAnnounce = 1 //healther announcer toggle + var/crit = 1 //crit beeping toggle + var/nextTick = OP_COMPUTER_COOLDOWN + var/healthAlarm = 50 + var/oxy = 1 //oxygen beeping toggle + +/obj/machinery/computer/operating/New() + ..() + for(dir in list(NORTH,EAST,SOUTH,WEST)) + table = locate(/obj/machinery/optable, get_step(src, dir)) + if(table) + table.computer = src + break + +/obj/machinery/computer/operating/Destroy() + if(table) + table.computer = null + table = null + if(victim) + victim = null + return ..() + +/obj/machinery/computer/operating/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + ui_interact(user) + + +/obj/machinery/computer/operating/attack_hand(mob/user) + if(..(user)) + return + + if(stat & (NOPOWER|BROKEN)) + return + + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + var/mob/living/carbon/human/occupant + if(table) + occupant = table.victim + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.dna.species) + var/datum/species/sp = occupant.dna.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(istype(occupant, /mob/living/simple_animal)) + var/mob/living/simple_animal/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + occupantData["bloodLevel"] = round(occupant.blood_volume) + occupantData["bloodMax"] = occupant.max_blood + occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) //copy pasta ends here + + occupantData["bloodType"] = occupant.dna.blood_type + if(occupant.surgeries.len) + occupantData["inSurgery"] = 1 + for(var/datum/surgery/procedure in occupant.surgeries) + occupantData["surgeryName"] = "[capitalize(procedure.name)]" + var/datum/surgery_step/surgery_step = procedure.get_surgery_step() + occupantData["stepName"] = "[capitalize(surgery_step.name)]" + + data["occupant"] = occupantData + data["verbose"]=verbose + data["oxyAlarm"]=oxyAlarm + data["choice"]=choice + data["health"]=healthAnnounce + data["crit"]=crit + data["healthAlarm"]=healthAlarm + data["oxy"]=oxy + + return data + + +/obj/machinery/computer/operating/Topic(href, href_list) + if(..()) + return 1 + if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if(href_list["verboseOn"]) + verbose=1 + if(href_list["verboseOff"]) + verbose=0 + if(href_list["healthOn"]) + healthAnnounce=1 + if(href_list["healthOff"]) + healthAnnounce=0 + if(href_list["critOn"]) + crit=1 + if(href_list["critOff"]) + crit=0 + if(href_list["oxyOn"]) + oxy=1 + if(href_list["oxyOff"]) + oxy=0 + if(href_list["oxy_adj"]!=0) + oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"]) + if(href_list["choiceOn"]) + choice=1 + if(href_list["choiceOff"]) + choice=0 + if(href_list["health_adj"]!=0) + healthAlarm=healthAlarm+text2num(href_list["health_adj"]) + return + + +/obj/machinery/computer/operating/process() + + if(table && table.check_victim()) + if(verbose) + if(patientName!=table.victim.name) + patientName=table.victim.name + atom_say("New patient detected, loading stats") + victim = table.victim + atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") + if(nextTick < world.time) + nextTick=world.time + OP_COMPUTER_COOLDOWN + if(crit && victim.health <= -50 ) + playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) + if(oxy && victim.getOxyLoss()>oxyAlarm) + playsound(src.loc, 'sound/machines/defib_saftyoff.ogg', 50, 0) + if(healthAnnounce && victim.health <= healthAlarm) + atom_say("[round(victim.health)]") diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index 8d27ac5e7fa..ba139e50855 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -158,7 +158,7 @@ var/open_for_latejoin = alert(user, "Would you like this core to be open for latejoining AIs?", "Latejoin", "Yes", "Yes", "No") == "Yes" var/obj/structure/AIcore/deactivated/D = new(loc) if(open_for_latejoin) - empty_playable_ai_cores += D + GLOB.empty_playable_ai_cores += D else if(brain.brainmob.mind) SSticker.mode.remove_cultist(brain.brainmob.mind, 1) @@ -248,8 +248,8 @@ circuit = new(src) /obj/structure/AIcore/deactivated/Destroy() - if(src in empty_playable_ai_cores) - empty_playable_ai_cores -= src + if(src in GLOB.empty_playable_ai_cores) + GLOB.empty_playable_ai_cores -= src return ..() /client/proc/empty_ai_core_toggle_latejoin() @@ -269,11 +269,11 @@ var/obj/structure/AIcore/deactivated/D = cores[id] if(!D) return - if(D in empty_playable_ai_cores) - empty_playable_ai_cores -= D + if(D in GLOB.empty_playable_ai_cores) + GLOB.empty_playable_ai_cores -= D to_chat(src, "\The [id] is now not available for latejoining AIs.") else - empty_playable_ai_cores += D + GLOB.empty_playable_ai_cores += D to_chat(src, "\The [id] is now available for latejoining AIs.") @@ -305,4 +305,4 @@ atom/proc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/ to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.
    ") qdel(src) else //If for some reason you use an empty card on an empty AI terminal. - to_chat(user, "There is no AI loaded on this terminal!") \ No newline at end of file + to_chat(user, "There is no AI loaded on this terminal!") diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index e9cf036d3ca..0d715fe5759 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -1,147 +1,147 @@ -/obj/machinery/computer/aifixer - name = "\improper AI system integrity restorer" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "rd_key" - icon_screen = "ai-fixer" - circuit = /obj/item/circuitboard/aifixer - req_access = list(access_captain, access_robotics, access_heads) - var/mob/living/silicon/ai/occupant = null - var/active = 0 - - light_color = LIGHT_COLOR_PURPLE - -/obj/machinery/computer/aifixer/attackby(I as obj, user as mob, params) - if(occupant && istype(I, /obj/item/screwdriver)) - if(stat & BROKEN) - ..() - if(stat & NOPOWER) - to_chat(user, "The screws on [name]'s screen won't budge.") - else - to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep!.") - else - return ..() - -/obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - - if(!ui) - ui = new(user, src, ui_key, "ai_fixer.tmpl", "AI System Integrity Restorer", 550, 500) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/aifixer/ui_data(mob/user, datum/topic_state/state) - var/data[0] - if(occupant) - data["occupant"] = occupant.name - data["reference"] = "\ref[occupant]" - data["integrity"] = (occupant.health+100)/2 - data["stat"] = occupant.stat - data["active"] = active - data["wireless"] = occupant.control_disabled - data["radio"] = occupant.aiRadio.disabledAi - - var/laws[0] - for(var/datum/ai_law/law in occupant.laws.all_laws()) - laws.Add(list(list("law" = law.law, "number" = law.get_index()))) - - data["laws"] = laws - - return data - -/obj/machinery/computer/aifixer/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["fix"]) - active = 1 - while(occupant.health < 100) - occupant.adjustOxyLoss(-1, FALSE) - occupant.adjustFireLoss(-1, FALSE) - occupant.adjustToxLoss(-1, FALSE) - occupant.adjustBruteLoss(-1, FALSE) - occupant.updatehealth() - if(occupant.health >= 0 && occupant.stat == DEAD) - occupant.update_revive() - occupant.lying = 0 - update_icon() - sleep(10) - active = 0 - add_fingerprint(usr) - - if(href_list["wireless"]) - var/wireless = text2num(href_list["wireless"]) - if(wireless == 0 || wireless == 1) - occupant.control_disabled = wireless - - if(href_list["radio"]) - var/radio = text2num(href_list["radio"]) - if(radio == 0 || radio == 1) - occupant.aiRadio.disabledAi = radio - - SSnanoui.update_uis(src) - update_icon() - return - -/obj/machinery/computer/aifixer/update_icon() - ..() - if(stat & (NOPOWER|BROKEN)) - return - else - var/overlay_layer = LIGHTING_LAYER+0.2 // +0.1 from the default computer overlays - if(active) - overlays += image(icon,"ai-fixer-on",overlay_layer) - if(occupant) - switch(occupant.stat) - if(0) - overlays += image(icon,"ai-fixer-full",overlay_layer) - if(2) - overlays += image(icon,"ai-fixer-404",overlay_layer) - else - overlays += image(icon,"ai-fixer-empty",overlay_layer) - -/obj/machinery/computer/aifixer/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/aicard/card) - if(!..()) - return - //Downloading AI from card to terminal. - if(interaction == AI_TRANS_FROM_CARD) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "[src] is offline and cannot take an AI at this time!") - return - AI.loc = src - occupant = AI - AI.control_disabled = 1 - AI.aiRadio.disabledAi = 1 - to_chat(AI, "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here.") - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") - update_icon() - - else //Uploading AI from terminal to card - if(occupant && !active) - to_chat(occupant, "You have been downloaded to a mobile storage device. Still no remote access.") - to_chat(user, "Transfer successful: [occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.") - occupant.loc = card - occupant = null - update_icon() - else if(active) - to_chat(user, "ERROR: Reconstruction in progress.") - else if(!occupant) - to_chat(user, "ERROR: Unable to locate artificial intelligence.") - -/obj/machinery/computer/aifixer/Destroy() - if(occupant) - occupant.ghostize() - QDEL_NULL(occupant) - return ..() - -/obj/machinery/computer/aifixer/emp_act() - if(occupant) - occupant.ghostize() - QDEL_NULL(occupant) - else - ..() +/obj/machinery/computer/aifixer + name = "\improper AI system integrity restorer" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "rd_key" + icon_screen = "ai-fixer" + circuit = /obj/item/circuitboard/aifixer + req_access = list(ACCESS_CAPTAIN, ACCESS_ROBOTICS, ACCESS_HEADS) + var/mob/living/silicon/ai/occupant = null + var/active = 0 + + light_color = LIGHT_COLOR_PURPLE + +/obj/machinery/computer/aifixer/attackby(I as obj, user as mob, params) + if(occupant && istype(I, /obj/item/screwdriver)) + if(stat & BROKEN) + ..() + if(stat & NOPOWER) + to_chat(user, "The screws on [name]'s screen won't budge.") + else + to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep!.") + else + return ..() + +/obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + + if(!ui) + ui = new(user, src, ui_key, "ai_fixer.tmpl", "AI System Integrity Restorer", 550, 500) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/aifixer/ui_data(mob/user, datum/topic_state/state) + var/data[0] + if(occupant) + data["occupant"] = occupant.name + data["reference"] = "\ref[occupant]" + data["integrity"] = (occupant.health+100)/2 + data["stat"] = occupant.stat + data["active"] = active + data["wireless"] = occupant.control_disabled + data["radio"] = occupant.aiRadio.disabledAi + + var/laws[0] + for(var/datum/ai_law/law in occupant.laws.all_laws()) + laws.Add(list(list("law" = law.law, "number" = law.get_index()))) + + data["laws"] = laws + + return data + +/obj/machinery/computer/aifixer/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["fix"]) + active = 1 + while(occupant.health < 100) + occupant.adjustOxyLoss(-1, FALSE) + occupant.adjustFireLoss(-1, FALSE) + occupant.adjustToxLoss(-1, FALSE) + occupant.adjustBruteLoss(-1, FALSE) + occupant.updatehealth() + if(occupant.health >= 0 && occupant.stat == DEAD) + occupant.update_revive() + occupant.lying = 0 + update_icon() + sleep(10) + active = 0 + add_fingerprint(usr) + + if(href_list["wireless"]) + var/wireless = text2num(href_list["wireless"]) + if(wireless == 0 || wireless == 1) + occupant.control_disabled = wireless + + if(href_list["radio"]) + var/radio = text2num(href_list["radio"]) + if(radio == 0 || radio == 1) + occupant.aiRadio.disabledAi = radio + + SSnanoui.update_uis(src) + update_icon() + return + +/obj/machinery/computer/aifixer/update_icon() + ..() + if(stat & (NOPOWER|BROKEN)) + return + else + var/overlay_layer = LIGHTING_LAYER+0.2 // +0.1 from the default computer overlays + if(active) + overlays += image(icon,"ai-fixer-on",overlay_layer) + if(occupant) + switch(occupant.stat) + if(0) + overlays += image(icon,"ai-fixer-full",overlay_layer) + if(2) + overlays += image(icon,"ai-fixer-404",overlay_layer) + else + overlays += image(icon,"ai-fixer-empty",overlay_layer) + +/obj/machinery/computer/aifixer/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/aicard/card) + if(!..()) + return + //Downloading AI from card to terminal. + if(interaction == AI_TRANS_FROM_CARD) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "[src] is offline and cannot take an AI at this time!") + return + AI.loc = src + occupant = AI + AI.control_disabled = 1 + AI.aiRadio.disabledAi = 1 + to_chat(AI, "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here.") + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") + update_icon() + + else //Uploading AI from terminal to card + if(occupant && !active) + to_chat(occupant, "You have been downloaded to a mobile storage device. Still no remote access.") + to_chat(user, "Transfer successful: [occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.") + occupant.loc = card + occupant = null + update_icon() + else if(active) + to_chat(user, "ERROR: Reconstruction in progress.") + else if(!occupant) + to_chat(user, "ERROR: Unable to locate artificial intelligence.") + +/obj/machinery/computer/aifixer/Destroy() + if(occupant) + occupant.ghostize() + QDEL_NULL(occupant) + return ..() + +/obj/machinery/computer/aifixer/emp_act() + if(occupant) + occupant.ghostize() + QDEL_NULL(occupant) + else + ..() diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 28f847d81eb..20ff79041a9 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -1,1023 +1,1023 @@ -/obj/machinery/computer/arcade - name = "random arcade" - desc = "random arcade machine" - icon = 'icons/obj/computer.dmi' - icon_state = "arcade" - icon_keyboard = null - icon_screen = "invaders" - light_color = "#00FF00" - var/prize = /obj/item/stack/tickets - -/obj/machinery/computer/arcade/proc/Reset() - return - -/obj/machinery/computer/arcade/New() - ..() - if(!circuit) - var/choice = pick(subtypesof(/obj/machinery/computer/arcade)) - new choice(loc) - qdel(src) - return - Reset() - - -/obj/machinery/computer/arcade/proc/prizevend(var/score) - if(!contents.len) - var/prize_amount - if(score) - prize_amount = score - else - prize_amount = rand(1, 10) - new prize(get_turf(src), prize_amount) - else - var/atom/movable/prize = pick(contents) - prize.loc = get_turf(src) - -/obj/machinery/computer/arcade/emp_act(severity) - ..(severity) - if(stat & (NOPOWER|BROKEN)) - return - var/num_of_prizes = 0 - switch(severity) - if(1) - num_of_prizes = rand(1,4) - if(2) - num_of_prizes = rand(0,2) - for(var/i = num_of_prizes; i > 0; i--) - prizevend() - explosion(get_turf(src), -1, 0, 1+num_of_prizes, flame_range = 1+num_of_prizes) - - -/obj/machinery/computer/arcade/battle - name = "arcade machine" - desc = "Does not support Pinball." - icon = 'icons/obj/computer.dmi' - icon_state = "arcade" - circuit = /obj/item/circuitboard/arcade/battle - var/enemy_name = "Space Villian" - var/temp = "Winners Don't Use Spacedrugs" //Temporary message, for attack messages, etc - var/player_hp = 30 //Player health/attack points - var/player_mp = 10 - var/enemy_hp = 45 //Enemy health/attack points - var/enemy_mp = 20 - var/gameover = 0 - var/blocked = 0 //Player cannot attack/heal while set - var/turtle = 0 - -/obj/machinery/computer/arcade/battle/Reset() - var/name_action - var/name_part1 - var/name_part2 - - name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") - - name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") - name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") - - enemy_name = replacetext((name_part1 + name_part2), "the ", "") - name = (name_action + name_part1 + name_part2) - -/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) - if(..()) - return - user.set_machine(src) - var/dat = "Close" - dat += "

    [enemy_name]

    " - - dat += "

    [temp]

    " - dat += "
    Health: [player_hp] | Magic: [player_mp] | Enemy Health: [enemy_hp]
    " - - if(gameover) - dat += "
    New Game" - else - dat += "
    Attack | " - dat += "Heal | " - dat += "Recharge Power" - - dat += "
    " - - //user << browse(dat, "window=arcade") - //onclose(user, "arcade") - var/datum/browser/popup = new(user, "arcade", "Space Villian 2000") - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() - return - -/obj/machinery/computer/arcade/battle/Topic(href, href_list) - if(..()) - return - - if(!blocked && !gameover) - if(href_list["attack"]) - blocked = 1 - var/attackamt = rand(2,6) - temp = "You attack for [attackamt] damage!" - playsound(src.loc, 'sound/arcade/hit.ogg', 20, 1, extrarange = -6, falloff = 10) - updateUsrDialog() - if(turtle > 0) - turtle-- - - sleep(10) - enemy_hp -= attackamt - arcade_action() - - else if(href_list["heal"]) - blocked = 1 - var/pointamt = rand(1,3) - var/healamt = rand(6,8) - temp = "You use [pointamt] magic to heal for [healamt] damage!" - playsound(src.loc, 'sound/arcade/heal.ogg', 20, 1, extrarange = -6, falloff = 10) - updateUsrDialog() - turtle++ - - sleep(10) - player_mp -= pointamt - player_hp += healamt - blocked = 1 - updateUsrDialog() - arcade_action() - - else if(href_list["charge"]) - blocked = 1 - var/chargeamt = rand(4,7) - temp = "You regain [chargeamt] points" - playsound(src.loc, 'sound/arcade/mana.ogg', 20, 1, extrarange = -6, falloff = 10) - player_mp += chargeamt - if(turtle > 0) - turtle-- - - updateUsrDialog() - sleep(10) - arcade_action() - - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=arcade") - - else if(href_list["newgame"]) //Reset everything - temp = "New Round" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - turtle = 0 - - if(emagged) - Reset() - emagged = 0 - - add_fingerprint(usr) - updateUsrDialog() - return - -/obj/machinery/computer/arcade/battle/proc/arcade_action() - if((enemy_mp <= 0) || (enemy_hp <= 0)) - if(!gameover) - gameover = 1 - temp = "[enemy_name] has fallen! Rejoice!" - playsound(src.loc, 'sound/arcade/win.ogg', 20, 1, extrarange = -6, falloff = 10) - - if(emagged) - feedback_inc("arcade_win_emagged") - new /obj/effect/spawner/newbomb/timer/syndicate(get_turf(src)) - new /obj/item/clothing/head/collectable/petehat(get_turf(src)) - message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") - log_game("[key_name(usr)] has outbombed Cuban Pete and been awarded a bomb.") - Reset() - emagged = 0 - else - feedback_inc("arcade_win_normal") - var/score = player_hp + player_mp + 5 - prizevend(score) - - else if(emagged && (turtle >= 4)) - var/boomamt = rand(5,10) - temp = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!" - playsound(src.loc, 'sound/arcade/boom.ogg', 20, 1, extrarange = -6, falloff = 10) - player_hp -= boomamt - - else if((enemy_mp <= 5) && (prob(70))) - var/stealamt = rand(2,3) - temp = "[enemy_name] steals [stealamt] of your power!" - playsound(src.loc, 'sound/arcade/steal.ogg', 20, 1, extrarange = -6, falloff = 10) - player_mp -= stealamt - updateUsrDialog() - - if(player_mp <= 0) - gameover = 1 - sleep(10) - temp = "You have been drained! GAME OVER" - playsound(src.loc, 'sound/arcade/lose.ogg', 20, 1, extrarange = -6, falloff = 10) - if(emagged) - feedback_inc("arcade_loss_mana_emagged") - usr.gib() - else - feedback_inc("arcade_loss_mana_normal") - - else if((enemy_hp <= 10) && (enemy_mp > 4)) - temp = "[enemy_name] heals for 4 health!" - playsound(src.loc, 'sound/arcade/heal.ogg', 20, 1, extrarange = -6, falloff = 10) - enemy_hp += 4 - enemy_mp -= 4 - - else - var/attackamt = rand(3,6) - temp = "[enemy_name] attacks for [attackamt] damage!" - playsound(src.loc, 'sound/arcade/hit.ogg', 20, 1, extrarange = -6, falloff = 10) - player_hp -= attackamt - - if((player_mp <= 0) || (player_hp <= 0)) - gameover = 1 - temp = "You have been crushed! GAME OVER" - playsound(src.loc, 'sound/arcade/lose.ogg', 20, 1, extrarange = -6, falloff = 10) - if(emagged) - feedback_inc("arcade_loss_hp_emagged") - usr.gib() - else - feedback_inc("arcade_loss_hp_normal") - - blocked = 0 - return - - -/obj/machinery/computer/arcade/battle/emag_act(user as mob) - if(!emagged) - temp = "If you die in the game, you die for real!" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - blocked = 0 - - emagged = 1 - - enemy_name = "Cuban Pete" - name = "Outbomb Cuban Pete" - - updateUsrDialog() - -// *** THE ORION TRAIL ** // - -#define ORION_TRAIL_WINTURN 9 - -//Orion Trail Events -#define ORION_TRAIL_RAIDERS "Raiders" -#define ORION_TRAIL_FLUX "Interstellar Flux" -#define ORION_TRAIL_ILLNESS "Illness" -#define ORION_TRAIL_BREAKDOWN "Breakdown" -#define ORION_TRAIL_LING "Changelings?" -#define ORION_TRAIL_LING_ATTACK "Changeling Ambush" -#define ORION_TRAIL_MALFUNCTION "Malfunction" -#define ORION_TRAIL_COLLISION "Collision" -#define ORION_TRAIL_SPACEPORT "Spaceport" -#define ORION_TRAIL_BLACKHOLE "BlackHole" - - -/obj/machinery/computer/arcade/orion_trail - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - icon_state = "arcade" - circuit = /obj/item/circuitboard/arcade/orion_trail - var/busy = 0 //prevent clickspam that allowed people to ~speedrun~ the game. - var/engine = 0 - var/hull = 0 - var/electronics = 0 - var/food = 80 - var/fuel = 60 - var/turns = 4 - var/playing = 0 - var/gameover = 0 - var/alive = 4 - var/eventdat = null - var/event = null - var/list/settlers = list("Harry","Larry","Bob") - var/list/events = list(ORION_TRAIL_RAIDERS = 3, - ORION_TRAIL_FLUX = 1, - ORION_TRAIL_ILLNESS = 3, - ORION_TRAIL_BREAKDOWN = 2, - ORION_TRAIL_LING = 3, - ORION_TRAIL_MALFUNCTION = 2, - ORION_TRAIL_COLLISION = 1, - ORION_TRAIL_SPACEPORT = 2 - ) - var/list/stops = list() - var/list/stopblurbs = list() - var/lings_aboard = 0 - var/spaceport_raided = 0 - var/spaceport_freebie = 0 - var/last_spaceport_action = "" - -/obj/machinery/computer/arcade/orion_trail/Reset() - // Sets up the main trail - stops = list("Pluto","Asteroid Belt","Proxima Centauri","Dead Space","Rigel Prime","Tau Ceti Beta","Black Hole","Space Outpost Beta-9","Orion Prime") - stopblurbs = list( - "Pluto, long since occupied with long-range sensors and scanners, stands ready to, and indeed continues to probe the far reaches of the galaxy.", - "At the edge of the Sol system lies a treacherous asteroid belt. Many have been crushed by stray asteroids and misguided judgement.", - "The nearest star system to Sol, in ages past it stood as a reminder of the boundaries of sub-light travel, now a low-population sanctuary for adventurers and traders.", - "This region of space is particularly devoid of matter. Such low-density pockets are known to exist, but the vastness of it is astounding.", - "Rigel Prime, the center of the Rigel system, burns hot, basking its planetary bodies in warmth and radiation.", - "Tau Ceti Beta has recently become a waypoint for colonists headed towards Orion. There are many ships and makeshift stations in the vicinity.", - "Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through. We could stay of course, but risk of being overcome by its gravity, or we could change course to go around, which will take longer.", - "You have come into range of the first man-made structure in this region of space. It has been constructed not by travellers from Sol, but by colonists from Orion. It stands as a monument to the colonists' success.", - "You have made it to Orion! Congratulations! Your crew is one of the few to start a new foothold for mankind!" - ) - -/obj/machinery/computer/arcade/orion_trail/proc/newgame() - // Set names of settlers in crew - settlers = list() - for(var/i = 1; i <= 3; i++) - add_crewmember() - add_crewmember("[usr]") - // Re-set items to defaults - engine = 1 - hull = 1 - electronics = 1 - food = 80 - fuel = 60 - alive = 4 - turns = 1 - event = null - playing = 1 - gameover = 0 - lings_aboard = 0 - - //spaceport junk - spaceport_raided = 0 - spaceport_freebie = 0 - last_spaceport_action = "" - -/obj/machinery/computer/arcade/orion_trail/attack_hand(mob/user) - if(..()) - return - if(fuel <= 0 || food <=0 || settlers.len == 0) - gameover = 1 - event = null - user.set_machine(src) - var/dat = "" - if(gameover) - dat = "

    Game Over

    " - dat += "Like many before you, your crew never made it to Orion, lost to space...
    Forever." - if(settlers.len == 0) - dat += "
    Your entire crew died, your ship joins the fleet of ghost-ships littering the galaxy." - else - if(food <= 0) - dat += "
    You ran out of food and starved." - if(emagged) - user.set_nutrition(0) //yeah you pretty hongry - to_chat(user, "Your body instantly contracts to that of one who has not eaten in months. Agonizing cramps seize you as you fall to the floor.") - if(fuel <= 0) - dat += "
    You ran out of fuel, and drift, slowly, into a star." - if(emagged) - var/mob/living/M = user - M.adjust_fire_stacks(5) - M.IgniteMob() //flew into a star, so you're on fire - to_chat(user, "You feel an immense wave of heat emanate from the arcade machine. Your skin bursts into flames.") - dat += "

    OK...

    " - - if(emagged) - to_chat(user, "You're never going to make it to Orion...") - user.death() - emagged = 0 //removes the emagged status after you lose - playing = 0 //also a new game - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - - else if(event) - dat = eventdat - else if(playing) - var/title = stops[turns] - var/subtext = stopblurbs[turns] - dat = "

    [title]

    " - dat += "[subtext]" - dat += "

    Crew:

    " - dat += english_list(settlers) - dat += "
    Food: [food] | Fuel: [fuel]" - dat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" - if(turns == 7) - dat += "

    Go Around Continue

    " - else - dat += "

    Continue

    " - dat += "

    Kill a crewmember

    " - dat += "

    Close

    " - else - dat = "

    The Orion Trail

    " - dat += "

    Experience the journey of your ancestors!



    " - dat += "
    New Game
    " - dat += "

    Close

    " - var/datum/browser/popup = new(user, "arcade", "The Orion Trail",400,700) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() - return - -/obj/machinery/computer/arcade/orion_trail/Topic(href, href_list) - . = ..() - if(.) - return - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=arcade") - - if(busy) - return - busy = 1 - - if(href_list["continue"]) //Continue your travels - if(turns >= ORION_TRAIL_WINTURN) - win() - else - food -= (alive+lings_aboard)*2 - fuel -= 5 - if(turns == 2 && prob(30)) - event = ORION_TRAIL_COLLISION - event() - else if(prob(75)) - event = pickweight(events) - if(lings_aboard) - if(event == ORION_TRAIL_LING || prob(55)) - event = ORION_TRAIL_LING_ATTACK - event() - turns += 1 - if(emagged) - var/mob/living/carbon/M = usr //for some vars - switch(event) - if(ORION_TRAIL_RAIDERS) - if(prob(50)) - to_chat(usr, "You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?") - M.AdjustHallucinate(30) - else - to_chat(usr, "Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...") - M.take_organ_damage(30) - playsound(loc, 'sound/weapons/genhit2.ogg', 100, 1) - if(ORION_TRAIL_ILLNESS) - var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS - if(severity == 1) - to_chat(M, "You suddenly feel slightly nauseous.")//got off lucky - - if(severity == 2) - to_chat(usr, "You suddenly feel extremely nauseous and hunch over until it passes.") - M.Stun(3) - if(severity >= 3) //you didn't pray hard enough - to_chat(M, "An overpowering wave of nausea consumes over you. You hunch over, your stomach's contents preparing for a spectacular exit.") - M.Stun(5) - sleep(30) - atom_say("[M] violently throws up!") - playsound(loc, 'sound/effects/splat.ogg', 50, 1) - M.adjust_nutrition(-50) //lose a lot of food - var/turf/location = usr.loc - if(istype(location, /turf/simulated)) - location.add_vomit_floor(TRUE) - if(ORION_TRAIL_FLUX) - if(prob(75)) - M.Weaken(3) - atom_say("A sudden gust of powerful wind slams [M] into the floor!") - M.take_organ_damage(25) - playsound(src.loc, 'sound/weapons/genhit.ogg', 100, 1) - else - to_chat(M, "A violent gale blows past you, and you barely manage to stay standing!") - if(ORION_TRAIL_COLLISION) //by far the most damaging event - if(prob(90)) - playsound(src.loc, 'sound/effects/bang.ogg', 100, 1) - var/turf/simulated/floor/F - for(F in orange(1, src)) - F.ChangeTurf(F.baseturf) - atom_say("Something slams into the floor around [src], exposing it to space!") - if(hull) - sleep(10) - atom_say("A new floor suddenly appears around [src]. What the hell?") - playsound(src.loc, 'sound/weapons/genhit.ogg', 100, 1) - var/turf/space/T - for(T in orange(1, src)) - T.ChangeTurf(/turf/simulated/floor/plating) - else - atom_say("Something slams into the floor around [src] - luckily, it didn't get through!") - playsound(src.loc, 'sound/effects/bang.ogg', 20, 1) - if(ORION_TRAIL_MALFUNCTION) - playsound(src.loc, 'sound/effects/empulse.ogg', 20, 1) - visible_message("[src] malfunctions, randomizing in-game stats!") - var/oldfood = food - var/oldfuel = fuel - food = rand(10,80) / rand(1,2) - fuel = rand(10,60) / rand(1,2) - if(electronics) - sleep(10) - if(oldfuel > fuel && oldfood > food) - audible_message("[src] lets out a somehow reassuring chime.") - else if(oldfuel < fuel || oldfood < food) - audible_message("[src] lets out a somehow ominous chime.") - food = oldfood - fuel = oldfuel - playsound(src.loc, 'sound/machines/chime.ogg', 20, 1) - - else if(href_list["newgame"]) //Reset everything - newgame() - else if(href_list["menu"]) //back to the main menu - playing = 0 - event = null - gameover = 0 - food = 80 - fuel = 60 - settlers = list("Harry","Larry","Bob") - else if(href_list["slow"]) //slow down - food -= (alive+lings_aboard)*2 - fuel -= 5 - event = null - else if(href_list["pastblack"]) //slow down - food -= ((alive+lings_aboard)*2)*3 - fuel -= 15 - turns += 1 - event = null - else if(href_list["useengine"]) //use parts - engine = max(0, --engine) - event = null - else if(href_list["useelec"]) //use parts - electronics = max(0, --electronics) - event = null - else if(href_list["usehull"]) //use parts - hull = max(0, --hull) - event = null - else if(href_list["wait"]) //wait 3 days - food -= ((alive+lings_aboard)*2)*3 - event = null - else if(href_list["keepspeed"]) //keep speed - if(prob(75)) - event = "Breakdown" - event() - else - event = null - else if(href_list["blackhole"]) //keep speed past a black hole - if(prob(75)) - event = ORION_TRAIL_BLACKHOLE - event() - if(emagged) //has to be here because otherwise it doesn't work - playsound(src.loc, 'sound/effects/supermatter.ogg', 100, 1) - atom_say("A miniature black hole suddenly appears in front of [src], devouring [usr] alive!") - usr.Stun(10) //you can't run :^) - var/S = new /obj/singularity/academy(usr.loc) - emagged = 0 //immediately removes emagged status so people can't kill themselves by sprinting up and interacting - sleep(50) - atom_say("[S] winks out, just as suddenly as it appeared.") - qdel(S) - else - event = null - turns += 1 - else if(href_list["holedeath"]) - gameover = 1 - event = null - else if(href_list["eventclose"]) //end an event - event = null - - else if(href_list["killcrew"]) //shoot a crewmember - var/sheriff = remove_crewmember() //I shot the sheriff - playsound(loc,'sound/weapons/gunshots/gunshot.ogg', 100, 1) - - if(settlers.len == 0 || alive == 0) - atom_say("The last crewmember [sheriff], shot themselves, GAME OVER!") - if(emagged) - usr.death(0) - emagged = 0 - gameover = 1 - event = null - else if(emagged) - if(usr.name == sheriff) - atom_say("The crew of the ship chose to kill [usr.name]!") - usr.death(0) - - if(event == ORION_TRAIL_LING) //only ends the ORION_TRAIL_LING event, since you can do this action in multiple places - event = null - - //Spaceport specific interactions - //they get a header because most of them don't reset event (because it's a shop, you leave when you want to) - //they also call event() again, to regen the eventdata, which is kind of odd but necessary - else if(href_list["buycrew"]) //buy a crewmember - var/bought = add_crewmember() - last_spaceport_action = "You hired [bought] as a new crewmember." - fuel -= 10 - food -= 10 - event() - - else if(href_list["sellcrew"]) //sell a crewmember - var/sold = remove_crewmember() - last_spaceport_action = "You sold your crewmember, [sold]!" - fuel += 7 - food += 7 - event() - - else if(href_list["leave_spaceport"]) - event = null - spaceport_raided = 0 - spaceport_freebie = 0 - last_spaceport_action = "" - - else if(href_list["raid_spaceport"]) - var/success = min(15 * alive,100) //default crew (4) have a 60% chance - spaceport_raided = 1 - - var/FU = 0 - var/FO = 0 - if(prob(success)) - FU = rand(5,15) - FO = rand(5,15) - last_spaceport_action = "You successfully raided the spaceport! you gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)" - else - FU = rand(-5,-15) - FO = rand(-5,-15) - last_spaceport_action = "You failed to raid the spaceport! you lost [FU*-1] Fuel and [FO*-1] Food in your scramble to escape! ([FU]FU,[FO]FO)" - - //your chance of lose a crewmember is 1/2 your chance of success - //this makes higher % failures hurt more, don't get cocky space cowboy! - if(prob(success*5)) - var/lost_crew = remove_crewmember() - last_spaceport_action = "You failed to raid the spaceport! you lost [FU*-1] Fuel and [FO*-1] Food, AND [lost_crew] in your scramble to escape! ([FU]FI,[FO]FO,-Crew)" - if(emagged) - atom_say("WEEWOO WEEWOO, Spaceport Security en route!") - for(var/i, i<=3, i++) - var/mob/living/simple_animal/hostile/syndicate/ranged/orion/O = new/mob/living/simple_animal/hostile/syndicate/ranged/orion(get_turf(src)) - O.target = usr - - - fuel += FU - food += FO - event() - - else if(href_list["buyparts"]) - switch(text2num(href_list["buyparts"])) - if(1) //Engine Parts - engine++ - last_spaceport_action = "Bought Engine Parts" - if(2) //Hull Plates - hull++ - last_spaceport_action = "Bought Hull Plates" - if(3) //Spare Electronics - electronics++ - last_spaceport_action = "Bought Spare Electronics" - fuel -= 5 //they all cost 5 - event() - - else if(href_list["trade"]) - switch(text2num(href_list["trade"])) - if(1) //Fuel - fuel -= 5 - food += 5 - last_spaceport_action = "Traded Fuel for Food" - if(2) //Food - fuel += 5 - food -= 5 - last_spaceport_action = "Traded Food for Fuel" - event() - - add_fingerprint(usr) - updateUsrDialog() - busy = 0 - return - - -/obj/machinery/computer/arcade/orion_trail/proc/event() - eventdat = "

    [event]

    " - - switch(event) - if(ORION_TRAIL_RAIDERS) - eventdat += "Raiders have come aboard your ship!" - if(prob(50)) - var/sfood = rand(1,10) - var/sfuel = rand(1,10) - food -= sfood - fuel -= sfuel - eventdat += "
    They have stolen [sfood] Food and [sfuel] Fuel." - else if(prob(10)) - var/deadname = remove_crewmember() - eventdat += "
    [deadname] tried to fight back but was killed." - else - eventdat += "
    Fortunately you fended them off without any trouble." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_FLUX) - eventdat += "This region of space is highly turbulent.
    If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies." - eventdat += "
    What will you do?" - eventdat += "

    Slow Down Continue

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_ILLNESS) - eventdat += "A deadly illness has been contracted!" - var/deadname = remove_crewmember() - eventdat += "
    [deadname] was killed by the disease." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_BREAKDOWN) - eventdat += "Oh no! The engine has broken down!" - eventdat += "
    You can repair it with an engine part, or you can make repairs for 3 days." - if(engine >= 1) - eventdat += "

    Use PartWait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_MALFUNCTION) - eventdat += "The ship's systems are malfunctioning!" - eventdat += "
    You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI." - if(electronics >= 1) - eventdat += "

    Use PartWait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_COLLISION) - eventdat += "Something hit us! Looks like there's some hull damage." - if(prob(25)) - var/sfood = rand(5,15) - var/sfuel = rand(5,15) - food -= sfood - fuel -= sfuel - eventdat += "
    [sfood] Food and [sfuel] Fuel was vented out into space." - if(prob(10)) - var/deadname = remove_crewmember() - eventdat += "
    [deadname] was killed by rapid depressurization." - eventdat += "
    You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together." - if(hull >= 1) - eventdat += "

    Use PartWait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_BLACKHOLE) - eventdat += "You were swept away into the black hole." - eventdat += "

    Oh...

    " - eventdat += "

    Close

    " - settlers = list() - - if(ORION_TRAIL_LING) - eventdat += "Strange reports warn of changelings infiltrating crews on trips to Orion..." - if(settlers.len <= 2) - eventdat += "
    Your crew's chance of reaching Orion is so slim the changelings likely avoided your ship..." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - if(prob(10)) // "likely", I didn't say it was guaranteed! - lings_aboard = min(++lings_aboard,2) - else - if(lings_aboard) //less likely to stack lings - if(prob(20)) - lings_aboard = min(++lings_aboard,2) - else if(prob(70)) - lings_aboard = min(++lings_aboard,2) - - eventdat += "

    Kill a crewmember

    " - eventdat += "

    Risk it

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_LING_ATTACK) - if(lings_aboard <= 0) //shouldn't trigger, but hey. - eventdat += "Haha, fooled you, there are no changelings on board!" - eventdat += "
    (You should report this to a coder :S)" - else - var/ling1 = remove_crewmember() - var/ling2 = "" - if(lings_aboard >= 2) - ling2 = remove_crewmember() - - eventdat += "Oh no, some of your crew are Changelings!" - if(ling2) - eventdat += "
    [ling1] and [ling2]'s arms twist and contort into grotesque blades!" - else - eventdat += "
    [ling1]'s arm twists and contorts into a grotesque blade!" - - var/chance2attack = alive*20 - if(prob(chance2attack)) - var/chancetokill = 30*lings_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 lings, 2 crew is 50% chance - if(prob(chancetokill)) - var/deadguy = remove_crewmember() - eventdat += "
    The Changeling[ling2 ? "s":""] run[ling2 ? "":"s"] up to [deadguy] and capitulates them!" - else - eventdat += "
    You valiantly fight off the Changeling[ling2 ? "s":""]!" - eventdat += "
    You cut the Changeling[ling2 ? "s":""] up into meat... Eww" - if(ling2) - food += 30 - lings_aboard = max(0,lings_aboard-2) - else - food += 15 - lings_aboard = max(0,--lings_aboard) - else - eventdat += "
    The Changeling[ling2 ? "s":""] run[ling2 ? "":"s"] away, What wimps!" - if(ling2) - lings_aboard = max(0,lings_aboard-2) - else - lings_aboard = max(0,--lings_aboard) - - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - - - if(ORION_TRAIL_SPACEPORT) - if(spaceport_raided) - eventdat += "The Spaceport is on high alert! They wont let you dock since you tried to attack them!" - if(last_spaceport_action) - eventdat += "
    Last Spaceport Action: [last_spaceport_action]" - eventdat += "

    Depart Spaceport

    " - eventdat += "

    Close

    " - else - eventdat += "You pull the ship up to dock at a nearby Spaceport, lucky find!" - eventdat += "
    This Spaceport is home to travellers who failed to reach Orion, but managed to find a different home..." - eventdat += "
    Trading terms: FU = Fuel, FO = Food" - if(last_spaceport_action) - eventdat += "
    Last Spaceport Action: [last_spaceport_action]" - eventdat += "

    Crew:

    " - eventdat += english_list(settlers) - eventdat += "
    Food: [food] | Fuel: [fuel]" - eventdat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" - - - //If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port) - if(!spaceport_freebie && (fuel < 20 || food < 20)) - spaceport_freebie++ - var/FU = 10 - var/FO = 10 - var/freecrew = 0 - if(prob(30)) - FU = 25 - FO = 25 - - if(prob(10)) - add_crewmember() - freecrew++ - - eventdat += "
    The traders of the spaceport take pitty on you, and give you some food and fuel (+[FU]FU,+[FO]FO)" - if(freecrew) - eventdat += "
    You also gain a new crewmember!" - - fuel += FU - food += FO - - //CREW INTERACTIONS - eventdat += "

    Crew Management:

    " - - //Buy crew - if(food >= 10 && fuel >= 10) - eventdat += "

    Hire a new Crewmember (-10FU,-10FO)

    " - else - eventdat += "

    Cant afford a new Crewmember

    " - - //Sell crew - if(settlers.len > 1) - eventdat += "

    Sell crew for Fuel and Food (+15FU,+15FO)

    " - else - eventdat += "

    Cant afford to sell a Crewmember

    " - - //BUY/SELL STUFF - eventdat += "

    Spare Parts:

    " - - //Engine parts - if(fuel > 5) - eventdat += "

    Buy Engine Parts (-5FU)

    " - else - eventdat += "

    Cant afford to buy Engine Parts" - - //Hull plates - if(fuel > 5) - eventdat += "

    Buy Hull Plates (-5FU)

    " - else - eventdat += "

    Cant afford to buy Hull Plates" - - //Electronics - if(fuel > 5) - eventdat += "

    Buy Spare Electronics (-5FU)

    " - else - eventdat += "

    Cant afford to buy Spare Electronics" - - //Trade - if(fuel > 5) - eventdat += "

    Trade Fuel for Food (-5FU,+5FO)

    " - else - eventdat += "

    Cant afford to Trade Fuel for Food 5) - eventdat += "

    Trade Food for Fuel (+5FU,-5FO)

    " - else - eventdat += "

    Cant afford to Trade Food for Fuel= 1) //need to make sure we even have anyone to remove - removed = pick(safe2remove) - - if(removed) - if(lings_aboard && prob(40*lings_aboard)) //if there are 2 lings you're twice as likely to get one, obviously - lings_aboard = max(0,--lings_aboard) - settlers -= removed - alive-- - return removed - - -/obj/machinery/computer/arcade/orion_trail/proc/win() - playing = 0 - turns = 1 - atom_say("Congratulations, you made it to Orion!") - if(emagged) - new /obj/item/orion_ship(get_turf(src)) - message_admins("[key_name_admin(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") - log_game("[key_name(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") - else - var/score = alive + round(food/2) + round(fuel/5) + engine + hull + electronics - lings_aboard - prizevend(score) - emagged = 0 - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - -/obj/machinery/computer/arcade/orion_trail/emag_act(mob/user) - if(!emagged) - to_chat(user, "You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Realism Mode.") - name = "The Orion Trail: Realism Edition" - desc = "Learn how our ancestors got to Orion, and try not to die in the process!" - newgame() - emagged = 1 - -/mob/living/simple_animal/hostile/syndicate/ranged/orion - name = "spaceport security" - desc = "Premier corporate security forces for all spaceports found along the Orion Trail." - faction = list("orion") - loot = list() - del_on_death = TRUE - -/obj/item/orion_ship - name = "model settler ship" - desc = "A model spaceship, it looks like those used back in the day when travelling to Orion! It even has a miniature FX-293 reactor, which was renowned for its instability and tendency to explode..." - icon = 'icons/obj/toy.dmi' - icon_state = "ship" - w_class = WEIGHT_CLASS_SMALL - var/active = 0 //if the ship is on - -/obj/item/orion_ship/examine(mob/user) - . = ..() - if(in_range(user, src)) - if(!active) - . += "There's a little switch on the bottom. It's flipped down." - else - . += "There's a little switch on the bottom. It's flipped up." - -/obj/item/orion_ship/attack_self(mob/user) //Minibomb-level explosion. Should probably be more because of how hard it is to survive the machine! Also, just over a 5-second fuse - if(active) - return - - message_admins("[key_name_admin(usr)] primed an explosive Orion ship for detonation.") - log_game("[key_name(usr)] primed an explosive Orion ship for detonation.") - - to_chat(user, "You flip the switch on the underside of [src].") - active = 1 - visible_message("[src] softly beeps and whirs to life!") - playsound(src.loc, 'sound/machines/defib_saftyon.ogg', 25, 1) - atom_say("This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.") - sleep(20) - visible_message("[src] begins to vibrate...") - atom_say("Uh, Port? Having some issues with our reactor, could you check it out? Over.") - sleep(30) - atom_say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-") - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 25, 1) - sleep(3.6) - visible_message("[src] explodes!") - explosion(src.loc, 1,2,4, flame_range = 3) - qdel(src) - - -#undef ORION_TRAIL_WINTURN -#undef ORION_TRAIL_RAIDERS -#undef ORION_TRAIL_FLUX -#undef ORION_TRAIL_ILLNESS -#undef ORION_TRAIL_BREAKDOWN -#undef ORION_TRAIL_LING -#undef ORION_TRAIL_LING_ATTACK -#undef ORION_TRAIL_MALFUNCTION -#undef ORION_TRAIL_COLLISION -#undef ORION_TRAIL_SPACEPORT -#undef ORION_TRAIL_BLACKHOLE +/obj/machinery/computer/arcade + name = "random arcade" + desc = "random arcade machine" + icon = 'icons/obj/computer.dmi' + icon_state = "arcade" + icon_keyboard = null + icon_screen = "invaders" + light_color = "#00FF00" + var/prize = /obj/item/stack/tickets + +/obj/machinery/computer/arcade/proc/Reset() + return + +/obj/machinery/computer/arcade/New() + ..() + if(!circuit) + var/choice = pick(subtypesof(/obj/machinery/computer/arcade)) + new choice(loc) + qdel(src) + return + Reset() + + +/obj/machinery/computer/arcade/proc/prizevend(var/score) + if(!contents.len) + var/prize_amount + if(score) + prize_amount = score + else + prize_amount = rand(1, 10) + new prize(get_turf(src), prize_amount) + else + var/atom/movable/prize = pick(contents) + prize.loc = get_turf(src) + +/obj/machinery/computer/arcade/emp_act(severity) + ..(severity) + if(stat & (NOPOWER|BROKEN)) + return + var/num_of_prizes = 0 + switch(severity) + if(1) + num_of_prizes = rand(1,4) + if(2) + num_of_prizes = rand(0,2) + for(var/i = num_of_prizes; i > 0; i--) + prizevend() + explosion(get_turf(src), -1, 0, 1+num_of_prizes, flame_range = 1+num_of_prizes) + + +/obj/machinery/computer/arcade/battle + name = "arcade machine" + desc = "Does not support Pinball." + icon = 'icons/obj/computer.dmi' + icon_state = "arcade" + circuit = /obj/item/circuitboard/arcade/battle + var/enemy_name = "Space Villian" + var/temp = "Winners Don't Use Spacedrugs" //Temporary message, for attack messages, etc + var/player_hp = 30 //Player health/attack points + var/player_mp = 10 + var/enemy_hp = 45 //Enemy health/attack points + var/enemy_mp = 20 + var/gameover = 0 + var/blocked = 0 //Player cannot attack/heal while set + var/turtle = 0 + +/obj/machinery/computer/arcade/battle/Reset() + var/name_action + var/name_part1 + var/name_part2 + + name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") + + name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") + name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") + + enemy_name = replacetext((name_part1 + name_part2), "the ", "") + name = (name_action + name_part1 + name_part2) + +/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) + if(..()) + return + user.set_machine(src) + var/dat = "Close" + dat += "

    [enemy_name]

    " + + dat += "

    [temp]

    " + dat += "
    Health: [player_hp] | Magic: [player_mp] | Enemy Health: [enemy_hp]
    " + + if(gameover) + dat += "
    New Game" + else + dat += "
    Attack | " + dat += "Heal | " + dat += "Recharge Power" + + dat += "
    " + + //user << browse(dat, "window=arcade") + //onclose(user, "arcade") + var/datum/browser/popup = new(user, "arcade", "Space Villian 2000") + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) + popup.open() + return + +/obj/machinery/computer/arcade/battle/Topic(href, href_list) + if(..()) + return + + if(!blocked && !gameover) + if(href_list["attack"]) + blocked = 1 + var/attackamt = rand(2,6) + temp = "You attack for [attackamt] damage!" + playsound(src.loc, 'sound/arcade/hit.ogg', 20, 1, extrarange = -6, falloff = 10) + updateUsrDialog() + if(turtle > 0) + turtle-- + + sleep(10) + enemy_hp -= attackamt + arcade_action() + + else if(href_list["heal"]) + blocked = 1 + var/pointamt = rand(1,3) + var/healamt = rand(6,8) + temp = "You use [pointamt] magic to heal for [healamt] damage!" + playsound(src.loc, 'sound/arcade/heal.ogg', 20, 1, extrarange = -6, falloff = 10) + updateUsrDialog() + turtle++ + + sleep(10) + player_mp -= pointamt + player_hp += healamt + blocked = 1 + updateUsrDialog() + arcade_action() + + else if(href_list["charge"]) + blocked = 1 + var/chargeamt = rand(4,7) + temp = "You regain [chargeamt] points" + playsound(src.loc, 'sound/arcade/mana.ogg', 20, 1, extrarange = -6, falloff = 10) + player_mp += chargeamt + if(turtle > 0) + turtle-- + + updateUsrDialog() + sleep(10) + arcade_action() + + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=arcade") + + else if(href_list["newgame"]) //Reset everything + temp = "New Round" + player_hp = 30 + player_mp = 10 + enemy_hp = 45 + enemy_mp = 20 + gameover = 0 + turtle = 0 + + if(emagged) + Reset() + emagged = 0 + + add_fingerprint(usr) + updateUsrDialog() + return + +/obj/machinery/computer/arcade/battle/proc/arcade_action() + if((enemy_mp <= 0) || (enemy_hp <= 0)) + if(!gameover) + gameover = 1 + temp = "[enemy_name] has fallen! Rejoice!" + playsound(src.loc, 'sound/arcade/win.ogg', 20, 1, extrarange = -6, falloff = 10) + + if(emagged) + feedback_inc("arcade_win_emagged") + new /obj/effect/spawner/newbomb/timer/syndicate(get_turf(src)) + new /obj/item/clothing/head/collectable/petehat(get_turf(src)) + message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") + log_game("[key_name(usr)] has outbombed Cuban Pete and been awarded a bomb.") + Reset() + emagged = 0 + else + feedback_inc("arcade_win_normal") + var/score = player_hp + player_mp + 5 + prizevend(score) + + else if(emagged && (turtle >= 4)) + var/boomamt = rand(5,10) + temp = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!" + playsound(src.loc, 'sound/arcade/boom.ogg', 20, 1, extrarange = -6, falloff = 10) + player_hp -= boomamt + + else if((enemy_mp <= 5) && (prob(70))) + var/stealamt = rand(2,3) + temp = "[enemy_name] steals [stealamt] of your power!" + playsound(src.loc, 'sound/arcade/steal.ogg', 20, 1, extrarange = -6, falloff = 10) + player_mp -= stealamt + updateUsrDialog() + + if(player_mp <= 0) + gameover = 1 + sleep(10) + temp = "You have been drained! GAME OVER" + playsound(src.loc, 'sound/arcade/lose.ogg', 20, 1, extrarange = -6, falloff = 10) + if(emagged) + feedback_inc("arcade_loss_mana_emagged") + usr.gib() + else + feedback_inc("arcade_loss_mana_normal") + + else if((enemy_hp <= 10) && (enemy_mp > 4)) + temp = "[enemy_name] heals for 4 health!" + playsound(src.loc, 'sound/arcade/heal.ogg', 20, 1, extrarange = -6, falloff = 10) + enemy_hp += 4 + enemy_mp -= 4 + + else + var/attackamt = rand(3,6) + temp = "[enemy_name] attacks for [attackamt] damage!" + playsound(src.loc, 'sound/arcade/hit.ogg', 20, 1, extrarange = -6, falloff = 10) + player_hp -= attackamt + + if((player_mp <= 0) || (player_hp <= 0)) + gameover = 1 + temp = "You have been crushed! GAME OVER" + playsound(src.loc, 'sound/arcade/lose.ogg', 20, 1, extrarange = -6, falloff = 10) + if(emagged) + feedback_inc("arcade_loss_hp_emagged") + usr.gib() + else + feedback_inc("arcade_loss_hp_normal") + + blocked = 0 + return + + +/obj/machinery/computer/arcade/battle/emag_act(user as mob) + if(!emagged) + temp = "If you die in the game, you die for real!" + player_hp = 30 + player_mp = 10 + enemy_hp = 45 + enemy_mp = 20 + gameover = 0 + blocked = 0 + + emagged = 1 + + enemy_name = "Cuban Pete" + name = "Outbomb Cuban Pete" + + updateUsrDialog() + +// *** THE ORION TRAIL ** // + +#define ORION_TRAIL_WINTURN 9 + +//Orion Trail Events +#define ORION_TRAIL_RAIDERS "Raiders" +#define ORION_TRAIL_FLUX "Interstellar Flux" +#define ORION_TRAIL_ILLNESS "Illness" +#define ORION_TRAIL_BREAKDOWN "Breakdown" +#define ORION_TRAIL_LING "Changelings?" +#define ORION_TRAIL_LING_ATTACK "Changeling Ambush" +#define ORION_TRAIL_MALFUNCTION "Malfunction" +#define ORION_TRAIL_COLLISION "Collision" +#define ORION_TRAIL_SPACEPORT "Spaceport" +#define ORION_TRAIL_BLACKHOLE "BlackHole" + + +/obj/machinery/computer/arcade/orion_trail + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + icon_state = "arcade" + circuit = /obj/item/circuitboard/arcade/orion_trail + var/busy = 0 //prevent clickspam that allowed people to ~speedrun~ the game. + var/engine = 0 + var/hull = 0 + var/electronics = 0 + var/food = 80 + var/fuel = 60 + var/turns = 4 + var/playing = 0 + var/gameover = 0 + var/alive = 4 + var/eventdat = null + var/event = null + var/list/settlers = list("Harry","Larry","Bob") + var/list/events = list(ORION_TRAIL_RAIDERS = 3, + ORION_TRAIL_FLUX = 1, + ORION_TRAIL_ILLNESS = 3, + ORION_TRAIL_BREAKDOWN = 2, + ORION_TRAIL_LING = 3, + ORION_TRAIL_MALFUNCTION = 2, + ORION_TRAIL_COLLISION = 1, + ORION_TRAIL_SPACEPORT = 2 + ) + var/list/stops = list() + var/list/stopblurbs = list() + var/lings_aboard = 0 + var/spaceport_raided = 0 + var/spaceport_freebie = 0 + var/last_spaceport_action = "" + +/obj/machinery/computer/arcade/orion_trail/Reset() + // Sets up the main trail + stops = list("Pluto","Asteroid Belt","Proxima Centauri","Dead Space","Rigel Prime","Tau Ceti Beta","Black Hole","Space Outpost Beta-9","Orion Prime") + stopblurbs = list( + "Pluto, long since occupied with long-range sensors and scanners, stands ready to, and indeed continues to probe the far reaches of the galaxy.", + "At the edge of the Sol system lies a treacherous asteroid belt. Many have been crushed by stray asteroids and misguided judgement.", + "The nearest star system to Sol, in ages past it stood as a reminder of the boundaries of sub-light travel, now a low-population sanctuary for adventurers and traders.", + "This region of space is particularly devoid of matter. Such low-density pockets are known to exist, but the vastness of it is astounding.", + "Rigel Prime, the center of the Rigel system, burns hot, basking its planetary bodies in warmth and radiation.", + "Tau Ceti Beta has recently become a waypoint for colonists headed towards Orion. There are many ships and makeshift stations in the vicinity.", + "Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through. We could stay of course, but risk of being overcome by its gravity, or we could change course to go around, which will take longer.", + "You have come into range of the first man-made structure in this region of space. It has been constructed not by travellers from Sol, but by colonists from Orion. It stands as a monument to the colonists' success.", + "You have made it to Orion! Congratulations! Your crew is one of the few to start a new foothold for mankind!" + ) + +/obj/machinery/computer/arcade/orion_trail/proc/newgame() + // Set names of settlers in crew + settlers = list() + for(var/i = 1; i <= 3; i++) + add_crewmember() + add_crewmember("[usr]") + // Re-set items to defaults + engine = 1 + hull = 1 + electronics = 1 + food = 80 + fuel = 60 + alive = 4 + turns = 1 + event = null + playing = 1 + gameover = 0 + lings_aboard = 0 + + //spaceport junk + spaceport_raided = 0 + spaceport_freebie = 0 + last_spaceport_action = "" + +/obj/machinery/computer/arcade/orion_trail/attack_hand(mob/user) + if(..()) + return + if(fuel <= 0 || food <=0 || settlers.len == 0) + gameover = 1 + event = null + user.set_machine(src) + var/dat = "" + if(gameover) + dat = "

    Game Over

    " + dat += "Like many before you, your crew never made it to Orion, lost to space...
    Forever." + if(settlers.len == 0) + dat += "
    Your entire crew died, your ship joins the fleet of ghost-ships littering the galaxy." + else + if(food <= 0) + dat += "
    You ran out of food and starved." + if(emagged) + user.set_nutrition(0) //yeah you pretty hongry + to_chat(user, "Your body instantly contracts to that of one who has not eaten in months. Agonizing cramps seize you as you fall to the floor.") + if(fuel <= 0) + dat += "
    You ran out of fuel, and drift, slowly, into a star." + if(emagged) + var/mob/living/M = user + M.adjust_fire_stacks(5) + M.IgniteMob() //flew into a star, so you're on fire + to_chat(user, "You feel an immense wave of heat emanate from the arcade machine. Your skin bursts into flames.") + dat += "

    OK...

    " + + if(emagged) + to_chat(user, "You're never going to make it to Orion...") + user.death() + emagged = 0 //removes the emagged status after you lose + playing = 0 //also a new game + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + + else if(event) + dat = eventdat + else if(playing) + var/title = stops[turns] + var/subtext = stopblurbs[turns] + dat = "

    [title]

    " + dat += "[subtext]" + dat += "

    Crew:

    " + dat += english_list(settlers) + dat += "
    Food: [food] | Fuel: [fuel]" + dat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" + if(turns == 7) + dat += "

    Go Around Continue

    " + else + dat += "

    Continue

    " + dat += "

    Kill a crewmember

    " + dat += "

    Close

    " + else + dat = "

    The Orion Trail

    " + dat += "

    Experience the journey of your ancestors!



    " + dat += "
    New Game
    " + dat += "

    Close

    " + var/datum/browser/popup = new(user, "arcade", "The Orion Trail",400,700) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) + popup.open() + return + +/obj/machinery/computer/arcade/orion_trail/Topic(href, href_list) + . = ..() + if(.) + return + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=arcade") + + if(busy) + return + busy = 1 + + if(href_list["continue"]) //Continue your travels + if(turns >= ORION_TRAIL_WINTURN) + win() + else + food -= (alive+lings_aboard)*2 + fuel -= 5 + if(turns == 2 && prob(30)) + event = ORION_TRAIL_COLLISION + event() + else if(prob(75)) + event = pickweight(events) + if(lings_aboard) + if(event == ORION_TRAIL_LING || prob(55)) + event = ORION_TRAIL_LING_ATTACK + event() + turns += 1 + if(emagged) + var/mob/living/carbon/M = usr //for some vars + switch(event) + if(ORION_TRAIL_RAIDERS) + if(prob(50)) + to_chat(usr, "You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?") + M.AdjustHallucinate(30) + else + to_chat(usr, "Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...") + M.take_organ_damage(30) + playsound(loc, 'sound/weapons/genhit2.ogg', 100, 1) + if(ORION_TRAIL_ILLNESS) + var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS + if(severity == 1) + to_chat(M, "You suddenly feel slightly nauseous.")//got off lucky + + if(severity == 2) + to_chat(usr, "You suddenly feel extremely nauseous and hunch over until it passes.") + M.Stun(3) + if(severity >= 3) //you didn't pray hard enough + to_chat(M, "An overpowering wave of nausea consumes over you. You hunch over, your stomach's contents preparing for a spectacular exit.") + M.Stun(5) + sleep(30) + atom_say("[M] violently throws up!") + playsound(loc, 'sound/effects/splat.ogg', 50, 1) + M.adjust_nutrition(-50) //lose a lot of food + var/turf/location = usr.loc + if(istype(location, /turf/simulated)) + location.add_vomit_floor(TRUE) + if(ORION_TRAIL_FLUX) + if(prob(75)) + M.Weaken(3) + atom_say("A sudden gust of powerful wind slams [M] into the floor!") + M.take_organ_damage(25) + playsound(src.loc, 'sound/weapons/genhit.ogg', 100, 1) + else + to_chat(M, "A violent gale blows past you, and you barely manage to stay standing!") + if(ORION_TRAIL_COLLISION) //by far the most damaging event + if(prob(90)) + playsound(src.loc, 'sound/effects/bang.ogg', 100, 1) + var/turf/simulated/floor/F + for(F in orange(1, src)) + F.ChangeTurf(F.baseturf) + atom_say("Something slams into the floor around [src], exposing it to space!") + if(hull) + sleep(10) + atom_say("A new floor suddenly appears around [src]. What the hell?") + playsound(src.loc, 'sound/weapons/genhit.ogg', 100, 1) + var/turf/space/T + for(T in orange(1, src)) + T.ChangeTurf(/turf/simulated/floor/plating) + else + atom_say("Something slams into the floor around [src] - luckily, it didn't get through!") + playsound(src.loc, 'sound/effects/bang.ogg', 20, 1) + if(ORION_TRAIL_MALFUNCTION) + playsound(src.loc, 'sound/effects/empulse.ogg', 20, 1) + visible_message("[src] malfunctions, randomizing in-game stats!") + var/oldfood = food + var/oldfuel = fuel + food = rand(10,80) / rand(1,2) + fuel = rand(10,60) / rand(1,2) + if(electronics) + sleep(10) + if(oldfuel > fuel && oldfood > food) + audible_message("[src] lets out a somehow reassuring chime.") + else if(oldfuel < fuel || oldfood < food) + audible_message("[src] lets out a somehow ominous chime.") + food = oldfood + fuel = oldfuel + playsound(src.loc, 'sound/machines/chime.ogg', 20, 1) + + else if(href_list["newgame"]) //Reset everything + newgame() + else if(href_list["menu"]) //back to the main menu + playing = 0 + event = null + gameover = 0 + food = 80 + fuel = 60 + settlers = list("Harry","Larry","Bob") + else if(href_list["slow"]) //slow down + food -= (alive+lings_aboard)*2 + fuel -= 5 + event = null + else if(href_list["pastblack"]) //slow down + food -= ((alive+lings_aboard)*2)*3 + fuel -= 15 + turns += 1 + event = null + else if(href_list["useengine"]) //use parts + engine = max(0, --engine) + event = null + else if(href_list["useelec"]) //use parts + electronics = max(0, --electronics) + event = null + else if(href_list["usehull"]) //use parts + hull = max(0, --hull) + event = null + else if(href_list["wait"]) //wait 3 days + food -= ((alive+lings_aboard)*2)*3 + event = null + else if(href_list["keepspeed"]) //keep speed + if(prob(75)) + event = "Breakdown" + event() + else + event = null + else if(href_list["blackhole"]) //keep speed past a black hole + if(prob(75)) + event = ORION_TRAIL_BLACKHOLE + event() + if(emagged) //has to be here because otherwise it doesn't work + playsound(src.loc, 'sound/effects/supermatter.ogg', 100, 1) + atom_say("A miniature black hole suddenly appears in front of [src], devouring [usr] alive!") + usr.Stun(10) //you can't run :^) + var/S = new /obj/singularity/academy(usr.loc) + emagged = 0 //immediately removes emagged status so people can't kill themselves by sprinting up and interacting + sleep(50) + atom_say("[S] winks out, just as suddenly as it appeared.") + qdel(S) + else + event = null + turns += 1 + else if(href_list["holedeath"]) + gameover = 1 + event = null + else if(href_list["eventclose"]) //end an event + event = null + + else if(href_list["killcrew"]) //shoot a crewmember + var/sheriff = remove_crewmember() //I shot the sheriff + playsound(loc,'sound/weapons/gunshots/gunshot.ogg', 100, 1) + + if(settlers.len == 0 || alive == 0) + atom_say("The last crewmember [sheriff], shot themselves, GAME OVER!") + if(emagged) + usr.death(0) + emagged = 0 + gameover = 1 + event = null + else if(emagged) + if(usr.name == sheriff) + atom_say("The crew of the ship chose to kill [usr.name]!") + usr.death(0) + + if(event == ORION_TRAIL_LING) //only ends the ORION_TRAIL_LING event, since you can do this action in multiple places + event = null + + //Spaceport specific interactions + //they get a header because most of them don't reset event (because it's a shop, you leave when you want to) + //they also call event() again, to regen the eventdata, which is kind of odd but necessary + else if(href_list["buycrew"]) //buy a crewmember + var/bought = add_crewmember() + last_spaceport_action = "You hired [bought] as a new crewmember." + fuel -= 10 + food -= 10 + event() + + else if(href_list["sellcrew"]) //sell a crewmember + var/sold = remove_crewmember() + last_spaceport_action = "You sold your crewmember, [sold]!" + fuel += 7 + food += 7 + event() + + else if(href_list["leave_spaceport"]) + event = null + spaceport_raided = 0 + spaceport_freebie = 0 + last_spaceport_action = "" + + else if(href_list["raid_spaceport"]) + var/success = min(15 * alive,100) //default crew (4) have a 60% chance + spaceport_raided = 1 + + var/FU = 0 + var/FO = 0 + if(prob(success)) + FU = rand(5,15) + FO = rand(5,15) + last_spaceport_action = "You successfully raided the spaceport! you gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)" + else + FU = rand(-5,-15) + FO = rand(-5,-15) + last_spaceport_action = "You failed to raid the spaceport! you lost [FU*-1] Fuel and [FO*-1] Food in your scramble to escape! ([FU]FU,[FO]FO)" + + //your chance of lose a crewmember is 1/2 your chance of success + //this makes higher % failures hurt more, don't get cocky space cowboy! + if(prob(success*5)) + var/lost_crew = remove_crewmember() + last_spaceport_action = "You failed to raid the spaceport! you lost [FU*-1] Fuel and [FO*-1] Food, AND [lost_crew] in your scramble to escape! ([FU]FI,[FO]FO,-Crew)" + if(emagged) + atom_say("WEEWOO WEEWOO, Spaceport Security en route!") + for(var/i, i<=3, i++) + var/mob/living/simple_animal/hostile/syndicate/ranged/orion/O = new/mob/living/simple_animal/hostile/syndicate/ranged/orion(get_turf(src)) + O.target = usr + + + fuel += FU + food += FO + event() + + else if(href_list["buyparts"]) + switch(text2num(href_list["buyparts"])) + if(1) //Engine Parts + engine++ + last_spaceport_action = "Bought Engine Parts" + if(2) //Hull Plates + hull++ + last_spaceport_action = "Bought Hull Plates" + if(3) //Spare Electronics + electronics++ + last_spaceport_action = "Bought Spare Electronics" + fuel -= 5 //they all cost 5 + event() + + else if(href_list["trade"]) + switch(text2num(href_list["trade"])) + if(1) //Fuel + fuel -= 5 + food += 5 + last_spaceport_action = "Traded Fuel for Food" + if(2) //Food + fuel += 5 + food -= 5 + last_spaceport_action = "Traded Food for Fuel" + event() + + add_fingerprint(usr) + updateUsrDialog() + busy = 0 + return + + +/obj/machinery/computer/arcade/orion_trail/proc/event() + eventdat = "

    [event]

    " + + switch(event) + if(ORION_TRAIL_RAIDERS) + eventdat += "Raiders have come aboard your ship!" + if(prob(50)) + var/sfood = rand(1,10) + var/sfuel = rand(1,10) + food -= sfood + fuel -= sfuel + eventdat += "
    They have stolen [sfood] Food and [sfuel] Fuel." + else if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
    [deadname] tried to fight back but was killed." + else + eventdat += "
    Fortunately you fended them off without any trouble." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_FLUX) + eventdat += "This region of space is highly turbulent.
    If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies." + eventdat += "
    What will you do?" + eventdat += "

    Slow Down Continue

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_ILLNESS) + eventdat += "A deadly illness has been contracted!" + var/deadname = remove_crewmember() + eventdat += "
    [deadname] was killed by the disease." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_BREAKDOWN) + eventdat += "Oh no! The engine has broken down!" + eventdat += "
    You can repair it with an engine part, or you can make repairs for 3 days." + if(engine >= 1) + eventdat += "

    Use PartWait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_MALFUNCTION) + eventdat += "The ship's systems are malfunctioning!" + eventdat += "
    You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI." + if(electronics >= 1) + eventdat += "

    Use PartWait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_COLLISION) + eventdat += "Something hit us! Looks like there's some hull damage." + if(prob(25)) + var/sfood = rand(5,15) + var/sfuel = rand(5,15) + food -= sfood + fuel -= sfuel + eventdat += "
    [sfood] Food and [sfuel] Fuel was vented out into space." + if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
    [deadname] was killed by rapid depressurization." + eventdat += "
    You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together." + if(hull >= 1) + eventdat += "

    Use PartWait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_BLACKHOLE) + eventdat += "You were swept away into the black hole." + eventdat += "

    Oh...

    " + eventdat += "

    Close

    " + settlers = list() + + if(ORION_TRAIL_LING) + eventdat += "Strange reports warn of changelings infiltrating crews on trips to Orion..." + if(settlers.len <= 2) + eventdat += "
    Your crew's chance of reaching Orion is so slim the changelings likely avoided your ship..." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + if(prob(10)) // "likely", I didn't say it was guaranteed! + lings_aboard = min(++lings_aboard,2) + else + if(lings_aboard) //less likely to stack lings + if(prob(20)) + lings_aboard = min(++lings_aboard,2) + else if(prob(70)) + lings_aboard = min(++lings_aboard,2) + + eventdat += "

    Kill a crewmember

    " + eventdat += "

    Risk it

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_LING_ATTACK) + if(lings_aboard <= 0) //shouldn't trigger, but hey. + eventdat += "Haha, fooled you, there are no changelings on board!" + eventdat += "
    (You should report this to a coder :S)" + else + var/ling1 = remove_crewmember() + var/ling2 = "" + if(lings_aboard >= 2) + ling2 = remove_crewmember() + + eventdat += "Oh no, some of your crew are Changelings!" + if(ling2) + eventdat += "
    [ling1] and [ling2]'s arms twist and contort into grotesque blades!" + else + eventdat += "
    [ling1]'s arm twists and contorts into a grotesque blade!" + + var/chance2attack = alive*20 + if(prob(chance2attack)) + var/chancetokill = 30*lings_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 lings, 2 crew is 50% chance + if(prob(chancetokill)) + var/deadguy = remove_crewmember() + eventdat += "
    The Changeling[ling2 ? "s":""] run[ling2 ? "":"s"] up to [deadguy] and capitulates them!" + else + eventdat += "
    You valiantly fight off the Changeling[ling2 ? "s":""]!" + eventdat += "
    You cut the Changeling[ling2 ? "s":""] up into meat... Eww" + if(ling2) + food += 30 + lings_aboard = max(0,lings_aboard-2) + else + food += 15 + lings_aboard = max(0,--lings_aboard) + else + eventdat += "
    The Changeling[ling2 ? "s":""] run[ling2 ? "":"s"] away, What wimps!" + if(ling2) + lings_aboard = max(0,lings_aboard-2) + else + lings_aboard = max(0,--lings_aboard) + + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + + + if(ORION_TRAIL_SPACEPORT) + if(spaceport_raided) + eventdat += "The Spaceport is on high alert! They wont let you dock since you tried to attack them!" + if(last_spaceport_action) + eventdat += "
    Last Spaceport Action: [last_spaceport_action]" + eventdat += "

    Depart Spaceport

    " + eventdat += "

    Close

    " + else + eventdat += "You pull the ship up to dock at a nearby Spaceport, lucky find!" + eventdat += "
    This Spaceport is home to travellers who failed to reach Orion, but managed to find a different home..." + eventdat += "
    Trading terms: FU = Fuel, FO = Food" + if(last_spaceport_action) + eventdat += "
    Last Spaceport Action: [last_spaceport_action]" + eventdat += "

    Crew:

    " + eventdat += english_list(settlers) + eventdat += "
    Food: [food] | Fuel: [fuel]" + eventdat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" + + + //If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port) + if(!spaceport_freebie && (fuel < 20 || food < 20)) + spaceport_freebie++ + var/FU = 10 + var/FO = 10 + var/freecrew = 0 + if(prob(30)) + FU = 25 + FO = 25 + + if(prob(10)) + add_crewmember() + freecrew++ + + eventdat += "
    The traders of the spaceport take pitty on you, and give you some food and fuel (+[FU]FU,+[FO]FO)" + if(freecrew) + eventdat += "
    You also gain a new crewmember!" + + fuel += FU + food += FO + + //CREW INTERACTIONS + eventdat += "

    Crew Management:

    " + + //Buy crew + if(food >= 10 && fuel >= 10) + eventdat += "

    Hire a new Crewmember (-10FU,-10FO)

    " + else + eventdat += "

    Cant afford a new Crewmember

    " + + //Sell crew + if(settlers.len > 1) + eventdat += "

    Sell crew for Fuel and Food (+15FU,+15FO)

    " + else + eventdat += "

    Cant afford to sell a Crewmember

    " + + //BUY/SELL STUFF + eventdat += "

    Spare Parts:

    " + + //Engine parts + if(fuel > 5) + eventdat += "

    Buy Engine Parts (-5FU)

    " + else + eventdat += "

    Cant afford to buy Engine Parts" + + //Hull plates + if(fuel > 5) + eventdat += "

    Buy Hull Plates (-5FU)

    " + else + eventdat += "

    Cant afford to buy Hull Plates" + + //Electronics + if(fuel > 5) + eventdat += "

    Buy Spare Electronics (-5FU)

    " + else + eventdat += "

    Cant afford to buy Spare Electronics" + + //Trade + if(fuel > 5) + eventdat += "

    Trade Fuel for Food (-5FU,+5FO)

    " + else + eventdat += "

    Cant afford to Trade Fuel for Food 5) + eventdat += "

    Trade Food for Fuel (+5FU,-5FO)

    " + else + eventdat += "

    Cant afford to Trade Food for Fuel= 1) //need to make sure we even have anyone to remove + removed = pick(safe2remove) + + if(removed) + if(lings_aboard && prob(40*lings_aboard)) //if there are 2 lings you're twice as likely to get one, obviously + lings_aboard = max(0,--lings_aboard) + settlers -= removed + alive-- + return removed + + +/obj/machinery/computer/arcade/orion_trail/proc/win() + playing = 0 + turns = 1 + atom_say("Congratulations, you made it to Orion!") + if(emagged) + new /obj/item/orion_ship(get_turf(src)) + message_admins("[key_name_admin(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") + log_game("[key_name(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") + else + var/score = alive + round(food/2) + round(fuel/5) + engine + hull + electronics - lings_aboard + prizevend(score) + emagged = 0 + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + +/obj/machinery/computer/arcade/orion_trail/emag_act(mob/user) + if(!emagged) + to_chat(user, "You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Realism Mode.") + name = "The Orion Trail: Realism Edition" + desc = "Learn how our ancestors got to Orion, and try not to die in the process!" + newgame() + emagged = 1 + +/mob/living/simple_animal/hostile/syndicate/ranged/orion + name = "spaceport security" + desc = "Premier corporate security forces for all spaceports found along the Orion Trail." + faction = list("orion") + loot = list() + del_on_death = TRUE + +/obj/item/orion_ship + name = "model settler ship" + desc = "A model spaceship, it looks like those used back in the day when travelling to Orion! It even has a miniature FX-293 reactor, which was renowned for its instability and tendency to explode..." + icon = 'icons/obj/toy.dmi' + icon_state = "ship" + w_class = WEIGHT_CLASS_SMALL + var/active = 0 //if the ship is on + +/obj/item/orion_ship/examine(mob/user) + . = ..() + if(in_range(user, src)) + if(!active) + . += "There's a little switch on the bottom. It's flipped down." + else + . += "There's a little switch on the bottom. It's flipped up." + +/obj/item/orion_ship/attack_self(mob/user) //Minibomb-level explosion. Should probably be more because of how hard it is to survive the machine! Also, just over a 5-second fuse + if(active) + return + + message_admins("[key_name_admin(usr)] primed an explosive Orion ship for detonation.") + log_game("[key_name(usr)] primed an explosive Orion ship for detonation.") + + to_chat(user, "You flip the switch on the underside of [src].") + active = 1 + visible_message("[src] softly beeps and whirs to life!") + playsound(src.loc, 'sound/machines/defib_saftyon.ogg', 25, 1) + atom_say("This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.") + sleep(20) + visible_message("[src] begins to vibrate...") + atom_say("Uh, Port? Having some issues with our reactor, could you check it out? Over.") + sleep(30) + atom_say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-") + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 25, 1) + sleep(3.6) + visible_message("[src] explodes!") + explosion(src.loc, 1,2,4, flame_range = 3) + qdel(src) + + +#undef ORION_TRAIL_WINTURN +#undef ORION_TRAIL_RAIDERS +#undef ORION_TRAIL_FLUX +#undef ORION_TRAIL_ILLNESS +#undef ORION_TRAIL_BREAKDOWN +#undef ORION_TRAIL_LING +#undef ORION_TRAIL_LING_ATTACK +#undef ORION_TRAIL_MALFUNCTION +#undef ORION_TRAIL_COLLISION +#undef ORION_TRAIL_SPACEPORT +#undef ORION_TRAIL_BLACKHOLE diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index f8c2979e043..123cee3ae4e 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -1,5 +1,5 @@ -var/global/list/priority_air_alarms = list() -var/global/list/minor_air_alarms = list() +GLOBAL_LIST_EMPTY(priority_air_alarms) +GLOBAL_LIST_EMPTY(minor_air_alarms) /obj/machinery/computer/atmos_alert @@ -12,7 +12,7 @@ var/global/list/minor_air_alarms = list() /obj/machinery/computer/atmos_alert/New() ..() - SSalarms.atmosphere_alarm.register(src, /obj/machinery/computer/station_alert/update_icon) + SSalarms.atmosphere_alarm.register(src, /obj/machinery/computer/station_alert/.proc/update_icon) /obj/machinery/computer/atmos_alert/Destroy() SSalarms.atmosphere_alarm.unregister(src) @@ -67,12 +67,11 @@ var/global/list/minor_air_alarms = list() var/obj/machinery/alarm/air_alarm = alarm_source.source if(istype(air_alarm)) var/list/new_ref = list("atmos_reset" = 1) - air_alarm.Topic(href, new_ref, state = air_alarm_topic) + air_alarm.Topic(href, new_ref, state = GLOB.air_alarm_topic) update_icon() return 1 - -var/datum/topic_state/air_alarm_topic/air_alarm_topic = new() +GLOBAL_DATUM_INIT(air_alarm_topic, /datum/topic_state/air_alarm_topic, new) /datum/topic_state/air_alarm_topic/href_list(var/mob/user) var/list/extra_href = list() diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index 84ab146589e..7b07cda3bbb 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -7,7 +7,7 @@ density = 1 anchored = 1.0 circuit = /obj/item/circuitboard/atmoscontrol - req_access = list(access_atmospherics) + req_access = list(ACCESS_ATMOSPHERICS) var/list/monitored_alarm_ids = null var/datum/nano_module/atmos_control/atmos_control diff --git a/code/game/machinery/computer/brigcells.dm b/code/game/machinery/computer/brigcells.dm index d322e99ae4b..a391bfa4021 100644 --- a/code/game/machinery/computer/brigcells.dm +++ b/code/game/machinery/computer/brigcells.dm @@ -8,7 +8,7 @@ active_power_usage = 500 circuit = /obj/item/circuitboard/brigcells light_color = LIGHT_COLOR_DARKRED - req_access = list(access_brig) + req_access = list(ACCESS_BRIG) /obj/machinery/computer/brigcells/attack_ai(mob/user) attack_hand(user) @@ -30,7 +30,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/computer/brigcells/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/brigcells/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/list/timers = list() for(var/obj/machinery/door_timer/T in GLOB.celltimers_list) diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 6b0e167cbb6..2f8321b57bb 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -1,687 +1,687 @@ -/obj/structure/computerframe - density = 1 - anchored = 0 - name = "computer frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "0" - max_integrity = 100 - var/state = 0 - var/obj/item/circuitboard/circuit = null - var/base_mineral = /obj/item/stack/sheet/metal - -/obj/structure/computerframe/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - drop_computer_parts() - return ..() // will qdel the frame - -/obj/structure/computerframe/obj_break(damage_flag) - deconstruct() - -/obj/structure/computerframe/proc/drop_computer_parts() - new base_mineral(loc, 5) - if(circuit) - circuit.forceMove(loc) - circuit = null - if(state >= 3) - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 - if(state >= 4) - new /obj/item/stack/sheet/glass(loc, 2) - -/obj/item/circuitboard - density = 0 - anchored = 0 - w_class = WEIGHT_CLASS_SMALL - name = "circuit board" - icon = 'icons/obj/module.dmi' - icon_state = "id_mod" - item_state = "electronic" - origin_tech = "programming=2" - materials = list(MAT_GLASS=200) - var/id = null - var/frequency = null - var/build_path = null - var/board_type = "computer" - var/list/req_components = null - var/powernet = null - var/list/records = null - var/frame_desc = null - var/contain_parts = 1 - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - -/obj/item/circuitboard/computer - board_type = "computer" - -/obj/item/circuitboard/machine - board_type = "machine" - -/obj/item/circuitboard/examine(mob/user) - . = ..() - if(LAZYLEN(req_components)) - var/list/nice_list = list() - for(var/B in req_components) - var/atom/A = B - if(!ispath(A)) - continue - nice_list += list("[req_components[A]] [initial(A.name)]") - . += "Required components: [english_list(nice_list)]." - -/obj/item/circuitboard/message_monitor - name = "Circuit board (Message Monitor)" - build_path = /obj/machinery/computer/message_monitor - origin_tech = "programming=2" -/obj/item/circuitboard/camera - name = "Circuit board (Camera Monitor)" - build_path = /obj/machinery/computer/security - origin_tech = "programming=2;combat=2" - -/obj/item/circuitboard/camera/telescreen - name = "Circuit board (Telescreen)" - build_path = /obj/machinery/computer/security/telescreen -/obj/item/circuitboard/camera/telescreen/entertainment - name = "Circuit board (Entertainment Monitor)" - build_path = /obj/machinery/computer/security/telescreen/entertainment -/obj/item/circuitboard/camera/wooden_tv - name = "Circuit board (Wooden TV)" - build_path = /obj/machinery/computer/security/wooden_tv -/obj/item/circuitboard/camera/mining - name = "Circuit board (Outpost Camera Monitor)" - build_path = /obj/machinery/computer/security/mining -/obj/item/circuitboard/camera/engineering - name = "Circuit board (Engineering Camera Monitor)" - build_path = /obj/machinery/computer/security/engineering - - -/obj/item/circuitboard/xenobiology - name = "Circuit board (Xenobiology Console)" - build_path = /obj/machinery/computer/camera_advanced/xenobio - origin_tech = "programming=3;biotech=3" -/obj/item/circuitboard/aicore - name = "Circuit board (AI Core)" - origin_tech = "programming=3" - board_type = "other" -/obj/item/circuitboard/aiupload - name = "Circuit board (AI Upload)" - build_path = /obj/machinery/computer/aiupload - origin_tech = "programming=4;engineering=4" -/obj/item/circuitboard/borgupload - name = "Circuit board (Cyborg Upload)" - build_path = /obj/machinery/computer/borgupload - origin_tech = "programming=4;engineering=4" -/obj/item/circuitboard/med_data - name = "Circuit board (Medical Records)" - build_path = /obj/machinery/computer/med_data - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/pandemic - name = "circuit board (PanD.E.M.I.C. 2200)" - build_path = /obj/machinery/computer/pandemic - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/scan_consolenew - name = "Circuit board (DNA Machine)" - build_path = /obj/machinery/computer/scan_consolenew - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/communications - name = "Circuit board (Communications Console)" - build_path = /obj/machinery/computer/communications - origin_tech = "programming=3;magnets=3" -/obj/item/circuitboard/card - name = "Circuit board (ID Computer)" - build_path = /obj/machinery/computer/card - origin_tech = "programming=3" -/obj/item/circuitboard/card/minor - name = "Circuit board (Dept ID Computer)" - build_path = /obj/machinery/computer/card/minor - var/target_dept = TARGET_DEPT_GENERIC -/obj/item/circuitboard/card/minor/hos - name = "Circuit board (Sec ID Computer)" - build_path = /obj/machinery/computer/card/minor/hos - target_dept = TARGET_DEPT_SEC -/obj/item/circuitboard/card/minor/cmo - name = "Circuit board (Medical ID Computer)" - build_path = /obj/machinery/computer/card/minor/cmo - target_dept = TARGET_DEPT_MED -/obj/item/circuitboard/card/minor/rd - name = "Circuit board (Science ID Computer)" - build_path = /obj/machinery/computer/card/minor/rd - target_dept = TARGET_DEPT_SCI -/obj/item/circuitboard/card/minor/ce - name = "Circuit board (Engineering ID Computer)" - build_path = /obj/machinery/computer/card/minor/ce - target_dept = TARGET_DEPT_ENG -/obj/item/circuitboard/card/centcom - name = "Circuit board (CentComm ID Computer)" - build_path = /obj/machinery/computer/card/centcom -/obj/item/circuitboard/teleporter - name = "Circuit board (Teleporter Console)" - build_path = /obj/machinery/computer/teleporter - origin_tech = "programming=3;bluespace=3;plasmatech=3" -/obj/item/circuitboard/secure_data - name = "Circuit board (Security Records)" - build_path = /obj/machinery/computer/secure_data - origin_tech = "programming=2;combat=2" -/obj/item/circuitboard/skills - name = "Circuit board (Employment Records)" - build_path = /obj/machinery/computer/skills -/obj/item/circuitboard/stationalert_engineering - name = "Circuit Board (Station Alert Console (Engineering))" - build_path = /obj/machinery/computer/station_alert -/obj/item/circuitboard/stationalert_security - name = "Circuit Board (Station Alert Console (Security))" - build_path = /obj/machinery/computer/station_alert -/obj/item/circuitboard/stationalert_all - name = "Circuit Board (Station Alert Console (All))" - build_path = /obj/machinery/computer/station_alert/all -/obj/item/circuitboard/atmos_alert - name = "Circuit Board (Atmospheric Alert Computer)" - build_path = /obj/machinery/computer/atmos_alert -/obj/item/circuitboard/atmoscontrol - name = "Circuit Board (Central Atmospherics Computer)" - build_path = /obj/machinery/computer/atmoscontrol -/obj/item/circuitboard/air_management - name = "Circuit board (Atmospheric Monitor)" - build_path = /obj/machinery/computer/general_air_control -/obj/item/circuitboard/injector_control - name = "Circuit board (Injector Control)" - build_path = /obj/machinery/computer/general_air_control/fuel_injection -/obj/item/circuitboard/pod - name = "Circuit board (Massdriver Control)" - build_path = /obj/machinery/computer/pod -/obj/item/circuitboard/pod/deathsquad - name = "Circuit board (Deathsquad Massdriver control)" - build_path = /obj/machinery/computer/pod/deathsquad -/obj/item/circuitboard/robotics - name = "Circuit board (Robotics Control Console)" - build_path = /obj/machinery/computer/robotics - origin_tech = "programming=3" -/obj/item/circuitboard/drone_control - name = "Circuit board (Drone Control)" - build_path = /obj/machinery/computer/drone_control - origin_tech = "programming=3" -/obj/item/circuitboard/cloning - name = "Circuit board (Cloning Machine Console)" - build_path = /obj/machinery/computer/cloning - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/arcade/battle - name = "circuit board (Arcade Battle)" - build_path = /obj/machinery/computer/arcade/battle - origin_tech = "programming=1" -/obj/item/circuitboard/arcade/orion_trail - name = "circuit board (Orion Trail)" - build_path = /obj/machinery/computer/arcade/orion_trail - origin_tech = "programming=1" -/obj/item/circuitboard/solar_control - name = "Circuit board (Solar Control)" - build_path = /obj/machinery/power/solar_control - origin_tech = "programming=2;powerstorage=2" -/obj/item/circuitboard/powermonitor - name = "Circuit board (Power Monitor)" - build_path = /obj/machinery/computer/monitor - origin_tech = "programming=2;powerstorage=2" -/obj/item/circuitboard/powermonitor/secret - name = "Circuit board (Outdated Power Monitor)" - build_path = /obj/machinery/computer/monitor/secret - origin_tech = "programming=2;powerstorage=2" -/obj/item/circuitboard/olddoor - name = "Circuit board (DoorMex)" - build_path = /obj/machinery/computer/pod/old -/obj/item/circuitboard/syndicatedoor - name = "Circuit board (ProComp Executive)" - build_path = /obj/machinery/computer/pod/old/syndicate -/obj/item/circuitboard/swfdoor - name = "Circuit board (Magix)" - build_path = /obj/machinery/computer/pod/old/swf -/obj/item/circuitboard/prisoner - name = "Circuit board (Prisoner Management)" - build_path = /obj/machinery/computer/prisoner -/obj/item/circuitboard/brigcells - name = "Circuit board (Brig Cell Control)" - build_path = /obj/machinery/computer/brigcells - - -// RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever -/obj/item/circuitboard/rdconsole - name = "Circuit Board (RD Console)" - desc = "Swipe a Scientist level ID or higher to reconfigure." - build_path = /obj/machinery/computer/rdconsole/core - req_access = list(access_tox) // This is for adjusting the type of computer we're building - in case something messes up the pre-existing robotics or mechanics consoles - var/access_types = list("R&D Core", "Robotics", "E.X.P.E.R.I-MENTOR", "Mechanics", "Public") - id = 1 -/obj/item/circuitboard/rdconsole/robotics - name = "Circuit Board (RD Console - Robotics)" - build_path = /obj/machinery/computer/rdconsole/robotics - id = 2 -/obj/item/circuitboard/rdconsole/experiment - name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" - build_path = /obj/machinery/computer/rdconsole/experiment - id = 3 -/obj/item/circuitboard/rdconsole/mechanics - name = "Circuit Board (RD Console - Mechanics)" - build_path = /obj/machinery/computer/rdconsole/mechanics - id = 4 -/obj/item/circuitboard/rdconsole/public - name = "Circuit Board (RD Console - Public)" - build_path = /obj/machinery/computer/rdconsole/public - id = 5 - - -/obj/item/circuitboard/mecha_control - name = "Circuit Board (Exosuit Control Console)" - build_path = /obj/machinery/computer/mecha -/obj/item/circuitboard/pod_locater - name = "Circuit Board (Pod Location Console)" - build_path = /obj/machinery/computer/podtracker -/obj/item/circuitboard/rdservercontrol - name = "Circuit Board (RD Server Control)" - build_path = /obj/machinery/computer/rdservercontrol -/obj/item/circuitboard/crew - name = "Circuit board (Crew Monitoring Computer)" - build_path = /obj/machinery/computer/crew - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/mech_bay_power_console - name = "Circuit board (Mech Bay Power Control Console)" - build_path = /obj/machinery/computer/mech_bay_power_console - origin_tech = "programming=3;powerstorage=3" -/obj/item/circuitboard/ordercomp - name = "Circuit board (Supply Ordering Console)" - build_path = /obj/machinery/computer/ordercomp - origin_tech = "programming=3" -/obj/item/circuitboard/supplycomp - name = "Circuit board (Supply Shuttle Console)" - build_path = /obj/machinery/computer/supplycomp - origin_tech = "programming=3" - var/contraband_enabled = 0 - -/obj/item/circuitboard/operating - name = "Circuit board (Operating Computer)" - build_path = /obj/machinery/computer/operating - origin_tech = "programming=2;biotech=3" -/obj/item/circuitboard/comm_monitor - name = "Circuit board (Telecommunications Monitor)" - build_path = /obj/machinery/computer/telecomms/monitor - origin_tech = "programming=3;magnets=3;bluespace=2" -/obj/item/circuitboard/comm_server - name = "Circuit board (Telecommunications Server Monitor)" - build_path = /obj/machinery/computer/telecomms/server - origin_tech = "programming=3;magnets=3;bluespace=2" -/obj/item/circuitboard/comm_traffic - name = "Circuitboard (Telecommunications Traffic Control)" - build_path = /obj/machinery/computer/telecomms/traffic - origin_tech = "programming=3;magnets=3;bluespace=2" - - -/obj/item/circuitboard/shuttle - name = "circuit board (Shuttle)" - build_path = /obj/machinery/computer/shuttle - var/shuttleId - var/possible_destinations = "" - -/obj/item/circuitboard/labor_shuttle - name = "circuit Board (Labor Shuttle)" - build_path = /obj/machinery/computer/shuttle/labor -/obj/item/circuitboard/labor_shuttle/one_way - name = "circuit Board (Prisoner Shuttle Console)" - build_path = /obj/machinery/computer/shuttle/labor/one_way -/obj/item/circuitboard/ferry - name = "circuit Board (Transport Ferry)" - build_path = /obj/machinery/computer/shuttle/ferry -/obj/item/circuitboard/ferry/request - name = "circuit Board (Transport Ferry Console)" - build_path = /obj/machinery/computer/shuttle/ferry/request -/obj/item/circuitboard/mining_shuttle - name = "circuit Board (Mining Shuttle)" - build_path = /obj/machinery/computer/shuttle/mining -/obj/item/circuitboard/white_ship - name = "circuit Board (White Ship)" - build_path = /obj/machinery/computer/shuttle/white_ship -/obj/item/circuitboard/shuttle/syndicate - name = "circuit board (Syndicate Shuttle)" - build_path = /obj/machinery/computer/shuttle/syndicate -/obj/item/circuitboard/shuttle/syndicate/recall - name = "circuit board (Syndicate Shuttle Recall Terminal)" - build_path = /obj/machinery/computer/shuttle/syndicate/recall -/obj/item/circuitboard/shuttle/syndicate/drop_pod - name = "circuit board (Syndicate Drop Pod)" - build_path = /obj/machinery/computer/shuttle/syndicate/drop_pod -/obj/item/circuitboard/shuttle/golem_ship - name = "circuit Board (Golem Ship)" - build_path = /obj/machinery/computer/shuttle/golem_ship - -/obj/item/circuitboard/HolodeckControl - name = "Circuit board (Holodeck Control)" - build_path = /obj/machinery/computer/HolodeckControl - origin_tech = "programming=4" -/obj/item/circuitboard/aifixer - name = "Circuit board (AI Integrity Restorer)" - build_path = /obj/machinery/computer/aifixer - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/area_atmos - name = "Circuit board (Area Air Control)" - build_path = /obj/machinery/computer/area_atmos - origin_tech = "programming=2" -/obj/item/circuitboard/telesci_console - name = "Circuit board (Telepad Control Console)" - build_path = /obj/machinery/computer/telescience - origin_tech = "programming=3;bluespace=3;plasmatech=4" - -/obj/item/circuitboard/atmos_automation - name = "Circuit board (Atmospherics Automation)" - build_path = /obj/machinery/computer/general_air_control/atmos_automation -/obj/item/circuitboard/large_tank_control - name = "Circuit board (Atmospheric Tank Control)" - build_path = /obj/machinery/computer/general_air_control/large_tank_control - origin_tech = "programming=2;engineering=3;materials=2" - -/obj/item/circuitboard/turbine_computer - name = "circuit board (Turbine Computer)" - build_path = /obj/machinery/computer/turbine_computer - origin_tech = "programming=4;engineering=4;powerstorage=4" - -/obj/item/circuitboard/HONKputer - name = "Circuit board (HONKputer)" - build_path = /obj/machinery/computer/HONKputer - origin_tech = "programming=2" - icon = 'icons/obj/machines/HONKputer.dmi' - icon_state = "bananium_board" - board_type = "honkcomputer" - - -/obj/item/circuitboard/supplycomp/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I,/obj/item/multitool)) - var/catastasis = contraband_enabled - var/opposite_catastasis - if(catastasis) - opposite_catastasis = "STANDARD" - catastasis = "BROAD" - else - opposite_catastasis = "BROAD" - catastasis = "STANDARD" - - switch( alert("Current receiver spectrum is set to: [catastasis]","Multitool-Circuitboard interface","Switch to [opposite_catastasis]","Cancel") ) - //switch( alert("Current receiver spectrum is set to: " {(contraband_enabled) ? ("BROAD") : ("STANDARD")} , "Multitool-Circuitboard interface" , "Switch to " {(contraband_enabled) ? ("STANDARD") : ("BROAD")}, "Cancel") ) - if("Switch to STANDARD","Switch to BROAD") - contraband_enabled = !contraband_enabled - - if("Cancel") - return - else - to_chat(user, "DERP! BUG! Report this (And what you were doing to cause it) to Agouri") - return - return ..() - -/obj/item/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I,/obj/item/card/id)||istype(I, /obj/item/pda)) - if(allowed(user)) - user.visible_message("\the [user] waves [user.p_their()] ID past the [src]'s access protocol scanner.", "You swipe your ID past the [src]'s access protocol scanner.") - var/console_choice = input(user, "What do you want to configure the access to?", "Access Modification", "R&D Core") as null|anything in access_types - if(console_choice == null) - return - switch(console_choice) - if("R&D Core") - name = "Circuit Board (RD Console)" - build_path = /obj/machinery/computer/rdconsole/core - id = 1 - if("Robotics") - name = "Circuit Board (RD Console - Robotics)" - build_path = /obj/machinery/computer/rdconsole/robotics - id = 2 - if("E.X.P.E.R.I-MENTOR") - name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" - build_path = /obj/machinery/computer/rdconsole/experiment - id = 3 - if("Mechanics") - name = "Circuit Board (RD Console - Mechanics)" - build_path = /obj/machinery/computer/rdconsole/mechanics - id = 4 - if("Public") - name = "Circuit Board (RD Console - Public)" - build_path = /obj/machinery/computer/rdconsole/public - id = 5 - - to_chat(user, "Access protocols set to [console_choice].") - else - to_chat(user, "Access Denied") - return - return ..() - -/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob, params) - switch(state) - if(0) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed, target = src)) - to_chat(user, "You wrench the frame into place.") - anchored = 1 - state = 1 - return - if(1) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed, target = src)) - to_chat(user, "You unfasten the frame.") - anchored = 0 - state = 0 - return - if(istype(P, /obj/item/circuitboard) && !circuit) - var/obj/item/circuitboard/B = P - if(B.board_type == "computer") - playsound(loc, B.usesound, 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - icon_state = "1" - circuit = P - user.drop_item() - P.loc = src - else - to_chat(user, "This frame does not accept circuit boards of this type!") - return - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - state = 2 - icon_state = "2" - return - if(istype(P, /obj/item/crowbar) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - state = 1 - icon_state = "0" - circuit.loc = loc - circuit = null - return - if(2) - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - state = 1 - icon_state = "1" - return - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) - playsound(loc, C.usesound, 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 2 && C.amount >= 5 && C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 3 - icon_state = "3" - else - to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") - return - else - to_chat(user, "You need five lengths of cable to wire the frame.") - return - if(3) - if(istype(P, /obj/item/wirecutters)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 2 - icon_state = "2" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 - return - if(istype(P, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = P - if(G.amount >= 2) - playsound(loc, G.usesound, 50, 1) - to_chat(user, "You start to add the glass panel to the frame.") - if(do_after(user, 20 * G.toolspeed, target = src)) - if(state == 3 && G.amount >= 2 && G.use(2)) - to_chat(user, "You put in the glass panel.") - state = 4 - icon_state = "4" - else - to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") - return - else - to_chat(user, "You need two sheets of glass for this.") - return - if(4) - if(istype(P, /obj/item/crowbar)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - state = 3 - icon_state = "3" - new /obj/item/stack/sheet/glass(loc, 2) - return - if(istype(P, /obj/item/screwdriver)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - var/B = new circuit.build_path (loc) - if(circuit.powernet) B:powernet = circuit.powernet - if(circuit.id) B:id = circuit.id - if(circuit.records) B:records = circuit.records - if(circuit.frequency) B:frequency = circuit.frequency - if(istype(circuit,/obj/item/circuitboard/supplycomp)) - var/obj/machinery/computer/supplycomp/SC = B - var/obj/item/circuitboard/supplycomp/C = circuit - SC.can_order_contraband = C.contraband_enabled - qdel(src) - return - if(user.a_intent == INTENT_HARM) - return ..() - - -/obj/structure/computerframe/welder_act(mob/user, obj/item/I) - if(state) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_SLICING_MESSAGE - if(I.use_tool(src, user, 50, volume = I.tool_volume) && !state) - to_chat(user, "You deconstruct [src].") - deconstruct(TRUE) - - - -/obj/structure/computerframe/HONKputer - name = "Bananium Computer-frame" - icon = 'icons/obj/machines/HONKputer.dmi' - base_mineral = /obj/item/stack/sheet/mineral/bananium - -/obj/structure/computerframe/HONKputer/attackby(obj/item/P as obj, mob/user as mob, params) - switch(state) - if(0) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20, target = src)) - to_chat(user, "You wrench the frame into place.") - anchored = 1 - state = 1 - if(1) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed, target = src)) - to_chat(user, "You unfasten the frame.") - anchored = 0 - state = 0 - if(istype(P, /obj/item/circuitboard) && !circuit) - var/obj/item/circuitboard/B = P - if(B.board_type == "honkcomputer") - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - icon_state = "1" - circuit = P - user.drop_item() - P.loc = src - else - to_chat(user, "This frame does not accept circuit boards of this type!") - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - state = 2 - icon_state = "2" - if(istype(P, /obj/item/crowbar) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - state = 1 - icon_state = "0" - circuit.loc = loc - circuit = null - return - if(2) - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - state = 1 - icon_state = "1" - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) - playsound(loc, C.usesound, 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 2 && C.amount >= 5 && C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 3 - icon_state = "3" - else - to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") - return - else - to_chat(user, "You need five lengths of cable to wire the frame.") - return - if(3) - if(istype(P, /obj/item/wirecutters)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 2 - icon_state = "2" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 - - if(istype(P, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = P - if(G.amount >= 2) - playsound(loc, G.usesound, 50, 1) - to_chat(user, "You start to add the glass panel to the frame.") - if(do_after(user, 20 * G.toolspeed, target = src)) - if(state == 3 && G.amount >= 2 && G.use(2)) - to_chat(user, "You put in the glass panel.") - state = 4 - icon_state = "4" - else - to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") - return - else - to_chat(user, "You need two sheets of glass for this.") - return - if(4) - if(istype(P, /obj/item/crowbar)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - state = 3 - icon_state = "3" - new /obj/item/stack/sheet/glass(loc, 2) - if(istype(P, /obj/item/screwdriver)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - var/B = new circuit.build_path (loc) - if(circuit.powernet) B:powernet = circuit.powernet - if(circuit.id) B:id = circuit.id - if(circuit.records) B:records = circuit.records - if(circuit.frequency) B:frequency = circuit.frequency - qdel(src) - return - return ..() \ No newline at end of file +/obj/structure/computerframe + density = 1 + anchored = 0 + name = "computer frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "0" + max_integrity = 100 + var/state = 0 + var/obj/item/circuitboard/circuit = null + var/base_mineral = /obj/item/stack/sheet/metal + +/obj/structure/computerframe/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + drop_computer_parts() + return ..() // will qdel the frame + +/obj/structure/computerframe/obj_break(damage_flag) + deconstruct() + +/obj/structure/computerframe/proc/drop_computer_parts() + new base_mineral(loc, 5) + if(circuit) + circuit.forceMove(loc) + circuit = null + if(state >= 3) + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + if(state >= 4) + new /obj/item/stack/sheet/glass(loc, 2) + +/obj/item/circuitboard + density = 0 + anchored = 0 + w_class = WEIGHT_CLASS_SMALL + name = "circuit board" + icon = 'icons/obj/module.dmi' + icon_state = "id_mod" + item_state = "electronic" + origin_tech = "programming=2" + materials = list(MAT_GLASS=200) + var/id = null + var/frequency = null + var/build_path = null + var/board_type = "computer" + var/list/req_components = null + var/powernet = null + var/list/records = null + var/frame_desc = null + var/contain_parts = 1 + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + +/obj/item/circuitboard/computer + board_type = "computer" + +/obj/item/circuitboard/machine + board_type = "machine" + +/obj/item/circuitboard/examine(mob/user) + . = ..() + if(LAZYLEN(req_components)) + var/list/nice_list = list() + for(var/B in req_components) + var/atom/A = B + if(!ispath(A)) + continue + nice_list += list("[req_components[A]] [initial(A.name)]") + . += "Required components: [english_list(nice_list)]." + +/obj/item/circuitboard/message_monitor + name = "Circuit board (Message Monitor)" + build_path = /obj/machinery/computer/message_monitor + origin_tech = "programming=2" +/obj/item/circuitboard/camera + name = "Circuit board (Camera Monitor)" + build_path = /obj/machinery/computer/security + origin_tech = "programming=2;combat=2" + +/obj/item/circuitboard/camera/telescreen + name = "Circuit board (Telescreen)" + build_path = /obj/machinery/computer/security/telescreen +/obj/item/circuitboard/camera/telescreen/entertainment + name = "Circuit board (Entertainment Monitor)" + build_path = /obj/machinery/computer/security/telescreen/entertainment +/obj/item/circuitboard/camera/wooden_tv + name = "Circuit board (Wooden TV)" + build_path = /obj/machinery/computer/security/wooden_tv +/obj/item/circuitboard/camera/mining + name = "Circuit board (Outpost Camera Monitor)" + build_path = /obj/machinery/computer/security/mining +/obj/item/circuitboard/camera/engineering + name = "Circuit board (Engineering Camera Monitor)" + build_path = /obj/machinery/computer/security/engineering + + +/obj/item/circuitboard/xenobiology + name = "Circuit board (Xenobiology Console)" + build_path = /obj/machinery/computer/camera_advanced/xenobio + origin_tech = "programming=3;biotech=3" +/obj/item/circuitboard/aicore + name = "Circuit board (AI Core)" + origin_tech = "programming=3" + board_type = "other" +/obj/item/circuitboard/aiupload + name = "Circuit board (AI Upload)" + build_path = /obj/machinery/computer/aiupload + origin_tech = "programming=4;engineering=4" +/obj/item/circuitboard/borgupload + name = "Circuit board (Cyborg Upload)" + build_path = /obj/machinery/computer/borgupload + origin_tech = "programming=4;engineering=4" +/obj/item/circuitboard/med_data + name = "Circuit board (Medical Records)" + build_path = /obj/machinery/computer/med_data + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/pandemic + name = "circuit board (PanD.E.M.I.C. 2200)" + build_path = /obj/machinery/computer/pandemic + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/scan_consolenew + name = "Circuit board (DNA Machine)" + build_path = /obj/machinery/computer/scan_consolenew + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/communications + name = "Circuit board (Communications Console)" + build_path = /obj/machinery/computer/communications + origin_tech = "programming=3;magnets=3" +/obj/item/circuitboard/card + name = "Circuit board (ID Computer)" + build_path = /obj/machinery/computer/card + origin_tech = "programming=3" +/obj/item/circuitboard/card/minor + name = "Circuit board (Dept ID Computer)" + build_path = /obj/machinery/computer/card/minor + var/target_dept = TARGET_DEPT_GENERIC +/obj/item/circuitboard/card/minor/hos + name = "Circuit board (Sec ID Computer)" + build_path = /obj/machinery/computer/card/minor/hos + target_dept = TARGET_DEPT_SEC +/obj/item/circuitboard/card/minor/cmo + name = "Circuit board (Medical ID Computer)" + build_path = /obj/machinery/computer/card/minor/cmo + target_dept = TARGET_DEPT_MED +/obj/item/circuitboard/card/minor/rd + name = "Circuit board (Science ID Computer)" + build_path = /obj/machinery/computer/card/minor/rd + target_dept = TARGET_DEPT_SCI +/obj/item/circuitboard/card/minor/ce + name = "Circuit board (Engineering ID Computer)" + build_path = /obj/machinery/computer/card/minor/ce + target_dept = TARGET_DEPT_ENG +/obj/item/circuitboard/card/centcom + name = "Circuit board (CentComm ID Computer)" + build_path = /obj/machinery/computer/card/centcom +/obj/item/circuitboard/teleporter + name = "Circuit board (Teleporter Console)" + build_path = /obj/machinery/computer/teleporter + origin_tech = "programming=3;bluespace=3;plasmatech=3" +/obj/item/circuitboard/secure_data + name = "Circuit board (Security Records)" + build_path = /obj/machinery/computer/secure_data + origin_tech = "programming=2;combat=2" +/obj/item/circuitboard/skills + name = "Circuit board (Employment Records)" + build_path = /obj/machinery/computer/skills +/obj/item/circuitboard/stationalert_engineering + name = "Circuit Board (Station Alert Console (Engineering))" + build_path = /obj/machinery/computer/station_alert +/obj/item/circuitboard/stationalert_security + name = "Circuit Board (Station Alert Console (Security))" + build_path = /obj/machinery/computer/station_alert +/obj/item/circuitboard/stationalert_all + name = "Circuit Board (Station Alert Console (All))" + build_path = /obj/machinery/computer/station_alert/all +/obj/item/circuitboard/atmos_alert + name = "Circuit Board (Atmospheric Alert Computer)" + build_path = /obj/machinery/computer/atmos_alert +/obj/item/circuitboard/atmoscontrol + name = "Circuit Board (Central Atmospherics Computer)" + build_path = /obj/machinery/computer/atmoscontrol +/obj/item/circuitboard/air_management + name = "Circuit board (Atmospheric Monitor)" + build_path = /obj/machinery/computer/general_air_control +/obj/item/circuitboard/injector_control + name = "Circuit board (Injector Control)" + build_path = /obj/machinery/computer/general_air_control/fuel_injection +/obj/item/circuitboard/pod + name = "Circuit board (Massdriver Control)" + build_path = /obj/machinery/computer/pod +/obj/item/circuitboard/pod/deathsquad + name = "Circuit board (Deathsquad Massdriver control)" + build_path = /obj/machinery/computer/pod/deathsquad +/obj/item/circuitboard/robotics + name = "Circuit board (Robotics Control Console)" + build_path = /obj/machinery/computer/robotics + origin_tech = "programming=3" +/obj/item/circuitboard/drone_control + name = "Circuit board (Drone Control)" + build_path = /obj/machinery/computer/drone_control + origin_tech = "programming=3" +/obj/item/circuitboard/cloning + name = "Circuit board (Cloning Machine Console)" + build_path = /obj/machinery/computer/cloning + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/arcade/battle + name = "circuit board (Arcade Battle)" + build_path = /obj/machinery/computer/arcade/battle + origin_tech = "programming=1" +/obj/item/circuitboard/arcade/orion_trail + name = "circuit board (Orion Trail)" + build_path = /obj/machinery/computer/arcade/orion_trail + origin_tech = "programming=1" +/obj/item/circuitboard/solar_control + name = "Circuit board (Solar Control)" + build_path = /obj/machinery/power/solar_control + origin_tech = "programming=2;powerstorage=2" +/obj/item/circuitboard/powermonitor + name = "Circuit board (Power Monitor)" + build_path = /obj/machinery/computer/monitor + origin_tech = "programming=2;powerstorage=2" +/obj/item/circuitboard/powermonitor/secret + name = "Circuit board (Outdated Power Monitor)" + build_path = /obj/machinery/computer/monitor/secret + origin_tech = "programming=2;powerstorage=2" +/obj/item/circuitboard/olddoor + name = "Circuit board (DoorMex)" + build_path = /obj/machinery/computer/pod/old +/obj/item/circuitboard/syndicatedoor + name = "Circuit board (ProComp Executive)" + build_path = /obj/machinery/computer/pod/old/syndicate +/obj/item/circuitboard/swfdoor + name = "Circuit board (Magix)" + build_path = /obj/machinery/computer/pod/old/swf +/obj/item/circuitboard/prisoner + name = "Circuit board (Prisoner Management)" + build_path = /obj/machinery/computer/prisoner +/obj/item/circuitboard/brigcells + name = "Circuit board (Brig Cell Control)" + build_path = /obj/machinery/computer/brigcells + + +// RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever +/obj/item/circuitboard/rdconsole + name = "Circuit Board (RD Console)" + desc = "Swipe a Scientist level ID or higher to reconfigure." + build_path = /obj/machinery/computer/rdconsole/core + req_access = list(ACCESS_TOX) // This is for adjusting the type of computer we're building - in case something messes up the pre-existing robotics or mechanics consoles + var/access_types = list("R&D Core", "Robotics", "E.X.P.E.R.I-MENTOR", "Mechanics", "Public") + id = 1 +/obj/item/circuitboard/rdconsole/robotics + name = "Circuit Board (RD Console - Robotics)" + build_path = /obj/machinery/computer/rdconsole/robotics + id = 2 +/obj/item/circuitboard/rdconsole/experiment + name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" + build_path = /obj/machinery/computer/rdconsole/experiment + id = 3 +/obj/item/circuitboard/rdconsole/mechanics + name = "Circuit Board (RD Console - Mechanics)" + build_path = /obj/machinery/computer/rdconsole/mechanics + id = 4 +/obj/item/circuitboard/rdconsole/public + name = "Circuit Board (RD Console - Public)" + build_path = /obj/machinery/computer/rdconsole/public + id = 5 + + +/obj/item/circuitboard/mecha_control + name = "Circuit Board (Exosuit Control Console)" + build_path = /obj/machinery/computer/mecha +/obj/item/circuitboard/pod_locater + name = "Circuit Board (Pod Location Console)" + build_path = /obj/machinery/computer/podtracker +/obj/item/circuitboard/rdservercontrol + name = "Circuit Board (RD Server Control)" + build_path = /obj/machinery/computer/rdservercontrol +/obj/item/circuitboard/crew + name = "Circuit board (Crew Monitoring Computer)" + build_path = /obj/machinery/computer/crew + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/mech_bay_power_console + name = "Circuit board (Mech Bay Power Control Console)" + build_path = /obj/machinery/computer/mech_bay_power_console + origin_tech = "programming=3;powerstorage=3" +/obj/item/circuitboard/ordercomp + name = "Circuit board (Supply Ordering Console)" + build_path = /obj/machinery/computer/ordercomp + origin_tech = "programming=3" +/obj/item/circuitboard/supplycomp + name = "Circuit board (Supply Shuttle Console)" + build_path = /obj/machinery/computer/supplycomp + origin_tech = "programming=3" + var/contraband_enabled = 0 + +/obj/item/circuitboard/operating + name = "Circuit board (Operating Computer)" + build_path = /obj/machinery/computer/operating + origin_tech = "programming=2;biotech=3" +/obj/item/circuitboard/comm_monitor + name = "Circuit board (Telecommunications Monitor)" + build_path = /obj/machinery/computer/telecomms/monitor + origin_tech = "programming=3;magnets=3;bluespace=2" +/obj/item/circuitboard/comm_server + name = "Circuit board (Telecommunications Server Monitor)" + build_path = /obj/machinery/computer/telecomms/server + origin_tech = "programming=3;magnets=3;bluespace=2" +/obj/item/circuitboard/comm_traffic + name = "Circuitboard (Telecommunications Traffic Control)" + build_path = /obj/machinery/computer/telecomms/traffic + origin_tech = "programming=3;magnets=3;bluespace=2" + + +/obj/item/circuitboard/shuttle + name = "circuit board (Shuttle)" + build_path = /obj/machinery/computer/shuttle + var/shuttleId + var/possible_destinations = "" + +/obj/item/circuitboard/labor_shuttle + name = "circuit Board (Labor Shuttle)" + build_path = /obj/machinery/computer/shuttle/labor +/obj/item/circuitboard/labor_shuttle/one_way + name = "circuit Board (Prisoner Shuttle Console)" + build_path = /obj/machinery/computer/shuttle/labor/one_way +/obj/item/circuitboard/ferry + name = "circuit Board (Transport Ferry)" + build_path = /obj/machinery/computer/shuttle/ferry +/obj/item/circuitboard/ferry/request + name = "circuit Board (Transport Ferry Console)" + build_path = /obj/machinery/computer/shuttle/ferry/request +/obj/item/circuitboard/mining_shuttle + name = "circuit Board (Mining Shuttle)" + build_path = /obj/machinery/computer/shuttle/mining +/obj/item/circuitboard/white_ship + name = "circuit Board (White Ship)" + build_path = /obj/machinery/computer/shuttle/white_ship +/obj/item/circuitboard/shuttle/syndicate + name = "circuit board (Syndicate Shuttle)" + build_path = /obj/machinery/computer/shuttle/syndicate +/obj/item/circuitboard/shuttle/syndicate/recall + name = "circuit board (Syndicate Shuttle Recall Terminal)" + build_path = /obj/machinery/computer/shuttle/syndicate/recall +/obj/item/circuitboard/shuttle/syndicate/drop_pod + name = "circuit board (Syndicate Drop Pod)" + build_path = /obj/machinery/computer/shuttle/syndicate/drop_pod +/obj/item/circuitboard/shuttle/golem_ship + name = "circuit Board (Golem Ship)" + build_path = /obj/machinery/computer/shuttle/golem_ship + +/obj/item/circuitboard/HolodeckControl + name = "Circuit board (Holodeck Control)" + build_path = /obj/machinery/computer/HolodeckControl + origin_tech = "programming=4" +/obj/item/circuitboard/aifixer + name = "Circuit board (AI Integrity Restorer)" + build_path = /obj/machinery/computer/aifixer + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/area_atmos + name = "Circuit board (Area Air Control)" + build_path = /obj/machinery/computer/area_atmos + origin_tech = "programming=2" +/obj/item/circuitboard/telesci_console + name = "Circuit board (Telepad Control Console)" + build_path = /obj/machinery/computer/telescience + origin_tech = "programming=3;bluespace=3;plasmatech=4" + +/obj/item/circuitboard/atmos_automation + name = "Circuit board (Atmospherics Automation)" + build_path = /obj/machinery/computer/general_air_control/atmos_automation +/obj/item/circuitboard/large_tank_control + name = "Circuit board (Atmospheric Tank Control)" + build_path = /obj/machinery/computer/general_air_control/large_tank_control + origin_tech = "programming=2;engineering=3;materials=2" + +/obj/item/circuitboard/turbine_computer + name = "circuit board (Turbine Computer)" + build_path = /obj/machinery/computer/turbine_computer + origin_tech = "programming=4;engineering=4;powerstorage=4" + +/obj/item/circuitboard/HONKputer + name = "Circuit board (HONKputer)" + build_path = /obj/machinery/computer/HONKputer + origin_tech = "programming=2" + icon = 'icons/obj/machines/HONKputer.dmi' + icon_state = "bananium_board" + board_type = "honkcomputer" + + +/obj/item/circuitboard/supplycomp/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I,/obj/item/multitool)) + var/catastasis = contraband_enabled + var/opposite_catastasis + if(catastasis) + opposite_catastasis = "STANDARD" + catastasis = "BROAD" + else + opposite_catastasis = "BROAD" + catastasis = "STANDARD" + + switch( alert("Current receiver spectrum is set to: [catastasis]","Multitool-Circuitboard interface","Switch to [opposite_catastasis]","Cancel") ) + //switch( alert("Current receiver spectrum is set to: " {(contraband_enabled) ? ("BROAD") : ("STANDARD")} , "Multitool-Circuitboard interface" , "Switch to " {(contraband_enabled) ? ("STANDARD") : ("BROAD")}, "Cancel") ) + if("Switch to STANDARD","Switch to BROAD") + contraband_enabled = !contraband_enabled + + if("Cancel") + return + else + to_chat(user, "DERP! BUG! Report this (And what you were doing to cause it) to Agouri") + return + return ..() + +/obj/item/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I,/obj/item/card/id)||istype(I, /obj/item/pda)) + if(allowed(user)) + user.visible_message("\the [user] waves [user.p_their()] ID past the [src]'s access protocol scanner.", "You swipe your ID past the [src]'s access protocol scanner.") + var/console_choice = input(user, "What do you want to configure the access to?", "Access Modification", "R&D Core") as null|anything in access_types + if(console_choice == null) + return + switch(console_choice) + if("R&D Core") + name = "Circuit Board (RD Console)" + build_path = /obj/machinery/computer/rdconsole/core + id = 1 + if("Robotics") + name = "Circuit Board (RD Console - Robotics)" + build_path = /obj/machinery/computer/rdconsole/robotics + id = 2 + if("E.X.P.E.R.I-MENTOR") + name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" + build_path = /obj/machinery/computer/rdconsole/experiment + id = 3 + if("Mechanics") + name = "Circuit Board (RD Console - Mechanics)" + build_path = /obj/machinery/computer/rdconsole/mechanics + id = 4 + if("Public") + name = "Circuit Board (RD Console - Public)" + build_path = /obj/machinery/computer/rdconsole/public + id = 5 + + to_chat(user, "Access protocols set to [console_choice].") + else + to_chat(user, "Access Denied") + return + return ..() + +/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob, params) + switch(state) + if(0) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed, target = src)) + to_chat(user, "You wrench the frame into place.") + anchored = 1 + state = 1 + return + if(1) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed, target = src)) + to_chat(user, "You unfasten the frame.") + anchored = 0 + state = 0 + return + if(istype(P, /obj/item/circuitboard) && !circuit) + var/obj/item/circuitboard/B = P + if(B.board_type == "computer") + playsound(loc, B.usesound, 50, 1) + to_chat(user, "You place the circuit board inside the frame.") + icon_state = "1" + circuit = P + user.drop_item() + P.loc = src + else + to_chat(user, "This frame does not accept circuit boards of this type!") + return + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You screw the circuit board into place.") + state = 2 + icon_state = "2" + return + if(istype(P, /obj/item/crowbar) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the circuit board.") + state = 1 + icon_state = "0" + circuit.loc = loc + circuit = null + return + if(2) + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You unfasten the circuit board.") + state = 1 + icon_state = "1" + return + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if(C.amount >= 5) + playsound(loc, C.usesound, 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20 * C.toolspeed, target = src)) + if(state == 2 && C.amount >= 5 && C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 3 + icon_state = "3" + else + to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") + return + else + to_chat(user, "You need five lengths of cable to wire the frame.") + return + if(3) + if(istype(P, /obj/item/wirecutters)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 2 + icon_state = "2" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + return + if(istype(P, /obj/item/stack/sheet/glass)) + var/obj/item/stack/sheet/glass/G = P + if(G.amount >= 2) + playsound(loc, G.usesound, 50, 1) + to_chat(user, "You start to add the glass panel to the frame.") + if(do_after(user, 20 * G.toolspeed, target = src)) + if(state == 3 && G.amount >= 2 && G.use(2)) + to_chat(user, "You put in the glass panel.") + state = 4 + icon_state = "4" + else + to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") + return + else + to_chat(user, "You need two sheets of glass for this.") + return + if(4) + if(istype(P, /obj/item/crowbar)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the glass panel.") + state = 3 + icon_state = "3" + new /obj/item/stack/sheet/glass(loc, 2) + return + if(istype(P, /obj/item/screwdriver)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You connect the monitor.") + var/B = new circuit.build_path (loc) + if(circuit.powernet) B:powernet = circuit.powernet + if(circuit.id) B:id = circuit.id + if(circuit.records) B:records = circuit.records + if(circuit.frequency) B:frequency = circuit.frequency + if(istype(circuit,/obj/item/circuitboard/supplycomp)) + var/obj/machinery/computer/supplycomp/SC = B + var/obj/item/circuitboard/supplycomp/C = circuit + SC.can_order_contraband = C.contraband_enabled + qdel(src) + return + if(user.a_intent == INTENT_HARM) + return ..() + + +/obj/structure/computerframe/welder_act(mob/user, obj/item/I) + if(state) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 50, volume = I.tool_volume) && !state) + to_chat(user, "You deconstruct [src].") + deconstruct(TRUE) + + + +/obj/structure/computerframe/HONKputer + name = "Bananium Computer-frame" + icon = 'icons/obj/machines/HONKputer.dmi' + base_mineral = /obj/item/stack/sheet/mineral/bananium + +/obj/structure/computerframe/HONKputer/attackby(obj/item/P as obj, mob/user as mob, params) + switch(state) + if(0) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20, target = src)) + to_chat(user, "You wrench the frame into place.") + anchored = 1 + state = 1 + if(1) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed, target = src)) + to_chat(user, "You unfasten the frame.") + anchored = 0 + state = 0 + if(istype(P, /obj/item/circuitboard) && !circuit) + var/obj/item/circuitboard/B = P + if(B.board_type == "honkcomputer") + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You place the circuit board inside the frame.") + icon_state = "1" + circuit = P + user.drop_item() + P.loc = src + else + to_chat(user, "This frame does not accept circuit boards of this type!") + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You screw the circuit board into place.") + state = 2 + icon_state = "2" + if(istype(P, /obj/item/crowbar) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the circuit board.") + state = 1 + icon_state = "0" + circuit.loc = loc + circuit = null + return + if(2) + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You unfasten the circuit board.") + state = 1 + icon_state = "1" + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if(C.amount >= 5) + playsound(loc, C.usesound, 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20 * C.toolspeed, target = src)) + if(state == 2 && C.amount >= 5 && C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 3 + icon_state = "3" + else + to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") + return + else + to_chat(user, "You need five lengths of cable to wire the frame.") + return + if(3) + if(istype(P, /obj/item/wirecutters)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 2 + icon_state = "2" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + + if(istype(P, /obj/item/stack/sheet/glass)) + var/obj/item/stack/sheet/glass/G = P + if(G.amount >= 2) + playsound(loc, G.usesound, 50, 1) + to_chat(user, "You start to add the glass panel to the frame.") + if(do_after(user, 20 * G.toolspeed, target = src)) + if(state == 3 && G.amount >= 2 && G.use(2)) + to_chat(user, "You put in the glass panel.") + state = 4 + icon_state = "4" + else + to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") + return + else + to_chat(user, "You need two sheets of glass for this.") + return + if(4) + if(istype(P, /obj/item/crowbar)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the glass panel.") + state = 3 + icon_state = "3" + new /obj/item/stack/sheet/glass(loc, 2) + if(istype(P, /obj/item/screwdriver)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You connect the monitor.") + var/B = new circuit.build_path (loc) + if(circuit.powernet) B:powernet = circuit.powernet + if(circuit.id) B:id = circuit.id + if(circuit.records) B:records = circuit.records + if(circuit.frequency) B:frequency = circuit.frequency + qdel(src) + return + return ..() diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 3462677d615..1649397a68f 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -1,356 +1,356 @@ -/obj/machinery/computer/security - name = "security camera console" - desc = "Used to access the various cameras networks on the station." - - icon_keyboard = "security_key" - icon_screen = "cameras" - light_color = LIGHT_COLOR_RED - circuit = /obj/item/circuitboard/camera - - var/mapping = 0 // For the overview file (overview.dm), not used on this page - - var/list/network = list() - var/list/available_networks = list() - var/list/watchers = list() //who's using the console, associated with the camera they're on. - -/obj/machinery/computer/security/New() // Lists existing networks and their required access. Format: available_networks[] = list() - generate_network_access() - ..() - -/obj/machinery/computer/security/proc/generate_network_access() - available_networks["SS13"] = list(access_hos,access_captain) - available_networks["Telecomms"] = list(access_hos,access_captain) - available_networks["Research Outpost"] = list(access_rd,access_hos,access_captain) - available_networks["Mining Outpost"] = list(access_qm,access_hop,access_hos,access_captain) - available_networks["Research"] = list(access_rd,access_hos,access_captain) - available_networks["Prison"] = list(access_hos,access_captain) - available_networks["Labor Camp"] = list(access_hos,access_captain) - available_networks["Interrogation"] = list(access_hos,access_captain) - available_networks["Atmosphere Alarms"] = list(access_ce,access_hos,access_captain) - available_networks["Fire Alarms"] = list(access_ce,access_hos,access_captain) - available_networks["Power Alarms"] = list(access_ce,access_hos,access_captain) - available_networks["Supermatter"] = list(access_ce,access_hos,access_captain) - available_networks["MiniSat"] = list(access_rd,access_hos,access_captain) - available_networks["Singularity"] = list(access_ce,access_hos,access_captain) - available_networks["Anomaly Isolation"] = list(access_rd,access_hos,access_captain) - available_networks["Toxins"] = list(access_rd,access_hos,access_captain) - available_networks["Telepad"] = list(access_rd,access_hos,access_captain) - available_networks["TestChamber"] = list(access_rd,access_hos,access_captain) - available_networks["ERT"] = list(access_cent_specops_commander,access_cent_commander) - available_networks["CentComm"] = list(access_cent_security,access_cent_commander) - available_networks["Thunderdome"] = list(access_cent_thunder,access_cent_commander) - -/obj/machinery/computer/security/Destroy() - if(watchers.len) - for(var/mob/M in watchers) - M.unset_machine() //to properly reset the view of the users if the console is deleted. - return ..() - -/obj/machinery/computer/security/proc/isCameraFarAway(obj/machinery/camera/C) - var/turf/consoleturf = get_turf(src) - var/turf/cameraturf = get_turf(C) - if((is_away_level(cameraturf.z) || is_away_level(consoleturf.z)) && !atoms_share_level(cameraturf, consoleturf)) //can only recieve away mission cameras on away missions - return TRUE - -/obj/machinery/computer/security/check_eye(mob/user) - if(!(user in watchers)) - user.unset_machine() - return - if(!watchers[user]) - user.unset_machine() - return - var/obj/machinery/camera/C = watchers[user] - if(isCameraFarAway(C)) - user.unset_machine() - return - if(!can_access_camera(C, user)) - user.unset_machine() - return - return 1 - -/obj/machinery/computer/security/on_unset_machine(mob/user) - watchers.Remove(user) - user.reset_perspective(null) - -/obj/machinery/computer/security/attack_hand(mob/user) - if(stat || ..()) - user.unset_machine() - return - - ui_interact(user) - -/obj/machinery/computer/security/telescreen/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/direction = input(user, "Which direction?", "Select direction!") as null|anything in list("North", "East", "South", "West", "Centre") - if(!direction || !Adjacent(user)) - return - pixel_x = 0 - pixel_y = 0 - switch(direction) - if("North") - pixel_y = 32 - if("East") - pixel_x = 32 - if("South") - pixel_y = -32 - if("West") - pixel_x = -32 - -/obj/machinery/computer/security/emag_act(user as mob) - if(!emagged) - emagged = 1 - to_chat(user, "You have authorized full network access!") - attack_hand(user) - else - attack_hand(user) - -/obj/machinery/computer/security/proc/get_user_access(mob/user) - var/list/access = list() - - if(emagged) - access = get_all_accesses() // Assume captain level access when emagged - else if(ishuman(user)) - access = user.get_access() - else if((isAI(user) || isrobot(user)) && CanUseTopic(user, default_state) == STATUS_INTERACTIVE) - access = get_all_accesses() // Assume captain level access when AI - else if(user.can_admin_interact()) - access = get_all_accesses() - return access - -/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) - - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "sec_camera_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "sec_camera_map_header.tmpl") - - ui.open() - -/obj/machinery/computer/security/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - var/list/cameras = list() - for(var/obj/machinery/camera/C in cameranet.cameras) - if(isCameraFarAway(C)) - continue - if(!can_access_camera(C, user)) - continue - - cameras[++cameras.len] = C.nano_structure() - - for(var/i = cameras.len, i > 0, i--) //based off /proc/camera_sort, sorts cameras alphabetically for the UI - for(var/j = 1 to i - 1) - var/a = cameras[j] - var/b = cameras[j + 1] - if(sorttext(a["name"], b["name"]) < 0) - cameras.Swap(j, j + 1) - - data["cameras"] = cameras - - var/list/access = get_user_access(user) - if(emagged) - data["emagged"] = 1 - - var/list/networks_list = list() - // Loop through the ID's permission, and check which networks the ID has access to. - for(var/net in available_networks) // Loop through networks. - for(var/req in available_networks[net]) // Loop through access levels of the networks. - if(req in access) - if(net in network) // Checks if the network is currently active. - networks_list.Add(list(list("name" = net, "active" = 1))) - else - networks_list.Add(list(list("name" = net, "active" = 0))) - break - - if(networks_list.len) - data["networks"] = networks_list - - data["current"] = null - if(watchers[user]) - var/obj/machinery/camera/watched = watchers[user] - data["current"] = watched.nano_structure() - - return data - -/obj/machinery/computer/security/Topic(href, href_list) - if(..()) - usr.unset_machine() - return 1 - - if(href_list["switchTo"]) - var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras - if(!C) - return 1 - - switch_to_camera(usr, C) - - else if(href_list["reset"]) - usr.unset_machine() - - else if(href_list["activate"]) // Activate: enable or disable networks - var/net = href_list["activate"] // Network to be enabled or disabled. - var/active = href_list["active"] // Is the network currently active. - var/list/access = get_user_access(usr) - for(var/a in available_networks[net]) - if(a in access) // Re-check for authorization. - if(text2num(active) == 1) - network -= net - break - else - network += net - break - - SSnanoui.update_uis(src) - -// Check if camera is accessible when jumping -/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C, var/mob/M) - if(CanUseTopic(M, default_state) != STATUS_INTERACTIVE || M.incapacitated() || !M.has_vision()) - return 0 - - if(isrobot(M)) - var/list/viewing = viewers(src) - if(!viewing.Find(M)) - return 0 - - if(isAI(M)) - var/mob/living/silicon/ai/A = M - if(!A.is_in_chassis()) - return 0 - - if(!issilicon(M) && !Adjacent(M)) - return 0 - - var/list/shared_networks = network & C.network - if(!shared_networks.len || !C.can_use()) - return 0 - - return 1 - -// Switching to cameras -/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) - if(!can_access_camera(C, user)) - user.unset_machine() - return 1 - - if(isAI(user)) - var/mob/living/silicon/ai/A = user - A.eyeobj.setLoc(get_turf(C)) - A.client.eye = A.eyeobj - else - user.reset_perspective(C) - watchers[user] = C - use_power(50) - -//Camera control: moving. -/obj/machinery/computer/security/proc/jump_on_click(var/mob/user, var/A) - if(user.machine != src) - return - - var/obj/machinery/camera/jump_to - - if(istype(A, /obj/machinery/camera)) - jump_to = A - - else if(ismob(A)) - if(ishuman(A)) - var/mob/living/carbon/human/H = A - jump_to = locate() in H.head - else if(isrobot(A)) - var/mob/living/silicon/robot/R = A - jump_to = R.camera - - else if(isobj(A)) - var/obj/O = A - jump_to = locate() in O - - else if(isturf(A)) - var/best_dist = INFINITY - for(var/obj/machinery/camera/camera in get_area(A)) - if(!camera.can_use()) - continue - if(!can_access_camera(camera, user)) - continue - var/dist = get_dist(camera,A) - if(dist < best_dist) - best_dist = dist - jump_to = camera - - if(isnull(jump_to)) - return - - if(can_access_camera(jump_to, user)) - switch_to_camera(user, jump_to) - -// Camera control: mouse. -/atom/DblClick() - ..() - if(istype(usr.machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = usr.machine - console.jump_on_click(usr, src) - -// Camera control: arrow keys. -/mob/Move(n, direct) - if(istype(machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = machine - var/turf/T = get_turf(console.watchers[src]) - for(var/i; i < 10; i++) - T = get_step(T, direct) - console.jump_on_click(src, T) - return - return ..(n,direct) - -// Other computer monitors. -/obj/machinery/computer/security/telescreen - name = "telescreen" - desc = "Used for watching camera networks." - icon_state = "telescreen_console" - icon_screen = "telescreen" - icon_keyboard = null - light_range_on = 0 - density = 0 - circuit = /obj/item/circuitboard/camera/telescreen - -/obj/machinery/computer/security/telescreen/entertainment - name = "entertainment monitor" - desc = "Damn, they better have Paradise TV on these things." - icon_state = "entertainment_console" - icon_screen = "entertainment" - light_color = "#FFEEDB" - light_range_on = 0 - network = list("news") - luminosity = 0 - circuit = /obj/item/circuitboard/camera/telescreen/entertainment - -/obj/machinery/computer/security/wooden_tv - name = "security camera monitor" - desc = "An old TV hooked into the station's camera network." - icon_state = "television" - icon_keyboard = null - icon_screen = "detective_tv" - light_color = "#3848B3" - light_power_on = 0.5 - network = list("SS13") - circuit = /obj/item/circuitboard/camera/wooden_tv - -/obj/machinery/computer/security/mining - name = "outpost camera monitor" - desc = "Used to access the various cameras on the outpost." - icon_keyboard = "mining_key" - icon_screen = "mining" - light_color = "#F9BBFC" - network = list("Mining Outpost") - circuit = /obj/item/circuitboard/camera/mining - -/obj/machinery/computer/security/engineering - name = "engineering camera monitor" - desc = "Used to monitor fires and breaches." - icon_keyboard = "power_key" - icon_screen = "engie_cams" - light_color = "#FAC54B" - network = list("Power Alarms","Atmosphere Alarms","Fire Alarms") - circuit = /obj/item/circuitboard/camera/engineering +/obj/machinery/computer/security + name = "security camera console" + desc = "Used to access the various cameras networks on the station." + + icon_keyboard = "security_key" + icon_screen = "cameras" + light_color = LIGHT_COLOR_RED + circuit = /obj/item/circuitboard/camera + + var/mapping = 0 // For the overview file (overview.dm), not used on this page + + var/list/network = list() + var/list/available_networks = list() + var/list/watchers = list() //who's using the console, associated with the camera they're on. + +/obj/machinery/computer/security/New() // Lists existing networks and their required access. Format: available_networks[] = list() + generate_network_access() + ..() + +/obj/machinery/computer/security/proc/generate_network_access() + available_networks["SS13"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Telecomms"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Research Outpost"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Mining Outpost"] = list(ACCESS_QM,ACCESS_HOP,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Research"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Prison"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Labor Camp"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Interrogation"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Atmosphere Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Fire Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Power Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Supermatter"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["MiniSat"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Singularity"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Anomaly Isolation"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Toxins"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Telepad"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["TestChamber"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["ERT"] = list(ACCESS_CENT_SPECOPS_COMMANDER,ACCESS_CENT_COMMANDER) + available_networks["CentComm"] = list(ACCESS_CENT_SECURITY,ACCESS_CENT_COMMANDER) + available_networks["Thunderdome"] = list(ACCESS_CENT_THUNDER,ACCESS_CENT_COMMANDER) + +/obj/machinery/computer/security/Destroy() + if(watchers.len) + for(var/mob/M in watchers) + M.unset_machine() //to properly reset the view of the users if the console is deleted. + return ..() + +/obj/machinery/computer/security/proc/isCameraFarAway(obj/machinery/camera/C) + var/turf/consoleturf = get_turf(src) + var/turf/cameraturf = get_turf(C) + if((is_away_level(cameraturf.z) || is_away_level(consoleturf.z)) && !atoms_share_level(cameraturf, consoleturf)) //can only recieve away mission cameras on away missions + return TRUE + +/obj/machinery/computer/security/check_eye(mob/user) + if(!(user in watchers)) + user.unset_machine() + return + if(!watchers[user]) + user.unset_machine() + return + var/obj/machinery/camera/C = watchers[user] + if(isCameraFarAway(C)) + user.unset_machine() + return + if(!can_access_camera(C, user)) + user.unset_machine() + return + return 1 + +/obj/machinery/computer/security/on_unset_machine(mob/user) + watchers.Remove(user) + user.reset_perspective(null) + +/obj/machinery/computer/security/attack_hand(mob/user) + if(stat || ..()) + user.unset_machine() + return + + ui_interact(user) + +/obj/machinery/computer/security/telescreen/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/direction = input(user, "Which direction?", "Select direction!") as null|anything in list("North", "East", "South", "West", "Centre") + if(!direction || !Adjacent(user)) + return + pixel_x = 0 + pixel_y = 0 + switch(direction) + if("North") + pixel_y = 32 + if("East") + pixel_x = 32 + if("South") + pixel_y = -32 + if("West") + pixel_x = -32 + +/obj/machinery/computer/security/emag_act(user as mob) + if(!emagged) + emagged = 1 + to_chat(user, "You have authorized full network access!") + attack_hand(user) + else + attack_hand(user) + +/obj/machinery/computer/security/proc/get_user_access(mob/user) + var/list/access = list() + + if(emagged) + access = get_all_accesses() // Assume captain level access when emagged + else if(ishuman(user)) + access = user.get_access() + else if((isAI(user) || isrobot(user)) && CanUseTopic(user, GLOB.default_state) == STATUS_INTERACTIVE) + access = get_all_accesses() // Assume captain level access when AI + else if(user.can_admin_interact()) + access = get_all_accesses() + return access + +/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) + + // adding a template with the key "mapContent" enables the map ui functionality + ui.add_template("mapContent", "sec_camera_map_content.tmpl") + // adding a template with the key "mapHeader" replaces the map header content + ui.add_template("mapHeader", "sec_camera_map_header.tmpl") + + ui.open() + +/obj/machinery/computer/security/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + var/list/cameras = list() + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + if(isCameraFarAway(C)) + continue + if(!can_access_camera(C, user)) + continue + + cameras[++cameras.len] = C.nano_structure() + + for(var/i = cameras.len, i > 0, i--) //based off /proc/camera_sort, sorts cameras alphabetically for the UI + for(var/j = 1 to i - 1) + var/a = cameras[j] + var/b = cameras[j + 1] + if(sorttext(a["name"], b["name"]) < 0) + cameras.Swap(j, j + 1) + + data["cameras"] = cameras + + var/list/access = get_user_access(user) + if(emagged) + data["emagged"] = 1 + + var/list/networks_list = list() + // Loop through the ID's permission, and check which networks the ID has access to. + for(var/net in available_networks) // Loop through networks. + for(var/req in available_networks[net]) // Loop through access levels of the networks. + if(req in access) + if(net in network) // Checks if the network is currently active. + networks_list.Add(list(list("name" = net, "active" = 1))) + else + networks_list.Add(list(list("name" = net, "active" = 0))) + break + + if(networks_list.len) + data["networks"] = networks_list + + data["current"] = null + if(watchers[user]) + var/obj/machinery/camera/watched = watchers[user] + data["current"] = watched.nano_structure() + + return data + +/obj/machinery/computer/security/Topic(href, href_list) + if(..()) + usr.unset_machine() + return 1 + + if(href_list["switchTo"]) + var/obj/machinery/camera/C = locate(href_list["switchTo"]) in GLOB.cameranet.cameras + if(!C) + return 1 + + switch_to_camera(usr, C) + + else if(href_list["reset"]) + usr.unset_machine() + + else if(href_list["activate"]) // Activate: enable or disable networks + var/net = href_list["activate"] // Network to be enabled or disabled. + var/active = href_list["active"] // Is the network currently active. + var/list/access = get_user_access(usr) + for(var/a in available_networks[net]) + if(a in access) // Re-check for authorization. + if(text2num(active) == 1) + network -= net + break + else + network += net + break + + SSnanoui.update_uis(src) + +// Check if camera is accessible when jumping +/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C, var/mob/M) + if(CanUseTopic(M, GLOB.default_state) != STATUS_INTERACTIVE || M.incapacitated() || !M.has_vision()) + return 0 + + if(isrobot(M)) + var/list/viewing = viewers(src) + if(!viewing.Find(M)) + return 0 + + if(isAI(M)) + var/mob/living/silicon/ai/A = M + if(!A.is_in_chassis()) + return 0 + + if(!issilicon(M) && !Adjacent(M)) + return 0 + + var/list/shared_networks = network & C.network + if(!shared_networks.len || !C.can_use()) + return 0 + + return 1 + +// Switching to cameras +/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) + if(!can_access_camera(C, user)) + user.unset_machine() + return 1 + + if(isAI(user)) + var/mob/living/silicon/ai/A = user + A.eyeobj.setLoc(get_turf(C)) + A.client.eye = A.eyeobj + else + user.reset_perspective(C) + watchers[user] = C + use_power(50) + +//Camera control: moving. +/obj/machinery/computer/security/proc/jump_on_click(var/mob/user, var/A) + if(user.machine != src) + return + + var/obj/machinery/camera/jump_to + + if(istype(A, /obj/machinery/camera)) + jump_to = A + + else if(ismob(A)) + if(ishuman(A)) + var/mob/living/carbon/human/H = A + jump_to = locate() in H.head + else if(isrobot(A)) + var/mob/living/silicon/robot/R = A + jump_to = R.camera + + else if(isobj(A)) + var/obj/O = A + jump_to = locate() in O + + else if(isturf(A)) + var/best_dist = INFINITY + for(var/obj/machinery/camera/camera in get_area(A)) + if(!camera.can_use()) + continue + if(!can_access_camera(camera, user)) + continue + var/dist = get_dist(camera,A) + if(dist < best_dist) + best_dist = dist + jump_to = camera + + if(isnull(jump_to)) + return + + if(can_access_camera(jump_to, user)) + switch_to_camera(user, jump_to) + +// Camera control: mouse. +/atom/DblClick() + ..() + if(istype(usr.machine, /obj/machinery/computer/security)) + var/obj/machinery/computer/security/console = usr.machine + console.jump_on_click(usr, src) + +// Camera control: arrow keys. +/mob/Move(n, direct) + if(istype(machine, /obj/machinery/computer/security)) + var/obj/machinery/computer/security/console = machine + var/turf/T = get_turf(console.watchers[src]) + for(var/i; i < 10; i++) + T = get_step(T, direct) + console.jump_on_click(src, T) + return + return ..() + +// Other computer monitors. +/obj/machinery/computer/security/telescreen + name = "telescreen" + desc = "Used for watching camera networks." + icon_state = "telescreen_console" + icon_screen = "telescreen" + icon_keyboard = null + light_range_on = 0 + density = 0 + circuit = /obj/item/circuitboard/camera/telescreen + +/obj/machinery/computer/security/telescreen/entertainment + name = "entertainment monitor" + desc = "Damn, they better have Paradise TV on these things." + icon_state = "entertainment_console" + icon_screen = "entertainment" + light_color = "#FFEEDB" + light_range_on = 0 + network = list("news") + luminosity = 0 + circuit = /obj/item/circuitboard/camera/telescreen/entertainment + +/obj/machinery/computer/security/wooden_tv + name = "security camera monitor" + desc = "An old TV hooked into the station's camera network." + icon_state = "television" + icon_keyboard = null + icon_screen = "detective_tv" + light_color = "#3848B3" + light_power_on = 0.5 + network = list("SS13") + circuit = /obj/item/circuitboard/camera/wooden_tv + +/obj/machinery/computer/security/mining + name = "outpost camera monitor" + desc = "Used to access the various cameras on the outpost." + icon_keyboard = "mining_key" + icon_screen = "mining" + light_color = "#F9BBFC" + network = list("Mining Outpost") + circuit = /obj/item/circuitboard/camera/mining + +/obj/machinery/computer/security/engineering + name = "engineering camera monitor" + desc = "Used to monitor fires and breaches." + icon_keyboard = "power_key" + icon_screen = "engie_cams" + light_color = "#FAC54B" + network = list("Power Alarms","Atmosphere Alarms","Fire Alarms") + circuit = /obj/item/circuitboard/camera/engineering diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index b70addea909..76242c4a0e4 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -75,7 +75,7 @@ if(!eyeobj.eye_initialized) var/camera_location - for(var/obj/machinery/camera/C in cameranet.cameras) + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) if(!C.can_use()) continue if(C.network&networks) @@ -139,7 +139,7 @@ T = get_turf(T) loc = T if(use_static) - cameranet.visibility(src, GetViewerClient()) + GLOB.cameranet.visibility(src, GetViewerClient()) if(visible_icon) if(eye_user.client) eye_user.client.images -= user_image @@ -189,7 +189,7 @@ var/list/L = list() - for(var/obj/machinery/camera/cam in cameranet.cameras) + for(var/obj/machinery/camera/cam in GLOB.cameranet.cameras) L.Add(cam) camera_sort(L) diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 86515e5ddea..40005f8fd12 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -1,13 +1,13 @@ //Keeps track of the time for the ID console. Having it as a global variable prevents people from dismantling/reassembling it to //increase the slots of many jobs. -var/time_last_changed_position = 0 +GLOBAL_VAR_INIT(time_last_changed_position, 0) /obj/machinery/computer/card name = "identification computer" desc = "Terminal for programming Nanotrasen employee ID cards to access parts of the station." icon_keyboard = "id_key" icon_screen = "id" - req_access = list(access_change_ids) + req_access = list(ACCESS_CHANGE_IDS) circuit = /obj/item/circuitboard/card light_color = LIGHT_COLOR_LIGHTBLUE var/obj/item/card/id/scan = null @@ -133,7 +133,7 @@ var/time_last_changed_position = 0 if(!istype(id_card)) return ..() - if(!scan && access_change_ids in id_card.access) + if(!scan && (ACCESS_CHANGE_IDS in id_card.access)) user.drop_item() id_card.loc = src scan = id_card @@ -160,7 +160,7 @@ var/time_last_changed_position = 0 if(job) if(!job_blacklisted_full(job) && !job_blacklisted_partial(job) && job_in_department(job, FALSE)) if((job.total_positions <= GLOB.player_list.len * (max_relative_positions / 100))) - var/delta = (world.time / 10) - time_last_changed_position + var/delta = (world.time / 10) - GLOB.time_last_changed_position if((change_position_cooldown < delta) || (opened_positions[job.title] < 0)) return 1 return -2 @@ -172,7 +172,7 @@ var/time_last_changed_position = 0 if(job) if(!job_blacklisted_full(job) && !job_blacklisted_partial(job) && job_in_department(job, FALSE)) if(job.total_positions > job.current_positions && !(job in SSjobs.prioritized_jobs)) - var/delta = (world.time / 10) - time_last_changed_position + var/delta = (world.time / 10) - GLOB.time_last_changed_position if((change_position_cooldown < delta) || (opened_positions[job.title] > 0)) return 1 return -2 @@ -200,7 +200,7 @@ var/time_last_changed_position = 0 return 1 if(!scan.assignment) return 0 - if(access_captain in scan.access) + if(ACCESS_CAPTAIN in scan.access) return 1 if(!targetjob || !targetjob.title) return 0 @@ -236,13 +236,13 @@ var/time_last_changed_position = 0 ui = new(user, src, ui_key, "identification_computer.tmpl", src.name, 775, 700) ui.open() -/obj/machinery/computer/card/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/card/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["src"] = UID() data["station_name"] = station_name() data["mode"] = mode data["printing"] = printing - data["manifest"] = data_core ? data_core.get_manifest(0) : null + data["manifest"] = GLOB.data_core ? GLOB.data_core.get_manifest(0) : null data["target_name"] = modify ? modify.name : "-----" data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" data["target_rank"] = get_target_rank() @@ -260,19 +260,19 @@ var/time_last_changed_position = 0 var/list/job_formats = SSjobs.format_jobs_for_id_computer(modify) data["top_jobs"] = format_jobs(list("Captain", "Custom"), data["target_rank"], job_formats) - data["engineering_jobs"] = format_jobs(engineering_positions, data["target_rank"], job_formats) - data["medical_jobs"] = format_jobs(medical_positions, data["target_rank"], job_formats) - data["science_jobs"] = format_jobs(science_positions, data["target_rank"], job_formats) - data["security_jobs"] = format_jobs(security_positions, data["target_rank"], job_formats) - data["support_jobs"] = format_jobs(support_positions, data["target_rank"], job_formats) - data["civilian_jobs"] = format_jobs(civilian_positions, data["target_rank"], job_formats) - data["special_jobs"] = format_jobs(whitelisted_positions, data["target_rank"], job_formats) + data["engineering_jobs"] = format_jobs(GLOB.engineering_positions, data["target_rank"], job_formats) + data["medical_jobs"] = format_jobs(GLOB.medical_positions, data["target_rank"], job_formats) + data["science_jobs"] = format_jobs(GLOB.science_positions, data["target_rank"], job_formats) + data["security_jobs"] = format_jobs(GLOB.security_positions, data["target_rank"], job_formats) + data["support_jobs"] = format_jobs(GLOB.support_positions, data["target_rank"], job_formats) + data["civilian_jobs"] = format_jobs(GLOB.civilian_positions, data["target_rank"], job_formats) + data["special_jobs"] = format_jobs(GLOB.whitelisted_positions, data["target_rank"], job_formats) data["centcom_jobs"] = format_jobs(get_all_centcom_jobs(), data["target_rank"], job_formats) data["card_skins"] = format_card_skins(get_station_card_skins()) data["job_slots"] = format_job_slots() - var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - time_last_changed_position), 1) + var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - GLOB.time_last_changed_position), 1) var/mins = round(time_to_wait / 60) var/seconds = time_to_wait - (60*mins) data["cooldown_mins"] = mins @@ -321,7 +321,7 @@ var/time_last_changed_position = 0 switch(href_list["choice"]) if("modify") if(modify) - data_core.manifest_modify(modify.registered_name, modify.assignment) + GLOB.data_core.manifest_modify(modify.registered_name, modify.assignment) modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") if(ishuman(usr)) modify.forceMove(get_turf(src)) @@ -480,7 +480,7 @@ var/time_last_changed_position = 0 P.name = "crew manifest ([station_time_timestamp()])" P.info = {"

    Crew Manifest


    - [data_core ? data_core.get_manifest(0) : ""] + [GLOB.data_core ? GLOB.data_core.get_manifest(0) : ""] "} else if(modify && !mode) P.name = "access report" @@ -544,7 +544,7 @@ var/time_last_changed_position = 0 if(can_open_job(j) != 1) return 0 if(opened_positions[edit_job_target] >= 0) - time_last_changed_position = world.time / 10 + GLOB.time_last_changed_position = world.time / 10 j.total_positions++ opened_positions[edit_job_target]++ log_game("[key_name(usr)] has opened a job slot for job \"[j]\".") @@ -564,7 +564,7 @@ var/time_last_changed_position = 0 return 0 //Allow instant closing without cooldown if a position has been opened before if(opened_positions[edit_job_target] <= 0) - time_last_changed_position = world.time / 10 + GLOB.time_last_changed_position = world.time / 10 j.total_positions-- opened_positions[edit_job_target]-- log_game("[key_name(usr)] has closed a job slot for job \"[j]\".") @@ -598,7 +598,7 @@ var/time_last_changed_position = 0 /obj/machinery/computer/card/centcom name = "\improper CentComm identification computer" circuit = /obj/item/circuitboard/card/centcom - req_access = list(access_cent_commander) + req_access = list(ACCESS_CENT_COMMANDER) change_position_cooldown = -1 blacklisted_full = list() blacklisted_partial = list() @@ -615,14 +615,14 @@ var/time_last_changed_position = 0 target_dept = TARGET_DEPT_SEC icon_screen = "idhos" light_color = LIGHT_COLOR_RED - req_access = list(access_hos) + req_access = list(ACCESS_HOS) circuit = /obj/item/circuitboard/card/minor/hos /obj/machinery/computer/card/minor/cmo name = "medical management console" target_dept = TARGET_DEPT_MED icon_screen = "idcmo" - req_access = list(access_cmo) + req_access = list(ACCESS_CMO) circuit = /obj/item/circuitboard/card/minor/cmo /obj/machinery/computer/card/minor/rd @@ -630,7 +630,7 @@ var/time_last_changed_position = 0 target_dept = TARGET_DEPT_SCI icon_screen = "idrd" light_color = LIGHT_COLOR_PINK - req_access = list(access_rd) + req_access = list(ACCESS_RD) circuit = /obj/item/circuitboard/card/minor/rd /obj/machinery/computer/card/minor/ce @@ -638,5 +638,5 @@ var/time_last_changed_position = 0 target_dept = TARGET_DEPT_ENG icon_screen = "idce" light_color = COLOR_YELLOW - req_access = list(access_ce) - circuit = /obj/item/circuitboard/card/minor/ce \ No newline at end of file + req_access = list(ACCESS_CE) + circuit = /obj/item/circuitboard/card/minor/ce diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 539d4dfc0ed..3091caa82a2 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -4,7 +4,7 @@ icon_keyboard = "med_key" icon_screen = "dna" circuit = /obj/item/circuitboard/cloning - req_access = list(access_heads) //Only used for record deletion right now. + req_access = list(ACCESS_HEADS) //Only used for record deletion right now. var/obj/machinery/dna_scannernew/scanner = null //Linked scanner. For scanning. var/list/pods = list() //Linked cloning pods. var/temp = "" @@ -129,7 +129,7 @@ ui = new(user, src, ui_key, "cloning_console.tmpl", "Cloning Console UI", 640, 520) ui.open() -/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["menu"] = src.menu data["scanner"] = sanitize("[src.scanner]") @@ -358,35 +358,37 @@ return if(scan_brain && !can_brainscan()) return - if((isnull(subject)) || (!(ishuman(subject))) || (!subject.dna) || (NO_SCAN in subject.dna.species.species_traits)) - scantemp = "Error: Unable to locate valid genetic data." - SSnanoui.update_uis(src) - return + if(isnull(subject) || (!(ishuman(subject))) || (!subject.dna)) + if(isalien(subject)) + scantemp = "Error: Xenomorphs are not scannable." + SSnanoui.update_uis(src) + return + // can add more conditions for specific non-human messages here + else + scantemp = "Error: Subject species is not scannable." + SSnanoui.update_uis(src) + return if(subject.get_int_organ(/obj/item/organ/internal/brain)) var/obj/item/organ/internal/brain/Brn = subject.get_int_organ(/obj/item/organ/internal/brain) if(istype(Brn)) if(NO_SCAN in Brn.dna.species.species_traits) - scantemp = "Error: Subject's brain is incompatible." + scantemp = "Error: [Brn.dna.species.name_plural] are not scannable." SSnanoui.update_uis(src) return if(!subject.get_int_organ(/obj/item/organ/internal/brain)) - scantemp = "Error: No signs of intelligence detected." + scantemp = "Error: No brain detected in subject." SSnanoui.update_uis(src) return if(subject.suiciding) - scantemp = "Error: Subject's brain is not responding to scanning stimuli." + scantemp = "Error: Subject has committed suicide and is not scannable." SSnanoui.update_uis(src) return if((!subject.ckey) || (!subject.client)) - scantemp = "Error: Mental interface failure." + scantemp = "Error: Subject's brain is not responding. Further attempts after a short delay may succeed." SSnanoui.update_uis(src) return if((NOCLONE in subject.mutations) && src.scanner.scan_level < 2) - scantemp = "Error: Mental interface failure." - SSnanoui.update_uis(src) - return - if(scan_brain && !subject.get_int_organ(/obj/item/organ/internal/brain)) - scantemp = "Error: No brain found." + scantemp = "Error: Subject has incompatible genetic mutations." SSnanoui.update_uis(src) return if(!isnull(find_record(subject.ckey))) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index be27b0600cf..9a1fd08d289 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,569 +1,568 @@ -#define COMM_SCREEN_MAIN 1 -#define COMM_SCREEN_STAT 2 -#define COMM_SCREEN_MESSAGES 3 -#define COMM_SCREEN_SECLEVEL 4 - -#define COMM_AUTHENTICATION_NONE 0 -#define COMM_AUTHENTICATION_MIN 1 -#define COMM_AUTHENTICATION_MAX 2 - -// The communications computer -/obj/machinery/computer/communications - name = "communications console" - desc = "This can be used for various important functions. Still under developement." - icon_keyboard = "tech_key" - icon_screen = "comm" - req_access = list(access_heads) - circuit = /obj/item/circuitboard/communications - var/prints_intercept = 1 - var/authenticated = COMM_AUTHENTICATION_NONE - var/list/messagetitle = list() - var/list/messagetext = list() - var/currmsg = 0 - var/aicurrmsg = 0 - var/menu_state = COMM_SCREEN_MAIN - var/ai_menu_state = COMM_SCREEN_MAIN - var/message_cooldown = 0 - var/centcomm_message_cooldown = 0 - var/tmp_alertlevel = 0 - - var/stat_msg1 - var/stat_msg2 - var/display_type="blank" - - var/datum/announcement/priority/crew_announcement = new - - light_color = LIGHT_COLOR_LIGHTBLUE - -/obj/machinery/computer/communications/New() - GLOB.shuttle_caller_list += src - ..() - crew_announcement.newscast = 0 - -/obj/machinery/computer/communications/proc/is_authenticated(var/mob/user, var/message = 1) - if(authenticated == COMM_AUTHENTICATION_MAX) - return COMM_AUTHENTICATION_MAX - else if(user.can_admin_interact()) - return COMM_AUTHENTICATION_MAX - else if(authenticated) - return COMM_AUTHENTICATION_MIN - else - if(message) - to_chat(user, "Access denied.") - return COMM_AUTHENTICATION_NONE - -/obj/machinery/computer/communications/proc/change_security_level(var/new_level) - tmp_alertlevel = new_level - var/old_level = security_level - if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this - set_security_level(tmp_alertlevel) - if(security_level != old_level) - //Only notify the admins if an actual change happened - log_game("[key_name(usr)] has changed the security level to [get_security_level()].") - message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") - switch(security_level) - if(SEC_LEVEL_GREEN) - feedback_inc("alert_comms_green",1) - if(SEC_LEVEL_BLUE) - feedback_inc("alert_comms_blue",1) - tmp_alertlevel = 0 - -/obj/machinery/computer/communications/Topic(href, href_list) - if(..(href, href_list)) - return 1 - - if(!is_secure_level(src.z)) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return 1 - - if(href_list["login"]) - if(!ishuman(usr)) - to_chat(usr, "Access denied.") - return - - var/list/access = usr.get_access() - if(allowed(usr)) - authenticated = COMM_AUTHENTICATION_MIN - - if(access_captain in access) - authenticated = COMM_AUTHENTICATION_MAX - var/mob/living/carbon/human/H = usr - var/obj/item/card/id = H.get_idcard(TRUE) - if(istype(id)) - crew_announcement.announcer = GetNameAndAssignmentFromId(id) - - SSnanoui.update_uis(src) - return - - if(href_list["logout"]) - authenticated = COMM_AUTHENTICATION_NONE - crew_announcement.announcer = "" - setMenuState(usr,COMM_SCREEN_MAIN) - SSnanoui.update_uis(src) - return - - if(!is_authenticated(usr)) - return 1 - - switch(href_list["operation"]) - if("main") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("changeseclevel") - setMenuState(usr,COMM_SCREEN_SECLEVEL) - - if("newalertlevel") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, "Firewalls prevent you from changing the alert level.") - return 1 - else if(usr.can_admin_interact()) - change_security_level(text2num(href_list["level"])) - return 1 - else if(!ishuman(usr)) - to_chat(usr, "Security measures prevent you from changing the alert level.") - return 1 - - var/mob/living/carbon/human/L = usr - var/obj/item/card = L.get_active_hand() - var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda - if(istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if(I && istype(I)) - if(access_captain in I.access) - change_security_level(text2num(href_list["level"])) - else - to_chat(usr, "You are not authorized to do this.") - setMenuState(usr,COMM_SCREEN_MAIN) - else - to_chat(usr, "You need to swipe your ID.") - - if("announce") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(message_cooldown) - to_chat(usr, "Please allow at least one minute to pass between announcements.") - SSnanoui.update_uis(src) - return - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") - if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - crew_announcement.Announce(input) - message_cooldown = 1 - spawn(600)//One minute cooldown - message_cooldown = 0 - - if("callshuttle") - var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") - if(!input || ..() || !is_authenticated(usr)) - SSnanoui.update_uis(src) - return - - call_shuttle_proc(usr, input) - if(SSshuttle.emergency.timer) - post_status("shuttle") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("cancelshuttle") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, "Firewalls prevent you from recalling the shuttle.") - SSnanoui.update_uis(src) - return 1 - var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") - if(response == "Yes") - cancel_call_proc(usr) - if(SSshuttle.emergency.timer) - post_status("shuttle") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("messagelist") - currmsg = 0 - if(href_list["msgid"]) - setCurrentMessage(usr, text2num(href_list["msgid"])) - setMenuState(usr,COMM_SCREEN_MESSAGES) - - if("delmessage") - if(href_list["msgid"]) - currmsg = text2num(href_list["msgid"]) - var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") - if(response == "Yes") - if(currmsg) - var/id = getCurrentMessage() - var/title = messagetitle[id] - var/text = messagetext[id] - messagetitle.Remove(title) - messagetext.Remove(text) - if(currmsg == id) - currmsg = 0 - if(aicurrmsg == id) - aicurrmsg = 0 - setMenuState(usr,COMM_SCREEN_MESSAGES) - - if("status") - setMenuState(usr,COMM_SCREEN_STAT) - - // Status display stuff - if("setstat") - display_type=href_list["statdisp"] - switch(display_type) - if("message") - post_status("message", stat_msg1, stat_msg2, usr) - if("alert") - post_status("alert", href_list["alert"], user = usr) - else - post_status(href_list["statdisp"], user = usr) - setMenuState(usr,COMM_SCREEN_STAT) - - if("setmsg1") - stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1) - setMenuState(usr,COMM_SCREEN_STAT) - - if("setmsg2") - stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2) - setMenuState(usr,COMM_SCREEN_STAT) - - if("nukerequest") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - Nuke_request(input, usr) - to_chat(usr, "Request sent.") - log_game("[key_name(usr)] has requested the nuclear codes from Centcomm") - priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("MessageCentcomm") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - Centcomm_announce(input, usr) - print_centcom_report(input, station_time_timestamp() + " Captain's Message") - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made a Centcomm announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - // OMG SYNDICATE ...LETTERHEAD - if("MessageSyndicate") - if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged)) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - Syndicate_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made a Syndicate announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("RestoreBackup") - to_chat(usr, "Backup routing data restored!") - src.emagged = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("RestartNanoMob") - if(SSmob_hunt) - if(SSmob_hunt.manual_reboot()) - var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", - "Capturing all of them", "Fixing minor text issues", "Being the very best", - "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") - to_chat(usr, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") - else - to_chat(usr, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") - else - to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") - - if("ToggleATC") - atc.squelched = !atc.squelched - to_chat(usr, "ATC traffic is now: [atc.squelched ? "Disabled" : "Enabled"].") - - SSnanoui.update_uis(src) - return 1 - -/obj/machinery/computer/communications/emag_act(user as mob) - if(!emagged) - src.emagged = 1 - to_chat(user, "You scramble the communication routing circuits!") - SSnanoui.update_uis(src) - -/obj/machinery/computer/communications/attack_ai(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/computer/communications/attack_hand(var/mob/user as mob) - if(..(user)) - return - - if(stat & (NOPOWER|BROKEN)) - return - - if(!is_secure_level(src.z)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - - ui_interact(user) - -/obj/machinery/computer/communications/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "comm_console.tmpl", "Communications Console", 400, 500) - // open the new ui window - ui.open() - -/obj/machinery/computer/communications/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["is_ai"] = isAI(user) || isrobot(user) - data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state - data["emagged"] = emagged - data["authenticated"] = is_authenticated(user, 0) - data["screen"] = getMenuState(usr) - - data["stat_display"] = list( - "type" = display_type, - "line_1" = (stat_msg1 ? stat_msg1 : "-----"), - "line_2" = (stat_msg2 ? stat_msg2 : "-----"), - - "presets" = list( - list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), - list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), - list("name" = "message", "label" = "Message", "desc" = "A custom message.") - ), - - "alerts"=list( - list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."), - list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."), - list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."), - list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."), - ) - ) - - data["security_level"] = security_level - data["str_security_level"] = capitalize(get_security_level()) - data["levels"] = list( - list("id" = SEC_LEVEL_GREEN, "name" = "Green"), - list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), - //SEC_LEVEL_RED = list("name"="Red"), - ) - - var/list/msg_data = list() - for(var/i = 1; i <= messagetext.len; i++) - msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) - - data["messages"] = msg_data - if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg)) - data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg] - data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg] - - data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null - - var/shuttle[0] - switch(SSshuttle.emergency.mode) - if(SHUTTLE_IDLE, SHUTTLE_RECALL) - shuttle["callStatus"] = 2 //#define - else - shuttle["callStatus"] = 1 - if(SSshuttle.emergency.mode == SHUTTLE_CALL) - var/timeleft = SSshuttle.emergency.timeLeft() - shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" - - data["shuttle"] = shuttle - - data["atcSquelched"] = atc.squelched - - return data - - -/obj/machinery/computer/communications/proc/setCurrentMessage(var/mob/user,var/value) - if(isAI(user) || isrobot(user)) - aicurrmsg = value - else - currmsg = value - -/obj/machinery/computer/communications/proc/getCurrentMessage(var/mob/user) - if(isAI(user) || isrobot(user)) - return aicurrmsg - else - return currmsg - -/obj/machinery/computer/communications/proc/setMenuState(var/mob/user,var/value) - if(isAI(user) || isrobot(user)) - ai_menu_state=value - else - menu_state=value - -/obj/machinery/computer/communications/proc/getMenuState(var/mob/user) - if(isAI(user) || isrobot(user)) - return ai_menu_state - else - return menu_state - -/proc/enable_prison_shuttle(var/mob/user); - -/proc/call_shuttle_proc(var/mob/user, var/reason) - if(sent_strike_team == 1) - to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") - return - - if(SSshuttle.emergencyNoEscape) - to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.") - return - - if(SSshuttle.emergency.mode > SHUTTLE_ESCAPE) - to_chat(user, "The emergency shuttle may not be called while returning to Central Command.") - return - - if(SSticker.mode.name == "blob") - to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") - return - - SSshuttle.requestEvac(user, reason) - log_game("[key_name(user)] has called the shuttle.") - message_admins("[key_name_admin(user)] has called the shuttle.", 1) - - return - -/proc/init_shift_change(var/mob/user, var/force = 0) - // if force is 0, some things may stop the shuttle call - if(!force) - if(SSshuttle.emergencyNoEscape) - to_chat(user, "Central Command does not currently have a shuttle available in your sector. Please try again later.") - return - - if(sent_strike_team == 1) - to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") - return - - if(world.time < 54000) // 30 minute grace period to let the game get going - to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/600)] minutes before trying again.") - return - - if(SSticker.mode.name == "epidemic") - to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") - return - - if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes. - SSshuttle.emergency.request(null, 0.5, null, " Automatic Crew Transfer", 1) - SSshuttle.emergency.canRecall = FALSE - else - SSshuttle.emergency.request(null, 1, null, " Automatic Crew Transfer", 0) - SSshuttle.emergency.canRecall = FALSE - if(user) - log_game("[key_name(user)] has called the shuttle.") - message_admins("[key_name_admin(user)] has called the shuttle - [formatJumpTo(user)].", 1) - return - - -/proc/cancel_call_proc(var/mob/user) - if(SSticker.mode.name == "meteor") - return - - if(SSshuttle.cancelEvac(user)) - log_game("[key_name(user)] has recalled the shuttle.") - message_admins("[key_name_admin(user)] has recalled the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) - else - to_chat(user, "Central Command has refused the recall request!") - log_game("[key_name(user)] has tried and failed to recall the shuttle.") - message_admins("[key_name_admin(user)] has tried and failed to recall the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) - -/proc/post_status(command, data1, data2, mob/user = null) - - var/datum/radio_frequency/frequency = SSradio.return_frequency(DISPLAY_FREQ) - - if(!frequency) return - - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = 1 - status_signal.data["command"] = command - - switch(command) - if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 - log_admin("STATUS: [user] set status screen message with [src]: [data1] [data2]") - //message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") - if("alert") - status_signal.data["picture_state"] = data1 - - spawn(0) - frequency.post_signal(src, status_signal) - - -/obj/machinery/computer/communications/Destroy() - GLOB.shuttle_caller_list -= src - SSshuttle.autoEvac() - return ..() - -/obj/item/circuitboard/communications/New() - GLOB.shuttle_caller_list += src - ..() - -/obj/item/circuitboard/communications/Destroy() - GLOB.shuttle_caller_list -= src - SSshuttle.autoEvac() - return ..() - -/proc/print_command_report(text = "", title = "Central Command Update") - for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) - if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z)) - var/obj/item/paper/P = new /obj/item/paper(C.loc) - P.name = "paper- '[title]'" - P.info = text - P.update_icon() - C.messagetitle.Add("[title]") - C.messagetext.Add(text) - for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) - var/turf/T = get_turf(P.computer) - if(T && P.program_state != PROGRAM_STATE_KILLED && is_station_contact(T.z)) - if(P.computer) - var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] - if(printer) - printer.print_text(text, "paper- '[title]'") - P.messagetitle.Add("[title]") - P.messagetext.Add(text) - -/proc/print_centcom_report(text = "", title = "Incoming Message") - for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) - if(!(C.stat & (BROKEN|NOPOWER)) && is_admin_level(C.z)) - var/obj/item/paper/P = new /obj/item/paper(C.loc) - P.name = "paper- '[title]'" - P.info = text - P.update_icon() - C.messagetitle.Add("[title]") - C.messagetext.Add(text) - for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) - var/turf/T = get_turf(P.computer) - if(T && P.program_state != PROGRAM_STATE_KILLED && is_admin_level(T.z)) - if(P.computer) - var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] - if(printer) - printer.print_text(text, "paper- '[title]'") - P.messagetitle.Add("[title]") - P.messagetext.Add(text) \ No newline at end of file +#define COMM_SCREEN_MAIN 1 +#define COMM_SCREEN_STAT 2 +#define COMM_SCREEN_MESSAGES 3 +#define COMM_SCREEN_SECLEVEL 4 + +#define COMM_AUTHENTICATION_NONE 0 +#define COMM_AUTHENTICATION_MIN 1 +#define COMM_AUTHENTICATION_MAX 2 + +// The communications computer +/obj/machinery/computer/communications + name = "communications console" + desc = "This can be used for various important functions. Still under developement." + icon_keyboard = "tech_key" + icon_screen = "comm" + req_access = list(ACCESS_HEADS) + circuit = /obj/item/circuitboard/communications + var/prints_intercept = 1 + var/authenticated = COMM_AUTHENTICATION_NONE + var/list/messagetitle = list() + var/list/messagetext = list() + var/currmsg = 0 + var/aicurrmsg = 0 + var/menu_state = COMM_SCREEN_MAIN + var/ai_menu_state = COMM_SCREEN_MAIN + var/message_cooldown = 0 + var/centcomm_message_cooldown = 0 + var/tmp_alertlevel = 0 + + var/stat_msg1 + var/stat_msg2 + var/display_type="blank" + + var/datum/announcement/priority/crew_announcement = new + + light_color = LIGHT_COLOR_LIGHTBLUE + +/obj/machinery/computer/communications/New() + GLOB.shuttle_caller_list += src + ..() + crew_announcement.newscast = 0 + +/obj/machinery/computer/communications/proc/is_authenticated(var/mob/user, var/message = 1) + if(authenticated == COMM_AUTHENTICATION_MAX) + return COMM_AUTHENTICATION_MAX + else if(user.can_admin_interact()) + return COMM_AUTHENTICATION_MAX + else if(authenticated) + return COMM_AUTHENTICATION_MIN + else + if(message) + to_chat(user, "Access denied.") + return COMM_AUTHENTICATION_NONE + +/obj/machinery/computer/communications/proc/change_security_level(var/new_level) + tmp_alertlevel = new_level + var/old_level = GLOB.security_level + if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this + set_security_level(tmp_alertlevel) + if(GLOB.security_level != old_level) + //Only notify the admins if an actual change happened + log_game("[key_name(usr)] has changed the security level to [get_security_level()].") + message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") + switch(GLOB.security_level) + if(SEC_LEVEL_GREEN) + feedback_inc("alert_comms_green",1) + if(SEC_LEVEL_BLUE) + feedback_inc("alert_comms_blue",1) + tmp_alertlevel = 0 + +/obj/machinery/computer/communications/Topic(href, href_list) + if(..(href, href_list)) + return 1 + + if(!is_secure_level(src.z)) + to_chat(usr, "Unable to establish a connection: You're too far away from the station!") + return 1 + + if(href_list["login"]) + if(!ishuman(usr)) + to_chat(usr, "Access denied.") + return + + var/list/access = usr.get_access() + if(allowed(usr)) + authenticated = COMM_AUTHENTICATION_MIN + + if(ACCESS_CAPTAIN in access) + authenticated = COMM_AUTHENTICATION_MAX + var/mob/living/carbon/human/H = usr + var/obj/item/card/id = H.get_idcard(TRUE) + if(istype(id)) + crew_announcement.announcer = GetNameAndAssignmentFromId(id) + + SSnanoui.update_uis(src) + return + + if(href_list["logout"]) + authenticated = COMM_AUTHENTICATION_NONE + crew_announcement.announcer = "" + setMenuState(usr,COMM_SCREEN_MAIN) + SSnanoui.update_uis(src) + return + + if(!is_authenticated(usr)) + return 1 + + switch(href_list["operation"]) + if("main") + setMenuState(usr,COMM_SCREEN_MAIN) + + if("changeseclevel") + setMenuState(usr,COMM_SCREEN_SECLEVEL) + + if("newalertlevel") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from changing the alert level.") + return 1 + else if(usr.can_admin_interact()) + change_security_level(text2num(href_list["level"])) + return 1 + else if(!ishuman(usr)) + to_chat(usr, "Security measures prevent you from changing the alert level.") + return 1 + + var/mob/living/carbon/human/L = usr + var/obj/item/card = L.get_active_hand() + var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda + if(istype(I, /obj/item/pda)) + var/obj/item/pda/pda = I + I = pda.id + if(I && istype(I)) + if(ACCESS_CAPTAIN in I.access) + change_security_level(text2num(href_list["level"])) + else + to_chat(usr, "You are not authorized to do this.") + setMenuState(usr,COMM_SCREEN_MAIN) + else + to_chat(usr, "You need to swipe your ID.") + + if("announce") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(message_cooldown) + to_chat(usr, "Please allow at least one minute to pass between announcements.") + SSnanoui.update_uis(src) + return + var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") + if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + crew_announcement.Announce(input) + message_cooldown = 1 + spawn(600)//One minute cooldown + message_cooldown = 0 + + if("callshuttle") + var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") + if(!input || ..() || !is_authenticated(usr)) + SSnanoui.update_uis(src) + return + + call_shuttle_proc(usr, input) + if(SSshuttle.emergency.timer) + post_status("shuttle") + setMenuState(usr,COMM_SCREEN_MAIN) + + if("cancelshuttle") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from recalling the shuttle.") + SSnanoui.update_uis(src) + return 1 + var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") + if(response == "Yes") + cancel_call_proc(usr) + if(SSshuttle.emergency.timer) + post_status("shuttle") + setMenuState(usr,COMM_SCREEN_MAIN) + + if("messagelist") + currmsg = 0 + if(href_list["msgid"]) + setCurrentMessage(usr, text2num(href_list["msgid"])) + setMenuState(usr,COMM_SCREEN_MESSAGES) + + if("delmessage") + if(href_list["msgid"]) + currmsg = text2num(href_list["msgid"]) + var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") + if(response == "Yes") + if(currmsg) + var/id = getCurrentMessage() + var/title = messagetitle[id] + var/text = messagetext[id] + messagetitle.Remove(title) + messagetext.Remove(text) + if(currmsg == id) + currmsg = 0 + if(aicurrmsg == id) + aicurrmsg = 0 + setMenuState(usr,COMM_SCREEN_MESSAGES) + + if("status") + setMenuState(usr,COMM_SCREEN_STAT) + + // Status display stuff + if("setstat") + display_type=href_list["statdisp"] + switch(display_type) + if("message") + post_status("message", stat_msg1, stat_msg2, usr) + if("alert") + post_status("alert", href_list["alert"], user = usr) + else + post_status(href_list["statdisp"], user = usr) + setMenuState(usr,COMM_SCREEN_STAT) + + if("setmsg1") + stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1) + setMenuState(usr,COMM_SCREEN_STAT) + + if("setmsg2") + stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2) + setMenuState(usr,COMM_SCREEN_STAT) + + if("nukerequest") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + Nuke_request(input, usr) + to_chat(usr, "Request sent.") + log_game("[key_name(usr)] has requested the nuclear codes from Centcomm") + GLOB.priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("MessageCentcomm") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + Centcomm_announce(input, usr) + print_centcom_report(input, station_time_timestamp() + " Captain's Message") + to_chat(usr, "Message transmitted.") + log_game("[key_name(usr)] has made a Centcomm announcement: [input]") + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + // OMG SYNDICATE ...LETTERHEAD + if("MessageSyndicate") + if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged)) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + Syndicate_announce(input, usr) + to_chat(usr, "Message transmitted.") + log_game("[key_name(usr)] has made a Syndicate announcement: [input]") + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("RestoreBackup") + to_chat(usr, "Backup routing data restored!") + src.emagged = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("RestartNanoMob") + if(SSmob_hunt) + if(SSmob_hunt.manual_reboot()) + var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", + "Capturing all of them", "Fixing minor text issues", "Being the very best", + "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") + to_chat(usr, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") + else + to_chat(usr, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") + else + to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") + + if("ToggleATC") + GLOB.atc.squelched = !GLOB.atc.squelched + to_chat(usr, "ATC traffic is now: [GLOB.atc.squelched ? "Disabled" : "Enabled"].") + + SSnanoui.update_uis(src) + return 1 + +/obj/machinery/computer/communications/emag_act(user as mob) + if(!emagged) + src.emagged = 1 + to_chat(user, "You scramble the communication routing circuits!") + SSnanoui.update_uis(src) + +/obj/machinery/computer/communications/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/communications/attack_hand(var/mob/user as mob) + if(..(user)) + return + + if(stat & (NOPOWER|BROKEN)) + return + + if(!is_secure_level(src.z)) + to_chat(user, "Unable to establish a connection: You're too far away from the station!") + return + + ui_interact(user) + +/obj/machinery/computer/communications/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "comm_console.tmpl", "Communications Console", 400, 500) + // open the new ui window + ui.open() + +/obj/machinery/computer/communications/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["is_ai"] = isAI(user) || isrobot(user) + data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state + data["emagged"] = emagged + data["authenticated"] = is_authenticated(user, 0) + data["screen"] = getMenuState(usr) + + data["stat_display"] = list( + "type" = display_type, + "line_1" = (stat_msg1 ? stat_msg1 : "-----"), + "line_2" = (stat_msg2 ? stat_msg2 : "-----"), + + "presets" = list( + list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), + list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), + list("name" = "message", "label" = "Message", "desc" = "A custom message.") + ), + + "alerts"=list( + list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."), + list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."), + list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."), + list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."), + ) + ) + + data["security_level"] = GLOB.security_level + data["str_security_level"] = capitalize(get_security_level()) + data["levels"] = list( + list("id" = SEC_LEVEL_GREEN, "name" = "Green"), + list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), + //SEC_LEVEL_RED = list("name"="Red"), + ) + + var/list/msg_data = list() + for(var/i = 1; i <= messagetext.len; i++) + msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) + + data["messages"] = msg_data + if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg)) + data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg] + data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg] + + data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null + + var/shuttle[0] + switch(SSshuttle.emergency.mode) + if(SHUTTLE_IDLE, SHUTTLE_RECALL) + shuttle["callStatus"] = 2 //#define + else + shuttle["callStatus"] = 1 + if(SSshuttle.emergency.mode == SHUTTLE_CALL) + var/timeleft = SSshuttle.emergency.timeLeft() + shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" + + data["shuttle"] = shuttle + + data["atcSquelched"] = GLOB.atc.squelched + + return data + + +/obj/machinery/computer/communications/proc/setCurrentMessage(var/mob/user,var/value) + if(isAI(user) || isrobot(user)) + aicurrmsg = value + else + currmsg = value + +/obj/machinery/computer/communications/proc/getCurrentMessage(var/mob/user) + if(isAI(user) || isrobot(user)) + return aicurrmsg + else + return currmsg + +/obj/machinery/computer/communications/proc/setMenuState(var/mob/user,var/value) + if(isAI(user) || isrobot(user)) + ai_menu_state=value + else + menu_state=value + +/obj/machinery/computer/communications/proc/getMenuState(var/mob/user) + if(isAI(user) || isrobot(user)) + return ai_menu_state + else + return menu_state + +/proc/enable_prison_shuttle(var/mob/user); + +/proc/call_shuttle_proc(var/mob/user, var/reason) + if(GLOB.sent_strike_team == 1) + to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") + return + + if(SSshuttle.emergencyNoEscape) + to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.") + return + + if(SSshuttle.emergency.mode > SHUTTLE_ESCAPE) + to_chat(user, "The emergency shuttle may not be called while returning to Central Command.") + return + + if(SSticker.mode.name == "blob") + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") + return + + SSshuttle.requestEvac(user, reason) + log_game("[key_name(user)] has called the shuttle.") + message_admins("[key_name_admin(user)] has called the shuttle.", 1) + + return + +/proc/init_shift_change(var/mob/user, var/force = 0) + // if force is 0, some things may stop the shuttle call + if(!force) + if(SSshuttle.emergencyNoEscape) + to_chat(user, "Central Command does not currently have a shuttle available in your sector. Please try again later.") + return + + if(GLOB.sent_strike_team == 1) + to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") + return + + if(world.time < 54000) // 30 minute grace period to let the game get going + to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/600)] minutes before trying again.") + return + + if(SSticker.mode.name == "epidemic") + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") + return + + if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes. + SSshuttle.emergency.request(null, 0.5, null, " Automatic Crew Transfer", 1) + SSshuttle.emergency.canRecall = FALSE + else + SSshuttle.emergency.request(null, 1, null, " Automatic Crew Transfer", 0) + SSshuttle.emergency.canRecall = FALSE + if(user) + log_game("[key_name(user)] has called the shuttle.") + message_admins("[key_name_admin(user)] has called the shuttle - [formatJumpTo(user)].", 1) + return + + +/proc/cancel_call_proc(var/mob/user) + if(SSticker.mode.name == "meteor") + return + + if(SSshuttle.cancelEvac(user)) + log_game("[key_name(user)] has recalled the shuttle.") + message_admins("[key_name_admin(user)] has recalled the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) + else + to_chat(user, "Central Command has refused the recall request!") + log_game("[key_name(user)] has tried and failed to recall the shuttle.") + message_admins("[key_name_admin(user)] has tried and failed to recall the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) + +/proc/post_status(command, data1, data2, mob/user = null) + + var/datum/radio_frequency/frequency = SSradio.return_frequency(DISPLAY_FREQ) + + if(!frequency) return + + var/datum/signal/status_signal = new + status_signal.transmission_method = 1 + status_signal.data["command"] = command + + switch(command) + if("message") + status_signal.data["msg1"] = data1 + status_signal.data["msg2"] = data2 + log_admin("STATUS: [user] set status screen message: [data1] [data2]") + //message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") + if("alert") + status_signal.data["picture_state"] = data1 + + spawn(0) + frequency.post_signal(null, status_signal) + + +/obj/machinery/computer/communications/Destroy() + GLOB.shuttle_caller_list -= src + SSshuttle.autoEvac() + return ..() + +/obj/item/circuitboard/communications/New() + GLOB.shuttle_caller_list += src + ..() + +/obj/item/circuitboard/communications/Destroy() + GLOB.shuttle_caller_list -= src + SSshuttle.autoEvac() + return ..() + +/proc/print_command_report(text = "", title = "Central Command Update") + for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) + if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z)) + var/obj/item/paper/P = new /obj/item/paper(C.loc) + P.name = "paper- '[title]'" + P.info = text + P.update_icon() + C.messagetitle.Add("[title]") + C.messagetext.Add(text) + for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) + var/turf/T = get_turf(P.computer) + if(T && P.program_state != PROGRAM_STATE_KILLED && is_station_contact(T.z)) + if(P.computer) + var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] + if(printer) + printer.print_text(text, "paper- '[title]'") + P.messagetitle.Add("[title]") + P.messagetext.Add(text) + +/proc/print_centcom_report(text = "", title = "Incoming Message") + for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) + if(!(C.stat & (BROKEN|NOPOWER)) && is_admin_level(C.z)) + var/obj/item/paper/P = new /obj/item/paper(C.loc) + P.name = "paper- '[title]'" + P.info = text + P.update_icon() + C.messagetitle.Add("[title]") + C.messagetext.Add(text) + for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) + var/turf/T = get_turf(P.computer) + if(T && P.program_state != PROGRAM_STATE_KILLED && is_admin_level(T.z)) + if(P.computer) + var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] + if(printer) + printer.print_text(text, "paper- '[title]'") + P.messagetitle.Add("[title]") + P.messagetext.Add(text) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 4efdcb69ee8..a832e2b5d16 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -1,36 +1,36 @@ -/obj/machinery/computer/crew - name = "crew monitoring computer" - desc = "Used to monitor active health sensors built into most of the crew's uniforms." - icon_keyboard = "med_key" - icon_screen = "crew" - use_power = IDLE_POWER_USE - idle_power_usage = 250 - active_power_usage = 500 - light_color = LIGHT_COLOR_DARKBLUE - circuit = /obj/item/circuitboard/crew - var/datum/nano_module/crew_monitor/crew_monitor - -/obj/machinery/computer/crew/New() - crew_monitor = new(src) - ..() - -/obj/machinery/computer/crew/Destroy() - QDEL_NULL(crew_monitor) - return ..() - -/obj/machinery/computer/crew/attack_ai(mob/user) - attack_hand(user) - ui_interact(user) - - -/obj/machinery/computer/crew/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - ui_interact(user) - -/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - crew_monitor.ui_interact(user, ui_key, ui, force_open) - -/obj/machinery/computer/crew/interact(mob/user) - crew_monitor.ui_interact(user) +/obj/machinery/computer/crew + name = "crew monitoring computer" + desc = "Used to monitor active health sensors built into most of the crew's uniforms." + icon_keyboard = "med_key" + icon_screen = "crew" + use_power = IDLE_POWER_USE + idle_power_usage = 250 + active_power_usage = 500 + light_color = LIGHT_COLOR_DARKBLUE + circuit = /obj/item/circuitboard/crew + var/datum/nano_module/crew_monitor/crew_monitor + +/obj/machinery/computer/crew/New() + crew_monitor = new(src) + ..() + +/obj/machinery/computer/crew/Destroy() + QDEL_NULL(crew_monitor) + return ..() + +/obj/machinery/computer/crew/attack_ai(mob/user) + attack_hand(user) + ui_interact(user) + + +/obj/machinery/computer/crew/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + ui_interact(user) + +/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + crew_monitor.ui_interact(user, ui_key, ui, force_open) + +/obj/machinery/computer/crew/interact(mob/user) + crew_monitor.ui_interact(user) diff --git a/code/game/machinery/computer/depot.dm b/code/game/machinery/computer/depot.dm index 02be740b7d3..62c82d9bd95 100644 --- a/code/game/machinery/computer/depot.dm +++ b/code/game/machinery/computer/depot.dm @@ -12,7 +12,7 @@ icon_keyboard = "syndie_key" icon_screen = "tcboss" light_color = LIGHT_COLOR_PURE_CYAN - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) var/security_lockout = FALSE var/sound_yes = 'sound/machines/twobeep.ogg' var/sound_no = 'sound/machines/buzz-sigh.ogg' @@ -186,7 +186,7 @@ /obj/machinery/computer/syndicate_depot/shieldcontrol name = "shield control computer" icon_screen = "accelerator" - req_access = list(access_syndicate_leader) + req_access = list(ACCESS_SYNDICATE_LEADER) alerts_when_broken = TRUE var/area/syndicate_depot/perimeter/perimeterarea @@ -543,4 +543,4 @@ to_chat(user, "[B] has been recalled.") qdel(B) raise_alert("Sentry bot removed via emergency recall.") - playsound(user, sound_yes, 50, 0) \ No newline at end of file + playsound(user, sound_yes, 50, 0) diff --git a/code/game/machinery/computer/honkputer.dm b/code/game/machinery/computer/honkputer.dm index 842a65b5f50..9f5d77a642c 100644 --- a/code/game/machinery/computer/honkputer.dm +++ b/code/game/machinery/computer/honkputer.dm @@ -6,7 +6,7 @@ icon_keyboard = "key_honk" icon_screen = "honkcomms" light_color = LIGHT_COLOR_PINK - req_access = list(access_clown) + req_access = list(ACCESS_CLOWN) circuit = /obj/item/circuitboard/HONKputer var/authenticated = 0 var/message_cooldown = 0 @@ -115,4 +115,4 @@ A.icon_state = "4" qdel(src) else - return ..() \ No newline at end of file + return ..() diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 3a71ab6c6c8..9464c43b107 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -1,572 +1,572 @@ -#define MED_DATA_MAIN 1 // Main menu -#define MED_DATA_R_LIST 2 // Record list -#define MED_DATA_MAINT 3 // Records maintenance -#define MED_DATA_RECORD 4 // Record -#define MED_DATA_V_DATA 5 // Virus database -#define MED_DATA_MEDBOT 6 // Medbot monitor - -/obj/machinery/computer/med_data //TODO:SANITY - name = "medical records console" - desc = "This can be used to check medical records." - icon_keyboard = "med_key" - icon_screen = "medcomp" - req_one_access = list(access_medical, access_forensics_lockers) - circuit = /obj/item/circuitboard/med_data - var/obj/item/card/id/scan = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/temp = null - var/printing = null - - light_color = LIGHT_COLOR_DARKBLUE - -/obj/machinery/computer/med_data/Destroy() - active1 = null - active2 = null - return ..() - -/obj/machinery/computer/med_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - usr.drop_item() - O.forceMove(src) - scan = O - ui_interact(user) - return - return ..() - -/obj/machinery/computer/med_data/attack_hand(mob/user) - if(..()) - return - if(is_away_level(z)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/computer/med_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "med_data.tmpl", name, 800, 380) - ui.open() - -/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["screen"] = screen - if(authenticated) - switch(screen) - if(MED_DATA_R_LIST) - if(!isnull(data_core.general)) - var/list/records = list() - data["records"] = records - for(var/datum/data/record/R in sortRecord(data_core.general)) - records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"]) - if(MED_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null) - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null) - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat") - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat") - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) - general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - - var/list/medical = list() - data["medical"] = medical - if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) - var/list/fields = list() - medical["fields"] = fields - fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0) - fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1) - fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - medical["comments"] = active2.fields["comments"] - medical["empty"] = 0 - else - medical["empty"] = 1 - if(MED_DATA_V_DATA) - data["virus"] = list() - for(var/D in typesof(/datum/disease)) - var/datum/disease/DS = new D(0) - if(istype(DS, /datum/disease/advance)) - continue - if(!DS.desc) - continue - data["virus"] += list(list("name" = DS.name, "D" = D)) - if(MED_DATA_MEDBOT) - data["medbots"] = list() - for(var/mob/living/simple_animal/bot/medbot/M in world) - if(M.z != z) - continue - var/turf/T = get_turf(M) - if(T) - var/medbot = list() - medbot["name"] = M.name - medbot["x"] = T.x - medbot["y"] = T.y - medbot["on"] = M.on - if(!isnull(M.reagent_glass) && M.use_beaker) - medbot["use_beaker"] = 1 - medbot["total_volume"] = M.reagent_glass.reagents.total_volume - medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume - else - medbot["use_beaker"] = 0 - data["medbots"] += list(medbot) - return data - -/obj/machinery/computer/med_data/Topic(href, href_list) - if(..()) - return 1 - - if(!data_core.general.Find(active1)) - active1 = null - if(!data_core.medical.Find(active2)) - active2 = null - - if(href_list["temp"]) - temp = null - - if(href_list["temp_action"]) - if(href_list["temp_action"]) - var/temp_href = splittext(href_list["temp_action"], "=") - switch(temp_href[1]) - if("del_all2") - for(var/datum/data/record/R in data_core.medical) - qdel(R) - setTemp("

    All records deleted.

    ") - if("p_stat") - if(active1) - switch(temp_href[2]) - if("deceased") - active1.fields["p_stat"] = "*Deceased*" - if("ssd") - active1.fields["p_stat"] = "*SSD*" - if("active") - active1.fields["p_stat"] = "Active" - if("unfit") - active1.fields["p_stat"] = "Physically Unfit" - if("disabled") - active1.fields["p_stat"] = "Disabled" - if("m_stat") - if(active1) - switch(temp_href[2]) - if("insane") - active1.fields["m_stat"] = "*Insane*" - if("unstable") - active1.fields["m_stat"] = "*Unstable*" - if("watch") - active1.fields["m_stat"] = "*Watch*" - if("stable") - active1.fields["m_stat"] = "Stable" - if("blood_type") - if(active2) - switch(temp_href[2]) - if("an") - active2.fields["blood_type"] = "A-" - if("bn") - active2.fields["blood_type"] = "B-" - if("abn") - active2.fields["blood_type"] = "AB-" - if("on") - active2.fields["blood_type"] = "O-" - if("ap") - active2.fields["blood_type"] = "A+" - if("bp") - active2.fields["blood_type"] = "B+" - if("abp") - active2.fields["blood_type"] = "AB+" - if("op") - active2.fields["blood_type"] = "O+" - if("del_r2") - QDEL_NULL(active2) - - if(href_list["scan"]) - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - - if(href_list["login"]) - if(isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - else if(istype(scan, /obj/item/card/id)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - - if(authenticated) - active1 = null - active2 = null - screen = MED_DATA_MAIN - - if(authenticated) - if(href_list["logout"]) - authenticated = null - screen = null - active1 = null - active2 = null - - if(href_list["screen"]) - screen = text2num(href_list["screen"]) - if(screen < 1) - screen = MED_DATA_MAIN - - active1 = null - active2 = null - - if(href_list["vir"]) - var/type = href_list["vir"] - var/datum/disease/D = new type(0) - var/afs = "" - for(var/mob/M in D.viable_mobtypes) - afs += "[initial(M.name)];" - var/severity = D.severity - switch(severity) - if("Harmful", "Minor") - severity = "[severity]" - if("Medium") - severity = "[severity]" - if("Dangerous!") - severity = "[severity]" - if("BIOHAZARD THREAT!") - severity = "

    [severity]

    " - setTemp({"Name: [D.name] -
    Number of stages: [D.max_stages] -
    Spread: [D.spread_text] Transmission -
    Possible Cure: [(D.cure_text||"none")] -
    Affected Lifeforms:[afs]
    -
    Notes: [D.desc]
    -
    Severity: [severity]"}) - qdel(D) - - if(href_list["del_all"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1") - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null) - setTemp("

    Are you sure you wish to delete all records?

    ", buttons) - - if(href_list["field"]) - if(..()) - return 1 - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["age"] = t1 - if("mi_dis") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_dis"] = t1 - if("mi_dis_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_dis_d"] = t1 - if("ma_dis") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", active2.fields["ma_dis"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_dis"] = t1 - if("ma_dis_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_dis_d"] = t1 - if("alg") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", active2.fields["alg"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["alg"] = t1 - if("alg_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["alg_d"] = t1 - if("cdi") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", active2.fields["cdi"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["cdi"] = t1 - if("cdi_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["cdi_d"] = t1 - if("notes") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["notes"] = t1 - if("p_stat") - if(istype(active1, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "*Deceased*", "icon" = "stethoscope", "href" = "p_stat=deceased", "status" = (active1.fields["p_stat"] == "*Deceased*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*SSD*", "icon" = "stethoscope", "href" = "p_stat=ssd", "status" = (active1.fields["p_stat"] == "*SSD*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Active", "icon" = "stethoscope", "href" = "p_stat=active", "status" = (active1.fields["p_stat"] == "Active" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Physically Unfit", "icon" = "stethoscope", "href" = "p_stat=unfit", "status" = (active1.fields["p_stat"] == "Physically Unfit" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Disabled", "icon" = "stethoscope", "href" = "p_stat=disabled", "status" = (active1.fields["p_stat"] == "Disabled" ? "selected" : null)) - setTemp("

    Physical Condition

    ", buttons) - if("m_stat") - if(istype(active1, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "*Insane*", "icon" = "stethoscope", "href" = "m_stat=insane", "status" = (active1.fields["m_stat"] == "*Insane*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Unstable*", "icon" = "stethoscope", "href" = "m_stat=unstable", "status" = (active1.fields["m_stat"] == "*Unstable*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Watch*", "icon" = "stethoscope", "href" = "m_stat=watch", "status" = (active1.fields["m_stat"] == "*Watch*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Stable", "icon" = "stethoscope", "href" = "m_stat=stable", "status" = (active1.fields["m_stat"] == "Stable" ? "selected" : null)) - setTemp("

    Mental Condition

    ", buttons) - if("blood_type") - if(istype(active2, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "A-", "icon" = "tint", "href" = "blood_type=an", "status" = (active2.fields["blood_type"] == "A-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "A+", "icon" = "tint", "href" = "blood_type=ap", "status" = (active2.fields["blood_type"] == "A+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "B-", "icon" = "tint", "href" = "blood_type=bn", "status" = (active2.fields["blood_type"] == "B-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "B+", "icon" = "tint", "href" = "blood_type=bp", "status" = (active2.fields["blood_type"] == "B+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "AB-", "icon" = "tint", "href" = "blood_type=abn", "status" = (active2.fields["blood_type"] == "AB-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "AB+", "icon" = "tint", "href" = "blood_type=abp", "status" = (active2.fields["blood_type"] == "AB+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "O-", "icon" = "tint", "href" = "blood_type=on", "status" = (active2.fields["blood_type"] == "O-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "O+", "icon" = "tint", "href" = "blood_type=op", "status" = (active2.fields["blood_type"] == "O+" ? "selected" : null)) - setTemp("

    Blood Type

    ", buttons) - if("b_dna") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["b_dna"] = t1 - if("vir_name") - var/datum/data/record/v = locate(href_list["edit_vir"]) - if(v) - var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - v.fields["name"] = t1 - if("vir_desc") - var/datum/data/record/v = locate(href_list["edit_vir"]) - if(v) - var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - v.fields["description"] = t1 - - if(href_list["del_r"]) - if(active2) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (Medical Portion Only)?

    ", buttons) - - if(href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - var/datum/data/record/M = locate(href_list["d_rec"]) - if(!data_core.general.Find(R)) - setTemp("

    Record not found!

    ") - return 1 - for(var/datum/data/record/E in data_core.medical) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - M = E - active1 = R - active2 = M - screen = MED_DATA_RECORD - - if(href_list["new"]) - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Medical Record #[R.fields["id"]]" - R.fields["blood_type"] = "Unknown" - R.fields["b_dna"] = "Unknown" - R.fields["mi_dis"] = "None" - R.fields["mi_dis_d"] = "No minor disabilities have been declared." - R.fields["ma_dis"] = "None" - R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - R.fields["alg"] = "None" - R.fields["alg_d"] = "No allergies have been detected in this patient." - R.fields["cdi"] = "None" - R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - R.fields["notes"] = "No notes." - data_core.medical += R - active2 = R - screen = MED_DATA_RECORD - - if(href_list["add_c"]) - if(!istype(active2, /datum/data/record)) - return 1 - var/a2 = active2 - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["comments"] += "Made by [authenticated] ([rank]) on [current_date_string] [station_time_timestamp()]
    [t1]" - - if(href_list["del_c"]) - var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) - if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) - active2.fields["comments"] -= active2.fields["comments"][index] - - if(href_list["search"]) - var/t1 = clean_input("Search String: (Name, DNA, or ID)", "Med. records", null, null) - if(!t1 || ..()) - return 1 - active1 = null - active2 = null - t1 = lowertext(t1) - for(var/datum/data/record/R in data_core.medical) - if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) - active2 = R - if(!active2) - setTemp("

    Could not locate record [t1].

    ") - else - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) - active1 = E - screen = MED_DATA_RECORD - - if(href_list["print_p"]) - if(!printing) - printing = 1 - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - sleep(50) - var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
    Medical Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]]
    "} - else - P.info += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) - P.info += {"
    \n
    Medical Data
    -
    \nBlood Type: [active2.fields["blood_type"]] -
    \nDNA: [active2.fields["b_dna"]]
    \n -
    \nMinor Disabilities: [active2.fields["mi_dis"]] -
    \nDetails: [active2.fields["mi_dis_d"]]
    \n -
    \nMajor Disabilities: [active2.fields["ma_dis"]] -
    \nDetails: [active2.fields["ma_dis_d"]]
    \n -
    \nAllergies: [active2.fields["alg"]] -
    \nDetails: [active2.fields["alg_d"]]
    \n -
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) -
    \nDetails: [active2.fields["cdi_d"]]
    \n -
    \nImportant Notes: -
    \n\t[active2.fields["notes"]]
    \n -
    \n -
    Comments/Log

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c]
    " - else - P.info += "Medical Record Lost!
    " - P.info += "
    " - P.name = "paper- 'Medical Record: [active1.fields["name"]]'" - printing = 0 - return 1 - -/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list()) - temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) - -/obj/machinery/computer/med_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return ..(severity) - - for(var/datum/data/record/R in data_core.medical) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["blood_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") - if(5) - R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - - -/obj/machinery/computer/med_data/laptop - name = "medical laptop" - desc = "Cheap Nanotrasen laptop." - icon_state = "laptop" - icon_keyboard = "laptop_key" - icon_screen = "medlaptop" - density = 0 - -#undef MED_DATA_MAIN -#undef MED_DATA_R_LIST -#undef MED_DATA_MAINT -#undef MED_DATA_RECORD -#undef MED_DATA_V_DATA -#undef MED_DATA_MEDBOT +#define MED_DATA_MAIN 1 // Main menu +#define MED_DATA_R_LIST 2 // Record list +#define MED_DATA_MAINT 3 // Records maintenance +#define MED_DATA_RECORD 4 // Record +#define MED_DATA_V_DATA 5 // Virus database +#define MED_DATA_MEDBOT 6 // Medbot monitor + +/obj/machinery/computer/med_data //TODO:SANITY + name = "medical records console" + desc = "This can be used to check medical records." + icon_keyboard = "med_key" + icon_screen = "medcomp" + req_one_access = list(ACCESS_MEDICAL, ACCESS_FORENSICS_LOCKERS) + circuit = /obj/item/circuitboard/med_data + var/obj/item/card/id/scan = null + var/authenticated = null + var/rank = null + var/screen = null + var/datum/data/record/active1 = null + var/datum/data/record/active2 = null + var/temp = null + var/printing = null + + light_color = LIGHT_COLOR_DARKBLUE + +/obj/machinery/computer/med_data/Destroy() + active1 = null + active2 = null + return ..() + +/obj/machinery/computer/med_data/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/card/id) && !scan) + usr.drop_item() + O.forceMove(src) + scan = O + ui_interact(user) + return + return ..() + +/obj/machinery/computer/med_data/attack_hand(mob/user) + if(..()) + return + if(is_away_level(z)) + to_chat(user, "Unable to establish a connection: You're too far away from the station!") + return + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/computer/med_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "med_data.tmpl", name, 800, 380) + ui.open() + +/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["temp"] = temp + data["scan"] = scan ? scan.name : null + data["authenticated"] = authenticated + data["screen"] = screen + if(authenticated) + switch(screen) + if(MED_DATA_R_LIST) + if(!isnull(GLOB.data_core.general)) + var/list/records = list() + data["records"] = records + for(var/datum/data/record/R in sortRecord(GLOB.data_core.general)) + records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"]) + if(MED_DATA_RECORD) + var/list/general = list() + data["general"] = general + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + var/list/fields = list() + general["fields"] = fields + fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null) + fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null) + fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") + fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") + fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") + fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat") + fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat") + var/list/photos = list() + general["photos"] = photos + photos[++photos.len] = list("photo" = active1.fields["photo-south"]) + photos[++photos.len] = list("photo" = active1.fields["photo-west"]) + general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) + general["empty"] = 0 + else + general["empty"] = 1 + + var/list/medical = list() + data["medical"] = medical + if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) + var/list/fields = list() + medical["fields"] = fields + fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0) + fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1) + fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) + if(!active2.fields["comments"] || !islist(active2.fields["comments"])) + active2.fields["comments"] = list() + medical["comments"] = active2.fields["comments"] + medical["empty"] = 0 + else + medical["empty"] = 1 + if(MED_DATA_V_DATA) + data["virus"] = list() + for(var/D in typesof(/datum/disease)) + var/datum/disease/DS = new D(0) + if(istype(DS, /datum/disease/advance)) + continue + if(!DS.desc) + continue + data["virus"] += list(list("name" = DS.name, "D" = D)) + if(MED_DATA_MEDBOT) + data["medbots"] = list() + for(var/mob/living/simple_animal/bot/medbot/M in world) + if(M.z != z) + continue + var/turf/T = get_turf(M) + if(T) + var/medbot = list() + medbot["name"] = M.name + medbot["x"] = T.x + medbot["y"] = T.y + medbot["on"] = M.on + if(!isnull(M.reagent_glass) && M.use_beaker) + medbot["use_beaker"] = 1 + medbot["total_volume"] = M.reagent_glass.reagents.total_volume + medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume + else + medbot["use_beaker"] = 0 + data["medbots"] += list(medbot) + return data + +/obj/machinery/computer/med_data/Topic(href, href_list) + if(..()) + return 1 + + if(!GLOB.data_core.general.Find(active1)) + active1 = null + if(!GLOB.data_core.medical.Find(active2)) + active2 = null + + if(href_list["temp"]) + temp = null + + if(href_list["temp_action"]) + if(href_list["temp_action"]) + var/temp_href = splittext(href_list["temp_action"], "=") + switch(temp_href[1]) + if("del_all2") + for(var/datum/data/record/R in GLOB.data_core.medical) + qdel(R) + setTemp("

    All records deleted.

    ") + if("p_stat") + if(active1) + switch(temp_href[2]) + if("deceased") + active1.fields["p_stat"] = "*Deceased*" + if("ssd") + active1.fields["p_stat"] = "*SSD*" + if("active") + active1.fields["p_stat"] = "Active" + if("unfit") + active1.fields["p_stat"] = "Physically Unfit" + if("disabled") + active1.fields["p_stat"] = "Disabled" + if("m_stat") + if(active1) + switch(temp_href[2]) + if("insane") + active1.fields["m_stat"] = "*Insane*" + if("unstable") + active1.fields["m_stat"] = "*Unstable*" + if("watch") + active1.fields["m_stat"] = "*Watch*" + if("stable") + active1.fields["m_stat"] = "Stable" + if("blood_type") + if(active2) + switch(temp_href[2]) + if("an") + active2.fields["blood_type"] = "A-" + if("bn") + active2.fields["blood_type"] = "B-" + if("abn") + active2.fields["blood_type"] = "AB-" + if("on") + active2.fields["blood_type"] = "O-" + if("ap") + active2.fields["blood_type"] = "A+" + if("bp") + active2.fields["blood_type"] = "B+" + if("abp") + active2.fields["blood_type"] = "AB+" + if("op") + active2.fields["blood_type"] = "O+" + if("del_r2") + QDEL_NULL(active2) + + if(href_list["scan"]) + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + + if(href_list["login"]) + if(isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + else if(istype(scan, /obj/item/card/id)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + + if(authenticated) + active1 = null + active2 = null + screen = MED_DATA_MAIN + + if(authenticated) + if(href_list["logout"]) + authenticated = null + screen = null + active1 = null + active2 = null + + if(href_list["screen"]) + screen = text2num(href_list["screen"]) + if(screen < 1) + screen = MED_DATA_MAIN + + active1 = null + active2 = null + + if(href_list["vir"]) + var/type = href_list["vir"] + var/datum/disease/D = new type(0) + var/afs = "" + for(var/mob/M in D.viable_mobtypes) + afs += "[initial(M.name)];" + var/severity = D.severity + switch(severity) + if("Harmful", "Minor") + severity = "[severity]" + if("Medium") + severity = "[severity]" + if("Dangerous!") + severity = "[severity]" + if("BIOHAZARD THREAT!") + severity = "

    [severity]

    " + setTemp({"Name: [D.name] +
    Number of stages: [D.max_stages] +
    Spread: [D.spread_text] Transmission +
    Possible Cure: [(D.cure_text||"none")] +
    Affected Lifeforms:[afs]
    +
    Notes: [D.desc]
    +
    Severity: [severity]"}) + qdel(D) + + if(href_list["del_all"]) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1") + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null) + setTemp("

    Are you sure you wish to delete all records?

    ", buttons) + + if(href_list["field"]) + if(..()) + return 1 + var/a1 = active1 + var/a2 = active2 + switch(href_list["field"]) + if("fingerprint") + if(istype(active1, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["fingerprint"] = t1 + if("sex") + if(istype(active1, /datum/data/record)) + if(active1.fields["sex"] == "Male") + active1.fields["sex"] = "Female" + else + active1.fields["sex"] = "Male" + if("age") + if(istype(active1, /datum/data/record)) + var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["age"] = t1 + if("mi_dis") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["mi_dis"] = t1 + if("mi_dis_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["mi_dis_d"] = t1 + if("ma_dis") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", active2.fields["ma_dis"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["ma_dis"] = t1 + if("ma_dis_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["ma_dis_d"] = t1 + if("alg") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", active2.fields["alg"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["alg"] = t1 + if("alg_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["alg_d"] = t1 + if("cdi") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", active2.fields["cdi"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["cdi"] = t1 + if("cdi_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["cdi_d"] = t1 + if("notes") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["notes"] = t1 + if("p_stat") + if(istype(active1, /datum/data/record)) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "*Deceased*", "icon" = "stethoscope", "href" = "p_stat=deceased", "status" = (active1.fields["p_stat"] == "*Deceased*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*SSD*", "icon" = "stethoscope", "href" = "p_stat=ssd", "status" = (active1.fields["p_stat"] == "*SSD*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Active", "icon" = "stethoscope", "href" = "p_stat=active", "status" = (active1.fields["p_stat"] == "Active" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Physically Unfit", "icon" = "stethoscope", "href" = "p_stat=unfit", "status" = (active1.fields["p_stat"] == "Physically Unfit" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Disabled", "icon" = "stethoscope", "href" = "p_stat=disabled", "status" = (active1.fields["p_stat"] == "Disabled" ? "selected" : null)) + setTemp("

    Physical Condition

    ", buttons) + if("m_stat") + if(istype(active1, /datum/data/record)) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "*Insane*", "icon" = "stethoscope", "href" = "m_stat=insane", "status" = (active1.fields["m_stat"] == "*Insane*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*Unstable*", "icon" = "stethoscope", "href" = "m_stat=unstable", "status" = (active1.fields["m_stat"] == "*Unstable*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*Watch*", "icon" = "stethoscope", "href" = "m_stat=watch", "status" = (active1.fields["m_stat"] == "*Watch*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Stable", "icon" = "stethoscope", "href" = "m_stat=stable", "status" = (active1.fields["m_stat"] == "Stable" ? "selected" : null)) + setTemp("

    Mental Condition

    ", buttons) + if("blood_type") + if(istype(active2, /datum/data/record)) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "A-", "icon" = "tint", "href" = "blood_type=an", "status" = (active2.fields["blood_type"] == "A-" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "A+", "icon" = "tint", "href" = "blood_type=ap", "status" = (active2.fields["blood_type"] == "A+" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "B-", "icon" = "tint", "href" = "blood_type=bn", "status" = (active2.fields["blood_type"] == "B-" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "B+", "icon" = "tint", "href" = "blood_type=bp", "status" = (active2.fields["blood_type"] == "B+" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "AB-", "icon" = "tint", "href" = "blood_type=abn", "status" = (active2.fields["blood_type"] == "AB-" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "AB+", "icon" = "tint", "href" = "blood_type=abp", "status" = (active2.fields["blood_type"] == "AB+" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "O-", "icon" = "tint", "href" = "blood_type=on", "status" = (active2.fields["blood_type"] == "O-" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "O+", "icon" = "tint", "href" = "blood_type=op", "status" = (active2.fields["blood_type"] == "O+" ? "selected" : null)) + setTemp("

    Blood Type

    ", buttons) + if("b_dna") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["b_dna"] = t1 + if("vir_name") + var/datum/data/record/v = locate(href_list["edit_vir"]) + if(v) + var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + v.fields["name"] = t1 + if("vir_desc") + var/datum/data/record/v = locate(href_list["edit_vir"]) + if(v) + var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + v.fields["description"] = t1 + + if(href_list["del_r"]) + if(active2) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete the record (Medical Portion Only)?

    ", buttons) + + if(href_list["d_rec"]) + var/datum/data/record/R = locate(href_list["d_rec"]) + var/datum/data/record/M = locate(href_list["d_rec"]) + if(!GLOB.data_core.general.Find(R)) + setTemp("

    Record not found!

    ") + return 1 + for(var/datum/data/record/E in GLOB.data_core.medical) + if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) + M = E + active1 = R + active2 = M + screen = MED_DATA_RECORD + + if(href_list["new"]) + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Medical Record #[R.fields["id"]]" + R.fields["blood_type"] = "Unknown" + R.fields["b_dna"] = "Unknown" + R.fields["mi_dis"] = "None" + R.fields["mi_dis_d"] = "No minor disabilities have been declared." + R.fields["ma_dis"] = "None" + R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + R.fields["alg"] = "None" + R.fields["alg_d"] = "No allergies have been detected in this patient." + R.fields["cdi"] = "None" + R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + R.fields["notes"] = "No notes." + GLOB.data_core.medical += R + active2 = R + screen = MED_DATA_RECORD + + if(href_list["add_c"]) + if(!istype(active2, /datum/data/record)) + return 1 + var/a2 = active2 + var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + + if(href_list["del_c"]) + var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) + if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) + active2.fields["comments"] -= active2.fields["comments"][index] + + if(href_list["search"]) + var/t1 = clean_input("Search String: (Name, DNA, or ID)", "Med. records", null, null) + if(!t1 || ..()) + return 1 + active1 = null + active2 = null + t1 = lowertext(t1) + for(var/datum/data/record/R in GLOB.data_core.medical) + if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) + active2 = R + if(!active2) + setTemp("

    Could not locate record [t1].

    ") + else + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) + active1 = E + screen = MED_DATA_RECORD + + if(href_list["print_p"]) + if(!printing) + printing = 1 + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + sleep(50) + var/obj/item/paper/P = new /obj/item/paper(loc) + P.info = "
    Medical Record

    " + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
    \nSex: [active1.fields["sex"]] +
    \nAge: [active1.fields["age"]] +
    \nFingerprint: [active1.fields["fingerprint"]] +
    \nPhysical Status: [active1.fields["p_stat"]] +
    \nMental Status: [active1.fields["m_stat"]]
    "} + else + P.info += "General Record Lost!
    " + if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) + P.info += {"
    \n
    Medical Data
    +
    \nBlood Type: [active2.fields["blood_type"]] +
    \nDNA: [active2.fields["b_dna"]]
    \n +
    \nMinor Disabilities: [active2.fields["mi_dis"]] +
    \nDetails: [active2.fields["mi_dis_d"]]
    \n +
    \nMajor Disabilities: [active2.fields["ma_dis"]] +
    \nDetails: [active2.fields["ma_dis_d"]]
    \n +
    \nAllergies: [active2.fields["alg"]] +
    \nDetails: [active2.fields["alg_d"]]
    \n +
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) +
    \nDetails: [active2.fields["cdi_d"]]
    \n +
    \nImportant Notes: +
    \n\t[active2.fields["notes"]]
    \n +
    \n +
    Comments/Log

    "} + for(var/c in active2.fields["comments"]) + P.info += "[c]
    " + else + P.info += "Medical Record Lost!
    " + P.info += "
    " + P.name = "paper- 'Medical Record: [active1.fields["name"]]'" + printing = 0 + return 1 + +/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list()) + temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + +/obj/machinery/computer/med_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return ..(severity) + + for(var/datum/data/record/R in GLOB.data_core.medical) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["blood_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") + if(5) + R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + + +/obj/machinery/computer/med_data/laptop + name = "medical laptop" + desc = "Cheap Nanotrasen laptop." + icon_state = "laptop" + icon_keyboard = "laptop_key" + icon_screen = "medlaptop" + density = 0 + +#undef MED_DATA_MAIN +#undef MED_DATA_R_LIST +#undef MED_DATA_MAINT +#undef MED_DATA_RECORD +#undef MED_DATA_V_DATA +#undef MED_DATA_MEDBOT diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index 55107809ce3..7fa1dedf411 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -18,7 +18,7 @@ var/noserver = "ALERT: No server detected." var/incorrectkey = "ALERT: Incorrect decryption key!" var/defaultmsg = "Welcome. Please select an option." - var/rebootmsg = "%$&(£: Critical %$$@ Error // !RestArting! - ?pLeaSe wAit!" + var/rebootmsg = "%$&(�: Critical %$$@ Error // !RestArting! - ?pLeaSe wAit!" //Computer properties var/screen = 0 // 0 = Main menu, 1 = Message Logs, 2 = Hacked screen, 3 = Custom Message var/hacking = 0 // Is it being hacked into by the AI/Cyborg @@ -54,7 +54,7 @@ MK.loc = src.loc playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) // Will help make emagging the console not so easy to get away with. - MK.info += "

    £%@%(*$%&(£&?*(%&£/{}" + MK.info += "

    �%@%(*$%&(�&?*(%&�/{}" update_icon() spawn(100*length(src.linkedServer.decryptkey)) UnmagConsole() @@ -75,8 +75,8 @@ ..() //Is the server isn't linked to a server, and there's a server available, default it to the first one in the list. if(!linkedServer) - if(message_servers && message_servers.len > 0) - linkedServer = message_servers[1] + if(GLOB.message_servers && GLOB.message_servers.len > 0) + linkedServer = GLOB.message_servers[1] return /obj/machinery/computer/message_monitor/attack_hand(var/mob/user as mob) @@ -288,11 +288,11 @@ if(auth) linkedServer.active = !linkedServer.active //Find a server if(href_list["find"]) - if(message_servers && message_servers.len > 1) - src.linkedServer = input(usr,"Please select a server.", "Select a server.", null) as null|anything in message_servers + if(GLOB.message_servers && GLOB.message_servers.len > 1) + src.linkedServer = input(usr,"Please select a server.", "Select a server.", null) as null|anything in GLOB.message_servers message = "NOTICE: Server selected." - else if(message_servers && message_servers.len > 0) - linkedServer = message_servers[1] + else if(GLOB.message_servers && GLOB.message_servers.len > 0) + linkedServer = GLOB.message_servers[1] message = "NOTICE: Only Single Server Detected - Server selected." else message = noserver @@ -396,13 +396,13 @@ if("Recepient") //Get out list of viable PDAs var/list/obj/item/pda/sendPDAs = list() - for(var/obj/item/pda/P in PDAs) + for(var/obj/item/pda/P in GLOB.PDAs) var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) if(!PM || !PM.can_receive()) continue sendPDAs += P - if(PDAs && PDAs.len > 0) + if(GLOB.PDAs && GLOB.PDAs.len > 0) customrecepient = input(usr, "Select a PDA from the list.") as null|anything in sortAtom(sendPDAs) else customrecepient = null @@ -436,7 +436,7 @@ return src.attack_hand(usr) var/obj/item/pda/PDARec = null - for(var/obj/item/pda/P in PDAs) + for(var/obj/item/pda/P in GLOB.PDAs) var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) if(!PM || !PM.can_receive()) @@ -486,8 +486,8 @@ /obj/item/paper/monitorkey/New() ..() spawn(10) - if(message_servers) - for(var/obj/machinery/message_server/server in message_servers) + if(GLOB.message_servers) + for(var/obj/machinery/message_server/server in GLOB.message_servers) if(!isnull(server)) if(!isnull(server.decryptkey)) info = "

    Daily Key Reset


    The new message monitor key is '[server.decryptkey]'.
    Please keep this a secret and away from the clown.
    If necessary, change the password to a more secure one." diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm index fdb9f934516..6ea095028a2 100644 --- a/code/game/machinery/computer/pod.dm +++ b/code/game/machinery/computer/pod.dm @@ -268,7 +268,7 @@ /obj/machinery/computer/pod/old/syndicate name = "external airlock controls" desc = "The Syndicate operate on a tight budget. Operates external airlocks." - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) circuit = /obj/item/circuitboard/syndicatedoor light_color = "#00FFFF" diff --git a/code/game/machinery/computer/pod_tracking_console.dm b/code/game/machinery/computer/pod_tracking_console.dm index ae6e7a4f36a..c4a6d5d440b 100644 --- a/code/game/machinery/computer/pod_tracking_console.dm +++ b/code/game/machinery/computer/pod_tracking_console.dm @@ -4,7 +4,7 @@ icon_keyboard = "tech_key" icon_screen = "rdcomp" light_color = LIGHT_COLOR_PURPLE - req_access = list(access_robotics) + req_access = list(ACCESS_ROBOTICS) circuit = /obj/item/circuitboard/pod_locater /obj/machinery/computer/podtracker/attack_ai(var/mob/user as mob) @@ -20,7 +20,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/computer/podtracker/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/podtracker/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/list/pods[0] for(var/obj/item/spacepod_equipment/misc/tracker/TR in world) diff --git a/code/game/machinery/computer/power.dm b/code/game/machinery/computer/power.dm index 416c73d2570..bb38e59c800 100644 --- a/code/game/machinery/computer/power.dm +++ b/code/game/machinery/computer/power.dm @@ -1,65 +1,65 @@ -/obj/machinery/computer/monitor - name = "power monitoring console" - desc = "Used to monitor power levels across the station." - icon_screen = "power" - icon_keyboard = "power_key" - use_power = ACTIVE_POWER_USE - idle_power_usage = 20 - active_power_usage = 80 - light_color = LIGHT_COLOR_ORANGE - circuit = /obj/item/circuitboard/powermonitor - var/datum/powernet/powernet = null - var/datum/nano_module/power_monitor/power_monitor - var/is_secret_monitor = FALSE - -/obj/machinery/computer/monitor/secret //Hides the power monitor (such as ones on ruins & CentCom) from PDA's to prevent metagaming. - name = "outdated power monitoring console" - desc = "It monitors power levels across the local powernet." - circuit = /obj/item/circuitboard/powermonitor/secret - is_secret_monitor = TRUE - -/obj/machinery/computer/monitor/New() - ..() - GLOB.power_monitors += src - GLOB.power_monitors = sortAtom(GLOB.power_monitors) - power_monitor = new(src) - -/obj/machinery/computer/monitor/Initialize() - ..() - powermonitor_repository.update_cache() - powernet = find_powernet() - -/obj/machinery/computer/monitor/Destroy() - GLOB.power_monitors -= src - powermonitor_repository.update_cache() - QDEL_NULL(power_monitor) - return ..() - -/obj/machinery/computer/monitor/power_change() - ..() - powermonitor_repository.update_cache() - -/obj/machinery/computer/monitor/proc/find_powernet() - var/obj/structure/cable/attached = null - var/turf/T = loc - if(isturf(T)) - attached = locate() in T - if(attached) - return attached.powernet - -/obj/machinery/computer/monitor/attack_ai(mob/user) - attack_hand(user) - -/obj/machinery/computer/monitor/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - // Update the powernet - powernet = find_powernet() - ui_interact(user) - -/obj/machinery/computer/monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - power_monitor.ui_interact(user, ui_key, ui, force_open) - -/obj/machinery/computer/monitor/interact(mob/user) - power_monitor.ui_interact(user) +/obj/machinery/computer/monitor + name = "power monitoring console" + desc = "Used to monitor power levels across the station." + icon_screen = "power" + icon_keyboard = "power_key" + use_power = ACTIVE_POWER_USE + idle_power_usage = 20 + active_power_usage = 80 + light_color = LIGHT_COLOR_ORANGE + circuit = /obj/item/circuitboard/powermonitor + var/datum/powernet/powernet = null + var/datum/nano_module/power_monitor/power_monitor + var/is_secret_monitor = FALSE + +/obj/machinery/computer/monitor/secret //Hides the power monitor (such as ones on ruins & CentCom) from PDA's to prevent metagaming. + name = "outdated power monitoring console" + desc = "It monitors power levels across the local powernet." + circuit = /obj/item/circuitboard/powermonitor/secret + is_secret_monitor = TRUE + +/obj/machinery/computer/monitor/New() + ..() + GLOB.power_monitors += src + GLOB.power_monitors = sortAtom(GLOB.power_monitors) + power_monitor = new(src) + +/obj/machinery/computer/monitor/Initialize() + ..() + GLOB.powermonitor_repository.update_cache() + powernet = find_powernet() + +/obj/machinery/computer/monitor/Destroy() + GLOB.power_monitors -= src + GLOB.powermonitor_repository.update_cache() + QDEL_NULL(power_monitor) + return ..() + +/obj/machinery/computer/monitor/power_change() + ..() + GLOB.powermonitor_repository.update_cache() + +/obj/machinery/computer/monitor/proc/find_powernet() + var/obj/structure/cable/attached = null + var/turf/T = loc + if(isturf(T)) + attached = locate() in T + if(attached) + return attached.powernet + +/obj/machinery/computer/monitor/attack_ai(mob/user) + attack_hand(user) + +/obj/machinery/computer/monitor/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + // Update the powernet + powernet = find_powernet() + ui_interact(user) + +/obj/machinery/computer/monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + power_monitor.ui_interact(user, ui_key, ui, force_open) + +/obj/machinery/computer/monitor/interact(mob/user) + power_monitor.ui_interact(user) diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm index bcabdeb511e..42903d48ecd 100644 --- a/code/game/machinery/computer/prisoner.dm +++ b/code/game/machinery/computer/prisoner.dm @@ -3,7 +3,7 @@ icon = 'icons/obj/computer.dmi' icon_keyboard = "security_key" icon_screen = "explosive" - req_access = list(access_armory) + req_access = list(ACCESS_ARMORY) circuit = /obj/item/circuitboard/prisoner var/id = 0.0 var/temp = null diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 8def58c370b..6134860733a 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -1,240 +1,240 @@ -/obj/machinery/computer/robotics - name = "robotics control console" - desc = "Used to remotely lockdown or detonate linked Cyborgs." - icon = 'icons/obj/computer.dmi' - icon_keyboard = "tech_key" - icon_screen = "robot" - req_access = list(access_robotics) - circuit = /obj/item/circuitboard/robotics - var/temp = null - - light_color = LIGHT_COLOR_PURPLE - - var/safety = 1 - -/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) - return attack_hand(user) - -/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) - if(..()) - return - if(stat & (NOPOWER|BROKEN)) - return - ui_interact(user) - -/obj/machinery/computer/robotics/proc/is_authenticated(var/mob/user as mob) - if(user.can_admin_interact()) - return 1 - else if(allowed(user)) - return 1 - return 0 - -/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/robotics/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - var/list/robots = get_cyborgs(user) - if(robots.len) - data["robots"] = robots - data["safety"] = safety - // Also applies for cyborgs. Hides the manual self-destruct button. - data["is_ai"] = issilicon(user) - data["allowed"] = is_authenticated(user) - return data - -/obj/machinery/computer/robotics/Topic(href, href_list) - if(..()) - return 1 - - var/mob/user = usr - if(!is_authenticated(user)) - to_chat(user, "Access denied.") - return - - // Destroys the cyborg - if(href_list["detonate"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"]) - if(!target || !istype(target)) - return - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - // Cyborgs may blow up themselves via the console - if((isrobot(user) && user != target) || !is_authenticated(user)) - to_chat(user, "Access Denied.") - return - var/choice = input("Really detonate [target.name]?") in list ("Yes", "No") - if(choice != "Yes") - return - if(!target || !istype(target)) - return - - // Antagonistic cyborgs? Left here for downstream - if(target.mind && target.mind.special_role && target.emagged) - to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") - target.ResetSecurityCodes() - else - message_admins("[key_name_admin(usr)] detonated [key_name_admin(target)] (JMP)!") - log_game("\[key_name(usr)] detonated [key_name(target)]!") - to_chat(target, "Self-destruct command received.") - if(target.connected_ai) - to_chat(target.connected_ai, "

    ALERT - Cyborg detonation detected: [target.name]
    ") - spawn(10) - target.self_destruct() - - // Locks or unlocks the cyborg - else if(href_list["lockdown"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"]) - if(!target || !istype(target)) - return - - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - - if(isrobot(user)) - to_chat(user, "Access Denied.") - return - - var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No") - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - message_admins("[key_name_admin(usr)] [target.canmove ? "locked down" : "released"] [key_name_admin(target)]!") - log_game("[key_name(usr)] [target.canmove ? "locked down" : "released"] [key_name(target)]!") - target.SetLockdown(!target.lockcharge) - to_chat(target, "[!target.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") - if(target.connected_ai) - to_chat(target.connected_ai, "[!target.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [target.name]
    ") - - // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs. - else if(href_list["hack"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"]) - if(!target || !istype(target)) - return - - // Antag AI checks - if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user)) - to_chat(user, "Access Denied.") - return - - if(target.connected_ai != user) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - - if(target.emagged) - to_chat(user, "Robot is already hacked.") - return - - var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No") - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - message_admins("[key_name_admin(usr)] emagged [key_name_admin(target)] using robotic console!") - log_game("[key_name(usr)] emagged [key_name(target)] using robotic console!") - target.emagged = 1 - to_chat(target, "Failsafe protocols overriden. New tools available.") - - // Arms the emergency self-destruct system - else if(href_list["arm"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access Denied.") - return - - safety = !safety - to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct.") - - // Destroys all accessible cyborgs if safety is disabled - else if(href_list["nuke"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access Denied") - return - if(safety) - to_chat(user, "Self-destruct aborted - safety active") - return - - message_admins("[key_name_admin(usr)] detonated all cyborgs!") - log_game("\[key_name(usr)] detonated all cyborgs!") - - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue - to_chat(R, "Self-destruct command received.") - if(R.connected_ai) - to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") - spawn(10) - R.self_destruct() - -// Proc: get_cyborgs() -// Parameters: 1 (operator - mob which is operating the console.) -// Description: Returns NanoUI-friendly list of accessible cyborgs. -/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator) - var/list/robots = list() - - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - // Ignore drones - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue - - var/list/robot = list() - robot["name"] = R.name - if(R.stat) - robot["status"] = "Not Responding" - else if(!R.canmove) - robot["status"] = "Lockdown" - else - robot["status"] = "Operational" - - if(R.cell) - robot["cell"] = 1 - robot["cell_capacity"] = R.cell.maxcharge - robot["cell_current"] = R.cell.charge - robot["cell_percentage"] = round(R.cell.percent()) - else - robot["cell"] = 0 - - var/turf/pos = get_turf(R) - var/area/bot_area = get_area(R) - robot["xpos"] = pos.x - robot["ypos"] = pos.y - robot["zpos"] = pos.z - robot["area"] = format_text(bot_area.name) - - robot["health"] = round(R.health * 100 / R.maxHealth,0.1) - - robot["module"] = R.module ? R.module.name : "None" - robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None" - robot["hackable"] = 0 - // Antag AIs know whether linked cyborgs are hacked or not. - if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator)) - robot["hacked"] = R.emagged ? 1 : 0 - robot["hackable"] = R.emagged? 0 : 1 - robots.Add(list(robot)) - return robots - -// Proc: get_cyborg_by_name() -// Parameters: 1 (name - Cyborg we are trying to find) -// Description: Helper proc for finding cyborg by name -/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name) - if(!name) - return - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(R.name == name) - return R +/obj/machinery/computer/robotics + name = "robotics control console" + desc = "Used to remotely lockdown or detonate linked Cyborgs." + icon = 'icons/obj/computer.dmi' + icon_keyboard = "tech_key" + icon_screen = "robot" + req_access = list(ACCESS_ROBOTICS) + circuit = /obj/item/circuitboard/robotics + var/temp = null + + light_color = LIGHT_COLOR_PURPLE + + var/safety = 1 + +/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) + return attack_hand(user) + +/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) + if(..()) + return + if(stat & (NOPOWER|BROKEN)) + return + ui_interact(user) + +/obj/machinery/computer/robotics/proc/is_authenticated(var/mob/user as mob) + if(user.can_admin_interact()) + return 1 + else if(allowed(user)) + return 1 + return 0 + +/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/robotics/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + var/list/robots = get_cyborgs(user) + if(robots.len) + data["robots"] = robots + data["safety"] = safety + // Also applies for cyborgs. Hides the manual self-destruct button. + data["is_ai"] = issilicon(user) + data["allowed"] = is_authenticated(user) + return data + +/obj/machinery/computer/robotics/Topic(href, href_list) + if(..()) + return 1 + + var/mob/user = usr + if(!is_authenticated(user)) + to_chat(user, "Access denied.") + return + + // Destroys the cyborg + if(href_list["detonate"]) + var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"]) + if(!target || !istype(target)) + return + if(isAI(user) && (target.connected_ai != user)) + to_chat(user, "Access Denied. This robot is not linked to you.") + return + // Cyborgs may blow up themselves via the console + if((isrobot(user) && user != target) || !is_authenticated(user)) + to_chat(user, "Access Denied.") + return + var/choice = input("Really detonate [target.name]?") in list ("Yes", "No") + if(choice != "Yes") + return + if(!target || !istype(target)) + return + + // Antagonistic cyborgs? Left here for downstream + if(target.mind && target.mind.special_role && target.emagged) + to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") + target.ResetSecurityCodes() + else + message_admins("[key_name_admin(usr)] detonated [key_name_admin(target)] (JMP)!") + log_game("\[key_name(usr)] detonated [key_name(target)]!") + to_chat(target, "Self-destruct command received.") + if(target.connected_ai) + to_chat(target.connected_ai, "

    ALERT - Cyborg detonation detected: [target.name]
    ") + spawn(10) + target.self_destruct() + + // Locks or unlocks the cyborg + else if(href_list["lockdown"]) + var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"]) + if(!target || !istype(target)) + return + + if(isAI(user) && (target.connected_ai != user)) + to_chat(user, "Access Denied. This robot is not linked to you.") + return + + if(isrobot(user)) + to_chat(user, "Access Denied.") + return + + var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No") + if(choice != "Yes") + return + + if(!target || !istype(target)) + return + + message_admins("[key_name_admin(usr)] [target.canmove ? "locked down" : "released"] [key_name_admin(target)]!") + log_game("[key_name(usr)] [target.canmove ? "locked down" : "released"] [key_name(target)]!") + target.SetLockdown(!target.lockcharge) + to_chat(target, "[!target.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") + if(target.connected_ai) + to_chat(target.connected_ai, "[!target.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [target.name]
    ") + + // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs. + else if(href_list["hack"]) + var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"]) + if(!target || !istype(target)) + return + + // Antag AI checks + if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user)) + to_chat(user, "Access Denied.") + return + + if(target.connected_ai != user) + to_chat(user, "Access Denied. This robot is not linked to you.") + return + + if(target.emagged) + to_chat(user, "Robot is already hacked.") + return + + var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No") + if(choice != "Yes") + return + + if(!target || !istype(target)) + return + + message_admins("[key_name_admin(usr)] emagged [key_name_admin(target)] using robotic console!") + log_game("[key_name(usr)] emagged [key_name(target)] using robotic console!") + target.emagged = 1 + to_chat(target, "Failsafe protocols overriden. New tools available.") + + // Arms the emergency self-destruct system + else if(href_list["arm"]) + if(istype(user, /mob/living/silicon)) + to_chat(user, "Access Denied.") + return + + safety = !safety + to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct.") + + // Destroys all accessible cyborgs if safety is disabled + else if(href_list["nuke"]) + if(istype(user, /mob/living/silicon)) + to_chat(user, "Access Denied") + return + if(safety) + to_chat(user, "Self-destruct aborted - safety active") + return + + message_admins("[key_name_admin(usr)] detonated all cyborgs!") + log_game("\[key_name(usr)] detonated all cyborgs!") + + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(istype(R, /mob/living/silicon/robot/drone)) + continue + // Ignore antagonistic cyborgs + if(R.scrambledcodes) + continue + to_chat(R, "Self-destruct command received.") + if(R.connected_ai) + to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") + spawn(10) + R.self_destruct() + +// Proc: get_cyborgs() +// Parameters: 1 (operator - mob which is operating the console.) +// Description: Returns NanoUI-friendly list of accessible cyborgs. +/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator) + var/list/robots = list() + + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + // Ignore drones + if(istype(R, /mob/living/silicon/robot/drone)) + continue + // Ignore antagonistic cyborgs + if(R.scrambledcodes) + continue + + var/list/robot = list() + robot["name"] = R.name + if(R.stat) + robot["status"] = "Not Responding" + else if(!R.canmove) + robot["status"] = "Lockdown" + else + robot["status"] = "Operational" + + if(R.cell) + robot["cell"] = 1 + robot["cell_capacity"] = R.cell.maxcharge + robot["cell_current"] = R.cell.charge + robot["cell_percentage"] = round(R.cell.percent()) + else + robot["cell"] = 0 + + var/turf/pos = get_turf(R) + var/area/bot_area = get_area(R) + robot["xpos"] = pos.x + robot["ypos"] = pos.y + robot["zpos"] = pos.z + robot["area"] = format_text(bot_area.name) + + robot["health"] = round(R.health * 100 / R.maxHealth,0.1) + + robot["module"] = R.module ? R.module.name : "None" + robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None" + robot["hackable"] = 0 + // Antag AIs know whether linked cyborgs are hacked or not. + if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator)) + robot["hacked"] = R.emagged ? 1 : 0 + robot["hackable"] = R.emagged? 0 : 1 + robots.Add(list(robot)) + return robots + +// Proc: get_cyborg_by_name() +// Parameters: 1 (name - Cyborg we are trying to find) +// Description: Helper proc for finding cyborg by name +/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name) + if(!name) + return + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(R.name == name) + return R diff --git a/code/game/machinery/computer/salvage_ship.dm b/code/game/machinery/computer/salvage_ship.dm index ba7e7200c49..18e4718bf97 100644 --- a/code/game/machinery/computer/salvage_ship.dm +++ b/code/game/machinery/computer/salvage_ship.dm @@ -6,7 +6,7 @@ icon = 'icons/obj/computer.dmi' icon_keyboard = "syndie_key" icon_screen = "syndishuttle" - req_access = list(access_salvage_captain) + req_access = list(ACCESS_SALVAGE_CAPTAIN) var/area/curr_location var/moving = 0 var/lastMove = 0 @@ -106,4 +106,4 @@ return /obj/machinery/computer/salvage_ship/bullet_act(var/obj/item/projectile/Proj) - visible_message("[Proj] ricochets off [src]!") \ No newline at end of file + visible_message("[Proj] ricochets off [src]!") diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index ce872e598f4..8c815198d31 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -1,578 +1,587 @@ -#define SEC_DATA_R_LIST 1 // Record list -#define SEC_DATA_MAINT 2 // Records maintenance -#define SEC_DATA_RECORD 3 // Record - -/obj/machinery/computer/secure_data//TODO:SANITY - name = "security records" - desc = "Used to view and edit personnel's security records." - icon_keyboard = "security_key" - icon_screen = "security" - req_one_access = list(access_security, access_forensics_lockers) - circuit = /obj/item/circuitboard/secure_data - var/obj/item/card/id/scan = null - var/authenticated = null - var/rank = null - var/list/authcard_access = list() - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/temp = null - var/printing = null - //Sorting Variables - var/sortBy = "name" - var/order = 1 // -1 = Descending - 1 = Ascending - - light_color = LIGHT_COLOR_RED - -/obj/machinery/computer/secure_data/Destroy() - active1 = null - active2 = null - return ..() - -/obj/machinery/computer/secure_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - user.drop_item() - O.forceMove(src) - scan = O - ui_interact(user) - return - return ..() - -//Someone needs to break down the dat += into chunks instead of long ass lines. -/obj/machinery/computer/secure_data/attack_hand(mob/user) - if(..()) - return - if(is_away_level(z)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/computer/secure_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "secure_data.tmpl", name, 800, 800) - ui.open() - -/obj/machinery/computer/secure_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["screen"] = screen - if(authenticated) - switch(screen) - if(SEC_DATA_R_LIST) - if(!isnull(data_core.general)) - for(var/datum/data/record/R in sortRecord(data_core.general, sortBy, order)) - var/crimstat = "null" - for(var/datum/data/record/E in data_core.security) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - crimstat = E.fields["criminal"] - break - var/background = "''" - switch(crimstat) - if("*Execute*") - background = "'background-color:#5E0A1A'" - if("*Arrest*") - background = "'background-color:#890E26'" - if("Incarcerated") - background = "'background-color:#743B03'" - if("Parolled") - background = "'background-color:#743B03'" - if("Released") - background = "'background-color:#216489'" - if("None") - background = "'background-color:#007f47'" - if("null") - crimstat = "No record." - data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"], "background" = background, "crimstat" = crimstat)) - if(SEC_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = "name") - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = "id") - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") - fields[++fields.len] = list("field" = "Rank:", "value" = active1.fields["rank"], "edit" = "rank") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = null) - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = null) - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) - general["has_photos"] += (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - - var/list/security = list() - data["security"] = security - if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) - var/list/fields = list() - security["fields"] = fields - fields[++fields.len] = list("field" = "Criminal Status:", "value" = active2.fields["criminal"], "edit" = "criminal", "line_break" = 1) - fields[++fields.len] = list("field" = "Minor Crimes:", "value" = active2.fields["mi_crim"], "edit" = "mi_crim", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_crim_d"], "edit" = "mi_crim_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Major Crimes:", "value" = active2.fields["ma_crim"], "edit" = "ma_crim", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_crim_d"], "edit" = "ma_crim_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - security["comments"] = active2.fields["comments"] - security["empty"] = 0 - else - security["empty"] = 1 - return data - -/obj/machinery/computer/secure_data/Topic(href, href_list) - if(..()) - return 1 - - if(!data_core.general.Find(active1)) - active1 = null - if(!data_core.security.Find(active2)) - active2 = null - - if(href_list["temp"]) - temp = null - - if(href_list["temp_action"]) - var/temp_href = splittext(href_list["temp_action"], "=") - switch(temp_href[1]) - if("del_all2") - for(var/datum/data/record/R in data_core.security) - qdel(R) - update_all_mob_security_hud() - setTemp("

    All records deleted.

    ") - if("del_alllogs2") - if(GLOB.cell_logs.len) - setTemp("

    All cell logs deleted.

    ") - GLOB.cell_logs.Cut() - else - to_chat(usr, "Error; No cell logs to delete.") - if("del_r2") - if(active2) - qdel(active2) - update_all_mob_security_hud() - if("del_rg2") - if(active1) - for(var/datum/data/record/R in data_core.medical) - if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"]) - qdel(R) - QDEL_NULL(active1) - QDEL_NULL(active2) - update_all_mob_security_hud() - screen = SEC_DATA_R_LIST - if("criminal") - if(active2) - var/t1 - if(temp_href[2] == "execute") - t1 = copytext(trim(sanitize(input("Explain why they are being executed. Include a list of their crimes, and victims.", "EXECUTION ORDER", null, null) as text)), 1, MAX_MESSAGE_LEN) - else - t1 = copytext(trim(sanitize(input("Enter Reason:", "Secure. records", null, null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1) - t1 = "(none)" - if(!set_criminal_status(usr, active2, temp_href[2], t1, rank, authcard_access)) - setTemp("

    Error: permission denied.

    ") - return 1 - if("rank") - if(active1) - active1.fields["rank"] = temp_href[2] - if(temp_href[2] in GLOB.joblist) - active1.fields["real_rank"] = temp_href[2] - - if(href_list["scan"]) - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - - if(href_list["login"]) - if(isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - else if(istype(scan, /obj/item/card/id)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - authcard_access = scan.access - - if(authenticated) - active1 = null - active2 = null - screen = SEC_DATA_R_LIST - - if(authenticated) - if(href_list["logout"]) - authenticated = null - screen = null - active1 = null - active2 = null - authcard_access = list() - - else if(href_list["sort"]) - // Reverse the order if clicked twice - if(sortBy == href_list["sort"]) - if(order == 1) - order = -1 - else - order = 1 - else - sortBy = href_list["sort"] - order = initial(order) - - else if(href_list["screen"]) - screen = text2num(href_list["screen"]) - if(screen < 1) - screen = SEC_DATA_R_LIST - - active1 = null - active2 = null - - else if(href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - var/datum/data/record/M = locate(href_list["d_rec"]) - if(!data_core.general.Find(R)) - setTemp("

    Record not found!

    ") - return 1 - for(var/datum/data/record/E in data_core.security) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - M = E - active1 = R - active2 = M - screen = SEC_DATA_RECORD - - else if(href_list["del_all"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete all records?

    ", buttons) - - else if(href_list["del_alllogs"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_alllogs2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete all cell logs?

    ", buttons) - - else if(href_list["del_rg"]) - if(active1) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_rg2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (ALL)?

    ", buttons) - - else if(href_list["del_r"]) - if(active1) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (Security Portion Only)?

    ", buttons) - - else if(href_list["new_s"]) - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Security Record #[R.fields["id"]]" - R.fields["criminal"] = "None" - R.fields["mi_crim"] = "None" - R.fields["mi_crim_d"] = "No minor crime convictions." - R.fields["ma_crim"] = "None" - R.fields["ma_crim_d"] = "No major crime convictions." - R.fields["notes"] = "No notes." - data_core.security += R - active2 = R - screen = SEC_DATA_RECORD - - else if(href_list["new_g"]) - var/datum/data/record/G = new /datum/data/record() - G.fields["name"] = "New Record" - G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]" - G.fields["rank"] = "Unassigned" - G.fields["real_rank"] = "Unassigned" - G.fields["sex"] = "Male" - G.fields["age"] = "Unknown" - G.fields["fingerprint"] = "Unknown" - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["species"] = "Human" - data_core.general += G - active1 = G - active2 = null - - else if(href_list["print_r"]) - if(!printing) - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - sleep(50) - var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
    Security Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]]
    "} - else - P.info += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) - P.info += {"
    \n
    Security Data
    -
    \nCriminal Status: [active2.fields["criminal"]]
    \n -
    \nMinor Crimes: [active2.fields["mi_crim"]] -
    \nDetails: [active2.fields["mi_crim_d"]]
    \n -
    \nMajor Crimes: [active2.fields["ma_crim"]] -
    \nDetails: [active2.fields["ma_crim_d"]]
    \n -
    \nImportant Notes: -
    \n\t[active2.fields["notes"]]
    \n
    \n
    Comments/Log

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c]
    " - else - P.info += "Security Record Lost!
    " - P.info += "" - P.name = "paper - 'Security Record: [active1.fields["name"]]'" - printing = 0 - -/* Removed due to BYOND issue - else if(href_list["print_p"]) - if(!printing) - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - sleep(50) - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - create_record_photo(active1) - printing = 0 -*/ - - else if(href_list["printlogs"]) - if(GLOB.cell_logs.len && !printing) - var/obj/item/paper/P = input(usr, "Select log to print", "Available Cell Logs") as null|anything in GLOB.cell_logs - if(!P) - return 0 - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - to_chat(usr, "Printing file [P.name].") - sleep(50) - var/obj/item/paper/log = new /obj/item/paper(loc) - log.name = P.name - log.info = P.info - printing = 0 - return 1 - else - to_chat(usr, "[src] has no logs stored or is already printing.") - - - else if(href_list["add_c"]) - if(istype(active2, /datum/data/record)) - var/a2 = active2 - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Secure. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["comments"] += "Made by [authenticated] ([rank]) on [current_date_string] [station_time_timestamp()]
    [t1]" - - else if(href_list["del_c"]) - var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) - if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) - active2.fields["comments"] -= active2.fields["comments"][index] - - if(href_list["field"]) - if(..()) - return 1 - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("name") - if(istype(active1, /datum/data/record)) - var/t1 = reject_bad_name(clean_input("Please input name:", "Secure. records", active1.fields["name"], null)) - if(!t1 || !length(trim(t1)) || ..() || active1 != a1) - return 1 - active1.fields["name"] = t1 - if(istype(active2, /datum/data/record)) - active2.fields["name"] = t1 - if("id") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["id"] = t1 - if(istype(active2, /datum/data/record)) - active2.fields["id"] = t1 - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["age"] = t1 - if("mi_crim") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input minor crimes list:", "Secure. records", active2.fields["mi_crim"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_crim"] = t1 - if("mi_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize minor crimes:", "Secure. records", active2.fields["mi_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_crim_d"] = t1 - if("ma_crim") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input major crimes list:", "Secure. records", active2.fields["ma_crim"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_crim"] = t1 - if("ma_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize major crimes:", "Secure. records", active2.fields["ma_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_crim_d"] = t1 - if("notes") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["notes"] = t1 - if("criminal") - if(istype(active2, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "None", "icon" = "unlock", "href" = "criminal=none", "status" = (active2.fields["criminal"] == "None" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Arrest*", "icon" = "lock", "href" = "criminal=arrest", "status" = (active2.fields["criminal"] == "*Arrest*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Incarcerated", "icon" = "lock", "href" = "criminal=incarcerated", "status" = (active2.fields["criminal"] == "Incarcerated" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Execute*", "icon" = "lock", "href" = "criminal=execute", "status" = (active2.fields["criminal"] == "*Execute*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Parolled", "icon" = "unlock-alt", "href" = "criminal=parolled", "status" = (active2.fields["criminal"] == "Parolled" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Released", "icon" = "unlock", "href" = "criminal=released", "status" = (active2.fields["criminal"] == "Released" ? "selected" : null)) - setTemp("

    Criminal Status

    ", buttons) - if("rank") - var/list/L = list("Head of Personnel", "Captain", "AI") - //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N - if(istype(active1, /datum/data/record) && L.Find(rank)) - var/list/buttons = list() - for(var/rank in GLOB.joblist) - buttons[++buttons.len] = list("name" = rank, "icon" = null, "href" = "rank=[rank]", "status" = (active1.fields["rank"] == rank ? "selected" : null)) - setTemp("

    Rank

    ", buttons) - else - setTemp("

    You do not have the required rank to do this!

    ") - if("species") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["species"] = t1 - return 1 - -/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list()) - temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) - -/* Proc disabled due to BYOND Issue - -/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R) - // basically copy-pasted from the camera code but different enough that it has to be redone - var/icon/photoimage = get_record_photo(R) - var/icon/small_img = icon(photoimage) - var/icon/tiny_img = icon(photoimage) - var/icon/ic = icon('icons/obj/items.dmi',"photo") - var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") - small_img.Scale(8, 8) - tiny_img.Scale(4, 4) - ic.Blend(small_img, ICON_OVERLAY, 10, 13) - pc.Blend(tiny_img, ICON_OVERLAY, 12, 19) - - var/datum/picture/P = new() - P.fields["name"] = "File Photo - [R.fields["name"]]" - P.fields["author"] = "Central Command" - P.fields["icon"] = ic - P.fields["tiny"] = pc - P.fields["img"] = photoimage - P.fields["desc"] = "You can see [R.fields["name"]] on the photo." - P.fields["pixel_x"] = rand(-10, 10) - P.fields["pixel_y"] = rand(-10, 10) - P.fields["size"] = 2 - - var/obj/item/photo/PH = new/obj/item/photo(loc) - PH.construct(P) - -*/ - -/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R) - // similar to the code to make a photo, but of course the actual rendering is completely different - var/icon/res = icon('icons/effects/96x96.dmi', "") - // will be 2x2 to fit the 2 directions - res.Scale(2 * 32, 2 * 32) - // transparent background (it's a plastic transparency, you see) with the front and side icons - res.Blend(icon(R.fields["photo"], dir = SOUTH), ICON_OVERLAY, 1, 17) - res.Blend(icon(R.fields["photo"], dir = WEST), ICON_OVERLAY, 33, 17) - - return res - -/obj/machinery/computer/secure_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in data_core.security) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") - if(5) - R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - -/obj/machinery/computer/secure_data/detective_computer - icon = 'icons/obj/computer.dmi' - icon_state = "messyfiles" - -/obj/machinery/computer/secure_data/laptop - name = "security laptop" - desc = "Nanotrasen Security laptop. Bringing modern compact computing to this century!" - icon_state = "laptop" - icon_keyboard = "seclaptop_key" - icon_screen = "seclaptop" - density = 0 - -#undef SEC_DATA_R_LIST -#undef SEC_DATA_MAINT -#undef SEC_DATA_RECORD +#define SEC_DATA_R_LIST 1 // Record list +#define SEC_DATA_MAINT 2 // Records maintenance +#define SEC_DATA_RECORD 3 // Record + +/obj/machinery/computer/secure_data//TODO:SANITY + name = "security records" + desc = "Used to view and edit personnel's security records." + icon_keyboard = "security_key" + icon_screen = "security" + req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) + circuit = /obj/item/circuitboard/secure_data + var/obj/item/card/id/scan = null + var/authenticated = null + var/rank = null + var/list/authcard_access = list() + var/screen = null + var/datum/data/record/active1 = null + var/datum/data/record/active2 = null + var/temp = null + var/printing = null + //Sorting Variables + var/sortBy = "name" + var/order = 1 // -1 = Descending - 1 = Ascending + + light_color = LIGHT_COLOR_RED + +/obj/machinery/computer/secure_data/Destroy() + active1 = null + active2 = null + return ..() + +/obj/machinery/computer/secure_data/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/card/id) && !scan) + user.drop_item() + O.forceMove(src) + scan = O + ui_interact(user) + return + return ..() + +//Someone needs to break down the dat += into chunks instead of long ass lines. +/obj/machinery/computer/secure_data/attack_hand(mob/user) + if(..()) + return + if(is_away_level(z)) + to_chat(user, "Unable to establish a connection: You're too far away from the station!") + return + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/computer/secure_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "secure_data.tmpl", name, 800, 800) + ui.open() + +/obj/machinery/computer/secure_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["temp"] = temp + data["scan"] = scan ? scan.name : null + data["authenticated"] = authenticated + data["screen"] = screen + if(authenticated) + switch(screen) + if(SEC_DATA_R_LIST) + if(!isnull(GLOB.data_core.general)) + for(var/datum/data/record/R in sortRecord(GLOB.data_core.general, sortBy, order)) + var/crimstat = "null" + for(var/datum/data/record/E in GLOB.data_core.security) + if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) + crimstat = E.fields["criminal"] + break + var/background = "''" + switch(crimstat) + if(SEC_RECORD_STATUS_EXECUTE) + background = "'background-color:#5E0A1A'" + if(SEC_RECORD_STATUS_ARREST) + background = "'background-color:#890E26'" + if(SEC_RECORD_STATUS_SEARCH) + background = "'background-color:#999900'" + if(SEC_RECORD_STATUS_MONITOR) + background = "'background-color:#004C99'" + if(SEC_RECORD_STATUS_DEMOTE) + background = "'background-color:#C2A111'" + if(SEC_RECORD_STATUS_INCARCERATED) + background = "'background-color:#743B03'" + if(SEC_RECORD_STATUS_PAROLLED) + background = "'background-color:#743B03'" + if(SEC_RECORD_STATUS_RELEASED) + background = "'background-color:#216489'" + if(SEC_RECORD_STATUS_NONE) + background = "'background-color:#007f47'" + if("null") + crimstat = "No record." + data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"], "background" = background, "crimstat" = crimstat)) + if(SEC_DATA_RECORD) + var/list/general = list() + data["general"] = general + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + var/list/fields = list() + general["fields"] = fields + fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = "name") + fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = "id") + fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") + fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") + fields[++fields.len] = list("field" = "Rank:", "value" = active1.fields["rank"], "edit" = "rank") + fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") + fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = null) + fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = null) + var/list/photos = list() + general["photos"] = photos + photos[++photos.len] = list("photo" = active1.fields["photo-south"]) + photos[++photos.len] = list("photo" = active1.fields["photo-west"]) + general["has_photos"] += (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) + general["empty"] = 0 + else + general["empty"] = 1 + + var/list/security = list() + data["security"] = security + if(istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2)) + var/list/fields = list() + security["fields"] = fields + fields[++fields.len] = list("field" = "Criminal Status:", "value" = active2.fields["criminal"], "edit" = "criminal", "line_break" = 1) + fields[++fields.len] = list("field" = "Minor Crimes:", "value" = active2.fields["mi_crim"], "edit" = "mi_crim", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_crim_d"], "edit" = "mi_crim_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Major Crimes:", "value" = active2.fields["ma_crim"], "edit" = "ma_crim", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_crim_d"], "edit" = "ma_crim_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) + if(!active2.fields["comments"] || !islist(active2.fields["comments"])) + active2.fields["comments"] = list() + security["comments"] = active2.fields["comments"] + security["empty"] = 0 + else + security["empty"] = 1 + return data + +/obj/machinery/computer/secure_data/Topic(href, href_list) + if(..()) + return 1 + + if(!GLOB.data_core.general.Find(active1)) + active1 = null + if(!GLOB.data_core.security.Find(active2)) + active2 = null + + if(href_list["temp"]) + temp = null + + if(href_list["temp_action"]) + var/temp_href = splittext(href_list["temp_action"], "=") + switch(temp_href[1]) + if("del_all2") + for(var/datum/data/record/R in GLOB.data_core.security) + qdel(R) + update_all_mob_security_hud() + setTemp("

    All records deleted.

    ") + if("del_alllogs2") + if(GLOB.cell_logs.len) + setTemp("

    All cell logs deleted.

    ") + GLOB.cell_logs.Cut() + else + to_chat(usr, "Error; No cell logs to delete.") + if("del_r2") + if(active2) + qdel(active2) + update_all_mob_security_hud() + if("del_rg2") + if(active1) + for(var/datum/data/record/R in GLOB.data_core.medical) + if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"]) + qdel(R) + QDEL_NULL(active1) + QDEL_NULL(active2) + update_all_mob_security_hud() + screen = SEC_DATA_R_LIST + if("criminal") + if(active2) + var/t1 + if(temp_href[2] == "execute") + t1 = copytext(trim(sanitize(input("Explain why they are being executed. Include a list of their crimes, and victims.", "EXECUTION ORDER", null, null) as text)), 1, MAX_MESSAGE_LEN) + else + t1 = copytext(trim(sanitize(input("Enter Reason:", "Secure. records", null, null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1) + t1 = "(none)" + if(!set_criminal_status(usr, active2, temp_href[2], t1, rank, authcard_access)) + setTemp("

    Error: permission denied.

    ") + return 1 + if("rank") + if(active1) + active1.fields["rank"] = temp_href[2] + if(temp_href[2] in GLOB.joblist) + active1.fields["real_rank"] = temp_href[2] + + if(href_list["scan"]) + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + + if(href_list["login"]) + if(isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + else if(istype(scan, /obj/item/card/id)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + authcard_access = scan.access + + if(authenticated) + active1 = null + active2 = null + screen = SEC_DATA_R_LIST + + if(authenticated) + if(href_list["logout"]) + authenticated = null + screen = null + active1 = null + active2 = null + authcard_access = list() + + else if(href_list["sort"]) + // Reverse the order if clicked twice + if(sortBy == href_list["sort"]) + if(order == 1) + order = -1 + else + order = 1 + else + sortBy = href_list["sort"] + order = initial(order) + + else if(href_list["screen"]) + screen = text2num(href_list["screen"]) + if(screen < 1) + screen = SEC_DATA_R_LIST + + active1 = null + active2 = null + + else if(href_list["d_rec"]) + var/datum/data/record/R = locate(href_list["d_rec"]) + var/datum/data/record/M = locate(href_list["d_rec"]) + if(!GLOB.data_core.general.Find(R)) + setTemp("

    Record not found!

    ") + return 1 + for(var/datum/data/record/E in GLOB.data_core.security) + if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) + M = E + active1 = R + active2 = M + screen = SEC_DATA_RECORD + + else if(href_list["del_all"]) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete all records?

    ", buttons) + + else if(href_list["del_alllogs"]) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_alllogs2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete all cell logs?

    ", buttons) + + else if(href_list["del_rg"]) + if(active1) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_rg2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete the record (ALL)?

    ", buttons) + + else if(href_list["del_r"]) + if(active1) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete the record (Security Portion Only)?

    ", buttons) + + else if(href_list["new_s"]) + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Security Record #[R.fields["id"]]" + R.fields["criminal"] = "None" + R.fields["mi_crim"] = "None" + R.fields["mi_crim_d"] = "No minor crime convictions." + R.fields["ma_crim"] = "None" + R.fields["ma_crim_d"] = "No major crime convictions." + R.fields["notes"] = "No notes." + GLOB.data_core.security += R + active2 = R + screen = SEC_DATA_RECORD + + else if(href_list["new_g"]) + var/datum/data/record/G = new /datum/data/record() + G.fields["name"] = "New Record" + G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]" + G.fields["rank"] = "Unassigned" + G.fields["real_rank"] = "Unassigned" + G.fields["sex"] = "Male" + G.fields["age"] = "Unknown" + G.fields["fingerprint"] = "Unknown" + G.fields["p_stat"] = "Active" + G.fields["m_stat"] = "Stable" + G.fields["species"] = "Human" + GLOB.data_core.general += G + active1 = G + active2 = null + + else if(href_list["print_r"]) + if(!printing) + printing = 1 + playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) + sleep(50) + var/obj/item/paper/P = new /obj/item/paper(loc) + P.info = "
    Security Record

    " + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
    \nSex: [active1.fields["sex"]] +
    \nAge: [active1.fields["age"]] +
    \nFingerprint: [active1.fields["fingerprint"]] +
    \nPhysical Status: [active1.fields["p_stat"]] +
    \nMental Status: [active1.fields["m_stat"]]
    "} + else + P.info += "General Record Lost!
    " + if(istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2)) + P.info += {"
    \n
    Security Data
    +
    \nCriminal Status: [active2.fields["criminal"]]
    \n +
    \nMinor Crimes: [active2.fields["mi_crim"]] +
    \nDetails: [active2.fields["mi_crim_d"]]
    \n +
    \nMajor Crimes: [active2.fields["ma_crim"]] +
    \nDetails: [active2.fields["ma_crim_d"]]
    \n +
    \nImportant Notes: +
    \n\t[active2.fields["notes"]]
    \n
    \n
    Comments/Log

    "} + for(var/c in active2.fields["comments"]) + P.info += "[c]
    " + else + P.info += "Security Record Lost!
    " + P.info += "" + P.name = "paper - 'Security Record: [active1.fields["name"]]'" + printing = 0 + +/* Removed due to BYOND issue + else if(href_list["print_p"]) + if(!printing) + printing = 1 + playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) + sleep(50) + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + create_record_photo(active1) + printing = 0 +*/ + + else if(href_list["printlogs"]) + if(GLOB.cell_logs.len && !printing) + var/obj/item/paper/P = input(usr, "Select log to print", "Available Cell Logs") as null|anything in GLOB.cell_logs + if(!P) + return 0 + printing = 1 + playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) + to_chat(usr, "Printing file [P.name].") + sleep(50) + var/obj/item/paper/log = new /obj/item/paper(loc) + log.name = P.name + log.info = P.info + printing = 0 + return 1 + else + to_chat(usr, "[src] has no logs stored or is already printing.") + + + else if(href_list["add_c"]) + if(istype(active2, /datum/data/record)) + var/a2 = active2 + var/t1 = copytext(trim(sanitize(input("Add Comment:", "Secure. records", null, null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + + else if(href_list["del_c"]) + var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) + if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) + active2.fields["comments"] -= active2.fields["comments"][index] + + if(href_list["field"]) + if(..()) + return 1 + var/a1 = active1 + var/a2 = active2 + switch(href_list["field"]) + if("name") + if(istype(active1, /datum/data/record)) + var/t1 = reject_bad_name(clean_input("Please input name:", "Secure. records", active1.fields["name"], null)) + if(!t1 || !length(trim(t1)) || ..() || active1 != a1) + return 1 + active1.fields["name"] = t1 + if(istype(active2, /datum/data/record)) + active2.fields["name"] = t1 + if("id") + if(istype(active1, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["id"] = t1 + if(istype(active2, /datum/data/record)) + active2.fields["id"] = t1 + if("fingerprint") + if(istype(active1, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["fingerprint"] = t1 + if("sex") + if(istype(active1, /datum/data/record)) + if(active1.fields["sex"] == "Male") + active1.fields["sex"] = "Female" + else + active1.fields["sex"] = "Male" + if("age") + if(istype(active1, /datum/data/record)) + var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["age"] = t1 + if("mi_crim") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input minor crimes list:", "Secure. records", active2.fields["mi_crim"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["mi_crim"] = t1 + if("mi_crim_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize minor crimes:", "Secure. records", active2.fields["mi_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["mi_crim_d"] = t1 + if("ma_crim") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input major crimes list:", "Secure. records", active2.fields["ma_crim"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["ma_crim"] = t1 + if("ma_crim_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize major crimes:", "Secure. records", active2.fields["ma_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["ma_crim_d"] = t1 + if("notes") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["notes"] = t1 + if("criminal") + if(istype(active2, /datum/data/record)) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "None", "icon" = "unlock", "href" = "criminal=none", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_NONE ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*Arrest*", "icon" = "lock", "href" = "criminal=arrest", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_ARREST ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Search", "icon" = "lock", "href" = "criminal=search", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_SEARCH ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Monitor", "icon" = "unlock", "href" = "criminal=monitor", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_MONITOR ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Demote", "icon" = "lock", "href" = "criminal=demote", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_DEMOTE ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Incarcerated", "icon" = "lock", "href" = "criminal=incarcerated", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_INCARCERATED ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*Execute*", "icon" = "lock", "href" = "criminal=execute", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_EXECUTE ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Parolled", "icon" = "unlock-alt", "href" = "criminal=parolled", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_PAROLLED ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Released", "icon" = "unlock", "href" = "criminal=released", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_RELEASED ? "selected" : null)) + setTemp("

    Criminal Status

    ", buttons) + if("rank") + var/list/L = list("Head of Personnel", "Captain", "AI") + //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N + if(istype(active1, /datum/data/record) && L.Find(rank)) + var/list/buttons = list() + for(var/rank in GLOB.joblist) + buttons[++buttons.len] = list("name" = rank, "icon" = null, "href" = "rank=[rank]", "status" = (active1.fields["rank"] == rank ? "selected" : null)) + setTemp("

    Rank

    ", buttons) + else + setTemp("

    You do not have the required rank to do this!

    ") + if("species") + if(istype(active1, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["species"] = t1 + return 1 + +/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list()) + temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + +/* Proc disabled due to BYOND Issue + +/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R) + // basically copy-pasted from the camera code but different enough that it has to be redone + var/icon/photoimage = get_record_photo(R) + var/icon/small_img = icon(photoimage) + var/icon/tiny_img = icon(photoimage) + var/icon/ic = icon('icons/obj/items.dmi',"photo") + var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") + small_img.Scale(8, 8) + tiny_img.Scale(4, 4) + ic.Blend(small_img, ICON_OVERLAY, 10, 13) + pc.Blend(tiny_img, ICON_OVERLAY, 12, 19) + + var/datum/picture/P = new() + P.fields["name"] = "File Photo - [R.fields["name"]]" + P.fields["author"] = "Central Command" + P.fields["icon"] = ic + P.fields["tiny"] = pc + P.fields["img"] = photoimage + P.fields["desc"] = "You can see [R.fields["name"]] on the photo." + P.fields["pixel_x"] = rand(-10, 10) + P.fields["pixel_y"] = rand(-10, 10) + P.fields["size"] = 2 + + var/obj/item/photo/PH = new/obj/item/photo(loc) + PH.construct(P) + +*/ + +/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R) + // similar to the code to make a photo, but of course the actual rendering is completely different + var/icon/res = icon('icons/effects/96x96.dmi', "") + // will be 2x2 to fit the 2 directions + res.Scale(2 * 32, 2 * 32) + // transparent background (it's a plastic transparency, you see) with the front and side icons + res.Blend(icon(R.fields["photo"], dir = SOUTH), ICON_OVERLAY, 1, 17) + res.Blend(icon(R.fields["photo"], dir = WEST), ICON_OVERLAY, 33, 17) + + return res + +/obj/machinery/computer/secure_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + for(var/datum/data/record/R in GLOB.data_core.security) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["criminal"] = pick(SEC_RECORD_STATUS_NONE, SEC_RECORD_STATUS_ARREST, SEC_RECORD_STATUS_SEARCH, SEC_RECORD_STATUS_MONITOR, SEC_RECORD_STATUS_DEMOTE, SEC_RECORD_STATUS_INCARCERATED, SEC_RECORD_STATUS_PAROLLED, SEC_RECORD_STATUS_RELEASED) + if(5) + R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + +/obj/machinery/computer/secure_data/detective_computer + icon = 'icons/obj/computer.dmi' + icon_state = "messyfiles" + +/obj/machinery/computer/secure_data/laptop + name = "security laptop" + desc = "Nanotrasen Security laptop. Bringing modern compact computing to this century!" + icon_state = "laptop" + icon_keyboard = "seclaptop_key" + icon_screen = "seclaptop" + density = 0 + +#undef SEC_DATA_R_LIST +#undef SEC_DATA_MAINT +#undef SEC_DATA_RECORD diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm index 125b5294860..b7361db01a3 100644 --- a/code/game/machinery/computer/skills.dm +++ b/code/game/machinery/computer/skills.dm @@ -10,7 +10,7 @@ icon_screen = "medlaptop" density = 0 light_color = LIGHT_COLOR_GREEN - req_one_access = list(access_heads) + req_one_access = list(ACCESS_HEADS) circuit = /obj/item/circuitboard/skills var/obj/item/card/id/scan = null var/authenticated = null @@ -52,7 +52,7 @@ ui = new(user, src, ui_key, "skills_data.tmpl", name, 800, 380) ui.open() -/obj/machinery/computer/skills/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/skills/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["temp"] = temp data["scan"] = scan ? scan.name : null @@ -61,13 +61,13 @@ if(authenticated) switch(screen) if(SKILL_DATA_R_LIST) - if(!isnull(data_core.general)) - for(var/datum/data/record/R in sortRecord(data_core.general, sortBy, order)) + if(!isnull(GLOB.data_core.general)) + for(var/datum/data/record/R in sortRecord(GLOB.data_core.general, sortBy, order)) data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"])) if(SKILL_DATA_RECORD) var/list/general = list() data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) var/list/fields = list() general["fields"] = fields fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "name" = "name") @@ -93,7 +93,7 @@ if(..()) return 1 - if(!data_core.general.Find(active1)) + if(!GLOB.data_core.general.Find(active1)) active1 = null if(href_list["temp"]) @@ -103,24 +103,24 @@ var/temp_list = splittext(href_list["temp_action"], "=") switch(temp_list[1]) if("del_all2") - if(PDA_Manifest && PDA_Manifest.len) - PDA_Manifest.Cut() - for(var/datum/data/record/R in data_core.security) + if(GLOB.PDA_Manifest && GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + for(var/datum/data/record/R in GLOB.data_core.security) qdel(R) setTemp("

    All employment records deleted.

    ") if("del_rg2") if(active1) - if(PDA_Manifest && PDA_Manifest.len) - PDA_Manifest.Cut() - for(var/datum/data/record/R in data_core.medical) + if(GLOB.PDA_Manifest && GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + for(var/datum/data/record/R in GLOB.data_core.medical) if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"]) qdel(R) QDEL_NULL(active1) screen = SKILL_DATA_R_LIST if("rank") if(active1) - if(PDA_Manifest && PDA_Manifest.len) - PDA_Manifest.Cut() + if(GLOB.PDA_Manifest && GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() active1.fields["rank"] = temp_list[2] if(temp_list[2] in GLOB.joblist) active1.fields["real_rank"] = temp_list[2] @@ -182,7 +182,7 @@ else if(href_list["d_rec"]) var/datum/data/record/R = locate(href_list["d_rec"]) - if(!data_core.general.Find(R)) + if(!GLOB.data_core.general.Find(R)) setTemp("

    Record not found!

    ") return 1 active1 = R @@ -202,8 +202,8 @@ setTemp("

    Are you sure you wish to delete the record (ALL)?

    ", buttons) else if(href_list["new_g"]) - if(PDA_Manifest.len) - PDA_Manifest.Cut() + if(GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() var/datum/data/record/G = new /datum/data/record() G.fields["name"] = "New Record" G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]" @@ -215,7 +215,7 @@ G.fields["p_stat"] = "Active" G.fields["m_stat"] = "Stable" G.fields["species"] = "Human" - data_core.general += G + GLOB.data_core.general += G active1 = G else if(href_list["print_r"]) @@ -225,7 +225,7 @@ sleep(50) var/obj/item/paper/P = new /obj/item/paper(loc) P.info = "
    Employment Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
    \nSex: [active1.fields["sex"]]
    \nAge: [active1.fields["age"]] @@ -300,7 +300,7 @@ ..(severity) return - for(var/datum/data/record/R in data_core.security) + for(var/datum/data/record/R in GLOB.data_core.security) if(prob(10/severity)) switch(rand(1,6)) if(1) @@ -310,7 +310,7 @@ if(3) R.fields["age"] = rand(5, 85) if(4) - R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") + R.fields["criminal"] = pick(SEC_RECORD_STATUS_NONE, SEC_RECORD_STATUS_ARREST, SEC_RECORD_STATUS_INCARCERATED, SEC_RECORD_STATUS_PAROLLED, SEC_RECORD_STATUS_RELEASED) if(5) R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") if(6) diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index 0602757944f..f509ee42d34 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -1,340 +1,340 @@ -//Config stuff -#define SPECOPS_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. -#define SPECOPS_STATION_AREATYPE "/area/shuttle/specops/station" //Type of the spec ops shuttle area for station -#define SPECOPS_DOCK_AREATYPE "/area/shuttle/specops/centcom" //Type of the spec ops shuttle area for dock -#define SPECOPS_RETURN_DELAY 6000 //Time between the shuttle is capable of moving. - -var/specops_shuttle_moving_to_station = 0 -var/specops_shuttle_moving_to_centcom = 0 -var/specops_shuttle_at_station = 0 -var/specops_shuttle_can_send = 1 -var/specops_shuttle_time = 0 -var/specops_shuttle_timeleft = 0 - -/obj/machinery/computer/specops_shuttle - name = "\improper Spec. Ops. shuttle console" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "security_key" - icon_screen = "syndishuttle" - light_color = LIGHT_COLOR_PURE_CYAN - req_access = list(access_cent_specops) -// req_access = list(ACCESS_CENT_SPECOPS) - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - var/specops_shuttle_timereset = 0 - -/proc/specops_return() - var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team", list(1,2)) - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - - var/area/start_location = locate(/area/shuttle/specops/station) - var/area/end_location = locate(/area/shuttle/specops/centcom) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived at Central Command. Operation has ended!") - - specops_shuttle_at_station = 0 - - for(var/obj/machinery/computer/specops_shuttle/S in world) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_process() - var/area/centcom/specops/special_ops = locate()//Where is the specops area located? - var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - //message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" - //announcer.autosay(message, "A.L.I.C.E.", "Response Team") - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if(!specops_can_move()) - to_chat(usr, "The Special Operations shuttle is unable to leave.") - return - - //Begin Marauder launchpad. - spawn(0)//So it parallel processes it. - for(var/obj/machinery/door/poddoor/M in special_ops) - switch(M.id_tag) - if("ASSAULT0") - spawn(10)//1 second delay between each. - M.open() - if("ASSAULT1") - spawn(20) - M.open() - if("ASSAULT2") - spawn(30) - M.open() - if("ASSAULT3") - spawn(40) - M.open() - - sleep(10) - - var/spawn_marauder[] = new() - for(var/obj/effect/landmark/L in world) - if(L.name == "Marauder Entry") - spawn_marauder.Add(L.loc) - for(var/obj/effect/landmark/L in world) - if(L.name == "Marauder Exit") - var/obj/effect/portal/P = new(L.loc, pick(spawn_marauder)) - //P.invisibility = 101//So it is not seen by anyone. - P.failchance = 0//So it has no fail chance when teleporting. - spawn_marauder.Remove(P.target) - - sleep(10) - - for(var/obj/machinery/mass_driver/M in special_ops) - switch(M.id_tag) - if("ASSAULT0") - spawn(10) - M.drive() - if("ASSAULT1") - spawn(20) - M.drive() - if("ASSAULT2") - spawn(30) - M.drive() - if("ASSAULT3") - spawn(40) - M.drive() - - sleep(50)//Doors remain open for 5 seconds. - - for(var/obj/machinery/door/poddoor/M in special_ops) - switch(M.id_tag)//Doors close at the same time. - if("ASSAULT0") - spawn(0) - M.close() - if("ASSAULT1") - spawn(0) - M.close() - if("ASSAULT2") - spawn(0) - M.close() - if("ASSAULT3") - spawn(0) - M.close() - special_ops.readyreset()//Reset firealarm after the team launched. - //End Marauder launchpad. - - var/area/start_location = locate(/area/shuttle/specops/centcom) - var/area/end_location = locate(/area/shuttle/specops/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived to [station_name()]. Commence operation!") - - for(var/obj/machinery/computer/specops_shuttle/S in world) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_can_move() - if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) - return 0 - for(var/obj/machinery/computer/specops_shuttle/S in world) - if(world.timeofday <= S.specops_shuttle_timereset) - return 0 - return 1 - -/obj/machinery/computer/specops_shuttle/attack_ai(var/mob/user as mob) - to_chat(user, "Access Denied.") - return 1 - -/obj/machinery/computer/specops_shuttle/attackby(I as obj, user as mob, params) - if(istype(I,/obj/item/card/emag)) - to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") - else - return ..() - -/obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) - if(!allowed(user)) - to_chat(user, "Access Denied.") - return - -//Commented out so admins can do shenanigans at their leisure. Also makes the force-spawned admin ERTs able to use the shuttle. -// if(sent_strike_team == 0 && send_emergency_team == 0) -// to_chat(usr, "The strike team has not yet deployed.") -// return - - if(..()) - return - - user.machine = src - var/dat - if(temp) - dat = temp - else - dat += {"
    Special Operations Shuttle
    - \nLocation: [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([specops_shuttle_timeleft] seconds.)":specops_shuttle_at_station ? "Station":"Dock"]
    - [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.*
    \n
    ":specops_shuttle_at_station ? "\nShuttle standing by...
    \n
    ":"\nDepart to [station_name()]
    \n
    "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - -/obj/machinery/computer/specops_shuttle/Topic(href, href_list) - if(..()) - return 1 - - if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.machine = src - - if(href_list["sendtodock"]) - if(!specops_shuttle_at_station|| specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if(!specops_can_move()) - to_chat(usr, "Central Command will not allow the Special Operations shuttle to return yet.") - if(world.timeofday <= specops_shuttle_timereset) - if(((world.timeofday - specops_shuttle_timereset)/10) > 60) - to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") - to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") - return - - to_chat(usr, "The Special Operations shuttle will arrive at Central Command in [(SPECOPS_MOVETIME/10)] seconds.") - - temp += "Shuttle departing.

    OK" - updateUsrDialog() - - specops_shuttle_moving_to_centcom = 1 - specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME - spawn(0) - specops_return() - - else if(href_list["sendtostation"]) - if(specops_shuttle_at_station || specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if(!specops_can_move()) - to_chat(usr, "The Special Operations shuttle is unable to leave.") - return - - to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.") - - temp += "Shuttle departing.

    OK" - updateUsrDialog() - - var/area/centcom/specops/special_ops = locate() - if(special_ops) - special_ops.readyalert()//Trigger alarm for the spec ops area. - specops_shuttle_moving_to_station = 1 - - specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME - spawn(0) - specops_process() - - else if(href_list["mainmenu"]) - temp = null - - add_fingerprint(usr) - updateUsrDialog() - return +//Config stuff +#define SPECOPS_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. +#define SPECOPS_STATION_AREATYPE "/area/shuttle/specops/station" //Type of the spec ops shuttle area for station +#define SPECOPS_DOCK_AREATYPE "/area/shuttle/specops/centcom" //Type of the spec ops shuttle area for dock +#define SPECOPS_RETURN_DELAY 6000 //Time between the shuttle is capable of moving. + +GLOBAL_VAR_INIT(specops_shuttle_moving_to_station, 0) +GLOBAL_VAR_INIT(specops_shuttle_moving_to_centcom, 0) +GLOBAL_VAR_INIT(specops_shuttle_at_station, 0) +GLOBAL_VAR_INIT(specops_shuttle_can_send, 1) +GLOBAL_VAR_INIT(specops_shuttle_time, 0) +GLOBAL_VAR_INIT(specops_shuttle_timeleft, 0) + +/obj/machinery/computer/specops_shuttle + name = "\improper Spec. Ops. shuttle console" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "security_key" + icon_screen = "syndishuttle" + light_color = LIGHT_COLOR_PURE_CYAN + req_access = list(ACCESS_CENT_SPECOPS) +// req_access = list(ACCESS_CENT_SPECOPS) + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + var/specops_shuttle_timereset = 0 + +/proc/specops_return() + var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + announcer.config(list("Response Team" = 0)) + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN\""//Initial message shown. + if(announcer) + announcer.autosay(message, "A.L.I.C.E.", "Response Team", list(1,2)) + + while(GLOB.specops_shuttle_time - world.timeofday > 0) + var/ticksleft = GLOB.specops_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + GLOB.specops_shuttle_time = world.timeofday + 10 // midnight rollover + GLOB.specops_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(GLOB.specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" + if(rounded_time_left==0) + message = "\"ALERT: TAKEOFF\"" + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + GLOB.specops_shuttle_moving_to_station = 0 + GLOB.specops_shuttle_moving_to_centcom = 0 + + GLOB.specops_shuttle_at_station = 1 + + var/area/start_location = locate(/area/shuttle/specops/station) + var/area/end_location = locate(/area/shuttle/specops/centcom) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... + bug.gib() + + for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... + pest.gib() + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived at Central Command. Operation has ended!") + + GLOB.specops_shuttle_at_station = 0 + + for(var/obj/machinery/computer/specops_shuttle/S in world) + S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY + + qdel(announcer) + +/proc/specops_process() + var/area/centcom/specops/special_ops = locate()//Where is the specops area located? + var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + announcer.config(list("Response Team" = 0)) + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH\""//Initial message shown. + if(announcer) + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + //message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" + //announcer.autosay(message, "A.L.I.C.E.", "Response Team") + + while(GLOB.specops_shuttle_time - world.timeofday > 0) + var/ticksleft = GLOB.specops_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + GLOB.specops_shuttle_time = world.timeofday + 10 // midnight rollover + GLOB.specops_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(GLOB.specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" + if(rounded_time_left==0) + message = "\"ALERT: TAKEOFF\"" + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + GLOB.specops_shuttle_moving_to_station = 0 + GLOB.specops_shuttle_moving_to_centcom = 0 + + GLOB.specops_shuttle_at_station = 1 + if(GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom) return + + if(!specops_can_move()) + to_chat(usr, "The Special Operations shuttle is unable to leave.") + return + + //Begin Marauder launchpad. + spawn(0)//So it parallel processes it. + for(var/obj/machinery/door/poddoor/M in special_ops) + switch(M.id_tag) + if("ASSAULT0") + spawn(10)//1 second delay between each. + M.open() + if("ASSAULT1") + spawn(20) + M.open() + if("ASSAULT2") + spawn(30) + M.open() + if("ASSAULT3") + spawn(40) + M.open() + + sleep(10) + + var/spawn_marauder[] = new() + for(var/obj/effect/landmark/L in world) + if(L.name == "Marauder Entry") + spawn_marauder.Add(L.loc) + for(var/obj/effect/landmark/L in world) + if(L.name == "Marauder Exit") + var/obj/effect/portal/P = new(L.loc, pick(spawn_marauder)) + //P.invisibility = 101//So it is not seen by anyone. + P.failchance = 0//So it has no fail chance when teleporting. + spawn_marauder.Remove(P.target) + + sleep(10) + + for(var/obj/machinery/mass_driver/M in special_ops) + switch(M.id_tag) + if("ASSAULT0") + spawn(10) + M.drive() + if("ASSAULT1") + spawn(20) + M.drive() + if("ASSAULT2") + spawn(30) + M.drive() + if("ASSAULT3") + spawn(40) + M.drive() + + sleep(50)//Doors remain open for 5 seconds. + + for(var/obj/machinery/door/poddoor/M in special_ops) + switch(M.id_tag)//Doors close at the same time. + if("ASSAULT0") + spawn(0) + M.close() + if("ASSAULT1") + spawn(0) + M.close() + if("ASSAULT2") + spawn(0) + M.close() + if("ASSAULT3") + spawn(0) + M.close() + special_ops.readyreset()//Reset firealarm after the team launched. + //End Marauder launchpad. + + var/area/start_location = locate(/area/shuttle/specops/centcom) + var/area/end_location = locate(/area/shuttle/specops/station) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived to [station_name()]. Commence operation!") + + for(var/obj/machinery/computer/specops_shuttle/S in world) + S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY + + qdel(announcer) + +/proc/specops_can_move() + if(GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom) + return 0 + for(var/obj/machinery/computer/specops_shuttle/S in world) + if(world.timeofday <= S.specops_shuttle_timereset) + return 0 + return 1 + +/obj/machinery/computer/specops_shuttle/attack_ai(var/mob/user as mob) + to_chat(user, "Access Denied.") + return 1 + +/obj/machinery/computer/specops_shuttle/attackby(I as obj, user as mob, params) + if(istype(I,/obj/item/card/emag)) + to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") + else + return ..() + +/obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) + if(!allowed(user)) + to_chat(user, "Access Denied.") + return + +//Commented out so admins can do shenanigans at their leisure. Also makes the force-spawned admin ERTs able to use the shuttle. +// if(sent_strike_team == 0 && send_emergency_team == 0) +// to_chat(usr, "The strike team has not yet deployed.") +// return + + if(..()) + return + + user.machine = src + var/dat + if(temp) + dat = temp + else + dat += {"
    Special Operations Shuttle
    + \nLocation: [GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([GLOB.specops_shuttle_timeleft] seconds.)":GLOB.specops_shuttle_at_station ? "Station":"Dock"]
    + [GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.*
    \n
    ":GLOB.specops_shuttle_at_station ? "\nShuttle standing by...
    \n
    ":"\nDepart to [station_name()]
    \n
    "] + \nClose"} + + user << browse(dat, "window=computer;size=575x450") + onclose(user, "computer") + return + +/obj/machinery/computer/specops_shuttle/Topic(href, href_list) + if(..()) + return 1 + + if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.machine = src + + if(href_list["sendtodock"]) + if(!GLOB.specops_shuttle_at_station|| GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom) return + + if(!specops_can_move()) + to_chat(usr, "Central Command will not allow the Special Operations shuttle to return yet.") + if(world.timeofday <= specops_shuttle_timereset) + if(((world.timeofday - specops_shuttle_timereset)/10) > 60) + to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") + to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") + return + + to_chat(usr, "The Special Operations shuttle will arrive at Central Command in [(SPECOPS_MOVETIME/10)] seconds.") + + temp += "Shuttle departing.

    OK" + updateUsrDialog() + + GLOB.specops_shuttle_moving_to_centcom = 1 + GLOB.specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME + spawn(0) + specops_return() + + else if(href_list["sendtostation"]) + if(GLOB.specops_shuttle_at_station || GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom) return + + if(!specops_can_move()) + to_chat(usr, "The Special Operations shuttle is unable to leave.") + return + + to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.") + + temp += "Shuttle departing.

    OK" + updateUsrDialog() + + var/area/centcom/specops/special_ops = locate() + if(special_ops) + special_ops.readyalert()//Trigger alarm for the spec ops area. + GLOB.specops_shuttle_moving_to_station = 1 + + GLOB.specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME + spawn(0) + specops_process() + + else if(href_list["mainmenu"]) + temp = null + + add_fingerprint(usr) + updateUsrDialog() + return diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index 0170d32b313..607918442ea 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -1,53 +1,53 @@ - -/obj/machinery/computer/station_alert - name = "station alert console" - desc = "Used to access the station's automated alert system." - icon_keyboard = "tech_key" - icon_screen = "alert:0" - light_color = LIGHT_COLOR_CYAN - circuit = /obj/item/circuitboard/stationalert_engineering - var/datum/nano_module/alarm_monitor/alarm_monitor - var/monitor_type = /datum/nano_module/alarm_monitor/engineering - -/obj/machinery/computer/station_alert/security - monitor_type = /datum/nano_module/alarm_monitor/security - circuit = /obj/item/circuitboard/stationalert_security - -/obj/machinery/computer/station_alert/all - monitor_type = /datum/nano_module/alarm_monitor/all - circuit = /obj/item/circuitboard/stationalert_all - -/obj/machinery/computer/station_alert/New() - ..() - alarm_monitor = new monitor_type(src) - alarm_monitor.register(src, /obj/machinery/computer/station_alert/update_icon) - -/obj/machinery/computer/station_alert/Destroy() - alarm_monitor.unregister(src) - QDEL_NULL(alarm_monitor) - return ..() - -/obj/machinery/computer/station_alert/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - interact(user) - -/obj/machinery/computer/station_alert/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - interact(user) - -/obj/machinery/computer/station_alert/interact(mob/user) - alarm_monitor.ui_interact(user) - -/obj/machinery/computer/station_alert/update_icon() - if(alarm_monitor) - var/list/alarms = alarm_monitor.major_alarms() - if(alarms.len) - icon_screen = "alert:2" - else - icon_screen = "alert:0" - - ..() \ No newline at end of file + +/obj/machinery/computer/station_alert + name = "station alert console" + desc = "Used to access the station's automated alert system." + icon_keyboard = "tech_key" + icon_screen = "alert:0" + light_color = LIGHT_COLOR_CYAN + circuit = /obj/item/circuitboard/stationalert_engineering + var/datum/nano_module/alarm_monitor/alarm_monitor + var/monitor_type = /datum/nano_module/alarm_monitor/engineering + +/obj/machinery/computer/station_alert/security + monitor_type = /datum/nano_module/alarm_monitor/security + circuit = /obj/item/circuitboard/stationalert_security + +/obj/machinery/computer/station_alert/all + monitor_type = /datum/nano_module/alarm_monitor/all + circuit = /obj/item/circuitboard/stationalert_all + +/obj/machinery/computer/station_alert/New() + ..() + alarm_monitor = new monitor_type(src) + alarm_monitor.register(src, /obj/machinery/computer/station_alert/.proc/update_icon) + +/obj/machinery/computer/station_alert/Destroy() + alarm_monitor.unregister(src) + QDEL_NULL(alarm_monitor) + return ..() + +/obj/machinery/computer/station_alert/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + interact(user) + +/obj/machinery/computer/station_alert/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + interact(user) + +/obj/machinery/computer/station_alert/interact(mob/user) + alarm_monitor.ui_interact(user) + +/obj/machinery/computer/station_alert/update_icon() + if(alarm_monitor) + var/list/alarms = alarm_monitor.major_alarms() + if(alarms.len) + icon_screen = "alert:2" + else + icon_screen = "alert:0" + + ..() diff --git a/code/game/machinery/computer/store.dm b/code/game/machinery/computer/store.dm index f49d91c53b3..856e2322f71 100644 --- a/code/game/machinery/computer/store.dm +++ b/code/game/machinery/computer/store.dm @@ -102,11 +102,11 @@ th.cost.toomuch {background:maroon;} "} - for(var/datum/storeitem/item in centcomm_store.items) + for(var/datum/storeitem/item in GLOB.centcomm_store.items) var/cost_class="affordable" if(item.cost>balance) cost_class="toomuch" - var/itemID=centcomm_store.items.Find(item) + var/itemID=GLOB.centcomm_store.items.Find(item) var/row_color="light" if(itemID%2 == 0) row_color="dark" @@ -143,7 +143,7 @@ th.cost.toomuch {background:maroon;} if(href_list["buy"]) var/itemID = text2num(href_list["buy"]) - var/datum/storeitem/item = centcomm_store.items[itemID] + var/datum/storeitem/item = GLOB.centcomm_store.items[itemID] var/sure = alert(usr,"Are you sure you wish to purchase [item.name] for $[item.cost]?","You sure?","Yes","No") in list("Yes","No") if(!Adjacent(usr)) to_chat(usr, "You are not close enough to do that.") @@ -151,7 +151,7 @@ th.cost.toomuch {background:maroon;} if(sure=="No") updateUsrDialog() return - if(!centcomm_store.PlaceOrder(usr,itemID)) + if(!GLOB.centcomm_store.PlaceOrder(usr,itemID)) to_chat(usr, "Unable to charge your account.") else to_chat(usr, "You've successfully purchased the item. It should be in your hands or on the floor.") diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index 92ed453e71e..0a65b6be408 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -1,256 +1,256 @@ -//Config stuff -#define SYNDICATE_ELITE_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. -#define SYNDICATE_ELITE_STATION_AREATYPE "/area/shuttle/syndicate_elite/station" //Type of the spec ops shuttle area for station -#define SYNDICATE_ELITE_DOCK_AREATYPE "/area/shuttle/syndicate_elite/mothership" //Type of the spec ops shuttle area for dock - -var/syndicate_elite_shuttle_moving_to_station = 0 -var/syndicate_elite_shuttle_moving_to_mothership = 0 -var/syndicate_elite_shuttle_at_station = 0 -var/syndicate_elite_shuttle_can_send = 1 -var/syndicate_elite_shuttle_time = 0 -var/syndicate_elite_shuttle_timeleft = 0 - -/obj/machinery/computer/syndicate_elite_shuttle - name = "\improper Elite Syndicate Squad shuttle console" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "syndie_key" - icon_screen = "syndishuttle" - light_color = LIGHT_COLOR_PURE_CYAN - req_access = list(access_syndicate) - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - -/proc/syndicate_elite_process() - var/area/syndicate_mothership/control/syndicate_ship = locate()//To find announcer. This area should exist for this proc to work. - var/area/syndicate_mothership/elite_squad/elite_squad = locate()//Where is the specops area located? - var/mob/living/silicon/decoy/announcer = locate() in syndicate_ship//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "THE SYNDICATE ELITE SHUTTLE IS PREPARING FOR LAUNCH"//Initial message shown. - if(announcer) - announcer.say(message) - // message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" - // announcer.say(message) - - while(syndicate_elite_shuttle_time - world.timeofday > 0) - var/ticksleft = syndicate_elite_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - syndicate_elite_shuttle_time = world.timeofday // midnight rollover - syndicate_elite_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(syndicate_elite_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN" - if(rounded_time_left==0) - message = "ALERT: TAKEOFF" - announcer.say(message) - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - syndicate_elite_shuttle_moving_to_station = 0 - syndicate_elite_shuttle_moving_to_mothership = 0 - - syndicate_elite_shuttle_at_station = 1 - if(syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - - if(!syndicate_elite_can_move()) - to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") - return - - sleep(600) -/* - //Begin Marauder launchpad. - spawn(0)//So it parallel processes it. - for(var/obj/machinery/door/poddoor/M in elite_squad) - switch(M.id) - if("ASSAULT0") - spawn(10)//1 second delay between each. - M.open() - if("ASSAULT1") - spawn(20) - M.open() - if("ASSAULT2") - spawn(30) - M.open() - if("ASSAULT3") - spawn(40) - M.open() - - sleep(10) - - var/spawn_marauder[] = new() - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Marauder Entry") - spawn_marauder.Add(L) - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Marauder Exit") - var/obj/effect/portal/P = new(L.loc) - P.invisibility = 101//So it is not seen by anyone. - P.failchance = 0//So it has no fail chance when teleporting. - P.target = pick(spawn_marauder)//Where the marauder will arrive. - spawn_marauder.Remove(P.target) - - sleep(10) - - for(var/obj/machinery/mass_driver/M in elite_squad) - switch(M.id) - if("ASSAULT0") - spawn(10) - M.drive() - if("ASSAULT1") - spawn(20) - M.drive() - if("ASSAULT2") - spawn(30) - M.drive() - if("ASSAULT3") - spawn(40) - M.drive() - - sleep(50)//Doors remain open for 5 seconds. - - for(var/obj/machinery/door/poddoor/M in elite_squad) - switch(M.id)//Doors close at the same time. - if("ASSAULT0") - spawn(0) - M.close() - if("ASSAULT1") - spawn(0) - M.close() - if("ASSAULT2") - spawn(0) - M.close() - if("ASSAULT3") - spawn(0) - M.close() - */ - elite_squad.readyreset()//Reset firealarm after the team launched. - //End Marauder launchpad. - - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Syndicate Breach Area") - explosion(L.loc,4,6,8,10,0) - - sleep(40) - - - var/area/start_location = locate(/area/shuttle/syndicate_elite/mothership) - var/area/end_location = locate(/area/shuttle/syndicate_elite/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs = T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived to [station_name()]. Commence operation!") - -/proc/syndicate_elite_can_move() - if(syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return 0 - else return 1 - -/obj/machinery/computer/syndicate_elite_shuttle/attack_ai(var/mob/user as mob) - to_chat(user, "Access Denied.") - return 1 - -/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob, params) - if(istype(I,/obj/item/card/emag)) - to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") - else - return ..() - -/obj/machinery/computer/syndicate_elite_shuttle/attack_hand(var/mob/user as mob) - if(!allowed(user)) - to_chat(user, "Access Denied.") - return - -// if(sent_syndicate_strike_team == 0) -// to_chat(usr, "The strike team has not yet deployed.") -// return - - if(..()) - return - - user.set_machine(src) - var/dat - if(temp) - dat = temp - else - dat = {"
    Special Operations Shuttle
    - \nLocation: [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "Departing for [station_name()] in ([syndicate_elite_shuttle_timeleft] seconds.)":syndicate_elite_shuttle_at_station ? "Station":"Dock"]
    - [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "\n*The Syndicate Elite shuttle is already leaving.*
    \n
    ":syndicate_elite_shuttle_at_station ? "\nShuttle Offline
    \n
    ":"\nDepart to [station_name()]
    \n
    "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - -/obj/machinery/computer/syndicate_elite_shuttle/Topic(href, href_list) - if(..()) - return 1 - - if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - if(href_list["sendtodock"]) - if(!syndicate_elite_shuttle_at_station|| syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - - to_chat(usr, "The Syndicate will not allow the Elite Squad shuttle to return.") - return - - else if(href_list["sendtostation"]) - if(syndicate_elite_shuttle_at_station || syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - - if(!specops_can_move()) - to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") - return - - to_chat(usr, "The Syndicate Elite shuttle will arrive on [station_name()] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds.") - - temp = "Shuttle departing.

    OK" - updateUsrDialog() - - var/area/syndicate_mothership/elite_squad/elite_squad = locate() - if(elite_squad) - elite_squad.readyalert()//Trigger alarm for the spec ops area. - syndicate_elite_shuttle_moving_to_station = 1 - - syndicate_elite_shuttle_time = world.timeofday + SYNDICATE_ELITE_MOVETIME - spawn(0) - syndicate_elite_process() - - - else if(href_list["mainmenu"]) - temp = null - - add_fingerprint(usr) - updateUsrDialog() - return +//Config stuff +#define SYNDICATE_ELITE_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. +#define SYNDICATE_ELITE_STATION_AREATYPE "/area/shuttle/syndicate_elite/station" //Type of the spec ops shuttle area for station +#define SYNDICATE_ELITE_DOCK_AREATYPE "/area/shuttle/syndicate_elite/mothership" //Type of the spec ops shuttle area for dock + +GLOBAL_VAR_INIT(syndicate_elite_shuttle_moving_to_station, 0) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_moving_to_mothership, 0) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_at_station, 0) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_can_send, 1) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_time, 0) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_timeleft, 0) + +/obj/machinery/computer/syndicate_elite_shuttle + name = "\improper Elite Syndicate Squad shuttle console" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "syndie_key" + icon_screen = "syndishuttle" + light_color = LIGHT_COLOR_PURE_CYAN + req_access = list(ACCESS_SYNDICATE) + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + +/proc/syndicate_elite_process() + var/area/syndicate_mothership/control/syndicate_ship = locate()//To find announcer. This area should exist for this proc to work. + var/area/syndicate_mothership/elite_squad/elite_squad = locate()//Where is the specops area located? + var/mob/living/silicon/decoy/announcer = locate() in syndicate_ship//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "THE SYNDICATE ELITE SHUTTLE IS PREPARING FOR LAUNCH"//Initial message shown. + if(announcer) + announcer.say(message) + // message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" + // announcer.say(message) + + while(GLOB.syndicate_elite_shuttle_time - world.timeofday > 0) + var/ticksleft = GLOB.syndicate_elite_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + GLOB.syndicate_elite_shuttle_time = world.timeofday // midnight rollover + GLOB.syndicate_elite_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(GLOB.syndicate_elite_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN" + if(rounded_time_left==0) + message = "ALERT: TAKEOFF" + announcer.say(message) + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + GLOB.syndicate_elite_shuttle_moving_to_station = 0 + GLOB.syndicate_elite_shuttle_moving_to_mothership = 0 + + GLOB.syndicate_elite_shuttle_at_station = 1 + if(GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership) return + + if(!syndicate_elite_can_move()) + to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") + return + + sleep(600) +/* + //Begin Marauder launchpad. + spawn(0)//So it parallel processes it. + for(var/obj/machinery/door/poddoor/M in elite_squad) + switch(M.id) + if("ASSAULT0") + spawn(10)//1 second delay between each. + M.open() + if("ASSAULT1") + spawn(20) + M.open() + if("ASSAULT2") + spawn(30) + M.open() + if("ASSAULT3") + spawn(40) + M.open() + + sleep(10) + + var/spawn_marauder[] = new() + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Marauder Entry") + spawn_marauder.Add(L) + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Marauder Exit") + var/obj/effect/portal/P = new(L.loc) + P.invisibility = 101//So it is not seen by anyone. + P.failchance = 0//So it has no fail chance when teleporting. + P.target = pick(spawn_marauder)//Where the marauder will arrive. + spawn_marauder.Remove(P.target) + + sleep(10) + + for(var/obj/machinery/mass_driver/M in elite_squad) + switch(M.id) + if("ASSAULT0") + spawn(10) + M.drive() + if("ASSAULT1") + spawn(20) + M.drive() + if("ASSAULT2") + spawn(30) + M.drive() + if("ASSAULT3") + spawn(40) + M.drive() + + sleep(50)//Doors remain open for 5 seconds. + + for(var/obj/machinery/door/poddoor/M in elite_squad) + switch(M.id)//Doors close at the same time. + if("ASSAULT0") + spawn(0) + M.close() + if("ASSAULT1") + spawn(0) + M.close() + if("ASSAULT2") + spawn(0) + M.close() + if("ASSAULT3") + spawn(0) + M.close() + */ + elite_squad.readyreset()//Reset firealarm after the team launched. + //End Marauder launchpad. + + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Syndicate Breach Area") + explosion(L.loc,4,6,8,10,0) + + sleep(40) + + + var/area/start_location = locate(/area/shuttle/syndicate_elite/mothership) + var/area/end_location = locate(/area/shuttle/syndicate_elite/station) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs = T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... + bug.gib() + + for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... + pest.gib() + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived to [station_name()]. Commence operation!") + +/proc/syndicate_elite_can_move() + if(GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership) return 0 + else return 1 + +/obj/machinery/computer/syndicate_elite_shuttle/attack_ai(var/mob/user as mob) + to_chat(user, "Access Denied.") + return 1 + +/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob, params) + if(istype(I,/obj/item/card/emag)) + to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") + else + return ..() + +/obj/machinery/computer/syndicate_elite_shuttle/attack_hand(var/mob/user as mob) + if(!allowed(user)) + to_chat(user, "Access Denied.") + return + +// if(sent_syndicate_strike_team == 0) +// to_chat(usr, "The strike team has not yet deployed.") +// return + + if(..()) + return + + user.set_machine(src) + var/dat + if(temp) + dat = temp + else + dat = {"
    Special Operations Shuttle
    + \nLocation: [GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership ? "Departing for [station_name()] in ([GLOB.syndicate_elite_shuttle_timeleft] seconds.)":GLOB.syndicate_elite_shuttle_at_station ? "Station":"Dock"]
    + [GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership ? "\n*The Syndicate Elite shuttle is already leaving.*
    \n
    ":GLOB.syndicate_elite_shuttle_at_station ? "\nShuttle Offline
    \n
    ":"\nDepart to [station_name()]
    \n
    "] + \nClose"} + + user << browse(dat, "window=computer;size=575x450") + onclose(user, "computer") + return + +/obj/machinery/computer/syndicate_elite_shuttle/Topic(href, href_list) + if(..()) + return 1 + + if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if(href_list["sendtodock"]) + if(!GLOB.syndicate_elite_shuttle_at_station|| GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership) return + + to_chat(usr, "The Syndicate will not allow the Elite Squad shuttle to return.") + return + + else if(href_list["sendtostation"]) + if(GLOB.syndicate_elite_shuttle_at_station || GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership) return + + if(!specops_can_move()) + to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") + return + + to_chat(usr, "The Syndicate Elite shuttle will arrive on [station_name()] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds.") + + temp = "Shuttle departing.

    OK" + updateUsrDialog() + + var/area/syndicate_mothership/elite_squad/elite_squad = locate() + if(elite_squad) + elite_squad.readyalert()//Trigger alarm for the spec ops area. + GLOB.syndicate_elite_shuttle_moving_to_station = 1 + + GLOB.syndicate_elite_shuttle_time = world.timeofday + SYNDICATE_ELITE_MOVETIME + spawn(0) + syndicate_elite_process() + + + else if(href_list["mainmenu"]) + temp = null + + add_fingerprint(usr) + updateUsrDialog() + return diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index b051ae34ca9..efee10767ba 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -1,1091 +1,1091 @@ -/obj/machinery/constructable_frame //Made into a seperate type to make future revisions easier. - name = "machine frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "box_0" - density = 1 - anchored = 1 - use_power = NO_POWER_USE - max_integrity = 250 - var/obj/item/circuitboard/circuit = null - var/list/components = null - var/list/req_components = null - var/list/req_component_names = null // user-friendly names of components - var/state = 1 - - // For pods - var/list/connected_parts = list() - var/pattern_idx=0 - -/obj/machinery/constructable_frame/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc, 5) - if(state >= 2) - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil(loc) - A.amount = 5 - if(circuit) - circuit.forceMove(loc) - circuit = null - return ..() - -/obj/machinery/constructable_frame/obj_break(damage_flag) - deconstruct() - -// unfortunately, we have to instance the objects really quickly to get the names -// fortunately, this is only called once when the board is added and the items are immediately GC'd -// and none of the parts do much in their constructors -/obj/machinery/constructable_frame/proc/update_namelist() - if(!req_components) - return - - req_component_names = new() - for(var/tname in req_components) - var/path = tname - var/obj/O = new path() - req_component_names[tname] = O.name - -/obj/machinery/constructable_frame/proc/get_req_components_amt() - var/amt = 0 - for(var/path in req_components) - amt += req_components[path] - return amt - -// update description of required components remaining -/obj/machinery/constructable_frame/proc/update_req_desc() - if(!req_components || !req_component_names) - return - - var/hasContent = 0 - desc = "Requires" - for(var/i = 1 to req_components.len) - var/tname = req_components[i] - var/amt = req_components[tname] - if(amt == 0) - continue - var/use_and = i == req_components.len - desc += "[(hasContent ? (use_and ? ", and" : ",") : "")] [amt] [amt == 1 ? req_component_names[tname] : "[req_component_names[tname]]\s"]" - hasContent = 1 - - if(!hasContent) - desc = "Does not require any more components." - else - desc += "." - -/obj/machinery/constructable_frame/machine_frame/attackby(obj/item/P, mob/user, params) - switch(state) - if(1) - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) - playsound(src.loc, C.usesound, 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 1 && C.amount >= 5 && C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 2 - icon_state = "box_1" - else - to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") - return - else - to_chat(user, "You need five lengths of cable to wire the frame.") - return - - if(istype(P, /obj/item/wrench)) - playsound(src.loc, P.usesound, 75, 1) - to_chat(user, "You dismantle the frame.") - deconstruct(TRUE) - return - if(2) - if(istype(P, /obj/item/circuitboard)) - var/obj/item/circuitboard/B = P - if(B.board_type == "machine") - playsound(src.loc, B.usesound, 50, 1) - to_chat(user, "You add the circuit board to the frame.") - circuit = P - user.drop_item() - P.loc = src - icon_state = "box_2" - state = 3 - components = list() - req_components = circuit.req_components.Copy() - update_namelist() - update_req_desc() - else - to_chat(user, "This frame does not accept circuit boards of this type!") - return - if(istype(P, /obj/item/wirecutters)) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 1 - icon_state = "box_0" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil(src.loc,5) - A.amount = 5 - return - if(3) - if(istype(P, /obj/item/crowbar)) - playsound(src.loc, P.usesound, 50, 1) - state = 2 - circuit.loc = src.loc - circuit = null - if(components.len == 0) - to_chat(user, "You remove the circuit board.") - else - to_chat(user, "You remove the circuit board and other components.") - for(var/obj/item/I in components) - I.loc = src.loc - desc = initial(desc) - req_components = null - components = null - icon_state = "box_1" - return - - if(istype(P, /obj/item/screwdriver)) - var/component_check = 1 - for(var/R in req_components) - if(req_components[R] > 0) - component_check = 0 - break - if(component_check) - playsound(src.loc, P.usesound, 50, 1) - var/obj/machinery/new_machine = new src.circuit.build_path(src.loc) - new_machine.on_construction() - for(var/obj/O in new_machine.component_parts) - qdel(O) - new_machine.component_parts = list() - for(var/obj/O in src) - O.loc = null - new_machine.component_parts += O - circuit.loc = null - new_machine.RefreshParts() - qdel(src) - return - - if(istype(P, /obj/item/storage/part_replacer) && P.contents.len && get_req_components_amt()) - var/obj/item/storage/part_replacer/replacer = P - var/list/added_components = list() - var/list/part_list = list() - - //Assemble a list of current parts, then sort them by their rating! - for(var/obj/item/stock_parts/co in replacer) - part_list += co - - for(var/path in req_components) - while(req_components[path] > 0 && (locate(path) in part_list)) - var/obj/item/part = (locate(path) in part_list) - added_components[part] = path - replacer.remove_from_storage(part, src) - req_components[path]-- - part_list -= part - - for(var/obj/item/stock_parts/part in added_components) - components += part - to_chat(user, "[part.name] applied.") - replacer.play_rped_sound() - - update_req_desc() - return - - if(istype(P, /obj/item)) - var/success - for(var/I in req_components) - if(istype(P, I) && (req_components[I] > 0) && (!(P.flags & NODROP) || istype(P, /obj/item/stack))) - success=1 - playsound(src.loc, P.usesound, 50, 1) - if(istype(P, /obj/item/stack)) - var/obj/item/stack/S = P - var/camt = min(S.amount, req_components[I]) - var/obj/item/stack/NS = new P.type(src) - NS.amount = camt - NS.update_icon() - S.use(camt) - components += NS - req_components[I] -= camt - update_req_desc() - break - user.drop_item() - P.forceMove(src) - components += P - req_components[I]-- - update_req_desc() - return 1 - if(!success) - to_chat(user, "You cannot add that to the machine!") - return 0 - return - if(user.a_intent == INTENT_HARM) - return ..() - - -//Machine Frame Circuit Boards -/*Common Parts: Parts List: Ignitor, Timer, Infra-red laser, Infra-red sensor, t_scanner, Capacitor, Valve, sensor unit, -micro-manipulator, console screen, beaker, Microlaser, matter bin, power cells. -Note: Once everything is added to the public areas, will add MAT_METAL and MAT_GLASS to circuit boards since autolathe won't be able -to destroy them and players will be able to make replacements. -*/ -/obj/item/circuitboard/vendor - name = "circuit board (Booze-O-Mat Vendor)" - board_type = "machine" - origin_tech = "programming=1" - frame_desc = "Requires 1 Resupply Canister." - build_path = /obj/machinery/vending/boozeomat - req_components = list(/obj/item/vending_refill/boozeomat = 1) - - var/static/list/vending_names_paths = list( - /obj/machinery/vending/boozeomat = "Booze-O-Mat", - /obj/machinery/vending/coffee = "Solar's Best Hot Drinks", - /obj/machinery/vending/snack = "Getmore Chocolate Corp", - /obj/machinery/vending/chinese = "Mr. Chang", - /obj/machinery/vending/cola = "Robust Softdrinks", - /obj/machinery/vending/cigarette = "ShadyCigs Deluxe", - /obj/machinery/vending/hatdispenser = "Hatlord 9000", - /obj/machinery/vending/suitdispenser = "Suitlord 9000", - /obj/machinery/vending/shoedispenser = "Shoelord 9000", - /obj/machinery/vending/clothing = "ClothesMate", - /obj/machinery/vending/medical = "NanoMed Plus", - /obj/machinery/vending/wallmed = "NanoMed", - /obj/machinery/vending/assist = "Vendomat", - /obj/machinery/vending/engivend = "Engi-Vend", - /obj/machinery/vending/hydronutrients = "NutriMax", - /obj/machinery/vending/hydroseeds = "MegaSeed Servitor", - /obj/machinery/vending/sustenance = "Sustenance Vendor", - /obj/machinery/vending/dinnerware = "Plasteel Chef's Dinnerware Vendor", - /obj/machinery/vending/cart = "PTech", - /obj/machinery/vending/robotics = "Robotech Deluxe", - /obj/machinery/vending/engineering = "Robco Tool Maker", - /obj/machinery/vending/sovietsoda = "BODA", - /obj/machinery/vending/security = "SecTech", - /obj/machinery/vending/modularpc = "Deluxe Silicate Selections", - /obj/machinery/vending/crittercare = "CritterCare") - -/obj/item/circuitboard/vendor/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/static/list/display_vending_names_paths - if(!display_vending_names_paths) - display_vending_names_paths = list() - for(var/path in vending_names_paths) - display_vending_names_paths[vending_names_paths[path]] = path - var/choice = input(user, "Choose a new brand","Select an Item") as null|anything in display_vending_names_paths - if(loc != user) - to_chat(user, "You need to keep [src] in your hands while doing that!") - return - set_type(display_vending_names_paths[choice]) - -/obj/item/circuitboard/vendor/proc/set_type(obj/machinery/vending/typepath) - build_path = typepath - name = "circuit board ([vending_names_paths[build_path]] Vendor)" - req_components = list(initial(typepath.refill_canister) = 1) - -/obj/item/circuitboard/smes - name = "circuit board (SMES)" - build_path = /obj/machinery/power/smes - board_type = "machine" - origin_tech = "programming=3;powerstorage=3;engineering=3" - frame_desc = "Requires 5 pieces of cable, 5 Power Cells and 1 Capacitor." - req_components = list( - /obj/item/stack/cable_coil = 5, - /obj/item/stock_parts/cell = 5, - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/emitter - name = "circuit board (Emitter)" - build_path = /obj/machinery/power/emitter - board_type = "machine" - origin_tech = "programming=3;powerstorage=4;engineering=4" - req_components = list( - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/power_compressor - name = "circuit board (Power Compressor)" - build_path = /obj/machinery/power/compressor - board_type = "machine" - origin_tech = "programming=4;powerstorage=4;engineering=4" - req_components = list( - /obj/item/stack/cable_coil = 5, - /obj/item/stock_parts/manipulator = 6) - -/obj/item/circuitboard/power_turbine - name = "circuit board (Power Turbine)" - build_path = /obj/machinery/power/turbine - board_type = "machine" - origin_tech = "programming=4;powerstorage=4;engineering=4" - req_components = list( - /obj/item/stack/cable_coil = 5, - /obj/item/stock_parts/capacitor = 6) - -/obj/item/circuitboard/thermomachine - name = "circuit board (Freezer)" - desc = "Use screwdriver to switch between heating and cooling modes." - build_path = /obj/machinery/atmospherics/unary/cold_sink/freezer - board_type = "machine" - origin_tech = "programming=3;plasmatech=3" - frame_desc = "Requires 2 Matter Bins, 2 Micro Lasers, 1 piece of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/micro_laser = 2, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/thermomachine/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) - if(build_path == /obj/machinery/atmospherics/unary/cold_sink/freezer) - build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater - name = "circuit board (Heater)" - to_chat(user, "You set the board to heating.") - else - build_path = /obj/machinery/atmospherics/unary/cold_sink/freezer - name = "circuit board (Freezer)" - to_chat(user, "You set the board to cooling.") - return - return ..() - -/obj/item/circuitboard/recharger - name = "circuit board (Recharger)" - build_path = /obj/machinery/recharger - board_type = "machine" - origin_tech = "powerstorage=3;materials=2" - frame_desc = "Requires 1 Capacitor" - req_components = list(/obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/snow_machine - name = "circuit board (snow machine)" - build_path = /obj/machinery/snow_machine - board_type = "machine" - origin_tech = "programming=2;materials=2" - frame_desc = "Requires 1 Matter Bin and 1 Micro Laser." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/micro_laser = 1) - -/obj/item/circuitboard/biogenerator - name = "circuit board (Biogenerator)" - build_path = /obj/machinery/biogenerator - board_type = "machine" - origin_tech = "programming=2;biotech=3;materials=3" - frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/plantgenes - name = "Plant DNA Manipulator (Machine Board)" - build_path = /obj/machinery/plantgenes - board_type = "machine" - origin_tech = "programming=3;biotech=3" - frame_desc = "Requires 1 Manipulator, 1 Micro Laser, 1 Console Screen, and 1 Scanning Module." - req_components = list( - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1, - /obj/item/stock_parts/scanning_module = 1) - -/obj/item/circuitboard/plantgenes/vault - -/obj/item/circuitboard/seed_extractor - name = "circuit board (Seed Extractor)" - build_path = /obj/machinery/seed_extractor - board_type = "machine" - origin_tech = "programming=1" - frame_desc = "Requires 1 Matter Bin and 1 Manipulator." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/hydroponics - name = "circuit board (Hydroponics Tray)" - build_path = /obj/machinery/hydroponics/constructable - board_type = "machine" - origin_tech = "programming=1;biotech=2" - frame_desc = "Requires 2 Matter Bins, 1 Manipulator, and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/microwave - name = "circuit board (Microwave)" - build_path = /obj/machinery/kitchen_machine/microwave - board_type = "machine" - origin_tech = "programming=2;magnets=2" - frame_desc = "Requires 1 Micro Laser, 2 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/cable_coil = 2, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/oven - name = "circuit board (Oven)" - build_path = /obj/machinery/kitchen_machine/oven - board_type = "machine" - origin_tech = "programming=2;magnets=2" - frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/micro_laser = 2, - /obj/item/stack/cable_coil = 5, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/grill - name = "circuit board (Grill)" - build_path = /obj/machinery/kitchen_machine/grill - board_type = "machine" - origin_tech = "programming=2;magnets=2" - frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/micro_laser = 2, - /obj/item/stack/cable_coil = 5, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/candy_maker - name = "circuit board (Candy Maker)" - build_path = /obj/machinery/kitchen_machine/candy_maker - board_type = "machine" - origin_tech = "programming=2;magnets=2" - frame_desc = "Requires 1 Manipulator, 5 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 5, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/deepfryer - name = "circuit board (Deep Fryer)" - build_path = /obj/machinery/cooker/deepfryer - board_type = "machine" - origin_tech = "programming=1" - frame_desc = "Requires 2 Micro Lasers and 5 pieces of cable." - req_components = list( - /obj/item/stock_parts/micro_laser = 2, - /obj/item/stack/cable_coil = 5) - -/obj/item/circuitboard/gibber - name = "circuit board (Gibber)" - build_path = /obj/machinery/gibber - board_type = "machine" - origin_tech = "programming=2;engineering=2" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/tesla_coil - name = "circuit board (Tesla Coil)" - build_path = /obj/machinery/power/tesla_coil - board_type = "machine" - origin_tech = "programming=3;magnets=3;powerstorage=3" - req_components = list( - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/grounding_rod - name = "circuit board (Grounding Rod)" - build_path = /obj/machinery/power/grounding_rod - board_type = "machine" - origin_tech = "programming=3;powerstorage=3;magnets=3;plasmatech=2" - req_components = list( - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/processor - name = "circuit board (Food processor)" - build_path = /obj/machinery/processor - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/recycler - name = "circuit board (Recycler)" - build_path = /obj/machinery/recycler - board_type = "machine" - origin_tech = "programming=2;engineering=2" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/smartfridge - name = "circuit board (Smartfridge)" - build_path = /obj/machinery/smartfridge - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/matter_bin = 1) - var/list/fridge_names_paths = list( - "\improper SmartFridge" = /obj/machinery/smartfridge, - "\improper MegaSeed Servitor" = /obj/machinery/smartfridge/seeds, - "\improper Refrigerated Medicine Storage" = /obj/machinery/smartfridge/medbay, - "\improper Slime Extract Storage" = /obj/machinery/smartfridge/secure/extract, - "\improper Secure Refrigerated Medicine Storage" = /obj/machinery/smartfridge/secure/medbay, - "\improper Smart Chemical Storage" = /obj/machinery/smartfridge/secure/chemistry, - "smart virus storage" = /obj/machinery/smartfridge/secure/chemistry/virology, - "\improper Drink Showcase" = /obj/machinery/smartfridge/drinks - ) - - - -/obj/item/circuitboard/smartfridge/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) - set_type(null, user) - return - return ..() - -/obj/item/circuitboard/smartfridge/proc/set_type(typepath, mob/user) - var/new_name = "" - if(!typepath) - new_name = input("Circuit Setting", "What would you change the board setting to?") in fridge_names_paths - typepath = fridge_names_paths[new_name] - else - for(var/name in fridge_names_paths) - if(fridge_names_paths[name] == typepath) - new_name = name - break - build_path = typepath - name = new_name - if(findtextEx(new_name, "\improper")) - new_name = replacetext(new_name, "\improper", "") - if(user) - to_chat(user, "You set the board to [new_name].") - -/obj/item/circuitboard/monkey_recycler - name = "circuit board (Monkey Recycler)" - build_path = /obj/machinery/monkey_recycler - board_type = "machine" - origin_tech = "programming=1;biotech=2" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/holopad - name = "circuit board (AI Holopad)" - build_path = /obj/machinery/hologram/holopad - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/chem_dispenser - name = "circuit board (Chem Dispenser)" - build_path = /obj/machinery/chem_dispenser - board_type = "machine" - origin_tech = "materials=4;programming=4;plasmatech=4;biotech=3" - frame_desc = "Requires 2 Matter Bins, 1 Capacitor, 1 Manipulator, 1 Console Screen, and 1 Power Cell." - req_components = list( /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/capacitor = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/sheet/glass = 1, - /obj/item/stock_parts/cell = 1) - -/obj/item/circuitboard/chem_master - name = "circuit board (ChemMaster 3000)" - build_path = /obj/machinery/chem_master - board_type = "machine" - origin_tech = "materials=3;programming=2;biotech=3" - req_components = list( - /obj/item/reagent_containers/glass/beaker = 2, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/chem_master/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/new_name = "ChemMaster" - var/new_path = /obj/machinery/chem_master - - if(build_path == /obj/machinery/chem_master) - new_name = "CondiMaster" - new_path = /obj/machinery/chem_master/condimaster - - build_path = new_path - name = "circuit board ([new_name] 3000)" - to_chat(user, "You change the circuit board setting to \"[new_name]\".") - -/obj/item/circuitboard/chem_master/condi_master - name = "circuit board (CondiMaster 3000)" - build_path = /obj/machinery/chem_master/condimaster - -/obj/item/circuitboard/chem_heater - name = "circuit board (Chemical Heater)" - build_path = /obj/machinery/chem_heater - board_type = "machine" - origin_tech = "programming=2;engineering=2;biotech=2" - frame_desc = "Requires 1 Micro Laser and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/reagentgrinder - name = "circuit board (All-In-One Grinder)" - build_path = /obj/machinery/reagentgrinder/empty - board_type = "machine" - origin_tech = "materials=2;engineering=2;biotech=2" - frame_desc = "Requires 2 Manipulators and 1 Matter Bin." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/matter_bin = 1) - -//Almost the same recipe as destructive analyzer to give people choices. -/obj/item/circuitboard/experimentor - name = "circuit board (E.X.P.E.R.I-MENTOR)" - build_path = /obj/machinery/r_n_d/experimentor - board_type = "machine" - origin_tech = "magnets=1;engineering=1;programming=1;biotech=1;bluespace=2" - req_components = list( - /obj/item/stock_parts/scanning_module = 1, - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/micro_laser = 2) - -/obj/item/circuitboard/destructive_analyzer - name = "Circuit board (Destructive Analyzer)" - build_path = /obj/machinery/r_n_d/destructive_analyzer - board_type = "machine" - origin_tech = "magnets=2;engineering=2;programming=2" - frame_desc = "Requires 1 Scanning Module, 1 Manipulator, and 1 Micro-Laser." - req_components = list( - /obj/item/stock_parts/scanning_module = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1) - -/obj/item/circuitboard/autolathe - name = "Circuit board (Autolathe)" - build_path = /obj/machinery/autolathe - board_type = "machine" - origin_tech = "engineering=2;programming=2" - frame_desc = "Requires 3 Matter Bins, 1 Manipulator, and 1 Console Screen." - 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/protolathe - name = "Circuit board (Protolathe)" - build_path = /obj/machinery/r_n_d/protolathe - board_type = "machine" - origin_tech = "engineering=2;programming=2" - frame_desc = "Requires 2 Matter Bins, 2 Manipulators, and 2 Beakers." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/manipulator = 2, - /obj/item/reagent_containers/glass/beaker = 2) - -/obj/item/circuitboard/chem_dispenser/soda - name = "Circuit board (Soda Machine)" - build_path = /obj/machinery/chem_dispenser/soda - -/obj/item/circuitboard/chem_dispenser/beer - name = "Circuit board (Beer Machine)" - build_path = /obj/machinery/chem_dispenser/beer - -/obj/item/circuitboard/circuit_imprinter - name = "Circuit board (Circuit Imprinter)" - build_path = /obj/machinery/r_n_d/circuit_imprinter - board_type = "machine" - origin_tech = "engineering=2;programming=2" - frame_desc = "Requires 1 Matter Bin, 1 Manipulator, and 2 Beakers." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/reagent_containers/glass/beaker = 2) - -/obj/item/circuitboard/pacman - name = "Circuit Board (PACMAN-type Generator)" - build_path = /obj/machinery/power/port_gen/pacman - board_type = "machine" - origin_tech = "programming=2;powerstorage=3;plasmatech=3;engineering=3" - frame_desc = "Requires 1 Matter Bin, 1 Micro-Laser, 2 Pieces of Cable, and 1 Capacitor." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/pacman/super - name = "Circuit Board (SUPERPACMAN-type Generator)" - build_path = /obj/machinery/power/port_gen/pacman/super - origin_tech = "programming=3;powerstorage=4;engineering=4" - -/obj/item/circuitboard/pacman/mrs - name = "Circuit Board (MRSPACMAN-type Generator)" - build_path = /obj/machinery/power/port_gen/pacman/mrs - origin_tech = "programming=3;powerstorage=4;engineering=4;plasmatech=4" - -/obj/item/circuitboard/rdserver - name = "Circuit Board (R&D Server)" - build_path = /obj/machinery/r_n_d/server - board_type = "machine" - origin_tech = "programming=3" - frame_desc = "Requires 2 pieces of cable, and 1 Scanning Module." - req_components = list( - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/scanning_module = 1) - -/obj/item/circuitboard/mechfab - name = "Circuit board (Exosuit Fabricator)" - build_path = /obj/machinery/mecha_part_fabricator - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Matter Bins, 1 Manipulator, 1 Micro-Laser and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/podfab - name = "Circuit board (Spacepod Fabricator)" - build_path = /obj/machinery/mecha_part_fabricator/spacepod - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Matter Bins, 1 Manipulators, 1 Micro-Lasers, and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1) - - -/obj/item/circuitboard/clonepod - name = "Circuit board (Clone Pod)" - build_path = /obj/machinery/clonepod - board_type = "machine" - origin_tech = "programming=2;biotech=2" - frame_desc = "Requires 2 Manipulator, 2 Scanning Module, 2 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/scanning_module = 2, - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/clonescanner - name = "Circuit board (Cloning Scanner)" - build_path = /obj/machinery/dna_scannernew - board_type = "machine" - origin_tech = "programming=2;biotech=2" - frame_desc = "Requires 1 Scanning Module, 1 Manipulator, 1 Micro-Laser, 2 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/scanning_module = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1, - /obj/item/stack/cable_coil = 2,) - -/obj/item/circuitboard/mech_recharger - name = "circuit board (Mech Bay Recharger)" - build_path = /obj/machinery/mech_bay_recharge_port - board_type = "machine" - origin_tech = "programming=3;powerstorage=3;engineering=3" - frame_desc = "Requires 1 piece of cable and 5 Capacitors." - req_components = list( - /obj/item/stack/cable_coil = 1, - /obj/item/stock_parts/capacitor = 5) - -/obj/item/circuitboard/teleporter_hub - name = "circuit board (Teleporter Hub)" - build_path = /obj/machinery/teleport/hub - board_type = "machine" - origin_tech = "programming=3;engineering=4;bluespace=4;materials=4" - frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 3, - /obj/item/stock_parts/matter_bin = 1) - -/obj/item/circuitboard/teleporter_station - name = "circuit board (Teleporter Station)" - build_path = /obj/machinery/teleport/station - board_type = "machine" - origin_tech = "programming=4;engineering=4;bluespace=4;plasmatech=3" - frame_desc = "Requires 2 Bluespace Crystals, 2 Capacitors and 1 Console Screen." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 2, - /obj/item/stock_parts/capacitor = 2, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/teleporter_perma - name = "circuit board (Permanent Teleporter)" - build_path = /obj/machinery/teleport/perma - board_type = "machine" - origin_tech = "programming=3;engineering=4;bluespace=4;materials=4" - frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 3, - /obj/item/stock_parts/matter_bin = 1) - var/target - -/obj/item/circuitboard/teleporter_perma/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/gps)) - var/obj/item/gps/L = I - if(L.locked_location) - target = get_turf(L.locked_location) - to_chat(user, "You upload the data from [L]") - return - return ..() - -/obj/item/circuitboard/telesci_pad - name = "Circuit board (Telepad)" - build_path = /obj/machinery/telepad - board_type = "machine" - origin_tech = "programming=4;engineering=3;plasmatech=4;bluespace=4" - frame_desc = "Requires 2 Bluespace Crystals, 1 Capacitor, 1 piece of cable and 1 Console Screen." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 2, - /obj/item/stock_parts/capacitor = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/quantumpad - name = "circuit board (Quantum Pad)" - build_path = /obj/machinery/quantumpad - board_type = "machine" - origin_tech = "programming=3;engineering=3;plasmatech=3;bluespace=4" - frame_desc = "Requires 1 Bluespace Crystal, 1 Capacitor, 1 piece of cable and 1 Manipulator." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 1, - /obj/item/stock_parts/capacitor = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 1) - -/obj/item/circuitboard/sleeper - name = "circuit board (Sleeper)" - build_path = /obj/machinery/sleeper - board_type = "machine" - origin_tech = "programming=3;biotech=2;engineering=3" - frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 2 Console Screens." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 2) - -/obj/item/circuitboard/sleeper/syndicate - name = "circuit board (Sleeper Syndicate)" - build_path = /obj/machinery/sleeper/syndie - -/obj/item/circuitboard/sleeper/survival - name = "circuit board (Sleeper Survival Pod)" - build_path = /obj/machinery/sleeper/survival_pod - - -/obj/item/circuitboard/bodyscanner - name = "circuit board (Body Scanner)" - build_path = /obj/machinery/bodyscanner - board_type = "machine" - origin_tech = "programming=3;biotech=2;engineering=3" - frame_desc = "Requires 1 Scanning Module, 2 pieces of cable and 2 Console Screens." - req_components = list( - /obj/item/stock_parts/scanning_module = 1, - /obj/item/stack/cable_coil = 2, - /obj/item/stack/sheet/glass = 2) - -/obj/item/circuitboard/cryo_tube - name = "circuit board (Cryotube)" - build_path = /obj/machinery/atmospherics/unary/cryo_cell - board_type = "machine" - origin_tech = "programming=4;biotech=3;engineering=4;plasmatech=3" - frame_desc = "Requires 1 Matter Bin, 1 piece of cable and 4 Console Screens." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 4) - -/obj/item/circuitboard/cyborgrecharger - name = "circuit board (Cyborg Recharger)" - build_path = /obj/machinery/recharge_station - board_type = "machine" - origin_tech = "powerstorage=3;engineering=3" - frame_desc = "Requires 2 Capacitors, 1 Power Cell and 1 Manipulator." - req_components = list( - /obj/item/stock_parts/capacitor = 2, - /obj/item/stock_parts/cell = 1, - /obj/item/stock_parts/manipulator = 1) - -// Telecomms circuit boards: -/obj/item/circuitboard/telecomms/receiver - name = "Circuit Board (Subspace Receiver)" - build_path = /obj/machinery/telecomms/receiver - board_type = "machine" - origin_tech = "programming=2;engineering=2;bluespace=1" - frame_desc = "Requires 1 Subspace Ansible, 1 Hyperwave Filter, 2 Manipulators, and 1 Micro-Laser." - req_components = list( - /obj/item/stock_parts/subspace/ansible = 1, - /obj/item/stock_parts/subspace/filter = 1, - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/micro_laser = 1) - -/obj/item/circuitboard/telecomms/hub - name = "Circuit Board (Hub Mainframe)" - build_path = /obj/machinery/telecomms/hub - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Manipulators, 2 Cable Coil and 2 Hyperwave Filter." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/subspace/filter = 2) - -/obj/item/circuitboard/telecomms/relay - name = "Circuit Board (Relay Mainframe)" - build_path = /obj/machinery/telecomms/relay - board_type = "machine" - origin_tech = "programming=2;engineering=2;bluespace=2" - frame_desc = "Requires 2 Manipulators, 2 Cable Coil and 2 Hyperwave Filters." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/subspace/filter = 2) - -/obj/item/circuitboard/telecomms/bus - name = "Circuit Board (Bus Mainframe)" - build_path = /obj/machinery/telecomms/bus - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Manipulators, 1 Cable Coil and 1 Hyperwave Filter." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 1, - /obj/item/stock_parts/subspace/filter = 1) - -/obj/item/circuitboard/telecomms/processor - name = "Circuit Board (Processor Unit)" - build_path = /obj/machinery/telecomms/processor - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 3 Manipulators, 1 Hyperwave Filter, 2 Treatment Disks, 1 Wavelength Analyzer, 2 Cable Coils and 1 Subspace Amplifier." - req_components = list( - /obj/item/stock_parts/manipulator = 3, - /obj/item/stock_parts/subspace/filter = 1, - /obj/item/stock_parts/subspace/treatment = 2, - /obj/item/stock_parts/subspace/analyzer = 1, - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/subspace/amplifier = 1) - -/obj/item/circuitboard/telecomms/server - name = "Circuit Board (Telecommunication Server)" - build_path = /obj/machinery/telecomms/server - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Manipulators, 1 Cable Coil and 1 Hyperwave Filter." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 1, - /obj/item/stock_parts/subspace/filter = 1) - -/obj/item/circuitboard/telecomms/broadcaster - name = "Circuit Board (Subspace Broadcaster)" - build_path = /obj/machinery/telecomms/broadcaster - board_type = "machine" - origin_tech = "programming=2;engineering=2;bluespace=1" - frame_desc = "Requires 2 Manipulators, 1 Cable Coil, 1 Hyperwave Filter, 1 Ansible Crystal and 2 High-Powered Micro-Lasers. " - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 1, - /obj/item/stock_parts/subspace/filter = 1, - /obj/item/stock_parts/subspace/crystal = 1, - /obj/item/stock_parts/micro_laser/high = 2) - -/obj/item/circuitboard/ore_redemption - name = "circuit board (Ore Redemption)" - build_path = /obj/machinery/mineral/ore_redemption - board_type = "machine" - origin_tech = "programming=1;engineering=2" - req_components = list( - /obj/item/stack/sheet/glass = 1, - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/assembly/igniter = 1) - -/obj/item/circuitboard/ore_redemption/golem - name = "circuit board (Golem Ore Redemption)" - build_path = /obj/machinery/mineral/ore_redemption/golem - -/obj/item/circuitboard/mining_equipment_vendor - name = "circuit board (Mining Equipment Vendor)" - build_path = /obj/machinery/mineral/equipment_vendor - board_type = "machine" - origin_tech = "programming=1;engineering=3" - req_components = list( - /obj/item/stack/sheet/glass = 1, - /obj/item/stock_parts/matter_bin = 3) - -/obj/item/circuitboard/mining_equipment_vendor/golem - name = "circuit board (Mining Equipment Vendor)" - build_path = /obj/machinery/mineral/equipment_vendor/golem - -/obj/item/circuitboard/clawgame - name = "circuit board (Claw Game)" - build_path = /obj/machinery/arcade/claw - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 5, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/prize_counter - name = "circuit board (Prize Counter)" - build_path = /obj/machinery/prize_counter - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/sheet/glass = 1, - /obj/item/stack/cable_coil = 1) - -/obj/item/circuitboard/gameboard - name = "circuit board (Virtual Gameboard)" - build_path = /obj/machinery/gameboard - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/cable_coil = 3, - /obj/item/stack/sheet/glass = 1) - -//Selectable mode board, like vending machine boards -/obj/item/circuitboard/logic_gate - name = "circuit board (Logic Connector)" - build_path = /obj/machinery/logic_gate - board_type = "machine" - origin_tech = "programming=1" //This stuff is pretty much the absolute basis of programming, so it's mostly useless for research - req_components = list(/obj/item/stack/cable_coil = 1) - - var/list/names_paths = list( - "NOT Gate" = /obj/machinery/logic_gate/not, - "OR Gate" = /obj/machinery/logic_gate/or, - "AND Gate" = /obj/machinery/logic_gate/and, - "NAND Gate" = /obj/machinery/logic_gate/nand, - "NOR Gate" = /obj/machinery/logic_gate/nor, - "XOR Gate" = /obj/machinery/logic_gate/xor, - "XNOR Gate" = /obj/machinery/logic_gate/xnor, - "STATUS Gate" = /obj/machinery/logic_gate/status, - "CONVERT Gate" = /obj/machinery/logic_gate/convert - ) - -/obj/item/circuitboard/logic_gate/New() - ..() - if(build_path == /obj/machinery/logic_gate) //If we spawn the base type board (determined by the base type machine as the build path), become a random gate board - var/new_path = names_paths[pick(names_paths)] - set_type(new_path) - -/obj/item/circuitboard/logic_gate/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) - set_type(null, user) - return - return ..() - -/obj/item/circuitboard/logic_gate/proc/set_type(typepath, mob/user) - var/new_name = "Logic Base" - if(!typepath) - new_name = input("Circuit Setting", "What would you change the board setting to?") in names_paths - typepath = names_paths[new_name] - else - for(var/name in names_paths) - if(names_paths[name] == typepath) - new_name = name - break - build_path = typepath - name = "circuit board ([new_name])" - if(user) - to_chat(user, "You set the board to [new_name].") +/obj/machinery/constructable_frame //Made into a seperate type to make future revisions easier. + name = "machine frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "box_0" + density = 1 + anchored = 1 + use_power = NO_POWER_USE + max_integrity = 250 + var/obj/item/circuitboard/circuit = null + var/list/components = null + var/list/req_components = null + var/list/req_component_names = null // user-friendly names of components + var/state = 1 + + // For pods + var/list/connected_parts = list() + var/pattern_idx=0 + +/obj/machinery/constructable_frame/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc, 5) + if(state >= 2) + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil(loc) + A.amount = 5 + if(circuit) + circuit.forceMove(loc) + circuit = null + return ..() + +/obj/machinery/constructable_frame/obj_break(damage_flag) + deconstruct() + +// unfortunately, we have to instance the objects really quickly to get the names +// fortunately, this is only called once when the board is added and the items are immediately GC'd +// and none of the parts do much in their constructors +/obj/machinery/constructable_frame/proc/update_namelist() + if(!req_components) + return + + req_component_names = new() + for(var/tname in req_components) + var/path = tname + var/obj/O = new path() + req_component_names[tname] = O.name + +/obj/machinery/constructable_frame/proc/get_req_components_amt() + var/amt = 0 + for(var/path in req_components) + amt += req_components[path] + return amt + +// update description of required components remaining +/obj/machinery/constructable_frame/proc/update_req_desc() + if(!req_components || !req_component_names) + return + + var/hasContent = 0 + desc = "Requires" + for(var/i = 1 to req_components.len) + var/tname = req_components[i] + var/amt = req_components[tname] + if(amt == 0) + continue + var/use_and = i == req_components.len + desc += "[(hasContent ? (use_and ? ", and" : ",") : "")] [amt] [amt == 1 ? req_component_names[tname] : "[req_component_names[tname]]\s"]" + hasContent = 1 + + if(!hasContent) + desc = "Does not require any more components." + else + desc += "." + +/obj/machinery/constructable_frame/machine_frame/attackby(obj/item/P, mob/user, params) + switch(state) + if(1) + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if(C.amount >= 5) + playsound(src.loc, C.usesound, 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20 * C.toolspeed, target = src)) + if(state == 1 && C.amount >= 5 && C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 2 + icon_state = "box_1" + else + to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") + return + else + to_chat(user, "You need five lengths of cable to wire the frame.") + return + + if(istype(P, /obj/item/wrench)) + playsound(src.loc, P.usesound, 75, 1) + to_chat(user, "You dismantle the frame.") + deconstruct(TRUE) + return + if(2) + if(istype(P, /obj/item/circuitboard)) + var/obj/item/circuitboard/B = P + if(B.board_type == "machine") + playsound(src.loc, B.usesound, 50, 1) + to_chat(user, "You add the circuit board to the frame.") + circuit = P + user.drop_item() + P.loc = src + icon_state = "box_2" + state = 3 + components = list() + req_components = circuit.req_components.Copy() + update_namelist() + update_req_desc() + else + to_chat(user, "This frame does not accept circuit boards of this type!") + return + if(istype(P, /obj/item/wirecutters)) + playsound(src.loc, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 1 + icon_state = "box_0" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil(src.loc,5) + A.amount = 5 + return + if(3) + if(istype(P, /obj/item/crowbar)) + playsound(src.loc, P.usesound, 50, 1) + state = 2 + circuit.loc = src.loc + circuit = null + if(components.len == 0) + to_chat(user, "You remove the circuit board.") + else + to_chat(user, "You remove the circuit board and other components.") + for(var/obj/item/I in components) + I.loc = src.loc + desc = initial(desc) + req_components = null + components = null + icon_state = "box_1" + return + + if(istype(P, /obj/item/screwdriver)) + var/component_check = 1 + for(var/R in req_components) + if(req_components[R] > 0) + component_check = 0 + break + if(component_check) + playsound(src.loc, P.usesound, 50, 1) + var/obj/machinery/new_machine = new src.circuit.build_path(src.loc) + new_machine.on_construction() + for(var/obj/O in new_machine.component_parts) + qdel(O) + new_machine.component_parts = list() + for(var/obj/O in src) + O.loc = null + new_machine.component_parts += O + circuit.loc = null + new_machine.RefreshParts() + qdel(src) + return + + if(istype(P, /obj/item/storage/part_replacer) && P.contents.len && get_req_components_amt()) + var/obj/item/storage/part_replacer/replacer = P + var/list/added_components = list() + var/list/part_list = list() + + //Assemble a list of current parts, then sort them by their rating! + for(var/obj/item/stock_parts/co in replacer) + part_list += co + + for(var/path in req_components) + while(req_components[path] > 0 && (locate(path) in part_list)) + var/obj/item/part = (locate(path) in part_list) + added_components[part] = path + replacer.remove_from_storage(part, src) + req_components[path]-- + part_list -= part + + for(var/obj/item/stock_parts/part in added_components) + components += part + to_chat(user, "[part.name] applied.") + replacer.play_rped_sound() + + update_req_desc() + return + + if(istype(P, /obj/item)) + var/success + for(var/I in req_components) + if(istype(P, I) && (req_components[I] > 0) && (!(P.flags & NODROP) || istype(P, /obj/item/stack))) + success=1 + playsound(src.loc, P.usesound, 50, 1) + if(istype(P, /obj/item/stack)) + var/obj/item/stack/S = P + var/camt = min(S.amount, req_components[I]) + var/obj/item/stack/NS = new P.type(src) + NS.amount = camt + NS.update_icon() + S.use(camt) + components += NS + req_components[I] -= camt + update_req_desc() + break + user.drop_item() + P.forceMove(src) + components += P + req_components[I]-- + update_req_desc() + return 1 + if(!success) + to_chat(user, "You cannot add that to the machine!") + return 0 + return + if(user.a_intent == INTENT_HARM) + return ..() + + +//Machine Frame Circuit Boards +/*Common Parts: Parts List: Ignitor, Timer, Infra-red laser, Infra-red sensor, t_scanner, Capacitor, Valve, sensor unit, +micro-manipulator, console screen, beaker, Microlaser, matter bin, power cells. +Note: Once everything is added to the public areas, will add MAT_METAL and MAT_GLASS to circuit boards since autolathe won't be able +to destroy them and players will be able to make replacements. +*/ +/obj/item/circuitboard/vendor + name = "circuit board (Booze-O-Mat Vendor)" + board_type = "machine" + origin_tech = "programming=1" + frame_desc = "Requires 1 Resupply Canister." + build_path = /obj/machinery/vending/boozeomat + req_components = list(/obj/item/vending_refill/boozeomat = 1) + + var/static/list/vending_names_paths = list( + /obj/machinery/vending/boozeomat = "Booze-O-Mat", + /obj/machinery/vending/coffee = "Solar's Best Hot Drinks", + /obj/machinery/vending/snack = "Getmore Chocolate Corp", + /obj/machinery/vending/chinese = "Mr. Chang", + /obj/machinery/vending/cola = "Robust Softdrinks", + /obj/machinery/vending/cigarette = "ShadyCigs Deluxe", + /obj/machinery/vending/hatdispenser = "Hatlord 9000", + /obj/machinery/vending/suitdispenser = "Suitlord 9000", + /obj/machinery/vending/shoedispenser = "Shoelord 9000", + /obj/machinery/vending/clothing = "ClothesMate", + /obj/machinery/vending/medical = "NanoMed Plus", + /obj/machinery/vending/wallmed = "NanoMed", + /obj/machinery/vending/assist = "Vendomat", + /obj/machinery/vending/engivend = "Engi-Vend", + /obj/machinery/vending/hydronutrients = "NutriMax", + /obj/machinery/vending/hydroseeds = "MegaSeed Servitor", + /obj/machinery/vending/sustenance = "Sustenance Vendor", + /obj/machinery/vending/dinnerware = "Plasteel Chef's Dinnerware Vendor", + /obj/machinery/vending/cart = "PTech", + /obj/machinery/vending/robotics = "Robotech Deluxe", + /obj/machinery/vending/engineering = "Robco Tool Maker", + /obj/machinery/vending/sovietsoda = "BODA", + /obj/machinery/vending/security = "SecTech", + /obj/machinery/vending/modularpc = "Deluxe Silicate Selections", + /obj/machinery/vending/crittercare = "CritterCare") + +/obj/item/circuitboard/vendor/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/static/list/display_vending_names_paths + if(!display_vending_names_paths) + display_vending_names_paths = list() + for(var/path in vending_names_paths) + display_vending_names_paths[vending_names_paths[path]] = path + var/choice = input(user, "Choose a new brand","Select an Item") as null|anything in display_vending_names_paths + if(loc != user) + to_chat(user, "You need to keep [src] in your hands while doing that!") + return + set_type(display_vending_names_paths[choice]) + +/obj/item/circuitboard/vendor/proc/set_type(obj/machinery/vending/typepath) + build_path = typepath + name = "circuit board ([vending_names_paths[build_path]] Vendor)" + req_components = list(initial(typepath.refill_canister) = 1) + +/obj/item/circuitboard/smes + name = "circuit board (SMES)" + build_path = /obj/machinery/power/smes + board_type = "machine" + origin_tech = "programming=3;powerstorage=3;engineering=3" + frame_desc = "Requires 5 pieces of cable, 5 Power Cells and 1 Capacitor." + req_components = list( + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/cell = 5, + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/emitter + name = "circuit board (Emitter)" + build_path = /obj/machinery/power/emitter + board_type = "machine" + origin_tech = "programming=3;powerstorage=4;engineering=4" + req_components = list( + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/power_compressor + name = "circuit board (Power Compressor)" + build_path = /obj/machinery/power/compressor + board_type = "machine" + origin_tech = "programming=4;powerstorage=4;engineering=4" + req_components = list( + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/manipulator = 6) + +/obj/item/circuitboard/power_turbine + name = "circuit board (Power Turbine)" + build_path = /obj/machinery/power/turbine + board_type = "machine" + origin_tech = "programming=4;powerstorage=4;engineering=4" + req_components = list( + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/capacitor = 6) + +/obj/item/circuitboard/thermomachine + name = "circuit board (Freezer)" + desc = "Use screwdriver to switch between heating and cooling modes." + build_path = /obj/machinery/atmospherics/unary/cold_sink/freezer + board_type = "machine" + origin_tech = "programming=3;plasmatech=3" + frame_desc = "Requires 2 Matter Bins, 2 Micro Lasers, 1 piece of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/micro_laser = 2, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/thermomachine/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver)) + if(build_path == /obj/machinery/atmospherics/unary/cold_sink/freezer) + build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater + name = "circuit board (Heater)" + to_chat(user, "You set the board to heating.") + else + build_path = /obj/machinery/atmospherics/unary/cold_sink/freezer + name = "circuit board (Freezer)" + to_chat(user, "You set the board to cooling.") + return + return ..() + +/obj/item/circuitboard/recharger + name = "circuit board (Recharger)" + build_path = /obj/machinery/recharger + board_type = "machine" + origin_tech = "powerstorage=3;materials=2" + frame_desc = "Requires 1 Capacitor" + req_components = list(/obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/snow_machine + name = "circuit board (snow machine)" + build_path = /obj/machinery/snow_machine + board_type = "machine" + origin_tech = "programming=2;materials=2" + frame_desc = "Requires 1 Matter Bin and 1 Micro Laser." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/micro_laser = 1) + +/obj/item/circuitboard/biogenerator + name = "circuit board (Biogenerator)" + build_path = /obj/machinery/biogenerator + board_type = "machine" + origin_tech = "programming=2;biotech=3;materials=3" + frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/plantgenes + name = "Plant DNA Manipulator (Machine Board)" + build_path = /obj/machinery/plantgenes + board_type = "machine" + origin_tech = "programming=3;biotech=3" + frame_desc = "Requires 1 Manipulator, 1 Micro Laser, 1 Console Screen, and 1 Scanning Module." + req_components = list( + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/scanning_module = 1) + +/obj/item/circuitboard/plantgenes/vault + +/obj/item/circuitboard/seed_extractor + name = "circuit board (Seed Extractor)" + build_path = /obj/machinery/seed_extractor + board_type = "machine" + origin_tech = "programming=1" + frame_desc = "Requires 1 Matter Bin and 1 Manipulator." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/hydroponics + name = "circuit board (Hydroponics Tray)" + build_path = /obj/machinery/hydroponics/constructable + board_type = "machine" + origin_tech = "programming=1;biotech=2" + frame_desc = "Requires 2 Matter Bins, 1 Manipulator, and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/microwave + name = "circuit board (Microwave)" + build_path = /obj/machinery/kitchen_machine/microwave + board_type = "machine" + origin_tech = "programming=2;magnets=2" + frame_desc = "Requires 1 Micro Laser, 2 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/cable_coil = 2, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/oven + name = "circuit board (Oven)" + build_path = /obj/machinery/kitchen_machine/oven + board_type = "machine" + origin_tech = "programming=2;magnets=2" + frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/micro_laser = 2, + /obj/item/stack/cable_coil = 5, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/grill + name = "circuit board (Grill)" + build_path = /obj/machinery/kitchen_machine/grill + board_type = "machine" + origin_tech = "programming=2;magnets=2" + frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/micro_laser = 2, + /obj/item/stack/cable_coil = 5, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/candy_maker + name = "circuit board (Candy Maker)" + build_path = /obj/machinery/kitchen_machine/candy_maker + board_type = "machine" + origin_tech = "programming=2;magnets=2" + frame_desc = "Requires 1 Manipulator, 5 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 5, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/deepfryer + name = "circuit board (Deep Fryer)" + build_path = /obj/machinery/cooker/deepfryer + board_type = "machine" + origin_tech = "programming=1" + frame_desc = "Requires 2 Micro Lasers and 5 pieces of cable." + req_components = list( + /obj/item/stock_parts/micro_laser = 2, + /obj/item/stack/cable_coil = 5) + +/obj/item/circuitboard/gibber + name = "circuit board (Gibber)" + build_path = /obj/machinery/gibber + board_type = "machine" + origin_tech = "programming=2;engineering=2" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/tesla_coil + name = "circuit board (Tesla Coil)" + build_path = /obj/machinery/power/tesla_coil + board_type = "machine" + origin_tech = "programming=3;magnets=3;powerstorage=3" + req_components = list( + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/grounding_rod + name = "circuit board (Grounding Rod)" + build_path = /obj/machinery/power/grounding_rod + board_type = "machine" + origin_tech = "programming=3;powerstorage=3;magnets=3;plasmatech=2" + req_components = list( + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/processor + name = "circuit board (Food processor)" + build_path = /obj/machinery/processor + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/recycler + name = "circuit board (Recycler)" + build_path = /obj/machinery/recycler + board_type = "machine" + origin_tech = "programming=2;engineering=2" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/smartfridge + name = "circuit board (Smartfridge)" + build_path = /obj/machinery/smartfridge + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/matter_bin = 1) + var/list/fridge_names_paths = list( + "\improper SmartFridge" = /obj/machinery/smartfridge, + "\improper MegaSeed Servitor" = /obj/machinery/smartfridge/seeds, + "\improper Refrigerated Medicine Storage" = /obj/machinery/smartfridge/medbay, + "\improper Slime Extract Storage" = /obj/machinery/smartfridge/secure/extract, + "\improper Secure Refrigerated Medicine Storage" = /obj/machinery/smartfridge/secure/medbay, + "\improper Smart Chemical Storage" = /obj/machinery/smartfridge/secure/chemistry, + "smart virus storage" = /obj/machinery/smartfridge/secure/chemistry/virology, + "\improper Drink Showcase" = /obj/machinery/smartfridge/drinks + ) + + + +/obj/item/circuitboard/smartfridge/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver)) + set_type(null, user) + return + return ..() + +/obj/item/circuitboard/smartfridge/proc/set_type(typepath, mob/user) + var/new_name = "" + if(!typepath) + new_name = input("Circuit Setting", "What would you change the board setting to?") in fridge_names_paths + typepath = fridge_names_paths[new_name] + else + for(var/name in fridge_names_paths) + if(fridge_names_paths[name] == typepath) + new_name = name + break + build_path = typepath + name = new_name + if(findtextEx(new_name, "\improper")) + new_name = replacetext(new_name, "\improper", "") + if(user) + to_chat(user, "You set the board to [new_name].") + +/obj/item/circuitboard/monkey_recycler + name = "circuit board (Monkey Recycler)" + build_path = /obj/machinery/monkey_recycler + board_type = "machine" + origin_tech = "programming=1;biotech=2" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/holopad + name = "circuit board (AI Holopad)" + build_path = /obj/machinery/hologram/holopad + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/chem_dispenser + name = "circuit board (Chem Dispenser)" + build_path = /obj/machinery/chem_dispenser + board_type = "machine" + origin_tech = "materials=4;programming=4;plasmatech=4;biotech=3" + frame_desc = "Requires 2 Matter Bins, 1 Capacitor, 1 Manipulator, 1 Console Screen, and 1 Power Cell." + req_components = list( /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/capacitor = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/cell = 1) + +/obj/item/circuitboard/chem_master + name = "circuit board (ChemMaster 3000)" + build_path = /obj/machinery/chem_master + board_type = "machine" + origin_tech = "materials=3;programming=2;biotech=3" + req_components = list( + /obj/item/reagent_containers/glass/beaker = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/chem_master/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/new_name = "ChemMaster" + var/new_path = /obj/machinery/chem_master + + if(build_path == /obj/machinery/chem_master) + new_name = "CondiMaster" + new_path = /obj/machinery/chem_master/condimaster + + build_path = new_path + name = "circuit board ([new_name] 3000)" + to_chat(user, "You change the circuit board setting to \"[new_name]\".") + +/obj/item/circuitboard/chem_master/condi_master + name = "circuit board (CondiMaster 3000)" + build_path = /obj/machinery/chem_master/condimaster + +/obj/item/circuitboard/chem_heater + name = "circuit board (Chemical Heater)" + build_path = /obj/machinery/chem_heater + board_type = "machine" + origin_tech = "programming=2;engineering=2;biotech=2" + frame_desc = "Requires 1 Micro Laser and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/reagentgrinder + name = "circuit board (All-In-One Grinder)" + build_path = /obj/machinery/reagentgrinder/empty + board_type = "machine" + origin_tech = "materials=2;engineering=2;biotech=2" + frame_desc = "Requires 2 Manipulators and 1 Matter Bin." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/matter_bin = 1) + +//Almost the same recipe as destructive analyzer to give people choices. +/obj/item/circuitboard/experimentor + name = "circuit board (E.X.P.E.R.I-MENTOR)" + build_path = /obj/machinery/r_n_d/experimentor + board_type = "machine" + origin_tech = "magnets=1;engineering=1;programming=1;biotech=1;bluespace=2" + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/micro_laser = 2) + +/obj/item/circuitboard/destructive_analyzer + name = "Circuit board (Destructive Analyzer)" + build_path = /obj/machinery/r_n_d/destructive_analyzer + board_type = "machine" + origin_tech = "magnets=2;engineering=2;programming=2" + frame_desc = "Requires 1 Scanning Module, 1 Manipulator, and 1 Micro-Laser." + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1) + +/obj/item/circuitboard/autolathe + name = "Circuit board (Autolathe)" + build_path = /obj/machinery/autolathe + board_type = "machine" + origin_tech = "engineering=2;programming=2" + frame_desc = "Requires 3 Matter Bins, 1 Manipulator, and 1 Console Screen." + 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/protolathe + name = "Circuit board (Protolathe)" + build_path = /obj/machinery/r_n_d/protolathe + board_type = "machine" + origin_tech = "engineering=2;programming=2" + frame_desc = "Requires 2 Matter Bins, 2 Manipulators, and 2 Beakers." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/manipulator = 2, + /obj/item/reagent_containers/glass/beaker = 2) + +/obj/item/circuitboard/chem_dispenser/soda + name = "Circuit board (Soda Machine)" + build_path = /obj/machinery/chem_dispenser/soda + +/obj/item/circuitboard/chem_dispenser/beer + name = "Circuit board (Beer Machine)" + build_path = /obj/machinery/chem_dispenser/beer + +/obj/item/circuitboard/circuit_imprinter + name = "Circuit board (Circuit Imprinter)" + build_path = /obj/machinery/r_n_d/circuit_imprinter + board_type = "machine" + origin_tech = "engineering=2;programming=2" + frame_desc = "Requires 1 Matter Bin, 1 Manipulator, and 2 Beakers." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/reagent_containers/glass/beaker = 2) + +/obj/item/circuitboard/pacman + name = "Circuit Board (PACMAN-type Generator)" + build_path = /obj/machinery/power/port_gen/pacman + board_type = "machine" + origin_tech = "programming=2;powerstorage=3;plasmatech=3;engineering=3" + frame_desc = "Requires 1 Matter Bin, 1 Micro-Laser, 2 Pieces of Cable, and 1 Capacitor." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/pacman/super + name = "Circuit Board (SUPERPACMAN-type Generator)" + build_path = /obj/machinery/power/port_gen/pacman/super + origin_tech = "programming=3;powerstorage=4;engineering=4" + +/obj/item/circuitboard/pacman/mrs + name = "Circuit Board (MRSPACMAN-type Generator)" + build_path = /obj/machinery/power/port_gen/pacman/mrs + origin_tech = "programming=3;powerstorage=4;engineering=4;plasmatech=4" + +/obj/item/circuitboard/rdserver + name = "Circuit Board (R&D Server)" + build_path = /obj/machinery/r_n_d/server + board_type = "machine" + origin_tech = "programming=3" + frame_desc = "Requires 2 pieces of cable, and 1 Scanning Module." + req_components = list( + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/scanning_module = 1) + +/obj/item/circuitboard/mechfab + name = "Circuit board (Exosuit Fabricator)" + build_path = /obj/machinery/mecha_part_fabricator + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Matter Bins, 1 Manipulator, 1 Micro-Laser and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/podfab + name = "Circuit board (Spacepod Fabricator)" + build_path = /obj/machinery/mecha_part_fabricator/spacepod + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Matter Bins, 1 Manipulators, 1 Micro-Lasers, and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1) + + +/obj/item/circuitboard/clonepod + name = "Circuit board (Clone Pod)" + build_path = /obj/machinery/clonepod + board_type = "machine" + origin_tech = "programming=2;biotech=2" + frame_desc = "Requires 2 Manipulator, 2 Scanning Module, 2 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/scanning_module = 2, + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/clonescanner + name = "Circuit board (Cloning Scanner)" + build_path = /obj/machinery/dna_scannernew + board_type = "machine" + origin_tech = "programming=2;biotech=2" + frame_desc = "Requires 1 Scanning Module, 1 Manipulator, 1 Micro-Laser, 2 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stack/cable_coil = 2,) + +/obj/item/circuitboard/mech_recharger + name = "circuit board (Mech Bay Recharger)" + build_path = /obj/machinery/mech_bay_recharge_port + board_type = "machine" + origin_tech = "programming=3;powerstorage=3;engineering=3" + frame_desc = "Requires 1 piece of cable and 5 Capacitors." + req_components = list( + /obj/item/stack/cable_coil = 1, + /obj/item/stock_parts/capacitor = 5) + +/obj/item/circuitboard/teleporter_hub + name = "circuit board (Teleporter Hub)" + build_path = /obj/machinery/teleport/hub + board_type = "machine" + origin_tech = "programming=3;engineering=4;bluespace=4;materials=4" + frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 3, + /obj/item/stock_parts/matter_bin = 1) + +/obj/item/circuitboard/teleporter_station + name = "circuit board (Teleporter Station)" + build_path = /obj/machinery/teleport/station + board_type = "machine" + origin_tech = "programming=4;engineering=4;bluespace=4;plasmatech=3" + frame_desc = "Requires 2 Bluespace Crystals, 2 Capacitors and 1 Console Screen." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 2, + /obj/item/stock_parts/capacitor = 2, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/teleporter_perma + name = "circuit board (Permanent Teleporter)" + build_path = /obj/machinery/teleport/perma + board_type = "machine" + origin_tech = "programming=3;engineering=4;bluespace=4;materials=4" + frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 3, + /obj/item/stock_parts/matter_bin = 1) + var/target + +/obj/item/circuitboard/teleporter_perma/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/gps)) + var/obj/item/gps/L = I + if(L.locked_location) + target = get_turf(L.locked_location) + to_chat(user, "You upload the data from [L]") + return + return ..() + +/obj/item/circuitboard/telesci_pad + name = "Circuit board (Telepad)" + build_path = /obj/machinery/telepad + board_type = "machine" + origin_tech = "programming=4;engineering=3;plasmatech=4;bluespace=4" + frame_desc = "Requires 2 Bluespace Crystals, 1 Capacitor, 1 piece of cable and 1 Console Screen." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 2, + /obj/item/stock_parts/capacitor = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/quantumpad + name = "circuit board (Quantum Pad)" + build_path = /obj/machinery/quantumpad + board_type = "machine" + origin_tech = "programming=3;engineering=3;plasmatech=3;bluespace=4" + frame_desc = "Requires 1 Bluespace Crystal, 1 Capacitor, 1 piece of cable and 1 Manipulator." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 1, + /obj/item/stock_parts/capacitor = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 1) + +/obj/item/circuitboard/sleeper + name = "circuit board (Sleeper)" + build_path = /obj/machinery/sleeper + board_type = "machine" + origin_tech = "programming=3;biotech=2;engineering=3" + frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 2 Console Screens." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 2) + +/obj/item/circuitboard/sleeper/syndicate + name = "circuit board (Sleeper Syndicate)" + build_path = /obj/machinery/sleeper/syndie + +/obj/item/circuitboard/sleeper/survival + name = "circuit board (Sleeper Survival Pod)" + build_path = /obj/machinery/sleeper/survival_pod + + +/obj/item/circuitboard/bodyscanner + name = "circuit board (Body Scanner)" + build_path = /obj/machinery/bodyscanner + board_type = "machine" + origin_tech = "programming=3;biotech=2;engineering=3" + frame_desc = "Requires 1 Scanning Module, 2 pieces of cable and 2 Console Screens." + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stack/cable_coil = 2, + /obj/item/stack/sheet/glass = 2) + +/obj/item/circuitboard/cryo_tube + name = "circuit board (Cryotube)" + build_path = /obj/machinery/atmospherics/unary/cryo_cell + board_type = "machine" + origin_tech = "programming=4;biotech=3;engineering=4;plasmatech=3" + frame_desc = "Requires 1 Matter Bin, 1 piece of cable and 4 Console Screens." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 4) + +/obj/item/circuitboard/cyborgrecharger + name = "circuit board (Cyborg Recharger)" + build_path = /obj/machinery/recharge_station + board_type = "machine" + origin_tech = "powerstorage=3;engineering=3" + frame_desc = "Requires 2 Capacitors, 1 Power Cell and 1 Manipulator." + req_components = list( + /obj/item/stock_parts/capacitor = 2, + /obj/item/stock_parts/cell = 1, + /obj/item/stock_parts/manipulator = 1) + +// Telecomms circuit boards: +/obj/item/circuitboard/telecomms/receiver + name = "Circuit Board (Subspace Receiver)" + build_path = /obj/machinery/telecomms/receiver + board_type = "machine" + origin_tech = "programming=2;engineering=2;bluespace=1" + frame_desc = "Requires 1 Subspace Ansible, 1 Hyperwave Filter, 2 Manipulators, and 1 Micro-Laser." + req_components = list( + /obj/item/stock_parts/subspace/ansible = 1, + /obj/item/stock_parts/subspace/filter = 1, + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/micro_laser = 1) + +/obj/item/circuitboard/telecomms/hub + name = "Circuit Board (Hub Mainframe)" + build_path = /obj/machinery/telecomms/hub + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Manipulators, 2 Cable Coil and 2 Hyperwave Filter." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/subspace/filter = 2) + +/obj/item/circuitboard/telecomms/relay + name = "Circuit Board (Relay Mainframe)" + build_path = /obj/machinery/telecomms/relay + board_type = "machine" + origin_tech = "programming=2;engineering=2;bluespace=2" + frame_desc = "Requires 2 Manipulators, 2 Cable Coil and 2 Hyperwave Filters." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/subspace/filter = 2) + +/obj/item/circuitboard/telecomms/bus + name = "Circuit Board (Bus Mainframe)" + build_path = /obj/machinery/telecomms/bus + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Manipulators, 1 Cable Coil and 1 Hyperwave Filter." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 1, + /obj/item/stock_parts/subspace/filter = 1) + +/obj/item/circuitboard/telecomms/processor + name = "Circuit Board (Processor Unit)" + build_path = /obj/machinery/telecomms/processor + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 3 Manipulators, 1 Hyperwave Filter, 2 Treatment Disks, 1 Wavelength Analyzer, 2 Cable Coils and 1 Subspace Amplifier." + req_components = list( + /obj/item/stock_parts/manipulator = 3, + /obj/item/stock_parts/subspace/filter = 1, + /obj/item/stock_parts/subspace/treatment = 2, + /obj/item/stock_parts/subspace/analyzer = 1, + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/subspace/amplifier = 1) + +/obj/item/circuitboard/telecomms/server + name = "Circuit Board (Telecommunication Server)" + build_path = /obj/machinery/telecomms/server + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Manipulators, 1 Cable Coil and 1 Hyperwave Filter." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 1, + /obj/item/stock_parts/subspace/filter = 1) + +/obj/item/circuitboard/telecomms/broadcaster + name = "Circuit Board (Subspace Broadcaster)" + build_path = /obj/machinery/telecomms/broadcaster + board_type = "machine" + origin_tech = "programming=2;engineering=2;bluespace=1" + frame_desc = "Requires 2 Manipulators, 1 Cable Coil, 1 Hyperwave Filter, 1 Ansible Crystal and 2 High-Powered Micro-Lasers. " + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 1, + /obj/item/stock_parts/subspace/filter = 1, + /obj/item/stock_parts/subspace/crystal = 1, + /obj/item/stock_parts/micro_laser/high = 2) + +/obj/item/circuitboard/ore_redemption + name = "circuit board (Ore Redemption)" + build_path = /obj/machinery/mineral/ore_redemption + board_type = "machine" + origin_tech = "programming=1;engineering=2" + req_components = list( + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/assembly/igniter = 1) + +/obj/item/circuitboard/ore_redemption/golem + name = "circuit board (Golem Ore Redemption)" + build_path = /obj/machinery/mineral/ore_redemption/golem + +/obj/item/circuitboard/mining_equipment_vendor + name = "circuit board (Mining Equipment Vendor)" + build_path = /obj/machinery/mineral/equipment_vendor + board_type = "machine" + origin_tech = "programming=1;engineering=3" + req_components = list( + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/matter_bin = 3) + +/obj/item/circuitboard/mining_equipment_vendor/golem + name = "circuit board (Mining Equipment Vendor)" + build_path = /obj/machinery/mineral/equipment_vendor/golem + +/obj/item/circuitboard/clawgame + name = "circuit board (Claw Game)" + build_path = /obj/machinery/arcade/claw + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 5, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/prize_counter + name = "circuit board (Prize Counter)" + build_path = /obj/machinery/prize_counter + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stack/cable_coil = 1) + +/obj/item/circuitboard/gameboard + name = "circuit board (Virtual Gameboard)" + build_path = /obj/machinery/gameboard + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/cable_coil = 3, + /obj/item/stack/sheet/glass = 1) + +//Selectable mode board, like vending machine boards +/obj/item/circuitboard/logic_gate + name = "circuit board (Logic Connector)" + build_path = /obj/machinery/logic_gate + board_type = "machine" + origin_tech = "programming=1" //This stuff is pretty much the absolute basis of programming, so it's mostly useless for research + req_components = list(/obj/item/stack/cable_coil = 1) + + var/list/names_paths = list( + "NOT Gate" = /obj/machinery/logic_gate/not, + "OR Gate" = /obj/machinery/logic_gate/or, + "AND Gate" = /obj/machinery/logic_gate/and, + "NAND Gate" = /obj/machinery/logic_gate/nand, + "NOR Gate" = /obj/machinery/logic_gate/nor, + "XOR Gate" = /obj/machinery/logic_gate/xor, + "XNOR Gate" = /obj/machinery/logic_gate/xnor, + "STATUS Gate" = /obj/machinery/logic_gate/status, + "CONVERT Gate" = /obj/machinery/logic_gate/convert + ) + +/obj/item/circuitboard/logic_gate/New() + ..() + if(build_path == /obj/machinery/logic_gate) //If we spawn the base type board (determined by the base type machine as the build path), become a random gate board + var/new_path = names_paths[pick(names_paths)] + set_type(new_path) + +/obj/item/circuitboard/logic_gate/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver)) + set_type(null, user) + return + return ..() + +/obj/item/circuitboard/logic_gate/proc/set_type(typepath, mob/user) + var/new_name = "Logic Base" + if(!typepath) + new_name = input("Circuit Setting", "What would you change the board setting to?") in names_paths + typepath = names_paths[new_name] + else + for(var/name in names_paths) + if(names_paths[name] == typepath) + new_name = name + break + build_path = typepath + name = "circuit board ([new_name])" + if(user) + to_chat(user, "You set the board to [new_name].") diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 16e761d01c6..756a22259f0 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -1,517 +1,543 @@ -/obj/machinery/atmospherics/unary/cryo_cell - name = "cryo cell" - desc = "Lowers the body temperature so certain medications may take effect." - icon = 'icons/obj/cryogenics.dmi' - icon_state = "pod0" - density = 1 - anchored = 1.0 - layer = ABOVE_WINDOW_LAYER - plane = GAME_PLANE - interact_offline = 1 - max_integrity = 350 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30) - var/on = 0 - var/temperature_archived - var/mob/living/carbon/occupant = null - var/obj/item/reagent_containers/glass/beaker = null - var/autoeject = 0 - - var/next_trans = 0 - var/current_heat_capacity = 50 - var/efficiency - - var/running_bob_animation = 0 // This is used to prevent threads from building up if update_icons is called multiple times - - light_color = LIGHT_COLOR_WHITE - power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) - -/obj/machinery/atmospherics/unary/cryo_cell/New() - ..() - initialize_directions = dir - component_parts = list() - component_parts += new /obj/item/circuitboard/cryo_tube(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cryo_cell/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/cryo_tube(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cryo_cell/on_construction() - ..(dir,dir) - -/obj/machinery/atmospherics/unary/cryo_cell/RefreshParts() - var/C - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - C += M.rating - current_heat_capacity = 50 * C - efficiency = C - -/obj/machinery/atmospherics/unary/cryo_cell/atmos_init() - ..() - if(node) return - for(var/cdir in cardinal) - node = findConnecting(cdir) - if(node) - break - -/obj/machinery/atmospherics/unary/cryo_cell/Destroy() - QDEL_NULL(beaker) - return ..() - -/obj/machinery/atmospherics/unary/cryo_cell/ex_act(severity) - if(occupant) - occupant.ex_act(severity) - if(beaker) - beaker.ex_act(severity) - ..() - -/obj/machinery/atmospherics/unary/cryo_cell/handle_atom_del(atom/A) - ..() - if(A == beaker) - beaker = null - updateUsrDialog() - if(A == occupant) - occupant = null - updateUsrDialog() - update_icon() - -/obj/machinery/atmospherics/unary/cryo_cell/on_deconstruction() - if(beaker) - beaker.forceMove(drop_location()) - beaker = null - -/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) - if(O.loc == user) //no you can't pull things out of your ass - return - if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source - return - if(!ismob(O)) //humans only - return - if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robutts dont fit - return - if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper - return - if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems - return - if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? - return - if(occupant) - to_chat(user, "The cryo cell is already occupied!") - return - var/mob/living/L = O - if(!istype(L) || L.buckled) - return - if(L.abiotic()) - to_chat(user, "Subject cannot have abiotic items on.") - return - if(L.has_buckled_mobs()) //mob attached to us - to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") - return - if(put_mob(L)) - if(L == user) - visible_message("[user] climbs into the cryo cell.") - else - visible_message("[user] puts [L.name] into the cryo cell.") - add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL) - if(user.pulling == L) - user.stop_pulling() - -/obj/machinery/atmospherics/unary/cryo_cell/process() - ..() - if(autoeject) - if(occupant) - if(!occupant.has_organic_damage() && !occupant.has_mutated_organs()) - on = 0 - go_out() - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) - - if(air_contents) - if(occupant) - process_occupant() - - return 1 - -/obj/machinery/atmospherics/unary/cryo_cell/process_atmos() - ..() - if(!node) - return - if(!on) - return - - if(air_contents) - temperature_archived = air_contents.temperature - heat_gas_contents() - - if(abs(temperature_archived-air_contents.temperature) > 1) - parent.update = 1 - - -/obj/machinery/atmospherics/unary/cryo_cell/AllowDrop() - return FALSE - - -/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) - if(user.stat) - return - go_out() - return - -/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) - return attack_hand(user) - -/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) - if(user == occupant) - return - - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return - - ui_interact(user) - - - /** - * The ui_interact proc is used to open and update Nano UIs - * If ui_interact is not used then the UI will not update correctly - * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob) - * - * @param user /mob The mob who is interacting with this ui - * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") - * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui - * - * @return nothing - */ -/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 420) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["isOperating"] = on - data["hasOccupant"] = occupant ? 1 : 0 - - var/occupantData[0] - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["bodyTemperature"] = occupant.bodytemperature - data["occupant"] = occupantData; - - data["cellTemperature"] = round(air_contents.temperature) - data["cellTemperatureStatus"] = "good" - if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) - data["cellTemperatureStatus"] = "bad" - else if(air_contents.temperature > TCRYO) - data["cellTemperatureStatus"] = "average" - - data["isBeakerLoaded"] = beaker ? 1 : 0 - data["beakerLabel"] = null - data["beakerVolume"] = 0 - if(beaker) - data["beakerLabel"] = beaker.label_text ? beaker.label_text : null - if(beaker.reagents && beaker.reagents.reagent_list.len) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - data["beakerVolume"] += R.volume - - data["autoeject"] = autoeject - return data - -/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list) - if(usr == occupant) - return 0 // don't update UIs attached to this object - - if(..()) - return 0 // don't update UIs attached to this object - - if(href_list["switchOn"]) - on = 1 - update_icon() - - if(href_list["switchOff"]) - on = 0 - update_icon() - - if(href_list["autoejectOn"]) - autoeject = 1 - - if(href_list["autoejectOff"]) - autoeject = 0 - - if(href_list["ejectBeaker"]) - if(beaker) - beaker.forceMove(get_step(loc, SOUTH)) - beaker = null - - if(href_list["ejectOccupant"]) - if(!occupant || isslime(usr) || ispAI(usr)) - return 0 // don't update UIs attached to this object - add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL) - go_out() - - add_fingerprint(usr) - return 1 // update UIs attached to this object - -/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob, params) - if(istype(G, /obj/item/reagent_containers/glass)) - var/obj/item/reagent_containers/B = G - if(beaker) - to_chat(user, "A beaker is already loaded into the machine.") - return - if(!user.drop_item()) - to_chat(user, "[B] is stuck to you!") - return - B.forceMove(src) - beaker = B - add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]") - user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!") - return - - if(exchange_parts(user, G)) - return - - if(istype(G, /obj/item/grab)) - var/obj/item/grab/GG = G - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(!ismob(GG.affecting)) - return - if(GG.affecting.has_buckled_mobs()) //mob attached to us - to_chat(user, "[GG.affecting] will not fit into [src] because [GG.affecting.p_they()] [GG.affecting.p_have()] a slime latched onto [GG.affecting.p_their()] head.") - return - var/mob/M = GG.affecting - if(put_mob(M)) - qdel(GG) - return - return ..() - -/obj/machinery/atmospherics/unary/cryo_cell/crowbar_act(mob/user, obj/item/I) - if(default_deconstruction_crowbar(user, I)) - return - -/obj/machinery/atmospherics/unary/cryo_cell/screwdriver_act(mob/user, obj/item/I) - if(occupant || on) - to_chat(user, "The maintenance panel is locked.") - return TRUE - if(default_deconstruction_screwdriver(user, "pod0-o", "pod0", I)) - return TRUE - -/obj/machinery/atmospherics/unary/cryo_cell/update_icon() - handle_update_icon() - -/obj/machinery/atmospherics/unary/cryo_cell/proc/handle_update_icon() //making another proc to avoid spam in update_icon - overlays.Cut() //empty the overlay proc, just in case - icon_state = "pod[on]" //set the icon properly every time - - if(!src.occupant) - overlays += "lid[on]" //if no occupant, just put the lid overlay on, and ignore the rest - return - - if(occupant) - var/image/pickle = image(occupant.icon, occupant.icon_state) - pickle.overlays = occupant.overlays - pickle.pixel_y = 22 - - overlays += pickle - overlays += "lid[on]" - if(src.on && !running_bob_animation) //no bobbing if off - var/up = 0 //used to see if we are going up or down, 1 is down, 2 is up - spawn(0) // Without this, the icon update will block. The new thread will die once the occupant leaves. - running_bob_animation = 1 - while(occupant) - overlays -= "lid[on]" //have to remove the overlays first, to force an update- remove cloning pod overlay - overlays -= pickle //remove mob overlay - - switch(pickle.pixel_y) //this looks messy as fuck but it works, switch won't call itself twice - - if(23) //inbetween state, for smoothness - switch(up) //this is set later in the switch, to keep track of where the mob is supposed to go - if(2) //2 is up - pickle.pixel_y = 24 //set to highest - - if(1) //1 is down - pickle.pixel_y = 22 //set to lowest - - if(22) //mob is at it's lowest - pickle.pixel_y = 23 //set to inbetween - up = 2 //have to go up - - if(24) //mob is at it's highest - pickle.pixel_y = 23 //set to inbetween - up = 1 //have to go down - - overlays += pickle //re-add the mob to the icon - overlays += "lid[on]" //re-add the overlay of the pod, they are inside it, not floating - - sleep(7) //don't want to jiggle violently, just slowly bob - running_bob_animation = 0 - -/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() - if(air_contents.total_moles() < 10) - return - if(occupant) - if(occupant.stat == 2 || (occupant.health >= 100 && !occupant.has_mutated_organs())) //Why waste energy on dead or healthy people - occupant.bodytemperature = T0C - return - occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) - occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise - if(occupant.bodytemperature < T0C) - occupant.Sleeping(max(5/efficiency, (1/occupant.bodytemperature)*2000/efficiency)) - occupant.Paralyse(max(5/efficiency, (1/occupant.bodytemperature)*3000/efficiency)) - if(air_contents.oxygen > 2) - if(occupant.getOxyLoss()) - occupant.adjustOxyLoss(-6) - else - occupant.adjustOxyLoss(-1.2) - if(beaker && next_trans == 0) - var/proportion = 10 * min(1/beaker.volume, 1) - // Yes, this means you can get more bang for your buck with a beaker of SF vs a patch - // But it also means a giant beaker of SF won't heal people ridiculously fast 4 cheap - beaker.reagents.reaction(occupant, TOUCH, proportion) - beaker.reagents.trans_to(occupant, 1, 10) - next_trans++ - if(next_trans == 17) - next_trans = 0 - -/obj/machinery/atmospherics/unary/cryo_cell/proc/heat_gas_contents() - if(air_contents.total_moles() < 1) - return - var/air_heat_capacity = air_contents.heat_capacity() - var/combined_heat_capacity = current_heat_capacity + air_heat_capacity - if(combined_heat_capacity > 0) - var/combined_energy = T20C*current_heat_capacity + air_heat_capacity*air_contents.temperature - air_contents.temperature = combined_energy/combined_heat_capacity - -/obj/machinery/atmospherics/unary/cryo_cell/proc/go_out() - if(!occupant) - return - occupant.forceMove(get_step(loc, SOUTH)) //this doesn't account for walls or anything, but i don't forsee that being a problem. - if(occupant.bodytemperature < 261 && occupant.bodytemperature >= 70) //Patch by Aranclanos to stop people from taking burn damage after being ejected - occupant.bodytemperature = 261 - occupant = null - update_icon() - // eject trash the occupant dropped - for(var/atom/movable/A in contents - component_parts - list(beaker)) - A.forceMove(get_step(loc, SOUTH)) - -/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) - if(!istype(M)) - to_chat(usr, "The cryo cell cannot handle such a lifeform!") - return - if(occupant) - to_chat(usr, "The cryo cell is already occupied!") - return - if(M.abiotic()) - to_chat(usr, "Subject may not have abiotic items on.") - return - if(!node) - to_chat(usr, "The cell is not correctly connected to its pipe network!") - return - M.stop_pulling() - M.forceMove(src) - if(M.health > -100 && (M.health < 0 || M.sleeping)) - to_chat(M, "You feel a cold liquid surround you. Your skin starts to freeze up.") - occupant = M -// M.metabslow = 1 - add_fingerprint(usr) - update_icon() - M.ExtinguishMob() - return 1 - -/obj/machinery/atmospherics/unary/cryo_cell/verb/move_eject() - set name = "Eject occupant" - set category = "Object" - set src in oview(1) - - if(usr == occupant)//If the user is inside the tube... - if(usr.stat == DEAD) - return - to_chat(usr, "Release sequence activated. This will take two minutes.") - sleep(600) - if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already - return - go_out()//and release him from the eternal prison. - else - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - add_attack_logs(usr, occupant, "Ejected from cryo cell at [COORD(src)]") - go_out() - add_fingerprint(usr) - return - -/obj/machinery/atmospherics/unary/cryo_cell/narsie_act() - go_out() - new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! - color = "red"//force the icon to red - light_color = LIGHT_COLOR_RED - -/obj/machinery/atmospherics/unary/cryo_cell/verb/move_inside() - set name = "Move Inside" - set category = "Object" - set src in oview(1) - - if(usr.has_buckled_mobs()) //mob attached to us - to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") - return - - if(stat & (NOPOWER|BROKEN)) - return - - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - - put_mob(usr) - return - - - -/datum/data/function/proc/reset() - return - -/datum/data/function/proc/r_input(href, href_list, mob/user as mob) - return - -/datum/data/function/proc/display() - return - -/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user) - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1) - -/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user) - return //we don't see the pipe network while inside cryo. +#define AUTO_EJECT_DEAD (1<<0) +#define AUTO_EJECT_HEALTHY (1<<1) + +/obj/machinery/atmospherics/unary/cryo_cell + name = "cryo cell" + desc = "Lowers the body temperature so certain medications may take effect." + icon = 'icons/obj/cryogenics.dmi' + icon_state = "pod0" + density = 1 + anchored = 1.0 + layer = ABOVE_WINDOW_LAYER + plane = GAME_PLANE + interact_offline = 1 + max_integrity = 350 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30) + var/on = FALSE + var/temperature_archived + var/mob/living/carbon/occupant = null + var/obj/item/reagent_containers/glass/beaker = null + /// Holds two bitflags, AUTO_EJECT_DEAD and AUTO_EJECT_HEALTHY. Used to determine if the cryo cell will auto-eject dead and/or completely health patients. + var/auto_eject_prefs = NONE + + var/next_trans = 0 + var/current_heat_capacity = 50 + var/efficiency + + var/running_bob_animation = 0 // This is used to prevent threads from building up if update_icons is called multiple times + + light_color = LIGHT_COLOR_WHITE + power_change() + ..() + if(!(stat & (BROKEN|NOPOWER))) + set_light(2) + else + set_light(0) + +/obj/machinery/atmospherics/unary/cryo_cell/New() + ..() + initialize_directions = dir + component_parts = list() + component_parts += new /obj/item/circuitboard/cryo_tube(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cryo_cell/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/cryo_tube(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cryo_cell/on_construction() + ..(dir,dir) + +/obj/machinery/atmospherics/unary/cryo_cell/RefreshParts() + var/C + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + C += M.rating + current_heat_capacity = 50 * C + efficiency = C + +/obj/machinery/atmospherics/unary/cryo_cell/atmos_init() + ..() + if(node) return + for(var/cdir in GLOB.cardinal) + node = findConnecting(cdir) + if(node) + break + +/obj/machinery/atmospherics/unary/cryo_cell/Destroy() + QDEL_NULL(beaker) + return ..() + +/obj/machinery/atmospherics/unary/cryo_cell/ex_act(severity) + if(occupant) + occupant.ex_act(severity) + if(beaker) + beaker.ex_act(severity) + ..() + +/obj/machinery/atmospherics/unary/cryo_cell/handle_atom_del(atom/A) + ..() + if(A == beaker) + beaker = null + updateUsrDialog() + if(A == occupant) + occupant = null + updateUsrDialog() + update_icon() + +/obj/machinery/atmospherics/unary/cryo_cell/on_deconstruction() + if(beaker) + beaker.forceMove(drop_location()) + beaker = null + +/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) + if(O.loc == user) //no you can't pull things out of your ass + return + if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source + return + if(!ismob(O)) //humans only + return + if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robutts dont fit + return + if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper + return + if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems + return + if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? + return + if(occupant) + to_chat(user, "The cryo cell is already occupied!") + return + var/mob/living/L = O + if(!istype(L) || L.buckled) + return + if(L.abiotic()) + to_chat(user, "Subject cannot have abiotic items on.") + return + if(L.has_buckled_mobs()) //mob attached to us + to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") + return + if(put_mob(L)) + if(L == user) + visible_message("[user] climbs into the cryo cell.") + else + visible_message("[user] puts [L.name] into the cryo cell.") + add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL) + if(user.pulling == L) + user.stop_pulling() + +/obj/machinery/atmospherics/unary/cryo_cell/process() + ..() + if(!occupant) + return + + if((auto_eject_prefs & AUTO_EJECT_DEAD) && occupant.stat == DEAD) + auto_eject(AUTO_EJECT_DEAD) + return + if((auto_eject_prefs & AUTO_EJECT_HEALTHY) && !occupant.has_organic_damage() && !occupant.has_mutated_organs()) + auto_eject(AUTO_EJECT_HEALTHY) + return + + if(air_contents) + process_occupant() + + return TRUE + +/obj/machinery/atmospherics/unary/cryo_cell/process_atmos() + ..() + if(!node) + return + if(!on) + return + + if(air_contents) + temperature_archived = air_contents.temperature + heat_gas_contents() + + if(abs(temperature_archived-air_contents.temperature) > 1) + parent.update = 1 + + +/obj/machinery/atmospherics/unary/cryo_cell/AllowDrop() + return FALSE + + +/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) + if(user.stat) + return + go_out() + return + +/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) + return attack_hand(user) + +/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) + if(user == occupant) + return + + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return + + ui_interact(user) + + + /** + * The ui_interact proc is used to open and update Nano UIs + * If ui_interact is not used then the UI will not update correctly + * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob) + * + * @param user /mob The mob who is interacting with this ui + * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") + * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui + * + * @return nothing + */ +/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 480) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["isOperating"] = on + data["hasOccupant"] = occupant ? 1 : 0 + + var/occupantData[0] + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["bodyTemperature"] = occupant.bodytemperature + data["occupant"] = occupantData; + + data["cellTemperature"] = round(air_contents.temperature) + data["cellTemperatureStatus"] = "good" + if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) + data["cellTemperatureStatus"] = "bad" + else if(air_contents.temperature > TCRYO) + data["cellTemperatureStatus"] = "average" + + data["isBeakerLoaded"] = beaker ? 1 : 0 + data["beakerLabel"] = null + data["beakerVolume"] = 0 + if(beaker) + data["beakerLabel"] = beaker.label_text ? beaker.label_text : null + if(beaker.reagents && beaker.reagents.reagent_list.len) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + data["beakerVolume"] += R.volume + + data["auto_eject_healthy"] = (auto_eject_prefs & AUTO_EJECT_HEALTHY) ? TRUE : FALSE + data["auto_eject_dead"] = (auto_eject_prefs & AUTO_EJECT_DEAD) ? TRUE : FALSE + return data + +/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list) + if(usr == occupant) + return 0 // don't update UIs attached to this object + + if(..()) + return 0 // don't update UIs attached to this object + + if(href_list["switchOn"]) + on = TRUE + update_icon() + + if(href_list["switchOff"]) + on = FALSE + update_icon() + + if(href_list["auto_eject_healthy_on"]) + auto_eject_prefs |= AUTO_EJECT_HEALTHY + + if(href_list["auto_eject_healthy_off"]) + auto_eject_prefs &= ~AUTO_EJECT_HEALTHY + + if(href_list["auto_eject_dead_on"]) + auto_eject_prefs |= AUTO_EJECT_DEAD + + if(href_list["auto_eject_dead_off"]) + auto_eject_prefs &= ~AUTO_EJECT_DEAD + + if(href_list["ejectBeaker"]) + if(beaker) + beaker.forceMove(get_step(loc, SOUTH)) + beaker = null + + if(href_list["ejectOccupant"]) + if(!occupant || isslime(usr) || ispAI(usr)) + return 0 // don't update UIs attached to this object + add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL) + go_out() + + add_fingerprint(usr) + return 1 // update UIs attached to this object + +/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob, params) + if(istype(G, /obj/item/reagent_containers/glass)) + var/obj/item/reagent_containers/B = G + if(beaker) + to_chat(user, "A beaker is already loaded into the machine.") + return + if(!user.drop_item()) + to_chat(user, "[B] is stuck to you!") + return + B.forceMove(src) + beaker = B + add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]") + user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!") + return + + if(exchange_parts(user, G)) + return + + if(istype(G, /obj/item/grab)) + var/obj/item/grab/GG = G + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(!ismob(GG.affecting)) + return + if(GG.affecting.has_buckled_mobs()) //mob attached to us + to_chat(user, "[GG.affecting] will not fit into [src] because [GG.affecting.p_they()] [GG.affecting.p_have()] a slime latched onto [GG.affecting.p_their()] head.") + return + var/mob/M = GG.affecting + if(put_mob(M)) + qdel(GG) + return + return ..() + +/obj/machinery/atmospherics/unary/cryo_cell/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return + +/obj/machinery/atmospherics/unary/cryo_cell/screwdriver_act(mob/user, obj/item/I) + if(occupant || on) + to_chat(user, "The maintenance panel is locked.") + return TRUE + if(default_deconstruction_screwdriver(user, "pod0-o", "pod0", I)) + return TRUE + +/obj/machinery/atmospherics/unary/cryo_cell/update_icon() + handle_update_icon() + +/obj/machinery/atmospherics/unary/cryo_cell/proc/handle_update_icon() //making another proc to avoid spam in update_icon + overlays.Cut() //empty the overlay proc, just in case + icon_state = "pod[on]" //set the icon properly every time + + if(!src.occupant) + overlays += "lid[on]" //if no occupant, just put the lid overlay on, and ignore the rest + return + + if(occupant) + var/image/pickle = image(occupant.icon, occupant.icon_state) + pickle.overlays = occupant.overlays + pickle.pixel_y = 22 + + overlays += pickle + overlays += "lid[on]" + if(src.on && !running_bob_animation) //no bobbing if off + var/up = 0 //used to see if we are going up or down, 1 is down, 2 is up + spawn(0) // Without this, the icon update will block. The new thread will die once the occupant leaves. + running_bob_animation = 1 + while(occupant) + overlays -= "lid[on]" //have to remove the overlays first, to force an update- remove cloning pod overlay + overlays -= pickle //remove mob overlay + + switch(pickle.pixel_y) //this looks messy as fuck but it works, switch won't call itself twice + + if(23) //inbetween state, for smoothness + switch(up) //this is set later in the switch, to keep track of where the mob is supposed to go + if(2) //2 is up + pickle.pixel_y = 24 //set to highest + + if(1) //1 is down + pickle.pixel_y = 22 //set to lowest + + if(22) //mob is at it's lowest + pickle.pixel_y = 23 //set to inbetween + up = 2 //have to go up + + if(24) //mob is at it's highest + pickle.pixel_y = 23 //set to inbetween + up = 1 //have to go down + + overlays += pickle //re-add the mob to the icon + overlays += "lid[on]" //re-add the overlay of the pod, they are inside it, not floating + + sleep(7) //don't want to jiggle violently, just slowly bob + running_bob_animation = 0 + +/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() + if(air_contents.total_moles() < 10) + return + if(occupant) + if(occupant.stat == 2 || (occupant.health >= 100 && !occupant.has_mutated_organs())) //Why waste energy on dead or healthy people + occupant.bodytemperature = T0C + return + occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) + occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise + if(occupant.bodytemperature < T0C) + occupant.Sleeping(max(5/efficiency, (1/occupant.bodytemperature)*2000/efficiency)) + occupant.Paralyse(max(5/efficiency, (1/occupant.bodytemperature)*3000/efficiency)) + if(air_contents.oxygen > 2) + if(occupant.getOxyLoss()) + occupant.adjustOxyLoss(-6) + else + occupant.adjustOxyLoss(-1.2) + if(beaker && next_trans == 0) + var/proportion = 10 * min(1/beaker.volume, 1) + // Yes, this means you can get more bang for your buck with a beaker of SF vs a patch + // But it also means a giant beaker of SF won't heal people ridiculously fast 4 cheap + beaker.reagents.reaction(occupant, REAGENT_TOUCH, proportion) + beaker.reagents.trans_to(occupant, 1, 10) + next_trans++ + if(next_trans == 17) + next_trans = 0 + +/obj/machinery/atmospherics/unary/cryo_cell/proc/heat_gas_contents() + if(air_contents.total_moles() < 1) + return + var/air_heat_capacity = air_contents.heat_capacity() + var/combined_heat_capacity = current_heat_capacity + air_heat_capacity + if(combined_heat_capacity > 0) + var/combined_energy = T20C*current_heat_capacity + air_heat_capacity*air_contents.temperature + air_contents.temperature = combined_energy/combined_heat_capacity + +/obj/machinery/atmospherics/unary/cryo_cell/proc/go_out() + if(!occupant) + return + occupant.forceMove(get_step(loc, SOUTH)) //this doesn't account for walls or anything, but i don't forsee that being a problem. + if(occupant.bodytemperature < 261 && occupant.bodytemperature >= 70) //Patch by Aranclanos to stop people from taking burn damage after being ejected + occupant.bodytemperature = 261 + occupant = null + update_icon() + // eject trash the occupant dropped + for(var/atom/movable/A in contents - component_parts - list(beaker)) + A.forceMove(get_step(loc, SOUTH)) + +/// Called when either the occupant is dead and the AUTO_EJECT_DEAD flag is present, OR the occupant is alive, has no external damage, and the AUTO_EJECT_HEALTHY flag is present. +/obj/machinery/atmospherics/unary/cryo_cell/proc/auto_eject(eject_flag) + on = FALSE + go_out() + switch(eject_flag) + if(AUTO_EJECT_HEALTHY) + playsound(loc, 'sound/machines/ding.ogg', 50, 1) + if(AUTO_EJECT_DEAD) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 40) + +/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) + if(!istype(M)) + to_chat(usr, "The cryo cell cannot handle such a lifeform!") + return + if(occupant) + to_chat(usr, "The cryo cell is already occupied!") + return + if(M.abiotic()) + to_chat(usr, "Subject may not have abiotic items on.") + return + if(!node) + to_chat(usr, "The cell is not correctly connected to its pipe network!") + return + M.stop_pulling() + M.forceMove(src) + if(M.health > -100 && (M.health < 0 || M.sleeping)) + to_chat(M, "You feel a cold liquid surround you. Your skin starts to freeze up.") + occupant = M +// M.metabslow = 1 + add_fingerprint(usr) + update_icon() + M.ExtinguishMob() + return 1 + +/obj/machinery/atmospherics/unary/cryo_cell/verb/move_eject() + set name = "Eject occupant" + set category = "Object" + set src in oview(1) + + if(usr == occupant)//If the user is inside the tube... + if(usr.stat == DEAD) + return + to_chat(usr, "Release sequence activated. This will take two minutes.") + sleep(600) + if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already + return + go_out()//and release him from the eternal prison. + else + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + add_attack_logs(usr, occupant, "Ejected from cryo cell at [COORD(src)]") + go_out() + add_fingerprint(usr) + return + +/obj/machinery/atmospherics/unary/cryo_cell/narsie_act() + go_out() + new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! + color = "red"//force the icon to red + light_color = LIGHT_COLOR_RED + +/obj/machinery/atmospherics/unary/cryo_cell/verb/move_inside() + set name = "Move Inside" + set category = "Object" + set src in oview(1) + + if(usr.has_buckled_mobs()) //mob attached to us + to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") + return + + if(stat & (NOPOWER|BROKEN)) + return + + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + + put_mob(usr) + return + + + +/datum/data/function/proc/reset() + return + +/datum/data/function/proc/r_input(href, href_list, mob/user as mob) + return + +/datum/data/function/proc/display() + return + +/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user) + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1) + +/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user) + return //we don't see the pipe network while inside cryo. + +#undef AUTO_EJECT_HEALTHY +#undef AUTO_EJECT_DEAD diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index af2c9779717..de077917adf 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -17,7 +17,7 @@ circuit = /obj/item/circuitboard/cryopodcontrol density = 0 interact_offline = 1 - 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. + 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. resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF flags = NODECONSTRUCT var/mode = null @@ -40,7 +40,7 @@ /obj/machinery/computer/cryopod/New() ..() - for(var/T in potential_theft_objectives) + for(var/T in GLOB.potential_theft_objectives) theft_cache += new T /obj/machinery/computer/cryopod/attack_ai() @@ -275,6 +275,12 @@ return control_computer != null +// So the user can use movement keys to get out of the cryopod +/obj/machinery/cryopod/relaymove(mob/user) + if(user.incapacitated()) + return FALSE + go_out() + /obj/machinery/cryopod/proc/check_occupant_allowed(mob/M) var/correct_type = 0 for(var/type in allow_occupant_types) @@ -416,15 +422,15 @@ // Delete them from datacore. var/announce_rank = null - if(PDA_Manifest.len) - PDA_Manifest.Cut() - for(var/datum/data/record/R in data_core.medical) + if(GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + for(var/datum/data/record/R in GLOB.data_core.medical) if((R.fields["name"] == occupant.real_name)) qdel(R) - for(var/datum/data/record/T in data_core.security) + for(var/datum/data/record/T in GLOB.data_core.security) if((T.fields["name"] == occupant.real_name)) qdel(T) - for(var/datum/data/record/G in data_core.general) + for(var/datum/data/record/G in GLOB.data_core.general) if((G.fields["name"] == occupant.real_name)) announce_rank = G.fields["rank"] qdel(G) diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index 1fb2dfc422d..baf32ab742c 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -400,7 +400,7 @@ while(time) sleep(speed) for(var/i in 1 to speed) - M.setDir(pick(cardinal)) + M.setDir(pick(GLOB.cardinal)) M.resting = !M.resting M.update_canmove() time-- diff --git a/code/game/machinery/defib_mount.dm b/code/game/machinery/defib_mount.dm index ff9609a31bc..9ee32716e53 100644 --- a/code/game/machinery/defib_mount.dm +++ b/code/game/machinery/defib_mount.dm @@ -11,10 +11,13 @@ anchored = TRUE idle_power_usage = 1 power_channel = EQUIP - req_one_access = list(access_medical, access_heads) //used to control clamps + req_one_access = list(ACCESS_MEDICAL, ACCESS_HEADS) //used to control clamps var/obj/item/defibrillator/defib //this mount's defibrillator var/clamps_locked = FALSE //if true, and a defib is loaded, it can't be removed without unlocking the clamps +/obj/machinery/defibrillator_mount/attack_ai() + return + /obj/machinery/defibrillator_mount/get_cell() if(defib) return defib.get_cell() @@ -45,7 +48,7 @@ . = ..() if(defib) . += "There is a defib unit hooked up. Alt-click to remove it." - if(security_level >= SEC_LEVEL_RED) + if(GLOB.security_level >= SEC_LEVEL_RED) . += "Due to a security situation, its locking clamps can be toggled by swiping any ID." else . += "Its locking clamps can be [clamps_locked ? "dis" : ""]engaged by swiping an ID with access." @@ -100,7 +103,7 @@ return var/obj/item/card/id = I.GetID() if(id) - if(check_access(id) || security_level >= SEC_LEVEL_RED) //anyone can toggle the clamps in red alert! + if(check_access(id) || GLOB.security_level >= SEC_LEVEL_RED) //anyone can toggle the clamps in red alert! if(!defib) to_chat(user, "You can't engage the clamps on a defibrillator that isn't there.") return diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index f3f75f3c32c..94a3219c159 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -1,212 +1,215 @@ -#define SINGLE "single" -#define VERTICAL "vertical" -#define HORIZONTAL "horizontal" - -#define METAL 1 -#define WOOD 2 -#define SAND 3 - -//Barricades/cover - -/obj/structure/barricade - name = "chest high wall" - desc = "Looks like this would make good cover." - anchored = TRUE - density = TRUE - max_integrity = 100 - var/proj_pass_rate = 50 //How many projectiles will pass the cover. Lower means stronger cover - var/bar_material = METAL - var/drop_amount = 3 - var/stacktype = /obj/item/stack/sheet/metal - -/obj/structure/barricade/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - make_debris() - qdel(src) - - -/obj/structure/barricade/proc/make_debris() - if(stacktype) - new stacktype(get_turf(src), drop_amount) - -/obj/structure/barricade/welder_act(mob/user, obj/item/I) - if(obj_integrity >= max_integrity) - to_chat(user, "[src] does not need repairs.") - return - if(user.a_intent == INTENT_HARM) - return - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_REPAIR_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume)) - WELDER_REPAIR_SUCCESS_MESSAGE - obj_integrity = Clamp(obj_integrity + 20, 0, max_integrity) - update_icon() - return TRUE - -/obj/structure/barricade/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. - if(locate(/obj/structure/barricade) in get_turf(mover)) - return TRUE - else if(istype(mover, /obj/item/projectile)) - if(!anchored) - return TRUE - var/obj/item/projectile/proj = mover - if(proj.firer && Adjacent(proj.firer)) - return TRUE - if(prob(proj_pass_rate)) - return TRUE - return FALSE - else - return !density - - - -/////BARRICADE TYPES/////// - -/obj/structure/barricade/wooden - name = "wooden barricade" - desc = "This space is blocked off by a wooden barricade." - icon = 'icons/obj/structures.dmi' - icon_state = "woodenbarricade" - bar_material = WOOD - stacktype = /obj/item/stack/sheet/wood - -/obj/structure/barricade/wooden/attackby(obj/item/I, mob/user) - if(istype(I,/obj/item/stack/sheet/wood)) - var/obj/item/stack/sheet/wood/W = I - if(W.amount < 5) - to_chat(user, "You need at least five wooden planks to make a wall!") - return - else - to_chat(user, "You start adding [I] to [src]...") - if(do_after(user, 50, target = src)) - W.use(5) - new /turf/simulated/wall/mineral/wood/nonmetal(get_turf(src)) - qdel(src) - return - return ..() - -/obj/structure/barricade/wooden/crude - name = "crude plank barricade" - desc = "This space is blocked off by a crude assortment of planks." - icon_state = "woodenbarricade-old" - drop_amount = 1 - max_integrity = 50 - proj_pass_rate = 65 - -/obj/structure/barricade/wooden/crude/snow - desc = "This space is blocked off by a crude assortment of planks. It seems to be covered in a layer of snow." - icon_state = "woodenbarricade-snow-old" - max_integrity = 75 - -/obj/structure/barricade/sandbags - name = "sandbags" - desc = "Bags of sand. Self explanatory." - icon = 'icons/obj/smooth_structures/sandbags.dmi' - icon_state = "sandbags" - max_integrity = 280 - proj_pass_rate = 20 - pass_flags = LETPASSTHROW - bar_material = SAND - climbable = TRUE - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/barricade/sandbags, /turf/simulated/wall, /turf/simulated/wall/r_wall, /obj/structure/falsewall, /obj/structure/falsewall/reinforced, /turf/simulated/wall/rust, /turf/simulated/wall/r_wall/rust, /obj/structure/barricade/security) - stacktype = null - -/obj/structure/barricade/security - name = "security barrier" - desc = "A deployable barrier. Provides good cover in fire fights." - icon = 'icons/obj/objects.dmi' - icon_state = "barrier0" - density = FALSE - anchored = FALSE - max_integrity = 180 - proj_pass_rate = 20 - armor = list(melee = 10, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 10, acid = 0) - stacktype = null - var/deploy_time = 40 - var/deploy_message = TRUE - -/obj/structure/barricade/security/New() - ..() - addtimer(CALLBACK(src, .proc/deploy), deploy_time) - -/obj/structure/barricade/security/proc/deploy() - icon_state = "barrier1" - density = TRUE - anchored = TRUE - if(deploy_message) - visible_message("[src] deploys!") - - -/obj/item/grenade/barrier - name = "barrier grenade" - desc = "Instant cover." - icon = 'icons/obj/grenade.dmi' - icon_state = "flashbang" - item_state = "flashbang" - actions_types = list(/datum/action/item_action/toggle_barrier_spread) - var/mode = SINGLE - -/obj/item/grenade/barrier/examine(mob/user) - . = ..() - . += "Alt-click to toggle modes." - -/obj/item/grenade/barrier/AltClick(mob/living/carbon/user) - if(!istype(user) || !user.Adjacent(src) || user.incapacitated()) - return - toggle_mode(user) - -/obj/item/grenade/barrier/proc/toggle_mode(mob/user) - switch(mode) - if(SINGLE) - mode = VERTICAL - if(VERTICAL) - mode = HORIZONTAL - if(HORIZONTAL) - mode = SINGLE - - to_chat(user, "[src] is now in [mode] mode.") - -/obj/item/grenade/barrier/prime() - new /obj/structure/barricade/security(get_turf(loc)) - switch(mode) - if(VERTICAL) - var/target_turf = get_step(src, NORTH) - if(!(is_blocked_turf(target_turf))) - new /obj/structure/barricade/security(target_turf) - - var/target_turf2 = get_step(src, SOUTH) - if(!(is_blocked_turf(target_turf2))) - new /obj/structure/barricade/security(target_turf2) - if(HORIZONTAL) - var/target_turf = get_step(src, EAST) - if(!(is_blocked_turf(target_turf))) - new /obj/structure/barricade/security(target_turf) - - var/target_turf2 = get_step(src, WEST) - if(!(is_blocked_turf(target_turf2))) - new /obj/structure/barricade/security(target_turf2) - qdel(src) - -/obj/item/grenade/barrier/ui_action_click(mob/user) - toggle_mode(user) - - -/obj/structure/barricade/mime - name = "floor" - desc = "Is... this a floor?" - icon = 'icons/effects/water.dmi' - icon_state = "wet_floor_static" - stacktype = /obj/item/stack/sheet/mineral/tranquillite - -/obj/structure/barricade/mime/mrcd - stacktype = null - -#undef SINGLE -#undef VERTICAL -#undef HORIZONTAL - -#undef METAL -#undef WOOD -#undef SAND \ No newline at end of file +#define SINGLE "single" +#define VERTICAL "vertical" +#define HORIZONTAL "horizontal" + +#define METAL 1 +#define WOOD 2 +#define SAND 3 + +//Barricades/cover + +/obj/structure/barricade + name = "chest high wall" + desc = "Looks like this would make good cover." + anchored = TRUE + density = TRUE + max_integrity = 100 + var/proj_pass_rate = 50 //How many projectiles will pass the cover. Lower means stronger cover + var/bar_material = METAL + var/drop_amount = 3 + var/stacktype = /obj/item/stack/sheet/metal + +/obj/structure/barricade/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + make_debris() + qdel(src) + + +/obj/structure/barricade/proc/make_debris() + if(stacktype) + new stacktype(get_turf(src), drop_amount) + +/obj/structure/barricade/welder_act(mob/user, obj/item/I) + if(bar_material != METAL) + return + if(obj_integrity >= max_integrity) + to_chat(user, "[src] does not need repairs.") + return + if(user.a_intent == INTENT_HARM) + return + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_REPAIR_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume)) + WELDER_REPAIR_SUCCESS_MESSAGE + obj_integrity = Clamp(obj_integrity + 20, 0, max_integrity) + update_icon() + return TRUE + +/obj/structure/barricade/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. + if(locate(/obj/structure/barricade) in get_turf(mover)) + return TRUE + else if(istype(mover, /obj/item/projectile)) + if(!anchored) + return TRUE + var/obj/item/projectile/proj = mover + if(proj.firer && Adjacent(proj.firer)) + return TRUE + if(prob(proj_pass_rate)) + return TRUE + return FALSE + else + return !density + + + +/////BARRICADE TYPES/////// + +/obj/structure/barricade/wooden + name = "wooden barricade" + desc = "This space is blocked off by a wooden barricade." + icon = 'icons/obj/structures.dmi' + icon_state = "woodenbarricade" + bar_material = WOOD + stacktype = /obj/item/stack/sheet/wood + + +/obj/structure/barricade/wooden/attackby(obj/item/I, mob/user) + if(istype(I,/obj/item/stack/sheet/wood)) + var/obj/item/stack/sheet/wood/W = I + if(W.amount < 5) + to_chat(user, "You need at least five wooden planks to make a wall!") + return + else + to_chat(user, "You start adding [I] to [src]...") + if(do_after(user, 50, target = src)) + W.use(5) + new /turf/simulated/wall/mineral/wood/nonmetal(get_turf(src)) + qdel(src) + return + return ..() + +/obj/structure/barricade/wooden/crude + name = "crude plank barricade" + desc = "This space is blocked off by a crude assortment of planks." + icon_state = "woodenbarricade-old" + drop_amount = 1 + max_integrity = 50 + proj_pass_rate = 65 + +/obj/structure/barricade/wooden/crude/snow + desc = "This space is blocked off by a crude assortment of planks. It seems to be covered in a layer of snow." + icon_state = "woodenbarricade-snow-old" + max_integrity = 75 + +/obj/structure/barricade/sandbags + name = "sandbags" + desc = "Bags of sand. Self explanatory." + icon = 'icons/obj/smooth_structures/sandbags.dmi' + icon_state = "sandbags" + max_integrity = 280 + proj_pass_rate = 20 + pass_flags = LETPASSTHROW + bar_material = SAND + climbable = TRUE + smooth = SMOOTH_TRUE + canSmoothWith = list(/obj/structure/barricade/sandbags, /turf/simulated/wall, /turf/simulated/wall/r_wall, /obj/structure/falsewall, /obj/structure/falsewall/reinforced, /turf/simulated/wall/rust, /turf/simulated/wall/r_wall/rust, /obj/structure/barricade/security) + stacktype = null + +/obj/structure/barricade/security + name = "security barrier" + desc = "A deployable barrier. Provides good cover in fire fights." + icon = 'icons/obj/objects.dmi' + icon_state = "barrier0" + density = FALSE + anchored = FALSE + max_integrity = 180 + proj_pass_rate = 20 + armor = list(melee = 10, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 10, acid = 0) + stacktype = null + var/deploy_time = 40 + var/deploy_message = TRUE + +/obj/structure/barricade/security/New() + ..() + addtimer(CALLBACK(src, .proc/deploy), deploy_time) + +/obj/structure/barricade/security/proc/deploy() + icon_state = "barrier1" + density = TRUE + anchored = TRUE + if(deploy_message) + visible_message("[src] deploys!") + + +/obj/item/grenade/barrier + name = "barrier grenade" + desc = "Instant cover." + icon = 'icons/obj/grenade.dmi' + icon_state = "flashbang" + item_state = "flashbang" + actions_types = list(/datum/action/item_action/toggle_barrier_spread) + var/mode = SINGLE + +/obj/item/grenade/barrier/examine(mob/user) + . = ..() + . += "Alt-click to toggle modes." + +/obj/item/grenade/barrier/AltClick(mob/living/carbon/user) + if(!istype(user) || !user.Adjacent(src) || user.incapacitated()) + return + toggle_mode(user) + +/obj/item/grenade/barrier/proc/toggle_mode(mob/user) + switch(mode) + if(SINGLE) + mode = VERTICAL + if(VERTICAL) + mode = HORIZONTAL + if(HORIZONTAL) + mode = SINGLE + + to_chat(user, "[src] is now in [mode] mode.") + +/obj/item/grenade/barrier/prime() + new /obj/structure/barricade/security(get_turf(loc)) + switch(mode) + if(VERTICAL) + var/target_turf = get_step(src, NORTH) + if(!(is_blocked_turf(target_turf))) + new /obj/structure/barricade/security(target_turf) + + var/target_turf2 = get_step(src, SOUTH) + if(!(is_blocked_turf(target_turf2))) + new /obj/structure/barricade/security(target_turf2) + if(HORIZONTAL) + var/target_turf = get_step(src, EAST) + if(!(is_blocked_turf(target_turf))) + new /obj/structure/barricade/security(target_turf) + + var/target_turf2 = get_step(src, WEST) + if(!(is_blocked_turf(target_turf2))) + new /obj/structure/barricade/security(target_turf2) + qdel(src) + +/obj/item/grenade/barrier/ui_action_click(mob/user) + toggle_mode(user) + + +/obj/structure/barricade/mime + name = "floor" + desc = "Is... this a floor?" + icon = 'icons/effects/water.dmi' + icon_state = "wet_floor_static" + stacktype = /obj/item/stack/sheet/mineral/tranquillite + +/obj/structure/barricade/mime/mrcd + stacktype = null + +#undef SINGLE +#undef VERTICAL +#undef HORIZONTAL + +#undef METAL +#undef WOOD +#undef SAND diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index f6bbbb3bc02..8e9befd7a6a 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -1,126 +1,126 @@ -/obj/machinery/door_control - name = "remote door-control" - desc = "A remote control-switch for a door." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "doorctrl0" - power_channel = ENVIRON - var/id = null - var/safety_z_check = 1 - var/normaldoorcontrol = 0 - var/desiredstate = 0 // Zero is closed, 1 is open. - var/specialfunctions = 1 - /* - Bitflag, 1= open - 2= idscan, - 4= bolts - 8= shock - 16= door safties - - */ - - var/exposedwires = 0 - var/wires = 3 - /* - Bitflag, 1=checkID - 2=Network Access - */ - - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/door_control/attack_ai(mob/user as mob) - if(wires & 2) - return attack_hand(user) - else - to_chat(user, "Error, no route to host.") - -/obj/machinery/door_control/attackby(obj/item/W, mob/user as mob, params) - if(istype(W, /obj/item/detective_scanner)) - return - return ..() - -/obj/machinery/door_control/emag_act(user as mob) - if(!emagged) - emagged = 1 - req_access = list() - req_one_access = list() - playsound(loc, "sparks", 100, 1) - -/obj/machinery/door_control/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/door_control/attack_hand(mob/user as mob) - add_fingerprint(usr) - if(stat & (NOPOWER|BROKEN)) - return - - if(!allowed(user) && (wires & 1) && !user.can_advanced_admin_interact()) - to_chat(user, "Access Denied.") - flick("doorctrl-denied",src) - return - - use_power(5) - icon_state = "doorctrl1" - add_fingerprint(user) - - if(normaldoorcontrol) - for(var/obj/machinery/door/airlock/D in GLOB.airlocks) - if(safety_z_check && D.z != z) - continue - if(D.id_tag == id) - if(specialfunctions & OPEN) - if(D.density) - spawn(0) - D.open() - return - else - spawn(0) - D.close() - return - if(desiredstate == 1) - if(specialfunctions & IDSCAN) - D.aiDisabledIdScanner = 1 - if(specialfunctions & BOLTS) - D.lock() - if(specialfunctions & SHOCK) - D.electrify(-1) - if(specialfunctions & SAFE) - D.safe = 0 - else - if(specialfunctions & IDSCAN) - D.aiDisabledIdScanner = 0 - if(specialfunctions & BOLTS) - D.unlock() - if(specialfunctions & SHOCK) - D.electrify(0) - if(specialfunctions & SAFE) - D.safe = 1 - - else - for(var/obj/machinery/door/poddoor/M in GLOB.airlocks) - if(safety_z_check && M.z != z) - continue - if(M.id_tag == id) - if(M.density) - spawn( 0 ) - M.open() - return - else - spawn( 0 ) - M.close() - return - - desiredstate = !desiredstate - spawn(15) - if(!(stat & NOPOWER)) - icon_state = "doorctrl0" - -/obj/machinery/door_control/power_change() - ..() - if(stat & NOPOWER) - icon_state = "doorctrl-p" - else - icon_state = "doorctrl0" +/obj/machinery/door_control + name = "remote door-control" + desc = "A remote control-switch for a door." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "doorctrl0" + power_channel = ENVIRON + var/id = null + var/safety_z_check = 1 + var/normaldoorcontrol = 0 + var/desiredstate = 0 // Zero is closed, 1 is open. + var/specialfunctions = 1 + /* + Bitflag, 1= open + 2= idscan, + 4= bolts + 8= shock + 16= door safties + + */ + + var/exposedwires = 0 + var/wires = 3 + /* + Bitflag, 1=checkID + 2=Network Access + */ + + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/door_control/attack_ai(mob/user as mob) + if(wires & 2) + return attack_hand(user) + else + to_chat(user, "Error, no route to host.") + +/obj/machinery/door_control/attackby(obj/item/W, mob/user as mob, params) + if(istype(W, /obj/item/detective_scanner)) + return + return ..() + +/obj/machinery/door_control/emag_act(user as mob) + if(!emagged) + emagged = 1 + req_access = list() + req_one_access = list() + playsound(loc, "sparks", 100, 1) + +/obj/machinery/door_control/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/door_control/attack_hand(mob/user as mob) + add_fingerprint(usr) + if(stat & (NOPOWER|BROKEN)) + return + + if(!allowed(user) && (wires & 1) && !user.can_advanced_admin_interact()) + to_chat(user, "Access Denied.") + flick("doorctrl-denied",src) + return + + use_power(5) + icon_state = "doorctrl1" + add_fingerprint(user) + + if(normaldoorcontrol) + for(var/obj/machinery/door/airlock/D in GLOB.airlocks) + if(safety_z_check && D.z != z) + continue + if(D.id_tag == id) + if(specialfunctions & OPEN) + if(D.density) + spawn(0) + D.open() + return + else + spawn(0) + D.close() + return + if(desiredstate == 1) + if(specialfunctions & IDSCAN) + D.aiDisabledIdScanner = 1 + if(specialfunctions & BOLTS) + D.lock() + if(specialfunctions & SHOCK) + D.electrify(-1) + if(specialfunctions & SAFE) + D.safe = 0 + else + if(specialfunctions & IDSCAN) + D.aiDisabledIdScanner = 0 + if(specialfunctions & BOLTS) + D.unlock() + if(specialfunctions & SHOCK) + D.electrify(0) + if(specialfunctions & SAFE) + D.safe = 1 + + else + for(var/obj/machinery/door/poddoor/M in GLOB.airlocks) + if(safety_z_check && M.z != z) + continue + if(M.id_tag == id) + if(M.density) + spawn( 0 ) + M.open() + return + else + spawn( 0 ) + M.close() + return + + desiredstate = !desiredstate + spawn(15) + if(!(stat & NOPOWER)) + icon_state = "doorctrl0" + +/obj/machinery/door_control/power_change() + ..() + if(stat & NOPOWER) + icon_state = "doorctrl-p" + else + icon_state = "doorctrl0" diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 444c319334b..f69094715b0 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -38,7 +38,7 @@ #define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection #define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection -var/list/airlock_overlays = list() +GLOBAL_LIST_EMPTY(airlock_overlays) /obj/machinery/door/airlock name = "airlock" @@ -276,6 +276,7 @@ About the new airlock wires panel: if(usr) shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") usr.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(usr, src, "Electrified", ATKLOG_ALL) else shockedby += text("\[[time_stamp()]\] - EMP)") message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." @@ -288,8 +289,8 @@ About the new airlock wires panel: // shock user with probability prb (if all connections & power are working) // returns 1 if shocked, 0 otherwise // The preceding comment was borrowed from the grille's shock script -/obj/machinery/door/airlock/shock(mob/user, prb) - if(!arePowerSystemsOn()) +/obj/machinery/door/airlock/shock(mob/living/user, prb) + if(!istype(user) || !arePowerSystemsOn()) return FALSE if(shockCooldown > world.time) return FALSE //Already shocked someone recently? @@ -483,10 +484,10 @@ About the new airlock wires panel: /proc/get_airlock_overlay(icon_state, icon_file) var/iconkey = "[icon_state][icon_file]" - if(airlock_overlays[iconkey]) - return airlock_overlays[iconkey] - airlock_overlays[iconkey] = image(icon_file, icon_state) - return airlock_overlays[iconkey] + if(GLOB.airlock_overlays[iconkey]) + return GLOB.airlock_overlays[iconkey] + GLOB.airlock_overlays[iconkey] = image(icon_file, icon_state) + return GLOB.airlock_overlays[iconkey] /obj/machinery/door/airlock/do_animate(animation) switch(animation) @@ -550,7 +551,7 @@ About the new airlock wires panel: ui.open() ui.set_auto_update(1) -/obj/machinery/door/airlock/ui_data(mob/user, datum/topic_state/state = default_state) +/obj/machinery/door/airlock/ui_data(mob/user, datum/topic_state/state = GLOB.default_state) var/data[0] data["main_power_loss"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) @@ -763,6 +764,7 @@ About the new airlock wires panel: else if(activate) //electrify door for 30 seconds shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") usr.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(usr, src, "Electrified", ATKLOG_ALL) to_chat(usr, "The door is now electrified for thirty seconds.") electrify(30) if("electrify_permanently") @@ -774,6 +776,7 @@ About the new airlock wires panel: else if(activate) shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") usr.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(usr, src, "Electrified", ATKLOG_ALL) to_chat(usr, "The door is now electrified.") electrify(-1) if("open") @@ -866,7 +869,7 @@ About the new airlock wires panel: else if(istype(C, /obj/item/pai_cable)) // -- TLE var/obj/item/pai_cable/cable = C cable.plugin(src, user) - else if(istype(C, /obj/item/paper) || istype(C, /obj/item/photo)) + else if((istype(C, /obj/item/paper) && !istype(C, /obj/item/paper/talisman)) || istype(C, /obj/item/photo)) if(note) to_chat(user, "There's already something pinned to this airlock! Use wirecutters or your hands to remove it.") return @@ -1348,7 +1351,7 @@ About the new airlock wires panel: else user.visible_message("[user] cuts down [note] from [src].", "You remove [note] from [src].") playsound(src, 'sound/items/wirecutter.ogg', 50, 1) - note.forceMove(get_turf(user)) + user.put_in_hands(note) note = null update_icon() return TRUE diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index 01d90795a87..660c6df683e 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -1,102 +1,102 @@ -/obj/item/airlock_electronics - name = "airlock electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50, MAT_GLASS=50) - origin_tech = "engineering=2;programming=1" - req_access = list(access_engine) - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - var/list/conf_access = null - var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access - var/const/max_brain_damage = 60 // Maximum brain damage a mob can have until it can't use the electronics - var/unres_sides = 0 - var/unres_direction = null - -/obj/item/airlock_electronics/attack_self(mob/user) - if(!ishuman(user) && !isrobot(user)) - return ..() - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= max_brain_damage) - to_chat(user, "You forget how to use \the [src].") - return - - var/t1 = text("Access control
    \n") - t1 += "
    " - t1 += " Unrestricted Access Settings
    " - - var/list/Directions = list("North","South",,"East",,,,"West") - for(var/direction in cardinal) - if (unres_direction && unres_direction == direction) - t1 += "[Directions[direction]]
    " - else - t1 += "[Directions[direction]]
    " - - t1 += "
    " - t1 += "Access requirement is set to " - t1 += one_access ? "ONE
    " : "ALL
    " - - t1 += conf_access == null ? "All
    " : "All
    " - - var/list/accesses = get_all_accesses() - for(var/acc in accesses) - var/aname = get_access_desc(acc) - - if(!conf_access || !conf_access.len || !(acc in conf_access)) - t1 += "[aname]
    " - else if(one_access) - t1 += "[aname]
    " - else - t1 += "[aname]
    " - - t1 += "

    Close

    \n" - - var/datum/browser/popup = new(user, "airlock_electronics", name, 400, 400) - popup.set_content(t1) - popup.open(0) - onclose(user, "airlock") - -/obj/item/airlock_electronics/Topic(href, href_list) - ..() - - if(usr.incapacitated() || (!ishuman(usr) && !isrobot(usr))) - return 1 - - if(href_list["close"]) - usr << browse(null, "window=airlock_electronics") - return - - if(href_list["one_access"]) - one_access = !one_access - - if(href_list["access"]) - toggle_access(href_list["access"]) - - if(href_list["unres_direction"]) - unres_direction = text2num(href_list["unres_direction"]) - if (unres_sides == unres_direction) - unres_sides = 0 - unres_direction = null - else - unres_sides = unres_direction - - attack_self(usr) - -/obj/item/airlock_electronics/proc/toggle_access(access) - if(access == "all") - conf_access = null - else - var/req = text2num(access) - - if(conf_access == null) - conf_access = list() - - if(!(req in conf_access)) - conf_access += req - else - conf_access -= req - if(!conf_access.len) - conf_access = null +/obj/item/airlock_electronics + name = "airlock electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50, MAT_GLASS=50) + origin_tech = "engineering=2;programming=1" + req_access = list(ACCESS_ENGINE) + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + var/list/conf_access = null + var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access + var/const/max_brain_damage = 60 // Maximum brain damage a mob can have until it can't use the electronics + var/unres_sides = 0 + var/unres_direction = null + +/obj/item/airlock_electronics/attack_self(mob/user) + if(!ishuman(user) && !isrobot(user)) + return ..() + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= max_brain_damage) + to_chat(user, "You forget how to use \the [src].") + return + + var/t1 = text("Access control
    \n") + t1 += "
    " + t1 += " Unrestricted Access Settings
    " + + var/list/Directions = list("North","South",,"East",,,,"West") + for(var/direction in GLOB.cardinal) + if (unres_direction && unres_direction == direction) + t1 += "[Directions[direction]]
    " + else + t1 += "[Directions[direction]]
    " + + t1 += "
    " + t1 += "Access requirement is set to " + t1 += one_access ? "ONE
    " : "ALL
    " + + t1 += conf_access == null ? "All
    " : "All
    " + + var/list/accesses = get_all_accesses() + for(var/acc in accesses) + var/aname = get_access_desc(acc) + + if(!conf_access || !conf_access.len || !(acc in conf_access)) + t1 += "[aname]
    " + else if(one_access) + t1 += "[aname]
    " + else + t1 += "[aname]
    " + + t1 += "

    Close

    \n" + + var/datum/browser/popup = new(user, "airlock_electronics", name, 400, 400) + popup.set_content(t1) + popup.open(0) + onclose(user, "airlock") + +/obj/item/airlock_electronics/Topic(href, href_list) + ..() + + if(usr.incapacitated() || (!ishuman(usr) && !isrobot(usr))) + return 1 + + if(href_list["close"]) + usr << browse(null, "window=airlock_electronics") + return + + if(href_list["one_access"]) + one_access = !one_access + + if(href_list["access"]) + toggle_access(href_list["access"]) + + if(href_list["unres_direction"]) + unres_direction = text2num(href_list["unres_direction"]) + if (unres_sides == unres_direction) + unres_sides = 0 + unres_direction = null + else + unres_sides = unres_direction + + attack_self(usr) + +/obj/item/airlock_electronics/proc/toggle_access(access) + if(access == "all") + conf_access = null + else + var/req = text2num(access) + + if(conf_access == null) + conf_access = list() + + if(!(req in conf_access)) + conf_access += req + else + conf_access -= req + if(!conf_access.len) + conf_access = null diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index 96e4ec8da1c..fccb77c5e2a 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -180,7 +180,7 @@ PlasmaBurn(exposed_temperature) /obj/machinery/door/airlock/plasma/proc/PlasmaBurn(temperature) - atmos_spawn_air(SPAWN_HEAT | SPAWN_TOXINS, 500) + atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, 500) var/obj/structure/door_assembly/DA DA = new /obj/structure/door_assembly(loc) if(glass) diff --git a/code/game/machinery/doors/alarmlock.dm b/code/game/machinery/doors/alarmlock.dm index d3bf12f72f0..d5c78aa2325 100644 --- a/code/game/machinery/doors/alarmlock.dm +++ b/code/game/machinery/doors/alarmlock.dm @@ -1,44 +1,44 @@ -/obj/machinery/door/airlock/alarmlock - name = "glass alarm airlock" - icon = 'icons/obj/doors/airlocks/station2/glass.dmi' - overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' - opacity = 0 - glass = 1 - autoclose = 0 - var/datum/radio_frequency/air_connection - var/air_frequency = ATMOS_FIRE_FREQ - -/obj/machinery/door/airlock/alarmlock/New() - ..() - air_connection = new - -/obj/machinery/door/airlock/alarmlock/Destroy() - if(SSradio) - SSradio.remove_object(src,air_frequency) - air_connection = null - return ..() - -/obj/machinery/door/airlock/alarmlock/Initialize() - ..() - SSradio.remove_object(src, air_frequency) - air_connection = SSradio.add_object(src, air_frequency, RADIO_TO_AIRALARM) - open() - -/obj/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal) - ..() - if(stat & (NOPOWER|BROKEN)) - return - - var/alarm_area = signal.data["zone"] - var/alert = signal.data["alert"] - - var/area/our_area = get_area(src) - - if(alarm_area == our_area.name) - switch(alert) - if("severe") - autoclose = 1 - close() - if("minor", "clear") - autoclose = 0 - open() +/obj/machinery/door/airlock/alarmlock + name = "glass alarm airlock" + icon = 'icons/obj/doors/airlocks/station2/glass.dmi' + overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' + opacity = 0 + glass = 1 + autoclose = 0 + var/datum/radio_frequency/air_connection + var/air_frequency = ATMOS_FIRE_FREQ + +/obj/machinery/door/airlock/alarmlock/New() + ..() + air_connection = new + +/obj/machinery/door/airlock/alarmlock/Destroy() + if(SSradio) + SSradio.remove_object(src,air_frequency) + air_connection = null + return ..() + +/obj/machinery/door/airlock/alarmlock/Initialize() + ..() + SSradio.remove_object(src, air_frequency) + air_connection = SSradio.add_object(src, air_frequency, RADIO_TO_AIRALARM) + open() + +/obj/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal) + ..() + if(stat & (NOPOWER|BROKEN)) + return + + var/alarm_area = signal.data["zone"] + var/alert = signal.data["alert"] + + var/area/our_area = get_area(src) + + if(alarm_area == our_area.name) + switch(alert) + if("severe") + autoclose = 1 + close() + if("minor", "clear") + autoclose = 0 + open() diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 8bd7af5dad4..29549a05630 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -1,504 +1,504 @@ -#define CHARS_PER_LINE 5 -#define FONT_SIZE "5pt" -#define FONT_COLOR "#09f" -#define FONT_STYLE "Small Fonts" - -/////////////////////////////////////////////////////////////////////////////////////////////// -// Brig Door control displays. -// Description: This is a controls the timer for the brig doors, displays the timer on itself and -// has a popup window when used, allowing to set the timer. -// Code Notes: Combination of old brigdoor.dm code from rev4407 and the status_display.dm code -// Date: 01/September/2010 -// Programmer: Veryinky -///////////////////////////////////////////////////////////////////////////////////////////////// -/obj/machinery/door_timer - name = "door timer" - icon = 'icons/obj/status_display.dmi' - icon_state = "frame" - desc = "A remote control for a door." - req_access = list(access_brig) - anchored = 1 // can't pick it up - density = 0 // can walk through it. - var/id = null // id of door it controls. - var/releasetime = 0 // when world.timeofday reaches it - release the prisoner - var/timing = 0 // boolean, true/1 timer is on, false/0 means it's not timing - var/picture_state // icon_state of alert picture, if not displaying text/numbers - var/list/obj/machinery/targets = list() - var/timetoset = 0 // Used to set releasetime upon starting the timer - var/obj/item/radio/Radio - var/printed = 0 - var/datum/data/record/prisoner - maptext_height = 26 - maptext_width = 32 - maptext_y = -1 - var/occupant = "None" - var/crimes = "None" - var/time = 0 - var/officer = "None" - -/obj/machinery/door_timer/New() - GLOB.celltimers_list += src - return ..() - -/obj/machinery/door_timer/Destroy() - GLOB.celltimers_list -= src - return ..() - -/obj/machinery/door_timer/proc/print_report() - var/logname = input(usr, "Name of the guilty?","[id] log name") - var/logcharges = stripped_multiline_input(usr, "What have they been charged with?","[id] log charges") - - if(!logname || !logcharges) - return 0 - occupant = logname - crimes = logcharges - time = timetoset - officer = usr.name - - for(var/obj/machinery/computer/prisoner/C in GLOB.prisoncomputer_list) - var/obj/item/paper/P = new /obj/item/paper(C.loc) - P.name = "[id] log - [logname] [station_time_timestamp()]" - P.info = "
    [id] - Brig record



    " - P.info += {"
    [station_name()] - Security Department

    -
    Admission data:

    - Log generated at: [station_time_timestamp()]
    - Detainee: [logname]
    - Duration: [seconds_to_time(timetoset / 10)]
    - Charge(s): [logcharges]
    - Arresting Officer: [usr.name]


    - This log file was generated automatically upon activation of a cell timer."} - - playsound(C.loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - GLOB.cell_logs += P - - var/datum/data/record/G = find_record("name", logname, data_core.general) - var/prisoner_drank = "unknown" - var/prisoner_trank = "unknown" - if(G) - if(G.fields["rank"]) - prisoner_drank = G.fields["rank"] - if(G.fields["real_rank"]) // Ignore alt job titles - necessary for lookups - prisoner_trank = G.fields["real_rank"] - - var/datum/data/record/R = find_security_record("name", logname) - - var/announcetext = "Detainee [logname] ([prisoner_drank]) has been incarcerated for [seconds_to_time(timetoset / 10)] for the charges of, '[logcharges]'. \ - Arresting Officer: [usr.name].[R ? "" : " Detainee record not found, manual record update required."]" - Radio.autosay(announcetext, name, "Security", list(z)) - - if(prisoner_trank != "unknown") - notify_dept_head(prisoner_trank, announcetext) - - if(R) - prisoner = R - R.fields["criminal"] = "Incarcerated" - var/mob/living/carbon/human/M = usr - var/rank = "UNKNOWN RANK" - if(istype(M) && M.wear_id) - var/obj/item/card/id/I = M.wear_id - rank = I.assignment - if(!R.fields["comments"] || !islist(R.fields["comments"])) //copied from security computer code because apparently these need to be initialized - R.fields["comments"] = list() - R.fields["comments"] += "Autogenerated by [name] on [current_date_string] [station_time_timestamp()]
    Sentenced to [timetoset/10] seconds for the charges of \"[logcharges]\" by [rank] [usr.name]." - update_all_mob_security_hud() - return 1 - - -/obj/machinery/door_timer/proc/notify_dept_head(jobtitle, antext) - if(!jobtitle || !antext) - return - if(jobtitle == "Civilian") - // Don't notify the HoP about greytiding civilians - return - var/datum/job/brigged_job = SSjobs.GetJob(jobtitle) - if(!brigged_job) - return - if(!brigged_job.department_head[1]) - return - var/boss_title = brigged_job.department_head[1] - - var/obj/item/pda/target_pda - for(var/obj/item/pda/check_pda in PDAs) - if(check_pda.ownrank == boss_title) - target_pda = check_pda - if(!target_pda) - return - var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger) - if(PM && PM.can_receive()) - PM.notify("Message from Brig Timer (Automated), \"[antext]\" (Unable to Reply)") - - -/obj/machinery/door_timer/Initialize() - ..() - - Radio = new /obj/item/radio(src) - Radio.listening = 0 - Radio.config(list("Security" = 0)) - Radio.follow_target = src - - pixel_x = ((dir & 3)? (0) : (dir == 4 ? 32 : -32)) - pixel_y = ((dir & 3)? (dir ==1 ? 32 : -32) : (0)) - - spawn(20) - for(var/obj/machinery/door/window/brigdoor/M in GLOB.airlocks) - if(M.id == id) - targets += M - - for(var/obj/machinery/flasher/F in GLOB.machines) - if(F.id == id) - targets += F - - for(var/obj/structure/closet/secure_closet/brig/C in world) - if(C.id == id) - targets += C - - for(var/obj/machinery/treadmill_monitor/T in GLOB.machines) - if(T.id == id) - targets += T - - if(targets.len==0) - stat |= BROKEN - update_icon() - -/obj/machinery/door_timer/Destroy() - QDEL_NULL(Radio) - targets.Cut() - prisoner = null - return ..() - -//Main door timer loop, if it's timing and time is >0 reduce time by 1. -// if it's less than 0, open door, reset timer -// update the door_timer window and the icon -/obj/machinery/door_timer/process() - if(stat & (NOPOWER|BROKEN)) - return - if(timing) - if(timeleft() <= 0) - Radio.autosay("Timer has expired. Releasing prisoner.", name, "Security", list(z)) - occupant = "None" - timer_end() // open doors, reset timer, clear status screen - timing = 0 - . = PROCESS_KILL - - updateUsrDialog() - update_icon() - else - timer_end() - return PROCESS_KILL - -// has the door power situation changed, if so update icon. -/obj/machinery/door_timer/power_change() - ..() - update_icon() - - -// open/closedoor checks if door_timer has power, if so it checks if the -// linked door is open/closed (by density) then opens it/closes it. - -// Closes and locks doors, power check -/obj/machinery/door_timer/proc/timer_start() - - if(stat & (NOPOWER|BROKEN)) - return 0 - - if(!printed) - if(!print_report()) - timing = 0 - return 0 - - // Set releasetime - releasetime = world.timeofday + timetoset - START_PROCESSING(SSmachines, src) - - for(var/obj/machinery/door/window/brigdoor/door in targets) - if(door.density) - continue - spawn(0) - door.close() - - for(var/obj/structure/closet/secure_closet/brig/C in targets) - if(C.broken) - continue - if(C.opened && !C.close()) - continue - C.locked = 1 - C.icon_state = C.icon_locked - - for(var/obj/machinery/treadmill_monitor/T in targets) - T.total_joules = 0 - T.on = 1 - - return 1 - - -// Opens and unlocks doors, power check -/obj/machinery/door_timer/proc/timer_end() - if(stat & (NOPOWER|BROKEN)) - return 0 - - // Reset vars - occupant = "None" - crimes = "None" - time = 0 - officer = "None" - releasetime = 0 - printed = 0 - if(prisoner) - prisoner.fields["criminal"] = "Released" - update_all_mob_security_hud() - prisoner = null - - for(var/obj/machinery/door/window/brigdoor/door in targets) - if(!door.density) - continue - spawn(0) - door.open() - - for(var/obj/structure/closet/secure_closet/brig/C in targets) - if(C.broken) - continue - if(C.opened) - continue - C.locked = 0 - C.icon_state = C.icon_closed - - for(var/obj/machinery/treadmill_monitor/T in targets) - if(!T.stat) - T.redeem() - T.on = 0 - - return 1 - - -// Check for releasetime timeleft -/obj/machinery/door_timer/proc/timeleft() - var/time = releasetime - world.timeofday - if(time > MIDNIGHT_ROLLOVER / 2) - time -= MIDNIGHT_ROLLOVER - if(time < 0) - return 0 - return time / 10 - -// Set timetoset -/obj/machinery/door_timer/proc/timeset(seconds) - timetoset = seconds * 10 - - if(timetoset <= 0) - timetoset = 0 - - return - -//Allows AIs to use door_timer, see human attack_hand function below -/obj/machinery/door_timer/attack_ai(mob/user) - interact(user) - -/obj/machinery/door_timer/attack_ghost(mob/user) - interact(user) - -//Allows humans to use door_timer -//Opens dialog window when someone clicks on door timer -// Allows altering timer and the timing boolean. -// Flasher activation limited to 150 seconds -/obj/machinery/door_timer/attack_hand(mob/user) - if(..()) - return - interact(user) - -/obj/machinery/door_timer/interact(mob/user) - // Used for the 'time left' display - var/second = round(timeleft() % 60) - var/minute = round((timeleft() - second) / 60) - - // Used for 'set timer' - var/setsecond = round((timetoset / 10) % 60) - var/setminute = round(((timetoset / 10) - setsecond) / 60) - - user.set_machine(src) - - // dat - var/dat = "
    Timer System:" - dat += " Door [id] controls
    " - - // Start/Stop timer - if(timing) - dat += "Stop Timer and open door
    " - else - dat += "Activate Timer and close door
    " - - // Time Left display (uses releasetime) - dat += "Time Left: [(minute ? text("[minute]:") : null)][second]
    " - dat += "
    " - - // Set Timer display (uses timetoset) - if(timing) - dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond] Set
    " - else - dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond]
    " - - // Controls - dat += "Input Time" - - // Mounted flash controls - for(var/obj/machinery/flasher/F in targets) - if(F.last_flash && (F.last_flash + 150) > world.time) - dat += "
    Flash Charging" - else - dat += "
    Activate Flash" - - dat += "

    Close" - - var/datum/browser/popup = new(user, "door_timer", name, 400, 500) - popup.set_content(dat) - popup.open() - - -//Function for using door_timer dialog input, checks if user has permission -// href_list to -// "timing" turns on timer -// "tp" value to modify timer -// "fc" activates flasher -// "change" resets the timer to the timetoset amount while the timer is counting down -// Also updates dialog window and timer icon -/obj/machinery/door_timer/Topic(href, href_list) - if(..()) - return 1 - - if(!allowed(usr) && !usr.can_admin_interact()) - return 1 - - usr.set_machine(src) - - if(href_list["timing"]) - timing = text2num(href_list["timing"]) - - if(timing) - timer_start() - else - timer_end() - if(!isobserver(usr)) //spooky admin ghosts are in your brig, releasing your prisoners - Radio.autosay("Timer stopped manually by [usr.name].", name, "Security", list(z)) - - else - if(href_list["settime"]) - var/time = min(max(round(return_time_input(usr)), 0), 3600) - timeset(time) - - if(href_list["fc"]) - for(var/obj/machinery/flasher/F in targets) - F.flash() - - if(href_list["change"]) - printed = 1 - timer_start() - - add_fingerprint(usr) - updateUsrDialog() - update_icon() - - -//icon update function -// if NOPOWER, display blank -// if BROKEN, display blue screen of death icon AI uses -// if timing=true, run update display function -/obj/machinery/door_timer/update_icon() - if(stat & (NOPOWER)) - icon_state = "frame" - return - if(stat & (BROKEN)) - set_picture("ai_bsod") - return - if(timing) - var/disp1 = id - var/timeleft = timeleft() - var/disp2 = "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" - if(length(disp2) > CHARS_PER_LINE) - disp2 = "Error" - update_display(disp1, disp2) - else - if(maptext) maptext = "" - - -// Adds an icon in case the screen is broken/off, stolen from status_display.dm -/obj/machinery/door_timer/proc/set_picture(state) - picture_state = state - overlays.Cut() - overlays += image('icons/obj/status_display.dmi', icon_state=picture_state) - -/obj/machinery/door_timer/proc/return_time_input() - var/mins = input(usr, "Minutes", "Enter number of minutes", 0) as num - var/seconds = input(usr, "Seconds", "Enter number of seconds", 0) as num - var/totaltime = (seconds + (mins * 60)) - return totaltime - -//Checks to see if there's 1 line or 2, adds text-icons-numbers/letters over display -// Stolen from status_display -/obj/machinery/door_timer/proc/update_display(line1, line2) - line1 = uppertext(line1) - line2 = uppertext(line2) - var/new_text = {"
    [line1]
    [line2]
    "} - if(maptext != new_text) - maptext = new_text - - -//Actual string input to icon display for loop, with 5 pixel x offsets for each letter. -//Stolen from status_display -/obj/machinery/door_timer/proc/texticon(tn, px = 0, py = 0) - var/image/I = image('icons/obj/status_display.dmi', "blank") - var/len = length(tn) - - for(var/d = 1 to len) - var/char = copytext(tn, len-d+1, len-d+2) - if(char == " ") - continue - var/image/ID = image('icons/obj/status_display.dmi', icon_state=char) - ID.pixel_x = -(d-1)*5 + px - ID.pixel_y = py - I.overlays += ID - return I - - -/obj/machinery/door_timer/cell_1 - name = "Cell 1" - id = "Cell 1" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_2 - name = "Cell 2" - id = "Cell 2" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_3 - name = "Cell 3" - id = "Cell 3" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_4 - name = "Cell 4" - id = "Cell 4" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_5 - name = "Cell 5" - id = "Cell 5" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_6 - name = "Cell 6" - id = "Cell 6" - dir = 4 - pixel_x = 32 - -#undef FONT_SIZE -#undef FONT_COLOR -#undef FONT_STYLE -#undef CHARS_PER_LINE +#define CHARS_PER_LINE 5 +#define FONT_SIZE "5pt" +#define FONT_COLOR "#09f" +#define FONT_STYLE "Small Fonts" + +/////////////////////////////////////////////////////////////////////////////////////////////// +// Brig Door control displays. +// Description: This is a controls the timer for the brig doors, displays the timer on itself and +// has a popup window when used, allowing to set the timer. +// Code Notes: Combination of old brigdoor.dm code from rev4407 and the status_display.dm code +// Date: 01/September/2010 +// Programmer: Veryinky +///////////////////////////////////////////////////////////////////////////////////////////////// +/obj/machinery/door_timer + name = "door timer" + icon = 'icons/obj/status_display.dmi' + icon_state = "frame" + desc = "A remote control for a door." + req_access = list(ACCESS_BRIG) + anchored = 1 // can't pick it up + density = 0 // can walk through it. + var/id = null // id of door it controls. + var/releasetime = 0 // when world.timeofday reaches it - release the prisoner + var/timing = 0 // boolean, true/1 timer is on, false/0 means it's not timing + var/picture_state // icon_state of alert picture, if not displaying text/numbers + var/list/obj/machinery/targets = list() + var/timetoset = 0 // Used to set releasetime upon starting the timer + var/obj/item/radio/Radio + var/printed = 0 + var/datum/data/record/prisoner + maptext_height = 26 + maptext_width = 32 + maptext_y = -1 + var/occupant = "None" + var/crimes = "None" + var/time = 0 + var/officer = "None" + +/obj/machinery/door_timer/New() + GLOB.celltimers_list += src + return ..() + +/obj/machinery/door_timer/Destroy() + GLOB.celltimers_list -= src + return ..() + +/obj/machinery/door_timer/proc/print_report() + var/logname = input(usr, "Name of the guilty?","[id] log name") + var/logcharges = stripped_multiline_input(usr, "What have they been charged with?","[id] log charges") + + if(!logname || !logcharges) + return 0 + occupant = logname + crimes = logcharges + time = timetoset + officer = usr.name + + for(var/obj/machinery/computer/prisoner/C in GLOB.prisoncomputer_list) + var/obj/item/paper/P = new /obj/item/paper(C.loc) + P.name = "[id] log - [logname] [station_time_timestamp()]" + P.info = "
    [id] - Brig record



    " + P.info += {"
    [station_name()] - Security Department

    +
    Admission data:

    + Log generated at: [station_time_timestamp()]
    + Detainee: [logname]
    + Duration: [seconds_to_time(timetoset / 10)]
    + Charge(s): [logcharges]
    + Arresting Officer: [usr.name]


    + This log file was generated automatically upon activation of a cell timer."} + + playsound(C.loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) + GLOB.cell_logs += P + + var/datum/data/record/G = find_record("name", logname, GLOB.data_core.general) + var/prisoner_drank = "unknown" + var/prisoner_trank = "unknown" + if(G) + if(G.fields["rank"]) + prisoner_drank = G.fields["rank"] + if(G.fields["real_rank"]) // Ignore alt job titles - necessary for lookups + prisoner_trank = G.fields["real_rank"] + + var/datum/data/record/R = find_security_record("name", logname) + + var/announcetext = "Detainee [logname] ([prisoner_drank]) has been incarcerated for [seconds_to_time(timetoset / 10)] for the charges of, '[logcharges]'. \ + Arresting Officer: [usr.name].[R ? "" : " Detainee record not found, manual record update required."]" + Radio.autosay(announcetext, name, "Security", list(z)) + + if(prisoner_trank != "unknown") + notify_dept_head(prisoner_trank, announcetext) + + if(R) + prisoner = R + R.fields["criminal"] = "Incarcerated" + var/mob/living/carbon/human/M = usr + var/rank = "UNKNOWN RANK" + if(istype(M) && M.wear_id) + var/obj/item/card/id/I = M.wear_id + rank = I.assignment + if(!R.fields["comments"] || !islist(R.fields["comments"])) //copied from security computer code because apparently these need to be initialized + R.fields["comments"] = list() + R.fields["comments"] += "Autogenerated by [name] on [GLOB.current_date_string] [station_time_timestamp()]
    Sentenced to [timetoset/10] seconds for the charges of \"[logcharges]\" by [rank] [usr.name]." + update_all_mob_security_hud() + return 1 + + +/obj/machinery/door_timer/proc/notify_dept_head(jobtitle, antext) + if(!jobtitle || !antext) + return + if(jobtitle == "Civilian") + // Don't notify the HoP about greytiding civilians + return + var/datum/job/brigged_job = SSjobs.GetJob(jobtitle) + if(!brigged_job) + return + if(!brigged_job.department_head[1]) + return + var/boss_title = brigged_job.department_head[1] + + var/obj/item/pda/target_pda + for(var/obj/item/pda/check_pda in GLOB.PDAs) + if(check_pda.ownrank == boss_title) + target_pda = check_pda + if(!target_pda) + return + var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger) + if(PM && PM.can_receive()) + PM.notify("Message from Brig Timer (Automated), \"[antext]\" (Unable to Reply)") + + +/obj/machinery/door_timer/Initialize() + ..() + + Radio = new /obj/item/radio(src) + Radio.listening = 0 + Radio.config(list("Security" = 0)) + Radio.follow_target = src + + pixel_x = ((dir & 3)? (0) : (dir == 4 ? 32 : -32)) + pixel_y = ((dir & 3)? (dir ==1 ? 32 : -32) : (0)) + + spawn(20) + for(var/obj/machinery/door/window/brigdoor/M in GLOB.airlocks) + if(M.id == id) + targets += M + + for(var/obj/machinery/flasher/F in GLOB.machines) + if(F.id == id) + targets += F + + for(var/obj/structure/closet/secure_closet/brig/C in world) + if(C.id == id) + targets += C + + for(var/obj/machinery/treadmill_monitor/T in GLOB.machines) + if(T.id == id) + targets += T + + if(targets.len==0) + stat |= BROKEN + update_icon() + +/obj/machinery/door_timer/Destroy() + QDEL_NULL(Radio) + targets.Cut() + prisoner = null + return ..() + +//Main door timer loop, if it's timing and time is >0 reduce time by 1. +// if it's less than 0, open door, reset timer +// update the door_timer window and the icon +/obj/machinery/door_timer/process() + if(stat & (NOPOWER|BROKEN)) + return + if(timing) + if(timeleft() <= 0) + Radio.autosay("Timer has expired. Releasing prisoner.", name, "Security", list(z)) + occupant = "None" + timer_end() // open doors, reset timer, clear status screen + timing = 0 + . = PROCESS_KILL + + updateUsrDialog() + update_icon() + else + timer_end() + return PROCESS_KILL + +// has the door power situation changed, if so update icon. +/obj/machinery/door_timer/power_change() + ..() + update_icon() + + +// open/closedoor checks if door_timer has power, if so it checks if the +// linked door is open/closed (by density) then opens it/closes it. + +// Closes and locks doors, power check +/obj/machinery/door_timer/proc/timer_start() + + if(stat & (NOPOWER|BROKEN)) + return 0 + + if(!printed) + if(!print_report()) + timing = 0 + return 0 + + // Set releasetime + releasetime = world.timeofday + timetoset + START_PROCESSING(SSmachines, src) + + for(var/obj/machinery/door/window/brigdoor/door in targets) + if(door.density) + continue + spawn(0) + door.close() + + for(var/obj/structure/closet/secure_closet/brig/C in targets) + if(C.broken) + continue + if(C.opened && !C.close()) + continue + C.locked = 1 + C.icon_state = C.icon_locked + + for(var/obj/machinery/treadmill_monitor/T in targets) + T.total_joules = 0 + T.on = 1 + + return 1 + + +// Opens and unlocks doors, power check +/obj/machinery/door_timer/proc/timer_end() + if(stat & (NOPOWER|BROKEN)) + return 0 + + // Reset vars + occupant = "None" + crimes = "None" + time = 0 + officer = "None" + releasetime = 0 + printed = 0 + if(prisoner) + prisoner.fields["criminal"] = "Released" + update_all_mob_security_hud() + prisoner = null + + for(var/obj/machinery/door/window/brigdoor/door in targets) + if(!door.density) + continue + spawn(0) + door.open() + + for(var/obj/structure/closet/secure_closet/brig/C in targets) + if(C.broken) + continue + if(C.opened) + continue + C.locked = 0 + C.icon_state = C.icon_closed + + for(var/obj/machinery/treadmill_monitor/T in targets) + if(!T.stat) + T.redeem() + T.on = 0 + + return 1 + + +// Check for releasetime timeleft +/obj/machinery/door_timer/proc/timeleft() + var/time = releasetime - world.timeofday + if(time > MIDNIGHT_ROLLOVER / 2) + time -= MIDNIGHT_ROLLOVER + if(time < 0) + return 0 + return time / 10 + +// Set timetoset +/obj/machinery/door_timer/proc/timeset(seconds) + timetoset = seconds * 10 + + if(timetoset <= 0) + timetoset = 0 + + return + +//Allows AIs to use door_timer, see human attack_hand function below +/obj/machinery/door_timer/attack_ai(mob/user) + interact(user) + +/obj/machinery/door_timer/attack_ghost(mob/user) + interact(user) + +//Allows humans to use door_timer +//Opens dialog window when someone clicks on door timer +// Allows altering timer and the timing boolean. +// Flasher activation limited to 150 seconds +/obj/machinery/door_timer/attack_hand(mob/user) + if(..()) + return + interact(user) + +/obj/machinery/door_timer/interact(mob/user) + // Used for the 'time left' display + var/second = round(timeleft() % 60) + var/minute = round((timeleft() - second) / 60) + + // Used for 'set timer' + var/setsecond = round((timetoset / 10) % 60) + var/setminute = round(((timetoset / 10) - setsecond) / 60) + + user.set_machine(src) + + // dat + var/dat = "
    Timer System:" + dat += " Door [id] controls
    " + + // Start/Stop timer + if(timing) + dat += "Stop Timer and open door
    " + else + dat += "Activate Timer and close door
    " + + // Time Left display (uses releasetime) + dat += "Time Left: [(minute ? text("[minute]:") : null)][second]
    " + dat += "
    " + + // Set Timer display (uses timetoset) + if(timing) + dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond] Set
    " + else + dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond]
    " + + // Controls + dat += "Input Time" + + // Mounted flash controls + for(var/obj/machinery/flasher/F in targets) + if(F.last_flash && (F.last_flash + 150) > world.time) + dat += "
    Flash Charging" + else + dat += "
    Activate Flash" + + dat += "

    Close" + + var/datum/browser/popup = new(user, "door_timer", name, 400, 500) + popup.set_content(dat) + popup.open() + + +//Function for using door_timer dialog input, checks if user has permission +// href_list to +// "timing" turns on timer +// "tp" value to modify timer +// "fc" activates flasher +// "change" resets the timer to the timetoset amount while the timer is counting down +// Also updates dialog window and timer icon +/obj/machinery/door_timer/Topic(href, href_list) + if(..()) + return 1 + + if(!allowed(usr) && !usr.can_admin_interact()) + return 1 + + usr.set_machine(src) + + if(href_list["timing"]) + timing = text2num(href_list["timing"]) + + if(timing) + timer_start() + else + timer_end() + if(!isobserver(usr)) //spooky admin ghosts are in your brig, releasing your prisoners + Radio.autosay("Timer stopped manually by [usr.name].", name, "Security", list(z)) + + else + if(href_list["settime"]) + var/time = min(max(round(return_time_input(usr)), 0), 3600) + timeset(time) + + if(href_list["fc"]) + for(var/obj/machinery/flasher/F in targets) + F.flash() + + if(href_list["change"]) + printed = 1 + timer_start() + + add_fingerprint(usr) + updateUsrDialog() + update_icon() + + +//icon update function +// if NOPOWER, display blank +// if BROKEN, display blue screen of death icon AI uses +// if timing=true, run update display function +/obj/machinery/door_timer/update_icon() + if(stat & (NOPOWER)) + icon_state = "frame" + return + if(stat & (BROKEN)) + set_picture("ai_bsod") + return + if(timing) + var/disp1 = id + var/timeleft = timeleft() + var/disp2 = "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + if(length(disp2) > CHARS_PER_LINE) + disp2 = "Error" + update_display(disp1, disp2) + else + if(maptext) maptext = "" + + +// Adds an icon in case the screen is broken/off, stolen from status_display.dm +/obj/machinery/door_timer/proc/set_picture(state) + picture_state = state + overlays.Cut() + overlays += image('icons/obj/status_display.dmi', icon_state=picture_state) + +/obj/machinery/door_timer/proc/return_time_input() + var/mins = input(usr, "Minutes", "Enter number of minutes", 0) as num + var/seconds = input(usr, "Seconds", "Enter number of seconds", 0) as num + var/totaltime = (seconds + (mins * 60)) + return totaltime + +//Checks to see if there's 1 line or 2, adds text-icons-numbers/letters over display +// Stolen from status_display +/obj/machinery/door_timer/proc/update_display(line1, line2) + line1 = uppertext(line1) + line2 = uppertext(line2) + var/new_text = {"
    [line1]
    [line2]
    "} + if(maptext != new_text) + maptext = new_text + + +//Actual string input to icon display for loop, with 5 pixel x offsets for each letter. +//Stolen from status_display +/obj/machinery/door_timer/proc/texticon(tn, px = 0, py = 0) + var/image/I = image('icons/obj/status_display.dmi', "blank") + var/len = length(tn) + + for(var/d = 1 to len) + var/char = copytext(tn, len-d+1, len-d+2) + if(char == " ") + continue + var/image/ID = image('icons/obj/status_display.dmi', icon_state=char) + ID.pixel_x = -(d-1)*5 + px + ID.pixel_y = py + I.overlays += ID + return I + + +/obj/machinery/door_timer/cell_1 + name = "Cell 1" + id = "Cell 1" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_2 + name = "Cell 2" + id = "Cell 2" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_3 + name = "Cell 3" + id = "Cell 3" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_4 + name = "Cell 4" + id = "Cell 4" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_5 + name = "Cell 5" + id = "Cell 5" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_6 + name = "Cell 6" + id = "Cell 6" + dir = 4 + pixel_x = 32 + +#undef FONT_SIZE +#undef FONT_COLOR +#undef FONT_STYLE +#undef CHARS_PER_LINE diff --git a/code/game/machinery/doors/checkForMultipleDoors.dm b/code/game/machinery/doors/checkForMultipleDoors.dm index d1c3c2e2d6b..7884e3be989 100644 --- a/code/game/machinery/doors/checkForMultipleDoors.dm +++ b/code/game/machinery/doors/checkForMultipleDoors.dm @@ -1,16 +1,16 @@ -/obj/machinery/door/proc/checkForMultipleDoors() - if(!loc) - return 0 - for(var/obj/machinery/door/D in loc) - if(!istype(D, /obj/machinery/door/window) && D.density) - return 0 - return 1 - -/turf/simulated/wall/proc/checkForMultipleDoors() - if(!loc) - return 0 - for(var/obj/machinery/door/D in locate(x,y,z)) - if(!istype(D, /obj/machinery/door/window) && D.density) - return 0 - //There are no false wall checks because that would be fucking retarded - return 1 \ No newline at end of file +/obj/machinery/door/proc/checkForMultipleDoors() + if(!loc) + return 0 + for(var/obj/machinery/door/D in loc) + if(!istype(D, /obj/machinery/door/window) && D.density) + return 0 + return 1 + +/turf/simulated/wall/proc/checkForMultipleDoors() + if(!loc) + return 0 + for(var/obj/machinery/door/D in locate(x,y,z)) + if(!istype(D, /obj/machinery/door/window) && D.density) + return 0 + //There are no false wall checks because that would be foolish + return 1 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 14d1de97327..2d6fac3bb81 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -1,386 +1,386 @@ -/obj/machinery/door - name = "door" - desc = "It opens and closes." - icon = 'icons/obj/doors/doorint.dmi' - icon_state = "door1" - anchored = TRUE - opacity = 1 - density = TRUE - layer = OPEN_DOOR_LAYER - power_channel = ENVIRON - max_integrity = 350 - armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70) - flags = PREVENT_CLICK_UNDER - damage_deflection = 10 - var/closingLayer = CLOSED_DOOR_LAYER - var/visible = 1 - var/operating = FALSE - var/autoclose = 0 - var/safe = TRUE //whether the door detects things and mobs in its way and reopen or crushes them. - var/locked = FALSE //whether the door is bolted or not. - var/glass = FALSE - var/welded = FALSE - var/normalspeed = 1 - var/auto_close_time = 150 - var/auto_close_time_dangerous = 15 - var/assemblytype //the type of door frame to drop during deconstruction - var/datum/effect_system/spark_spread/spark_system - var/real_explosion_block //ignore this, just use explosion_block - var/heat_proof = FALSE // For rglass-windowed airlocks and firedoors - var/emergency = FALSE - var/unres_sides = 0 //Unrestricted sides. A bitflag for which direction (if any) can open the door with no access - //Multi-tile doors - var/width = 1 - -/obj/machinery/door/New() - ..() - set_init_door_layer() - update_dir() - update_freelook_sight() - GLOB.airlocks += src - spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(2, 1, src) - - //doors only block while dense though so we have to use the proc - real_explosion_block = explosion_block - explosion_block = EXPLOSION_BLOCK_PROC - -/obj/machinery/door/proc/set_init_door_layer() - if(density) - layer = closingLayer - else - layer = initial(layer) - -/obj/machinery/door/setDir(newdir) - ..() - update_dir() - -/obj/machinery/door/power_change() - ..() - update_icon() - -/obj/machinery/door/proc/update_dir() - if(width > 1) - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - -/obj/machinery/door/Initialize() - air_update_turf(1) - ..() - -/obj/machinery/door/Destroy() - density = 0 - air_update_turf(1) - update_freelook_sight() - GLOB.airlocks -= src - QDEL_NULL(spark_system) - return ..() - -/obj/machinery/door/Bumped(atom/AM) - if(operating || emagged) - return - if(ismob(AM)) - var/mob/B = AM - if((isrobot(B)) && B.stat) - return - if(isliving(AM)) - var/mob/living/M = AM - if(world.time - M.last_bumped <= 10) - return //Can bump-open one airlock per second. This is to prevent shock spam. - M.last_bumped = world.time - if(M.restrained() && !check_access(null)) - return - if(M.mob_size > MOB_SIZE_TINY) - bumpopen(M) - return - - if(ismecha(AM)) - var/obj/mecha/mecha = AM - if(density) - if(mecha.occupant) - if(world.time - mecha.occupant.last_bumped <= 10) - return - if(mecha.occupant && allowed(mecha.occupant) || check_access_list(mecha.operation_req_access)) - open() - else - do_animate("deny") - return - -/obj/machinery/door/Move(new_loc, new_dir) - var/turf/T = loc - . = ..() - move_update_air(T) - - if(width > 1) - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - -/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return !opacity - return !density - -/obj/machinery/door/CanAtmosPass() - return !density - -/obj/machinery/door/proc/bumpopen(mob/user) - if(operating) - return - add_fingerprint(user) - - if(density && !emagged) - if(allowed(user)) - open() - if(isbot(user)) - var/mob/living/simple_animal/bot/B = user - B.door_opened(src) - else - do_animate("deny") - -/obj/machinery/door/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/door/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/door/attack_hand(mob/user) - return try_to_activate_door(user) - -/obj/machinery/door/attack_tk(mob/user) - if(!allowed(null)) - return - ..() - -/obj/machinery/door/proc/try_to_activate_door(mob/user) - add_fingerprint(user) - if(operating || emagged) - return - if(requiresID() && (allowed(user) || user.can_advanced_admin_interact())) - if(density) - open() - else - close() - return - if(density) - do_animate("deny") - -/obj/machinery/door/allowed(mob/M) - if(emergency) - return TRUE - if(unrestricted_side(M)) - return TRUE - if(!requiresID()) - return FALSE // Intentional. machinery/door/requiresID() always == 1. airlocks, however, == 0 if ID scan is disabled. Yes, this var is poorly named. - return ..() - -/obj/machinery/door/proc/unrestricted_side(mob/M) //Allows for specific side of airlocks to be unrestrected (IE, can exit maint freely, but need access to enter) - return get_dir(src, M) & unres_sides - -/obj/machinery/door/proc/try_to_crowbar(mob/user, obj/item/I) - return - -/obj/machinery/door/attackby(obj/item/I, mob/user, params) - if(user.a_intent != INTENT_HARM && istype(I, /obj/item/twohanded/fireaxe)) - try_to_crowbar(user, I) - return 1 - else if(!(I.flags & NOBLUDGEON) && user.a_intent != INTENT_HARM) - try_to_activate_door(user) - return 1 - return ..() - - -/obj/machinery/door/crowbar_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - . = TRUE - if(operating) - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - try_to_crowbar(user, I) - -/obj/machinery/door/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - if(damage_amount >= 10 && prob(30)) - spark_system.start() - -/obj/machinery/door/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(glass) - playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) - else if(damage_amount) - playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE) - else - playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/machinery/door/emag_act(mob/user) - if(density) - flick("door_spark", src) - sleep(6) - open() - emagged = 1 - return 1 - -/obj/machinery/door/emp_act(severity) - if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) - spawn(0) - open() - ..() - -/obj/machinery/door/update_icon() - if(density) - icon_state = "door1" - else - icon_state = "door0" - -/obj/machinery/door/proc/do_animate(animation) - switch(animation) - if("opening") - if(panel_open) - flick("o_doorc0", src) - else - flick("doorc0", src) - if("closing") - if(panel_open) - flick("o_doorc1", src) - else - flick("doorc1", src) - if("deny") - if(!stat) - flick("door_deny", src) - -/obj/machinery/door/proc/open() - if(!density) - return TRUE - if(operating) - return - operating = TRUE - do_animate("opening") - set_opacity(0) - sleep(5) - density = FALSE - sleep(5) - layer = initial(layer) - update_icon() - set_opacity(0) - operating = FALSE - air_update_turf(1) - update_freelook_sight() - if(autoclose) - autoclose_in(normalspeed ? auto_close_time : auto_close_time_dangerous) - return TRUE - -/obj/machinery/door/proc/close() - if(density) - return TRUE - if(operating || welded) - return - if(safe) - for(var/turf/turf in locs) - for(var/atom/movable/M in turf) - if(M.density && M != src) //something is blocking the door - if(autoclose) - autoclose_in(60) - return - - operating = TRUE - - do_animate("closing") - layer = closingLayer - sleep(5) - density = TRUE - sleep(5) - update_icon() - if(visible && !glass) - set_opacity(1) - operating = 0 - air_update_turf(1) - update_freelook_sight() - if(safe) - CheckForMobs() - else - crush() - return TRUE - -/obj/machinery/door/proc/CheckForMobs() - if(locate(/mob/living) in get_turf(src)) - sleep(1) - open() - -/obj/machinery/door/proc/crush() - for(var/mob/living/L in get_turf(src)) - L.visible_message("[src] closes on [L], crushing [L.p_them()]!", "[src] closes on you and crushes you!") - if(isalien(L)) //For xenos - L.adjustBruteLoss(DOOR_CRUSH_DAMAGE * 1.5) //Xenos go into crit after aproximately the same amount of crushes as humans. - L.emote("roar") - else if(ishuman(L)) //For humans - L.adjustBruteLoss(DOOR_CRUSH_DAMAGE) - if(L.stat == CONSCIOUS) - L.emote("scream") - L.Weaken(5) - else //for simple_animals & borgs - L.adjustBruteLoss(DOOR_CRUSH_DAMAGE) - var/turf/location = get_turf(src) - L.add_splatter_floor(location) - for(var/obj/mecha/M in get_turf(src)) - M.take_damage(DOOR_CRUSH_DAMAGE) - -/obj/machinery/door/proc/requiresID() - return 1 - -/obj/machinery/door/proc/hasPower() - return !(stat & NOPOWER) - -/obj/machinery/door/proc/autoclose() - if(!QDELETED(src) && !density && !operating && !locked && !welded && autoclose) - close() - -/obj/machinery/door/proc/autoclose_in(wait) - addtimer(CALLBACK(src, .proc/autoclose), wait, TIMER_UNIQUE | TIMER_NO_HASH_WAIT | TIMER_OVERRIDE) - -/obj/machinery/door/proc/update_freelook_sight() - if(!glass && cameranet) - cameranet.updateVisibility(src, 0) - -/obj/machinery/door/BlockSuperconductivity() // All non-glass airlocks block heat, this is intended. - if(opacity || heat_proof) - return 1 - return 0 - -/obj/machinery/door/morgue - icon = 'icons/obj/doors/doormorgue.dmi' - -/obj/machinery/door/proc/lock() - return - -/obj/machinery/door/proc/unlock() - return - -/obj/machinery/door/proc/hostile_lockdown(mob/origin) - if(!stat) //So that only powered doors are closed. - close() //Close ALL the doors! - -/obj/machinery/door/proc/disable_lockdown() - if(!stat) //Opens only powered doors. - open() //Open everything! - -/obj/machinery/door/ex_act(severity) - //if it blows up a wall it should blow up a door - ..(severity ? max(1, severity - 1) : 0) - - -/obj/machinery/door/GetExplosionBlock() - return density ? real_explosion_block : 0 +/obj/machinery/door + name = "door" + desc = "It opens and closes." + icon = 'icons/obj/doors/doorint.dmi' + icon_state = "door1" + anchored = TRUE + opacity = 1 + density = TRUE + layer = OPEN_DOOR_LAYER + power_channel = ENVIRON + max_integrity = 350 + armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70) + flags = PREVENT_CLICK_UNDER + damage_deflection = 10 + var/closingLayer = CLOSED_DOOR_LAYER + var/visible = 1 + var/operating = FALSE + var/autoclose = 0 + var/safe = TRUE //whether the door detects things and mobs in its way and reopen or crushes them. + var/locked = FALSE //whether the door is bolted or not. + var/glass = FALSE + var/welded = FALSE + var/normalspeed = 1 + var/auto_close_time = 150 + var/auto_close_time_dangerous = 15 + var/assemblytype //the type of door frame to drop during deconstruction + var/datum/effect_system/spark_spread/spark_system + var/real_explosion_block //ignore this, just use explosion_block + var/heat_proof = FALSE // For rglass-windowed airlocks and firedoors + var/emergency = FALSE + var/unres_sides = 0 //Unrestricted sides. A bitflag for which direction (if any) can open the door with no access + //Multi-tile doors + var/width = 1 + +/obj/machinery/door/New() + ..() + set_init_door_layer() + update_dir() + update_freelook_sight() + GLOB.airlocks += src + spark_system = new /datum/effect_system/spark_spread + spark_system.set_up(2, 1, src) + + //doors only block while dense though so we have to use the proc + real_explosion_block = explosion_block + explosion_block = EXPLOSION_BLOCK_PROC + +/obj/machinery/door/proc/set_init_door_layer() + if(density) + layer = closingLayer + else + layer = initial(layer) + +/obj/machinery/door/setDir(newdir) + ..() + update_dir() + +/obj/machinery/door/power_change() + ..() + update_icon() + +/obj/machinery/door/proc/update_dir() + if(width > 1) + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + +/obj/machinery/door/Initialize() + air_update_turf(1) + ..() + +/obj/machinery/door/Destroy() + density = 0 + air_update_turf(1) + update_freelook_sight() + GLOB.airlocks -= src + QDEL_NULL(spark_system) + return ..() + +/obj/machinery/door/Bumped(atom/AM) + if(operating || emagged) + return + if(ismob(AM)) + var/mob/B = AM + if((isrobot(B)) && B.stat) + return + if(isliving(AM)) + var/mob/living/M = AM + if(world.time - M.last_bumped <= 10) + return //Can bump-open one airlock per second. This is to prevent shock spam. + M.last_bumped = world.time + if(M.restrained() && !check_access(null)) + return + if(M.mob_size > MOB_SIZE_TINY) + bumpopen(M) + return + + if(ismecha(AM)) + var/obj/mecha/mecha = AM + if(density) + if(mecha.occupant) + if(world.time - mecha.occupant.last_bumped <= 10) + return + if(mecha.occupant && allowed(mecha.occupant) || check_access_list(mecha.operation_req_access)) + open() + else + do_animate("deny") + return + +/obj/machinery/door/Move(new_loc, new_dir) + var/turf/T = loc + . = ..() + move_update_air(T) + + if(width > 1) + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + +/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return !opacity + return !density + +/obj/machinery/door/CanAtmosPass() + return !density + +/obj/machinery/door/proc/bumpopen(mob/user) + if(operating) + return + add_fingerprint(user) + + if(density && !emagged) + if(allowed(user)) + open() + if(isbot(user)) + var/mob/living/simple_animal/bot/B = user + B.door_opened(src) + else + do_animate("deny") + +/obj/machinery/door/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/door/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/door/attack_hand(mob/user) + return try_to_activate_door(user) + +/obj/machinery/door/attack_tk(mob/user) + if(!allowed(null)) + return + ..() + +/obj/machinery/door/proc/try_to_activate_door(mob/user) + add_fingerprint(user) + if(operating || emagged) + return + if(requiresID() && (allowed(user) || user.can_advanced_admin_interact())) + if(density) + open() + else + close() + return + if(density) + do_animate("deny") + +/obj/machinery/door/allowed(mob/M) + if(emergency) + return TRUE + if(unrestricted_side(M)) + return TRUE + if(!requiresID()) + return FALSE // Intentional. machinery/door/requiresID() always == 1. airlocks, however, == 0 if ID scan is disabled. Yes, this var is poorly named. + return ..() + +/obj/machinery/door/proc/unrestricted_side(mob/M) //Allows for specific side of airlocks to be unrestrected (IE, can exit maint freely, but need access to enter) + return get_dir(src, M) & unres_sides + +/obj/machinery/door/proc/try_to_crowbar(mob/user, obj/item/I) + return + +/obj/machinery/door/attackby(obj/item/I, mob/user, params) + if(user.a_intent != INTENT_HARM && istype(I, /obj/item/twohanded/fireaxe)) + try_to_crowbar(user, I) + return 1 + else if(!(I.flags & NOBLUDGEON) && user.a_intent != INTENT_HARM) + try_to_activate_door(user) + return 1 + return ..() + + +/obj/machinery/door/crowbar_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + . = TRUE + if(operating) + return + if(!I.use_tool(src, user, 0, volume = 0)) + return + try_to_crowbar(user, I) + +/obj/machinery/door/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + if(damage_amount >= 10 && prob(30)) + spark_system.start() + +/obj/machinery/door/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(glass) + playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) + else if(damage_amount) + playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/machinery/door/emag_act(mob/user) + if(density) + flick("door_spark", src) + sleep(6) + open() + emagged = 1 + return 1 + +/obj/machinery/door/emp_act(severity) + if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) + spawn(0) + open() + ..() + +/obj/machinery/door/update_icon() + if(density) + icon_state = "door1" + else + icon_state = "door0" + +/obj/machinery/door/proc/do_animate(animation) + switch(animation) + if("opening") + if(panel_open) + flick("o_doorc0", src) + else + flick("doorc0", src) + if("closing") + if(panel_open) + flick("o_doorc1", src) + else + flick("doorc1", src) + if("deny") + if(!stat) + flick("door_deny", src) + +/obj/machinery/door/proc/open() + if(!density) + return TRUE + if(operating) + return + operating = TRUE + do_animate("opening") + set_opacity(0) + sleep(5) + density = FALSE + sleep(5) + layer = initial(layer) + update_icon() + set_opacity(0) + operating = FALSE + air_update_turf(1) + update_freelook_sight() + if(autoclose) + autoclose_in(normalspeed ? auto_close_time : auto_close_time_dangerous) + return TRUE + +/obj/machinery/door/proc/close() + if(density) + return TRUE + if(operating || welded) + return + if(safe) + for(var/turf/turf in locs) + for(var/atom/movable/M in turf) + if(M.density && M != src) //something is blocking the door + if(autoclose) + autoclose_in(60) + return + + operating = TRUE + + do_animate("closing") + layer = closingLayer + sleep(5) + density = TRUE + sleep(5) + update_icon() + if(visible && !glass) + set_opacity(1) + operating = 0 + air_update_turf(1) + update_freelook_sight() + if(safe) + CheckForMobs() + else + crush() + return TRUE + +/obj/machinery/door/proc/CheckForMobs() + if(locate(/mob/living) in get_turf(src)) + sleep(1) + open() + +/obj/machinery/door/proc/crush() + for(var/mob/living/L in get_turf(src)) + L.visible_message("[src] closes on [L], crushing [L.p_them()]!", "[src] closes on you and crushes you!") + if(isalien(L)) //For xenos + L.adjustBruteLoss(DOOR_CRUSH_DAMAGE * 1.5) //Xenos go into crit after aproximately the same amount of crushes as humans. + L.emote("roar") + else if(ishuman(L)) //For humans + L.adjustBruteLoss(DOOR_CRUSH_DAMAGE) + if(L.stat == CONSCIOUS) + L.emote("scream") + L.Weaken(5) + else //for simple_animals & borgs + L.adjustBruteLoss(DOOR_CRUSH_DAMAGE) + var/turf/location = get_turf(src) + L.add_splatter_floor(location) + for(var/obj/mecha/M in get_turf(src)) + M.take_damage(DOOR_CRUSH_DAMAGE) + +/obj/machinery/door/proc/requiresID() + return 1 + +/obj/machinery/door/proc/hasPower() + return !(stat & NOPOWER) + +/obj/machinery/door/proc/autoclose() + if(!QDELETED(src) && !density && !operating && !locked && !welded && autoclose) + close() + +/obj/machinery/door/proc/autoclose_in(wait) + addtimer(CALLBACK(src, .proc/autoclose), wait, TIMER_UNIQUE | TIMER_NO_HASH_WAIT | TIMER_OVERRIDE) + +/obj/machinery/door/proc/update_freelook_sight() + if(!glass && GLOB.cameranet) + GLOB.cameranet.updateVisibility(src, 0) + +/obj/machinery/door/BlockSuperconductivity() // All non-glass airlocks block heat, this is intended. + if(opacity || heat_proof) + return 1 + return 0 + +/obj/machinery/door/morgue + icon = 'icons/obj/doors/doormorgue.dmi' + +/obj/machinery/door/proc/lock() + return + +/obj/machinery/door/proc/unlock() + return + +/obj/machinery/door/proc/hostile_lockdown(mob/origin) + if(!stat) //So that only powered doors are closed. + close() //Close ALL the doors! + +/obj/machinery/door/proc/disable_lockdown() + if(!stat) //Opens only powered doors. + open() //Open everything! + +/obj/machinery/door/ex_act(severity) + //if it blows up a wall it should blow up a door + ..(severity ? max(1, severity - 1) : 0) + + +/obj/machinery/door/GetExplosionBlock() + return density ? real_explosion_block : 0 diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 17b5756e581..448c410c1c8 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -1,493 +1,490 @@ -#define CONSTRUCTION_COMPLETE 0 //No construction done - functioning as normal -#define CONSTRUCTION_PANEL_OPEN 1 //Maintenance panel is open, still functioning -#define CONSTRUCTION_WIRES_EXPOSED 2 //Cover plate is removed, wires are available -#define CONSTRUCTION_GUTTED 3 //Wires are removed, circuit ready to remove -#define CONSTRUCTION_NOCIRCUIT 4 //Circuit board removed, can safely weld apart - -/var/const/FD_OPEN = 1 -/var/const/FD_CLOSED = 2 - -/obj/machinery/door/firedoor - name = "firelock" - desc = "Apply crowbar." - icon = 'icons/obj/doors/doorfireglass.dmi' - icon_state = "door_open" - opacity = 0 - density = FALSE - max_integrity = 300 - resistance_flags = FIRE_PROOF - heat_proof = TRUE - glass = TRUE - explosion_block = 1 - safe = FALSE - layer = BELOW_OPEN_DOOR_LAYER - closingLayer = CLOSED_FIREDOOR_LAYER - auto_close_time = 50 - assemblytype = /obj/structure/firelock_frame - armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 95, "acid" = 70) - var/can_force = TRUE - var/force_open_time = 300 - var/can_crush = TRUE - var/nextstate = null - var/boltslocked = TRUE - var/active_alarm = FALSE - -/obj/machinery/door/firedoor/examine(mob/user) - . = ..() - if(!density) - . += "It is open, but could be pried closed." - else if(!welded) - . += "It is closed, but could be pried open. Deconstruction would require it to be welded shut." - else if(boltslocked) - . += "It is welded shut. The floor bolts have been locked by screws." - else - . += "The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor." - -/obj/machinery/door/firedoor/closed - icon_state = "door_closed" - opacity = TRUE - density = TRUE - -/obj/machinery/door/firedoor/Bumped(atom/AM) - if(panel_open || operating) - return - if(!density) - return ..() - return 0 - -/obj/machinery/door/firedoor/power_change() - if(powered(power_channel)) - stat &= ~NOPOWER - latetoggle() - else - stat |= NOPOWER - update_icon() - -/obj/machinery/door/firedoor/attack_hand(mob/user) - if(operating || !density) - return - - add_fingerprint(user) - user.changeNext_move(CLICK_CD_MELEE) - - if(can_force && (!glass || user.a_intent != INTENT_HELP)) - user.visible_message("[user] begins forcing \the [src].", \ - "You begin forcing \the [src].") - if(do_after(user, force_open_time, target = src)) - user.visible_message("[user] forces \the [src].", \ - "You force \the [src].") - open() - else if(glass) - user.visible_message("[user] bangs on \the [src].", - "You bang on \the [src].") - playsound(get_turf(src), 'sound/effects/glassknock.ogg', 10, 1) - -/obj/machinery/door/firedoor/attackby(obj/item/C, mob/user, params) - add_fingerprint(user) - - if(operating) - return - return ..() - -/obj/machinery/door/firedoor/try_to_activate_door(mob/user) - return - -/obj/machinery/door/firedoor/screwdriver_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - if(operating || !welded) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - user.visible_message("[user] [boltslocked ? "unlocks" : "locks"] [src]'s bolts.", \ - "You [boltslocked ? "unlock" : "lock"] [src]'s floor bolts.") - boltslocked = !boltslocked - -/obj/machinery/door/firedoor/wrench_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - if(operating || !welded) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(boltslocked) - to_chat(user, "There are screws locking the bolts in place!") - return - user.visible_message("[user] starts undoing [src]'s bolts...", \ - "You start unfastening [src]'s floor bolts...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume) || boltslocked) - return - user.visible_message("[user] unfastens [src]'s bolts.", \ - "You undo [src]'s floor bolts.") - deconstruct(TRUE) - -/obj/machinery/door/firedoor/welder_act(mob/user, obj/item/I) - if(!density) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_WELD_MESSAGE - if(!I.use_tool(src, user, 40, volume = I.tool_volume)) - return - if(!density) //In case someone opens it while it's getting welded - return - WELDER_WELD_SUCCESS_MESSAGE - welded = !welded - update_icon() - -/obj/machinery/door/firedoor/try_to_crowbar(obj/item/I, mob/user) - if(welded || operating) - return - if(density) - open() - else - close() - -/obj/machinery/door/firedoor/attack_ai(mob/user) - forcetoggle() - -/obj/machinery/door/firedoor/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - forcetoggle(TRUE) - -/obj/machinery/door/firedoor/attack_alien(mob/user) - add_fingerprint(user) - if(welded) - to_chat(user, "[src] refuses to budge!") - return - open() - -/obj/machinery/door/firedoor/do_animate(animation) - switch(animation) - if("opening") - flick("door_opening", src) - playsound(src, 'sound/machines/airlock_ext_open.ogg', 30, 1) - if("closing") - flick("door_closing", src) - playsound(src, 'sound/machines/airlock_ext_close.ogg', 30, 1) - -/obj/machinery/door/firedoor/update_icon() - overlays.Cut() - if(active_alarm && hasPower()) - overlays += image('icons/obj/doors/doorfire.dmi', "alarmlights") - if(density) - icon_state = "door_closed" - if(welded) - overlays += "welded" - else - icon_state = "door_open" - if(welded) - overlays += "welded_open" - -/obj/machinery/door/firedoor/proc/activate_alarm() - active_alarm = TRUE - update_icon() - -/obj/machinery/door/firedoor/proc/deactivate_alarm() - active_alarm = FALSE - update_icon() - -/obj/machinery/door/firedoor/open(auto_close = TRUE) - if(welded) - return - . = ..() - latetoggle(auto_close) - - if(auto_close) - autoclose = TRUE - -/obj/machinery/door/firedoor/close() - . = ..() - latetoggle() - -/obj/machinery/door/firedoor/autoclose() - if(active_alarm) - . = ..() - -/obj/machinery/door/firedoor/proc/latetoggle(auto_close = TRUE) - if(operating || !hasPower() || !nextstate) - return - switch(nextstate) - if(FD_OPEN) - nextstate = null - open(auto_close) - if(FD_CLOSED) - nextstate = null - close() - -/obj/machinery/door/firedoor/proc/forcetoggle(magic = FALSE, auto_close = TRUE) - if(!magic && (operating || !hasPower())) - return - if(density) - open(auto_close) - else - close() - -/obj/machinery/door/firedoor/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - var/obj/structure/firelock_frame/F = new assemblytype(get_turf(src)) - if(disassembled) - F.constructionStep = CONSTRUCTION_PANEL_OPEN - else - F.constructionStep = CONSTRUCTION_WIRES_EXPOSED - F.obj_integrity = F.max_integrity * 0.5 - F.update_icon() - qdel(src) - -/obj/machinery/door/firedoor/border_only - icon = 'icons/obj/doors/edge_doorfire.dmi' - flags = ON_BORDER - can_crush = FALSE - -/obj/machinery/door/firedoor/border_only/closed - icon_state = "door_closed" - opacity = TRUE - density = TRUE - -/obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border - return !density - else - return 1 - -/obj/machinery/door/firedoor/border_only/CheckExit(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) - return !density - else - return 1 - -/obj/machinery/door/firedoor/border_only/CanAtmosPass(turf/T) - if(get_dir(loc, T) == dir) - return !density - else - return 1 - -/obj/machinery/door/firedoor/heavy - name = "heavy firelock" - icon = 'icons/obj/doors/doorfire.dmi' - glass = FALSE - opacity = 1 - explosion_block = 2 - assemblytype = /obj/structure/firelock_frame/heavy - can_force = FALSE - max_integrity = 550 - -/obj/item/firelock_electronics - name = "firelock electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - desc = "A circuit board used in construction of firelocks." - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50, MAT_GLASS=50) - origin_tech = "engineering=2;programming=1" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - -/obj/structure/firelock_frame - name = "firelock frame" - desc = "A partially completed firelock." - icon = 'icons/obj/doors/doorfire.dmi' - icon_state = "frame1" - anchored = FALSE - density = TRUE - var/constructionStep = CONSTRUCTION_NOCIRCUIT - var/reinforced = 0 - -/obj/structure/firelock_frame/examine(mob/user) - . = ..() - switch(constructionStep) - if(CONSTRUCTION_PANEL_OPEN) - . += "It is unbolted from the floor. A small loosely connected metal plate is covering the wires." - if(!reinforced) - . += "It could be reinforced with plasteel." - if(CONSTRUCTION_WIRES_EXPOSED) - . += "The maintenance plate has been pried away, and wires are trailing." - if(CONSTRUCTION_GUTTED) - . += "The maintenance panel is missing wires and the circuit board is loosely connected." - if(CONSTRUCTION_NOCIRCUIT) - . += "There are no firelock electronics in the frame. The frame could be cut apart." - -/obj/structure/firelock_frame/update_icon() - ..() - icon_state = "frame[constructionStep]" - -/obj/structure/firelock_frame/attackby(obj/item/C, mob/user) - switch(constructionStep) - if(CONSTRUCTION_PANEL_OPEN) - if(istype(C, /obj/item/stack/sheet/plasteel)) - var/obj/item/stack/sheet/plasteel/P = C - if(reinforced) - to_chat(user, "[src] is already reinforced.") - return - if(P.get_amount() < 2) - to_chat(user, "You need more plasteel to reinforce [src].") - return - user.visible_message("[user] begins reinforcing [src]...", \ - "You begin reinforcing [src]...") - playsound(get_turf(src), C.usesound, 50, 1) - if(do_after(user, 60 * C.toolspeed, target = src)) - if(constructionStep != CONSTRUCTION_PANEL_OPEN || reinforced || P.get_amount() < 2 || !P) - return - user.visible_message("[user] reinforces [src].", \ - "You reinforce [src].") - playsound(get_turf(src), C.usesound, 50, 1) - P.use(2) - reinforced = 1 - return - if(CONSTRUCTION_GUTTED) - if(iscoil(C)) - var/obj/item/stack/cable_coil/B = C - if(B.get_amount() < 5) - to_chat(user, "You need more wires to add wiring to [src].") - return - user.visible_message("[user] begins wiring [src]...", \ - "You begin adding wires to [src]...") - playsound(get_turf(src), B.usesound, 50, 1) - if(do_after(user, 60 * B.toolspeed, target = src)) - if(constructionStep != CONSTRUCTION_GUTTED || B.get_amount() < 5 || !B) - return - user.visible_message("[user] adds wires to [src].", \ - "You wire [src].") - playsound(get_turf(src), B.usesound, 50, 1) - B.use(5) - constructionStep = CONSTRUCTION_WIRES_EXPOSED - update_icon() - return - if(CONSTRUCTION_NOCIRCUIT) - if(istype(C, /obj/item/firelock_electronics)) - user.visible_message("[user] starts adding [C] to [src]...", \ - "You begin adding a circuit board to [src]...") - playsound(get_turf(src), C.usesound, 50, 1) - if(!do_after(user, 40 * C.toolspeed, target = src)) - return - if(constructionStep != CONSTRUCTION_NOCIRCUIT) - return - user.drop_item() - qdel(C) - user.visible_message("[user] adds a circuit to [src].", \ - "You insert and secure [C].") - playsound(get_turf(src), C.usesound, 50, 1) - constructionStep = CONSTRUCTION_GUTTED - update_icon() - return - return ..() - -/obj/structure/firelock_frame/crowbar_act(mob/user, obj/item/I) - if(!(constructionStep in list(CONSTRUCTION_WIRES_EXPOSED, CONSTRUCTION_PANEL_OPEN, CONSTRUCTION_GUTTED))) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(constructionStep == CONSTRUCTION_WIRES_EXPOSED) - user.visible_message("[user] starts prying a metal plate into [src]...", \ - "You begin prying the cover plate back onto [src]...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) - return - user.visible_message("[user] pries the metal plate into [src].", \ - "You pry [src]'s cover plate into place, hiding the wires.") - constructionStep = CONSTRUCTION_PANEL_OPEN - else if(constructionStep == CONSTRUCTION_PANEL_OPEN) - user.visible_message("[user] starts prying something out from [src]...", \ - "You begin prying out the wire cover...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_PANEL_OPEN) - return - user.visible_message("[user] pries out a metal plate from [src], exposing the wires.", \ - "You remove the cover plate from [src], exposing the wires.") - constructionStep = CONSTRUCTION_WIRES_EXPOSED - else if(constructionStep == CONSTRUCTION_GUTTED) - user.visible_message("[user] begins removing the circuit board from [src]...", \ - "You begin prying out the circuit board from [src]...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_GUTTED) - return - user.visible_message("[user] removes [src]'s circuit board.", \ - "You remove the circuit board from [src].") - new /obj/item/firelock_electronics(get_turf(src)) - constructionStep = CONSTRUCTION_NOCIRCUIT - update_icon() - -/obj/structure/firelock_frame/wirecutter_act(mob/user, obj/item/I) - if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) - return - . = TRUE - if(!I.tool_start_check(user, 0)) - return - - user.visible_message("[user] starts cutting the wires from [src]...", \ - "You begin removing [src]'s wires...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) - return - user.visible_message("[user] removes the wires from [src].", \ - "You remove the wiring from [src], exposing the circuit board.") - var/obj/item/stack/cable_coil/B = new(get_turf(src)) - B.amount = 5 - constructionStep = CONSTRUCTION_GUTTED - update_icon() - -/obj/structure/firelock_frame/wrench_act(mob/user, obj/item/I) - if(constructionStep != CONSTRUCTION_PANEL_OPEN) - return - . = TRUE - if(locate(/obj/machinery/door/firedoor) in get_turf(src)) - to_chat(user, "There's already a firelock there.") - return - if(!I.tool_start_check(user, 0)) - return - user.visible_message("[user] starts bolting down [src]...", \ - "You begin bolting [src]...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(locate(/obj/machinery/door/firedoor) in get_turf(src)) - return - user.visible_message("[user] finishes the firelock.", \ - "You finish the firelock.") - if(reinforced) - new /obj/machinery/door/firedoor/heavy(get_turf(src)) - else - new /obj/machinery/door/firedoor(get_turf(src)) - qdel(src) - - - - - -/obj/structure/firelock_frame/welder_act(mob/user, obj/item/I) - if(constructionStep != CONSTRUCTION_NOCIRCUIT) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_SLICING_MESSAGE - if(!I.use_tool(src, user, 40, amount = 1, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_NOCIRCUIT) - return - WELDER_SLICING_SUCCESS_MESSAGE - new /obj/item/stack/sheet/metal(drop_location(), 3) - if(reinforced) - new /obj/item/stack/sheet/plasteel(drop_location(), 2) - qdel(src) - -/obj/structure/firelock_frame/heavy - name = "heavy firelock frame" - reinforced = 1 - -#undef CONSTRUCTION_COMPLETE -#undef CONSTRUCTION_PANEL_OPEN -#undef CONSTRUCTION_WIRES_EXPOSED -#undef CONSTRUCTION_GUTTED -#undef CONSTRUCTION_NOCIRCUIT +#define CONSTRUCTION_COMPLETE 0 //No construction done - functioning as normal +#define CONSTRUCTION_PANEL_OPEN 1 //Maintenance panel is open, still functioning +#define CONSTRUCTION_WIRES_EXPOSED 2 //Cover plate is removed, wires are available +#define CONSTRUCTION_GUTTED 3 //Wires are removed, circuit ready to remove +#define CONSTRUCTION_NOCIRCUIT 4 //Circuit board removed, can safely weld apart + +/obj/machinery/door/firedoor + name = "firelock" + desc = "Apply crowbar." + icon = 'icons/obj/doors/doorfireglass.dmi' + icon_state = "door_open" + opacity = 0 + density = FALSE + max_integrity = 300 + resistance_flags = FIRE_PROOF + heat_proof = TRUE + glass = TRUE + explosion_block = 1 + safe = FALSE + layer = BELOW_OPEN_DOOR_LAYER + closingLayer = CLOSED_FIREDOOR_LAYER + auto_close_time = 50 + assemblytype = /obj/structure/firelock_frame + armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 95, "acid" = 70) + var/can_force = TRUE + var/force_open_time = 300 + var/can_crush = TRUE + var/nextstate = null + var/boltslocked = TRUE + var/active_alarm = FALSE + +/obj/machinery/door/firedoor/examine(mob/user) + . = ..() + if(!density) + . += "It is open, but could be pried closed." + else if(!welded) + . += "It is closed, but could be pried open. Deconstruction would require it to be welded shut." + else if(boltslocked) + . += "It is welded shut. The floor bolts have been locked by screws." + else + . += "The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor." + +/obj/machinery/door/firedoor/closed + icon_state = "door_closed" + opacity = TRUE + density = TRUE + +/obj/machinery/door/firedoor/Bumped(atom/AM) + if(panel_open || operating) + return + if(!density) + return ..() + return 0 + +/obj/machinery/door/firedoor/power_change() + if(powered(power_channel)) + stat &= ~NOPOWER + latetoggle() + else + stat |= NOPOWER + update_icon() + +/obj/machinery/door/firedoor/attack_hand(mob/user) + if(operating || !density) + return + + add_fingerprint(user) + user.changeNext_move(CLICK_CD_MELEE) + + if(can_force && (!glass || user.a_intent != INTENT_HELP)) + user.visible_message("[user] begins forcing \the [src].", \ + "You begin forcing \the [src].") + if(do_after(user, force_open_time, target = src)) + user.visible_message("[user] forces \the [src].", \ + "You force \the [src].") + open() + else if(glass) + user.visible_message("[user] bangs on \the [src].", + "You bang on \the [src].") + playsound(get_turf(src), 'sound/effects/glassknock.ogg', 10, 1) + +/obj/machinery/door/firedoor/attackby(obj/item/C, mob/user, params) + add_fingerprint(user) + + if(operating) + return + return ..() + +/obj/machinery/door/firedoor/try_to_activate_door(mob/user) + return + +/obj/machinery/door/firedoor/screwdriver_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + if(operating || !welded) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + user.visible_message("[user] [boltslocked ? "unlocks" : "locks"] [src]'s bolts.", \ + "You [boltslocked ? "unlock" : "lock"] [src]'s floor bolts.") + boltslocked = !boltslocked + +/obj/machinery/door/firedoor/wrench_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + if(operating || !welded) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(boltslocked) + to_chat(user, "There are screws locking the bolts in place!") + return + user.visible_message("[user] starts undoing [src]'s bolts...", \ + "You start unfastening [src]'s floor bolts...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume) || boltslocked) + return + user.visible_message("[user] unfastens [src]'s bolts.", \ + "You undo [src]'s floor bolts.") + deconstruct(TRUE) + +/obj/machinery/door/firedoor/welder_act(mob/user, obj/item/I) + if(!density) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(!I.use_tool(src, user, 40, volume = I.tool_volume)) + return + if(!density) //In case someone opens it while it's getting welded + return + WELDER_WELD_SUCCESS_MESSAGE + welded = !welded + update_icon() + +/obj/machinery/door/firedoor/try_to_crowbar(obj/item/I, mob/user) + if(welded || operating) + return + if(density) + open() + else + close() + +/obj/machinery/door/firedoor/attack_ai(mob/user) + forcetoggle() + +/obj/machinery/door/firedoor/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + forcetoggle(TRUE) + +/obj/machinery/door/firedoor/attack_alien(mob/user) + add_fingerprint(user) + if(welded) + to_chat(user, "[src] refuses to budge!") + return + open() + +/obj/machinery/door/firedoor/do_animate(animation) + switch(animation) + if("opening") + flick("door_opening", src) + playsound(src, 'sound/machines/airlock_ext_open.ogg', 30, 1) + if("closing") + flick("door_closing", src) + playsound(src, 'sound/machines/airlock_ext_close.ogg', 30, 1) + +/obj/machinery/door/firedoor/update_icon() + overlays.Cut() + if(active_alarm && hasPower()) + overlays += image('icons/obj/doors/doorfire.dmi', "alarmlights") + if(density) + icon_state = "door_closed" + if(welded) + overlays += "welded" + else + icon_state = "door_open" + if(welded) + overlays += "welded_open" + +/obj/machinery/door/firedoor/proc/activate_alarm() + active_alarm = TRUE + update_icon() + +/obj/machinery/door/firedoor/proc/deactivate_alarm() + active_alarm = FALSE + update_icon() + +/obj/machinery/door/firedoor/open(auto_close = TRUE) + if(welded) + return + . = ..() + latetoggle(auto_close) + + if(auto_close) + autoclose = TRUE + +/obj/machinery/door/firedoor/close() + . = ..() + latetoggle() + +/obj/machinery/door/firedoor/autoclose() + if(active_alarm) + . = ..() + +/obj/machinery/door/firedoor/proc/latetoggle(auto_close = TRUE) + if(operating || !hasPower() || !nextstate) + return + switch(nextstate) + if(FD_OPEN) + nextstate = null + open(auto_close) + if(FD_CLOSED) + nextstate = null + close() + +/obj/machinery/door/firedoor/proc/forcetoggle(magic = FALSE, auto_close = TRUE) + if(!magic && (operating || !hasPower())) + return + if(density) + open(auto_close) + else + close() + +/obj/machinery/door/firedoor/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + var/obj/structure/firelock_frame/F = new assemblytype(get_turf(src)) + if(disassembled) + F.constructionStep = CONSTRUCTION_PANEL_OPEN + else + F.constructionStep = CONSTRUCTION_WIRES_EXPOSED + F.obj_integrity = F.max_integrity * 0.5 + F.update_icon() + qdel(src) + +/obj/machinery/door/firedoor/border_only + icon = 'icons/obj/doors/edge_doorfire.dmi' + flags = ON_BORDER + can_crush = FALSE + +/obj/machinery/door/firedoor/border_only/closed + icon_state = "door_closed" + opacity = TRUE + density = TRUE + +/obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + return !density + else + return 1 + +/obj/machinery/door/firedoor/border_only/CheckExit(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) + return !density + else + return 1 + +/obj/machinery/door/firedoor/border_only/CanAtmosPass(turf/T) + if(get_dir(loc, T) == dir) + return !density + else + return 1 + +/obj/machinery/door/firedoor/heavy + name = "heavy firelock" + icon = 'icons/obj/doors/doorfire.dmi' + glass = FALSE + opacity = 1 + explosion_block = 2 + assemblytype = /obj/structure/firelock_frame/heavy + can_force = FALSE + max_integrity = 550 + +/obj/item/firelock_electronics + name = "firelock electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + desc = "A circuit board used in construction of firelocks." + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50, MAT_GLASS=50) + origin_tech = "engineering=2;programming=1" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + +/obj/structure/firelock_frame + name = "firelock frame" + desc = "A partially completed firelock." + icon = 'icons/obj/doors/doorfire.dmi' + icon_state = "frame1" + anchored = FALSE + density = TRUE + var/constructionStep = CONSTRUCTION_NOCIRCUIT + var/reinforced = 0 + +/obj/structure/firelock_frame/examine(mob/user) + . = ..() + switch(constructionStep) + if(CONSTRUCTION_PANEL_OPEN) + . += "It is unbolted from the floor. A small loosely connected metal plate is covering the wires." + if(!reinforced) + . += "It could be reinforced with plasteel." + if(CONSTRUCTION_WIRES_EXPOSED) + . += "The maintenance plate has been pried away, and wires are trailing." + if(CONSTRUCTION_GUTTED) + . += "The maintenance panel is missing wires and the circuit board is loosely connected." + if(CONSTRUCTION_NOCIRCUIT) + . += "There are no firelock electronics in the frame. The frame could be cut apart." + +/obj/structure/firelock_frame/update_icon() + ..() + icon_state = "frame[constructionStep]" + +/obj/structure/firelock_frame/attackby(obj/item/C, mob/user) + switch(constructionStep) + if(CONSTRUCTION_PANEL_OPEN) + if(istype(C, /obj/item/stack/sheet/plasteel)) + var/obj/item/stack/sheet/plasteel/P = C + if(reinforced) + to_chat(user, "[src] is already reinforced.") + return + if(P.get_amount() < 2) + to_chat(user, "You need more plasteel to reinforce [src].") + return + user.visible_message("[user] begins reinforcing [src]...", \ + "You begin reinforcing [src]...") + playsound(get_turf(src), C.usesound, 50, 1) + if(do_after(user, 60 * C.toolspeed, target = src)) + if(constructionStep != CONSTRUCTION_PANEL_OPEN || reinforced || P.get_amount() < 2 || !P) + return + user.visible_message("[user] reinforces [src].", \ + "You reinforce [src].") + playsound(get_turf(src), C.usesound, 50, 1) + P.use(2) + reinforced = 1 + return + if(CONSTRUCTION_GUTTED) + if(iscoil(C)) + var/obj/item/stack/cable_coil/B = C + if(B.get_amount() < 5) + to_chat(user, "You need more wires to add wiring to [src].") + return + user.visible_message("[user] begins wiring [src]...", \ + "You begin adding wires to [src]...") + playsound(get_turf(src), B.usesound, 50, 1) + if(do_after(user, 60 * B.toolspeed, target = src)) + if(constructionStep != CONSTRUCTION_GUTTED || B.get_amount() < 5 || !B) + return + user.visible_message("[user] adds wires to [src].", \ + "You wire [src].") + playsound(get_turf(src), B.usesound, 50, 1) + B.use(5) + constructionStep = CONSTRUCTION_WIRES_EXPOSED + update_icon() + return + if(CONSTRUCTION_NOCIRCUIT) + if(istype(C, /obj/item/firelock_electronics)) + user.visible_message("[user] starts adding [C] to [src]...", \ + "You begin adding a circuit board to [src]...") + playsound(get_turf(src), C.usesound, 50, 1) + if(!do_after(user, 40 * C.toolspeed, target = src)) + return + if(constructionStep != CONSTRUCTION_NOCIRCUIT) + return + user.drop_item() + qdel(C) + user.visible_message("[user] adds a circuit to [src].", \ + "You insert and secure [C].") + playsound(get_turf(src), C.usesound, 50, 1) + constructionStep = CONSTRUCTION_GUTTED + update_icon() + return + return ..() + +/obj/structure/firelock_frame/crowbar_act(mob/user, obj/item/I) + if(!(constructionStep in list(CONSTRUCTION_WIRES_EXPOSED, CONSTRUCTION_PANEL_OPEN, CONSTRUCTION_GUTTED))) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(constructionStep == CONSTRUCTION_WIRES_EXPOSED) + user.visible_message("[user] starts prying a metal plate into [src]...", \ + "You begin prying the cover plate back onto [src]...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) + return + user.visible_message("[user] pries the metal plate into [src].", \ + "You pry [src]'s cover plate into place, hiding the wires.") + constructionStep = CONSTRUCTION_PANEL_OPEN + else if(constructionStep == CONSTRUCTION_PANEL_OPEN) + user.visible_message("[user] starts prying something out from [src]...", \ + "You begin prying out the wire cover...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_PANEL_OPEN) + return + user.visible_message("[user] pries out a metal plate from [src], exposing the wires.", \ + "You remove the cover plate from [src], exposing the wires.") + constructionStep = CONSTRUCTION_WIRES_EXPOSED + else if(constructionStep == CONSTRUCTION_GUTTED) + user.visible_message("[user] begins removing the circuit board from [src]...", \ + "You begin prying out the circuit board from [src]...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_GUTTED) + return + user.visible_message("[user] removes [src]'s circuit board.", \ + "You remove the circuit board from [src].") + new /obj/item/firelock_electronics(get_turf(src)) + constructionStep = CONSTRUCTION_NOCIRCUIT + update_icon() + +/obj/structure/firelock_frame/wirecutter_act(mob/user, obj/item/I) + if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) + return + . = TRUE + if(!I.tool_start_check(user, 0)) + return + + user.visible_message("[user] starts cutting the wires from [src]...", \ + "You begin removing [src]'s wires...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) + return + user.visible_message("[user] removes the wires from [src].", \ + "You remove the wiring from [src], exposing the circuit board.") + var/obj/item/stack/cable_coil/B = new(get_turf(src)) + B.amount = 5 + constructionStep = CONSTRUCTION_GUTTED + update_icon() + +/obj/structure/firelock_frame/wrench_act(mob/user, obj/item/I) + if(constructionStep != CONSTRUCTION_PANEL_OPEN) + return + . = TRUE + if(locate(/obj/machinery/door/firedoor) in get_turf(src)) + to_chat(user, "There's already a firelock there.") + return + if(!I.tool_start_check(user, 0)) + return + user.visible_message("[user] starts bolting down [src]...", \ + "You begin bolting [src]...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(locate(/obj/machinery/door/firedoor) in get_turf(src)) + return + user.visible_message("[user] finishes the firelock.", \ + "You finish the firelock.") + if(reinforced) + new /obj/machinery/door/firedoor/heavy(get_turf(src)) + else + new /obj/machinery/door/firedoor(get_turf(src)) + qdel(src) + + + + + +/obj/structure/firelock_frame/welder_act(mob/user, obj/item/I) + if(constructionStep != CONSTRUCTION_NOCIRCUIT) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(!I.use_tool(src, user, 40, amount = 1, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_NOCIRCUIT) + return + WELDER_SLICING_SUCCESS_MESSAGE + new /obj/item/stack/sheet/metal(drop_location(), 3) + if(reinforced) + new /obj/item/stack/sheet/plasteel(drop_location(), 2) + qdel(src) + +/obj/structure/firelock_frame/heavy + name = "heavy firelock frame" + reinforced = 1 + +#undef CONSTRUCTION_COMPLETE +#undef CONSTRUCTION_PANEL_OPEN +#undef CONSTRUCTION_WIRES_EXPOSED +#undef CONSTRUCTION_GUTTED +#undef CONSTRUCTION_NOCIRCUIT diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm index 4d8fbf01974..c0dce817189 100644 --- a/code/game/machinery/doors/poddoor.dm +++ b/code/game/machinery/doors/poddoor.dm @@ -1,126 +1,135 @@ -/obj/machinery/door/poddoor - name = "blast door" - desc = "A heavy duty blast door that opens mechanically." - icon = 'icons/obj/doors/blastdoor.dmi' - icon_state = "closed" - layer = BLASTDOOR_LAYER - closingLayer = CLOSED_BLASTDOOR_LAYER - explosion_block = 3 - heat_proof = TRUE - safe = FALSE - max_integrity = 600 - armor = list("melee" = 50, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) - resistance_flags = FIRE_PROOF - damage_deflection = 70 - var/id_tag = 1.0 - var/protected = 1 - -/obj/machinery/door/poddoor/preopen - icon_state = "open" - density = FALSE - opacity = 0 - -/obj/machinery/door/poddoor/impassable - name = "reinforced blast door" - desc = "A heavy duty blast door that opens mechanically. Looks even tougher than usual." - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/machinery/door/poddoor/impassable/emag_act(mob/user) - to_chat(user, "The electronic systems in this door are far too advanced for your primitive hacking peripherals.") - return - -/obj/machinery/door/poddoor/Bumped(atom/AM) - if(density) - return - else - return 0 - -//"BLAST" doors are obviously stronger than regular doors when it comes to BLASTS. -/obj/machinery/door/poddoor/ex_act(severity) - if(severity == 3) - return - ..() - -/obj/machinery/door/poddoor/do_animate(animation) - switch(animation) - if("opening") - flick("opening", src) - playsound(src, 'sound/machines/blastdoor.ogg', 30, 1) - if("closing") - flick("closing", src) - playsound(src, 'sound/machines/blastdoor.ogg', 30, 1) - -/obj/machinery/door/poddoor/update_icon() - if(density) - icon_state = "closed" - else - icon_state = "open" - -/obj/machinery/door/poddoor/try_to_activate_door(mob/user) - return - -/obj/machinery/door/poddoor/try_to_crowbar(obj/item/I, mob/user) - if(!hasPower()) - open() - - // Whoever wrote the old code for multi-tile spesspod doors needs to burn in hell. - Unknown - // Wise words. - Bxil -/obj/machinery/door/poddoor/multi_tile - name = "large pod door" - layer = CLOSED_DOOR_LAYER - closingLayer = CLOSED_DOOR_LAYER - -/obj/machinery/door/poddoor/multi_tile/New() - . = ..() - apply_opacity_to_my_turfs(opacity) - -/obj/machinery/door/poddoor/multi_tile/open() - if(..()) - apply_opacity_to_my_turfs(opacity) - - -/obj/machinery/door/poddoor/multi_tile/close() - if(..()) - apply_opacity_to_my_turfs(opacity) - -/obj/machinery/door/poddoor/multi_tile/Destroy() - apply_opacity_to_my_turfs(0) - return ..() - -//Multi-tile poddoors don't turn invisible automatically, so we change the opacity of the turfs below instead one by one. -/obj/machinery/door/poddoor/multi_tile/proc/apply_opacity_to_my_turfs(var/new_opacity) - for(var/turf/T in locs) - T.opacity = new_opacity - T.has_opaque_atom = new_opacity - T.reconsider_lights() - update_freelook_sight() - -/obj/machinery/door/poddoor/multi_tile/four_tile_ver - icon = 'icons/obj/doors/1x4blast_vert.dmi' - width = 4 - dir = NORTH - -/obj/machinery/door/poddoor/multi_tile/three_tile_ver - icon = 'icons/obj/doors/1x3blast_vert.dmi' - width = 3 - dir = NORTH - -/obj/machinery/door/poddoor/multi_tile/two_tile_ver - icon = 'icons/obj/doors/1x2blast_vert.dmi' - width = 2 - dir = NORTH - -/obj/machinery/door/poddoor/multi_tile/four_tile_hor - icon = 'icons/obj/doors/1x4blast_hor.dmi' - width = 4 - dir = EAST - -/obj/machinery/door/poddoor/multi_tile/three_tile_hor - icon = 'icons/obj/doors/1x3blast_hor.dmi' - width = 3 - dir = EAST - -/obj/machinery/door/poddoor/multi_tile/two_tile_hor - icon = 'icons/obj/doors/1x2blast_hor.dmi' - width = 2 - dir = EAST +/obj/machinery/door/poddoor + name = "blast door" + desc = "A heavy duty blast door that opens mechanically." + icon = 'icons/obj/doors/blastdoor.dmi' + icon_state = "closed" + layer = BLASTDOOR_LAYER + closingLayer = CLOSED_BLASTDOOR_LAYER + explosion_block = 3 + heat_proof = TRUE + safe = FALSE + max_integrity = 600 + armor = list("melee" = 50, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) + resistance_flags = FIRE_PROOF + damage_deflection = 70 + var/id_tag = 1.0 + var/protected = 1 + +/obj/machinery/door/poddoor/preopen + icon_state = "open" + density = FALSE + opacity = 0 + +/obj/machinery/door/poddoor/impassable + name = "reinforced blast door" + desc = "A heavy duty blast door that opens mechanically. Looks even tougher than usual." + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/machinery/door/poddoor/impassable/emag_act(mob/user) + to_chat(user, "The electronic systems in this door are far too advanced for your primitive hacking peripherals.") + return + +/obj/machinery/door/poddoor/Bumped(atom/AM) + if(density) + return + else + return 0 + +//"BLAST" doors are obviously stronger than regular doors when it comes to BLASTS. +/obj/machinery/door/poddoor/ex_act(severity) + if(severity == 3) + return + ..() + +/obj/machinery/door/poddoor/do_animate(animation) + switch(animation) + if("opening") + flick("opening", src) + playsound(src, 'sound/machines/blastdoor.ogg', 30, 1) + if("closing") + flick("closing", src) + playsound(src, 'sound/machines/blastdoor.ogg', 30, 1) + +/obj/machinery/door/poddoor/update_icon() + if(density) + icon_state = "closed" + else + icon_state = "open" + +/obj/machinery/door/poddoor/try_to_activate_door(mob/user) + return + +/obj/machinery/door/poddoor/try_to_crowbar(mob/user, obj/item/I) + if(!density) + return + if(!hasPower()) + to_chat(user, "You start forcing [src] open...") + if(do_after(user, 50 * I.toolspeed, target = src)) + if(!hasPower()) + open() + else + to_chat(user, "[src] resists your efforts to force it!") + else + to_chat(user, "[src] resists your efforts to force it!") + + // Whoever wrote the old code for multi-tile spesspod doors needs to burn in hell. - Unknown + // Wise words. - Bxil +/obj/machinery/door/poddoor/multi_tile + name = "large pod door" + layer = CLOSED_DOOR_LAYER + closingLayer = CLOSED_DOOR_LAYER + +/obj/machinery/door/poddoor/multi_tile/New() + . = ..() + apply_opacity_to_my_turfs(opacity) + +/obj/machinery/door/poddoor/multi_tile/open() + if(..()) + apply_opacity_to_my_turfs(opacity) + + +/obj/machinery/door/poddoor/multi_tile/close() + if(..()) + apply_opacity_to_my_turfs(opacity) + +/obj/machinery/door/poddoor/multi_tile/Destroy() + apply_opacity_to_my_turfs(0) + return ..() + +//Multi-tile poddoors don't turn invisible automatically, so we change the opacity of the turfs below instead one by one. +/obj/machinery/door/poddoor/multi_tile/proc/apply_opacity_to_my_turfs(var/new_opacity) + for(var/turf/T in locs) + T.opacity = new_opacity + T.has_opaque_atom = new_opacity + T.reconsider_lights() + update_freelook_sight() + +/obj/machinery/door/poddoor/multi_tile/four_tile_ver + icon = 'icons/obj/doors/1x4blast_vert.dmi' + width = 4 + dir = NORTH + +/obj/machinery/door/poddoor/multi_tile/three_tile_ver + icon = 'icons/obj/doors/1x3blast_vert.dmi' + width = 3 + dir = NORTH + +/obj/machinery/door/poddoor/multi_tile/two_tile_ver + icon = 'icons/obj/doors/1x2blast_vert.dmi' + width = 2 + dir = NORTH + +/obj/machinery/door/poddoor/multi_tile/four_tile_hor + icon = 'icons/obj/doors/1x4blast_hor.dmi' + width = 4 + dir = EAST + +/obj/machinery/door/poddoor/multi_tile/three_tile_hor + icon = 'icons/obj/doors/1x3blast_hor.dmi' + width = 3 + dir = EAST + +/obj/machinery/door/poddoor/multi_tile/two_tile_hor + icon = 'icons/obj/doors/1x2blast_hor.dmi' + width = 2 + dir = EAST diff --git a/code/game/machinery/doors/shutters.dm b/code/game/machinery/doors/shutters.dm index e09d40689e2..457b056b7f9 100644 --- a/code/game/machinery/doors/shutters.dm +++ b/code/game/machinery/doors/shutters.dm @@ -1,14 +1,14 @@ -/obj/machinery/door/poddoor/shutters - gender = PLURAL - name = "shutters" - desc = "Heavy duty metal shutters that open mechanically." - icon = 'icons/obj/doors/shutters.dmi' - layer = SHUTTER_LAYER - closingLayer = SHUTTER_LAYER - damage_deflection = 20 - dir = EAST - -/obj/machinery/door/poddoor/shutters/preopen - icon_state = "open" - density = FALSE - opacity = 0 +/obj/machinery/door/poddoor/shutters + gender = PLURAL + name = "shutters" + desc = "Heavy duty metal shutters that open mechanically." + icon = 'icons/obj/doors/shutters.dmi' + layer = SHUTTER_LAYER + closingLayer = SHUTTER_LAYER + damage_deflection = 20 + dir = EAST + +/obj/machinery/door/poddoor/shutters/preopen + icon_state = "open" + density = FALSE + opacity = 0 diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index ae87480e929..e423f92683c 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -1,472 +1,472 @@ -/obj/machinery/door/window - name = "interior door" - desc = "A strong door." - icon = 'icons/obj/doors/windoor.dmi' - icon_state = "left" - layer = ABOVE_WINDOW_LAYER - closingLayer = ABOVE_WINDOW_LAYER - resistance_flags = ACID_PROOF - visible = 0 - flags = ON_BORDER - opacity = 0 - dir = EAST - max_integrity = 150 //If you change this, consider changing ../door/window/brigdoor/ max_integrity at the bottom of this .dm file - integrity_failure = 0 - armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 70, "acid" = 100) - var/obj/item/airlock_electronics/electronics - var/base_state = "left" - var/reinf = 0 - var/cancolor = TRUE - var/shards = 2 - var/rods = 2 - var/cable = 1 - var/list/debris = list() - -/obj/machinery/door/window/New(loc, set_dir) - ..() - if(set_dir) - setDir(set_dir) - if(req_access && req_access.len) - icon_state = "[icon_state]" - base_state = icon_state - if(!color && cancolor) - color = color_windows(src) - for(var/i in 1 to shards) - debris += new /obj/item/shard(src) - if(rods) - debris += new /obj/item/stack/rods(src, rods) - if(cable) - debris += new /obj/item/stack/cable_coil(src, cable) - -/obj/machinery/door/window/Destroy() - density = FALSE - QDEL_LIST(debris) - if(obj_integrity == 0) - playsound(src, "shatter", 70, 1) - QDEL_NULL(electronics) - return ..() - -/obj/machinery/door/window/update_icon() - if(density) - icon_state = base_state - else - icon_state = "[base_state]open" - -/obj/machinery/door/window/examine(mob/user) - . = ..() - if(emagged) - . += "Its access panel is smoking slightly." - -/obj/machinery/door/window/proc/open_and_close() - open() - if(check_access(null)) - sleep(50) - else //secure doors close faster - sleep(20) - close() - -/obj/machinery/door/window/Bumped(atom/movable/AM) - if(operating || !density) - return - if(!ismob(AM)) - if(ismecha(AM)) - var/obj/mecha/mecha = AM - if(mecha.occupant && allowed(mecha.occupant)) - open_and_close() - else - do_animate("deny") - return - if(!SSticker) - return - var/mob/living/M = AM - if(!M.restrained() && M.mob_size > MOB_SIZE_TINY && (!(isrobot(M) && M.stat))) - bumpopen(M) - -/obj/machinery/door/window/bumpopen(mob/user) - if(operating || !density) - return - add_fingerprint(user) - if(!requiresID() || allowed(user)) - open_and_close() - else - do_animate("deny") - -/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border - return !density - if(istype(mover, /obj/structure/window)) - var/obj/structure/window/W = mover - if(!valid_window_location(loc, W.ini_dir)) - return FALSE - else if(istype(mover, /obj/structure/windoor_assembly)) - var/obj/structure/windoor_assembly/W = mover - if(!valid_window_location(loc, W.ini_dir)) - return FALSE - else if(istype(mover, /obj/machinery/door/window) && !valid_window_location(loc, mover.dir)) - return FALSE - else - return 1 - -/obj/machinery/door/window/CanAtmosPass(turf/T) - if(get_dir(loc, T) == dir) - return !density - else - return 1 - -//used in the AStar algorithm to determinate if the turf the door is on is passable -/obj/machinery/door/window/CanAStarPass(obj/item/card/id/ID, to_dir) - return !density || (dir != to_dir) || (check_access(ID) && hasPower()) - -/obj/machinery/door/window/CheckExit(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) - return !density - else - return 1 - -/obj/machinery/door/window/open(forced=0) - if(operating) //doors can still open when emag-disabled - return 0 - if(!forced) - if(!hasPower()) - return 0 - if(forced < 2) - if(emagged) - return 0 - if(!operating) //in case of emag - operating = TRUE - do_animate("opening") - playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) - icon_state ="[base_state]open" - sleep(10) - - density = FALSE -// sd_set_opacity(0) //TODO: why is this here? Opaque windoors? ~Carn - air_update_turf(1) - update_freelook_sight() - - if(operating) //emag again - operating = FALSE - return 1 - -/obj/machinery/door/window/close(forced=0) - if(operating) - return 0 - if(!forced) - if(!hasPower()) - return 0 - if(forced < 2) - if(emagged) - return 0 - operating = TRUE - do_animate("closing") - playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) - icon_state = base_state - - density = 1 -// if(visible) -// set_opacity(1) //TODO: why is this here? Opaque windoors? ~Carn - air_update_turf(1) - update_freelook_sight() - sleep(10) - - operating = 0 - return 1 - -/obj/machinery/door/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - playsound(src, 'sound/effects/glasshit.ogg', 90, TRUE) - if(BURN) - playsound(src, 'sound/items/welder.ogg', 100, TRUE) - -/obj/machinery/door/window/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT) && !disassembled) - for(var/obj/fragment in debris) - fragment.forceMove(get_turf(src)) - transfer_fingerprints_to(fragment) - debris -= fragment - qdel(src) - -/obj/machinery/door/window/narsie_act() - color = NARSIE_WINDOW_COLOUR - -/obj/machinery/door/window/ratvar_act() - var/obj/machinery/door/window/clockwork/C = new(loc, dir) - C.name = name - qdel(src) - -/obj/machinery/door/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > T0C + (reinf ? 1600 : 800)) - take_damage(round(exposed_volume / 200), BURN, 0, 0) - -/obj/machinery/door/window/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/door/window/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/door/window/attack_hand(mob/user) - return try_to_activate_door(user) - -/obj/machinery/door/window/emag_act(mob/user, obj/weapon) - if(!operating && density && !emagged) - emagged = TRUE - operating = TRUE - flick("[base_state]spark", src) - playsound(src, "sparks", 75, 1) - sleep(6) - operating = FALSE - open(2) - return 1 - -/obj/machinery/door/window/attackby(obj/item/I, mob/living/user, params) - //If it's in the process of opening/closing, ignore the click - if(operating) - return - - add_fingerprint(user) - return ..() - -/obj/machinery/door/window/screwdriver_act(mob/user, obj/item/I) - if(flags & NODECONSTRUCT) - return - . = TRUE - if(density || operating) - to_chat(user, "You need to open the door to access the maintenance panel!") - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - panel_open = !panel_open - to_chat(user, "You [panel_open ? "open":"close"] the maintenance panel of the [src.name].") - - -/obj/machinery/door/window/crowbar_act(mob/user, obj/item/I) - if(operating) - return - if(flags & NODECONSTRUCT) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(panel_open && !density && !operating) - user.visible_message("[user] removes the electronics from the [name].", \ - "You start to remove electronics from the [name]...") - if(I.use_tool(src, user, 40, volume = I.tool_volume)) - if(panel_open && !density && !operating && loc) - var/obj/structure/windoor_assembly/WA = new /obj/structure/windoor_assembly(loc) - switch(base_state) - if("left") - WA.facing = "l" - if("right") - WA.facing = "r" - if("leftsecure") - WA.facing = "l" - WA.secure = TRUE - if("rightsecure") - WA.facing = "r" - WA.secure = TRUE - WA.anchored = TRUE - WA.state= "02" - WA.setDir(dir) - WA.ini_dir = dir - WA.update_icon() - WA.created_name = name - - if(emagged) - to_chat(user, "You discard the damaged electronics.") - qdel(src) - return - - to_chat(user, "You remove the airlock electronics.") - - var/obj/item/airlock_electronics/ae - if(!electronics) - ae = new/obj/item/airlock_electronics(loc) - if(!req_access) - check_access() - if(req_access.len) - ae.conf_access = req_access - else if(req_one_access.len) - ae.conf_access = req_one_access - ae.one_access = 1 - else - ae = electronics - electronics = null - ae.forceMove(loc) - - qdel(src) - else - try_to_crowbar(user, I) - -/obj/machinery/door/window/try_to_crowbar(mob/user, obj/item/I) - if(!hasPower()) - if(density) - open(2) - else - close(2) - else - to_chat(user, "The door's motors resist your efforts to force it!") - -/obj/machinery/door/window/do_animate(animation) - switch(animation) - if("opening") - flick("[base_state]opening", src) - if("closing") - flick("[base_state]closing", src) - if("deny") - flick("[base_state]deny", src) - -/obj/machinery/door/window/brigdoor - name = "secure door" - icon_state = "leftsecure" - base_state = "leftsecure" - max_integrity = 300 //Stronger doors for prison (regular window door health is 200) - reinf = 1 - explosion_block = 1 - var/id = null - -/obj/machinery/door/window/brigdoor/security/cell - name = "cell door" - desc = "For keeping in criminal scum." - req_access = list(access_brig) - -/obj/machinery/door/window/clockwork - name = "brass windoor" - desc = "A thin door with translucent brass paneling." - icon_state = "clockwork" - base_state = "clockwork" - shards = 0 - rods = 0 - resistance_flags = ACID_PROOF | FIRE_PROOF - cancolor = FALSE - var/made_glow = FALSE - -/obj/machinery/door/window/clockwork/New(loc, set_dir) - ..() - debris += new/obj/item/stack/tile/brass(src, 2) - -/obj/machinery/door/window/clockwork/setDir(direct) - if(!made_glow) - var/obj/effect/E = new /obj/effect/temp_visual/ratvar/door/window(get_turf(src)) - E.setDir(direct) - made_glow = TRUE - ..() - -/obj/machinery/door/window/clockwork/emp_act(severity) - if(prob(80/severity)) - open() - -/obj/machinery/door/window/clockwork/ratvar_act() - obj_integrity = max_integrity - -/obj/machinery/door/window/clockwork/hasPower() - return TRUE //yup that's power all right - -/obj/machinery/door/window/clockwork/narsie_act() - take_damage(rand(30, 60), BRUTE) - if(src) - var/previouscolor = color - color = "#960000" - animate(src, color = previouscolor, time = 8) - -/obj/machinery/door/window/northleft - dir = NORTH - -/obj/machinery/door/window/eastleft - dir = EAST - -/obj/machinery/door/window/westleft - dir = WEST - -/obj/machinery/door/window/southleft - dir = SOUTH - -/obj/machinery/door/window/northright - dir = NORTH - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/eastright - dir = EAST - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/westright - dir = WEST - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/southright - dir = SOUTH - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/brigdoor/northleft - dir = NORTH - -/obj/machinery/door/window/brigdoor/eastleft - dir = EAST - -/obj/machinery/door/window/brigdoor/westleft - dir = WEST - -/obj/machinery/door/window/brigdoor/southleft - dir = SOUTH - -/obj/machinery/door/window/brigdoor/northright - dir = NORTH - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/eastright - dir = EAST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/westright - dir = WEST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/southright - dir = SOUTH - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/security/cell/northleft - dir = NORTH - -/obj/machinery/door/window/brigdoor/security/cell/eastleft - dir = EAST - -/obj/machinery/door/window/brigdoor/security/cell/westleft - dir = WEST - -/obj/machinery/door/window/brigdoor/security/cell/southleft - dir = SOUTH - -/obj/machinery/door/window/brigdoor/security/cell/northright - dir = NORTH - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/security/cell/eastright - dir = EAST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/security/cell/westright - dir = WEST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/security/cell/southright - dir = SOUTH - icon_state = "rightsecure" - base_state = "rightsecure" +/obj/machinery/door/window + name = "interior door" + desc = "A strong door." + icon = 'icons/obj/doors/windoor.dmi' + icon_state = "left" + layer = ABOVE_WINDOW_LAYER + closingLayer = ABOVE_WINDOW_LAYER + resistance_flags = ACID_PROOF + visible = 0 + flags = ON_BORDER + opacity = 0 + dir = EAST + max_integrity = 150 //If you change this, consider changing ../door/window/brigdoor/ max_integrity at the bottom of this .dm file + integrity_failure = 0 + armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 70, "acid" = 100) + var/obj/item/airlock_electronics/electronics + var/base_state = "left" + var/reinf = 0 + var/cancolor = TRUE + var/shards = 2 + var/rods = 2 + var/cable = 1 + var/list/debris = list() + +/obj/machinery/door/window/New(loc, set_dir) + ..() + if(set_dir) + setDir(set_dir) + if(req_access && req_access.len) + icon_state = "[icon_state]" + base_state = icon_state + if(!color && cancolor) + color = color_windows(src) + for(var/i in 1 to shards) + debris += new /obj/item/shard(src) + if(rods) + debris += new /obj/item/stack/rods(src, rods) + if(cable) + debris += new /obj/item/stack/cable_coil(src, cable) + +/obj/machinery/door/window/Destroy() + density = FALSE + QDEL_LIST(debris) + if(obj_integrity == 0) + playsound(src, "shatter", 70, 1) + QDEL_NULL(electronics) + return ..() + +/obj/machinery/door/window/update_icon() + if(density) + icon_state = base_state + else + icon_state = "[base_state]open" + +/obj/machinery/door/window/examine(mob/user) + . = ..() + if(emagged) + . += "Its access panel is smoking slightly." + +/obj/machinery/door/window/proc/open_and_close() + open() + if(check_access(null)) + sleep(50) + else //secure doors close faster + sleep(20) + close() + +/obj/machinery/door/window/Bumped(atom/movable/AM) + if(operating || !density) + return + if(!ismob(AM)) + if(ismecha(AM)) + var/obj/mecha/mecha = AM + if(mecha.occupant && allowed(mecha.occupant)) + open_and_close() + else + do_animate("deny") + return + if(!SSticker) + return + var/mob/living/M = AM + if(!M.restrained() && M.mob_size > MOB_SIZE_TINY && (!(isrobot(M) && M.stat))) + bumpopen(M) + +/obj/machinery/door/window/bumpopen(mob/user) + if(operating || !density) + return + add_fingerprint(user) + if(!requiresID() || allowed(user)) + open_and_close() + else + do_animate("deny") + +/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + return !density + if(istype(mover, /obj/structure/window)) + var/obj/structure/window/W = mover + if(!valid_window_location(loc, W.ini_dir)) + return FALSE + else if(istype(mover, /obj/structure/windoor_assembly)) + var/obj/structure/windoor_assembly/W = mover + if(!valid_window_location(loc, W.ini_dir)) + return FALSE + else if(istype(mover, /obj/machinery/door/window) && !valid_window_location(loc, mover.dir)) + return FALSE + else + return 1 + +/obj/machinery/door/window/CanAtmosPass(turf/T) + if(get_dir(loc, T) == dir) + return !density + else + return 1 + +//used in the AStar algorithm to determinate if the turf the door is on is passable +/obj/machinery/door/window/CanAStarPass(obj/item/card/id/ID, to_dir) + return !density || (dir != to_dir) || (check_access(ID) && hasPower()) + +/obj/machinery/door/window/CheckExit(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) + return !density + else + return 1 + +/obj/machinery/door/window/open(forced=0) + if(operating) //doors can still open when emag-disabled + return 0 + if(!forced) + if(!hasPower()) + return 0 + if(forced < 2) + if(emagged) + return 0 + if(!operating) //in case of emag + operating = TRUE + do_animate("opening") + playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) + icon_state ="[base_state]open" + sleep(10) + + density = FALSE +// sd_set_opacity(0) //TODO: why is this here? Opaque windoors? ~Carn + air_update_turf(1) + update_freelook_sight() + + if(operating) //emag again + operating = FALSE + return 1 + +/obj/machinery/door/window/close(forced=0) + if(operating) + return 0 + if(!forced) + if(!hasPower()) + return 0 + if(forced < 2) + if(emagged) + return 0 + operating = TRUE + do_animate("closing") + playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) + icon_state = base_state + + density = 1 +// if(visible) +// set_opacity(1) //TODO: why is this here? Opaque windoors? ~Carn + air_update_turf(1) + update_freelook_sight() + sleep(10) + + operating = 0 + return 1 + +/obj/machinery/door/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + playsound(src, 'sound/effects/glasshit.ogg', 90, TRUE) + if(BURN) + playsound(src, 'sound/items/welder.ogg', 100, TRUE) + +/obj/machinery/door/window/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT) && !disassembled) + for(var/obj/fragment in debris) + fragment.forceMove(get_turf(src)) + transfer_fingerprints_to(fragment) + debris -= fragment + qdel(src) + +/obj/machinery/door/window/narsie_act() + color = NARSIE_WINDOW_COLOUR + +/obj/machinery/door/window/ratvar_act() + var/obj/machinery/door/window/clockwork/C = new(loc, dir) + C.name = name + qdel(src) + +/obj/machinery/door/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > T0C + (reinf ? 1600 : 800)) + take_damage(round(exposed_volume / 200), BURN, 0, 0) + +/obj/machinery/door/window/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/door/window/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/door/window/attack_hand(mob/user) + return try_to_activate_door(user) + +/obj/machinery/door/window/emag_act(mob/user, obj/weapon) + if(!operating && density && !emagged) + emagged = TRUE + operating = TRUE + flick("[base_state]spark", src) + playsound(src, "sparks", 75, 1) + sleep(6) + operating = FALSE + open(2) + return 1 + +/obj/machinery/door/window/attackby(obj/item/I, mob/living/user, params) + //If it's in the process of opening/closing, ignore the click + if(operating) + return + + add_fingerprint(user) + return ..() + +/obj/machinery/door/window/screwdriver_act(mob/user, obj/item/I) + if(flags & NODECONSTRUCT) + return + . = TRUE + if(density || operating) + to_chat(user, "You need to open the door to access the maintenance panel!") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + panel_open = !panel_open + to_chat(user, "You [panel_open ? "open":"close"] the maintenance panel of the [src.name].") + + +/obj/machinery/door/window/crowbar_act(mob/user, obj/item/I) + if(operating) + return + if(flags & NODECONSTRUCT) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(panel_open && !density && !operating) + user.visible_message("[user] removes the electronics from the [name].", \ + "You start to remove electronics from the [name]...") + if(I.use_tool(src, user, 40, volume = I.tool_volume)) + if(panel_open && !density && !operating && loc) + var/obj/structure/windoor_assembly/WA = new /obj/structure/windoor_assembly(loc) + switch(base_state) + if("left") + WA.facing = "l" + if("right") + WA.facing = "r" + if("leftsecure") + WA.facing = "l" + WA.secure = TRUE + if("rightsecure") + WA.facing = "r" + WA.secure = TRUE + WA.anchored = TRUE + WA.state= "02" + WA.setDir(dir) + WA.ini_dir = dir + WA.update_icon() + WA.created_name = name + + if(emagged) + to_chat(user, "You discard the damaged electronics.") + qdel(src) + return + + to_chat(user, "You remove the airlock electronics.") + + var/obj/item/airlock_electronics/ae + if(!electronics) + ae = new/obj/item/airlock_electronics(loc) + if(!req_access) + check_access() + if(req_access.len) + ae.conf_access = req_access + else if(req_one_access.len) + ae.conf_access = req_one_access + ae.one_access = 1 + else + ae = electronics + electronics = null + ae.forceMove(loc) + + qdel(src) + else + try_to_crowbar(user, I) + +/obj/machinery/door/window/try_to_crowbar(mob/user, obj/item/I) + if(!hasPower()) + if(density) + open(2) + else + close(2) + else + to_chat(user, "The door's motors resist your efforts to force it!") + +/obj/machinery/door/window/do_animate(animation) + switch(animation) + if("opening") + flick("[base_state]opening", src) + if("closing") + flick("[base_state]closing", src) + if("deny") + flick("[base_state]deny", src) + +/obj/machinery/door/window/brigdoor + name = "secure door" + icon_state = "leftsecure" + base_state = "leftsecure" + max_integrity = 300 //Stronger doors for prison (regular window door health is 200) + reinf = 1 + explosion_block = 1 + var/id = null + +/obj/machinery/door/window/brigdoor/security/cell + name = "cell door" + desc = "For keeping in criminal scum." + req_access = list(ACCESS_BRIG) + +/obj/machinery/door/window/clockwork + name = "brass windoor" + desc = "A thin door with translucent brass paneling." + icon_state = "clockwork" + base_state = "clockwork" + shards = 0 + rods = 0 + resistance_flags = ACID_PROOF | FIRE_PROOF + cancolor = FALSE + var/made_glow = FALSE + +/obj/machinery/door/window/clockwork/New(loc, set_dir) + ..() + debris += new/obj/item/stack/tile/brass(src, 2) + +/obj/machinery/door/window/clockwork/setDir(direct) + if(!made_glow) + var/obj/effect/E = new /obj/effect/temp_visual/ratvar/door/window(get_turf(src)) + E.setDir(direct) + made_glow = TRUE + ..() + +/obj/machinery/door/window/clockwork/emp_act(severity) + if(prob(80/severity)) + open() + +/obj/machinery/door/window/clockwork/ratvar_act() + obj_integrity = max_integrity + +/obj/machinery/door/window/clockwork/hasPower() + return TRUE //yup that's power all right + +/obj/machinery/door/window/clockwork/narsie_act() + take_damage(rand(30, 60), BRUTE) + if(src) + var/previouscolor = color + color = "#960000" + animate(src, color = previouscolor, time = 8) + +/obj/machinery/door/window/northleft + dir = NORTH + +/obj/machinery/door/window/eastleft + dir = EAST + +/obj/machinery/door/window/westleft + dir = WEST + +/obj/machinery/door/window/southleft + dir = SOUTH + +/obj/machinery/door/window/northright + dir = NORTH + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/eastright + dir = EAST + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/westright + dir = WEST + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/southright + dir = SOUTH + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/brigdoor/northleft + dir = NORTH + +/obj/machinery/door/window/brigdoor/eastleft + dir = EAST + +/obj/machinery/door/window/brigdoor/westleft + dir = WEST + +/obj/machinery/door/window/brigdoor/southleft + dir = SOUTH + +/obj/machinery/door/window/brigdoor/northright + dir = NORTH + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/eastright + dir = EAST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/westright + dir = WEST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/southright + dir = SOUTH + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/security/cell/northleft + dir = NORTH + +/obj/machinery/door/window/brigdoor/security/cell/eastleft + dir = EAST + +/obj/machinery/door/window/brigdoor/security/cell/westleft + dir = WEST + +/obj/machinery/door/window/brigdoor/security/cell/southleft + dir = SOUTH + +/obj/machinery/door/window/brigdoor/security/cell/northright + dir = NORTH + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/security/cell/eastright + dir = EAST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/security/cell/westright + dir = WEST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/security/cell/southright + dir = SOUTH + icon_state = "rightsecure" + base_state = "rightsecure" diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index d0fe753a9de..05198a7a55f 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -1,230 +1,230 @@ -var/list/doppler_arrays = list() - -/obj/machinery/doppler_array - name = "tachyon-doppler array" - desc = "A highly precise directional sensor array which measures the release of quants from decaying tachyons. The doppler shifting of the mirror-image formed by these quants can reveal the size, location and temporal affects of energetic disturbances within a large radius ahead of the array." - icon = 'icons/obj/machines/research.dmi' - icon_state = "tdoppler" - density = 1 - anchored = 1 - atom_say_verb = "states coldly" - var/list/logged_explosions = list() - var/explosion_target - var/datum/tech/toxins/toxins_tech - var/max_toxins_tech = 7 - -/datum/explosion_log - var/logged_time - var/epicenter - var/actual_size_message - var/theoretical_size_message - -/datum/explosion_log/New(var/log_time, var/log_epicenter, var/log_actual_size_message, var/log_theoretical_size_message) - ..() - logged_time = log_time - epicenter = log_epicenter - actual_size_message = log_actual_size_message - theoretical_size_message = log_theoretical_size_message - -/obj/machinery/doppler_array/New() - ..() - doppler_arrays += src - explosion_target = rand(8, 20) - toxins_tech = new /datum/tech/toxins(src) - -/obj/machinery/doppler_array/Destroy() - doppler_arrays -= src - logged_explosions.Cut() - return ..() - -/obj/machinery/doppler_array/process() - return PROCESS_KILL - -/obj/machinery/doppler_array/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/disk/tech_disk)) - var/obj/item/disk/tech_disk/disk = I - disk.load_tech(toxins_tech) - to_chat(user, "You swipe the disk into [src].") - return - return ..() - -/obj/machinery/doppler_array/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!anchored && !isinspace()) - anchored = TRUE - WRENCH_ANCHOR_MESSAGE - else if(anchored) - anchored = FALSE - WRENCH_UNANCHOR_MESSAGE - power_change() - -/obj/machinery/doppler_array/attack_hand(mob/user) - if(..()) - return - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/doppler_array/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/doppler_array/AltClick(mob/user) - rotate(user) - -/obj/machinery/doppler_array/verb/rotate(mob/user) - set name = "Rotate Tachyon-doppler Dish" - set category = "Object" - set src in oview(1) - - if(user.incapacitated()) - return - if(!Adjacent(user)) - return - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do that!") - return - dir = turn(dir, 90) - to_chat(user, "You rotate [src].") - -/obj/machinery/doppler_array/proc/print_explosive_logs(mob/user) - if(!logged_explosions.len) - atom_say("No logs currently stored in internal database.") - return - if(active_timers) - to_chat(user, "[src] is already printing something, please wait.") - return - atom_say("Printing explosive log. Standby...") - addtimer(CALLBACK(src, .print), 50) - -/obj/machinery/doppler_array/proc/print() - visible_message("[src] prints a piece of paper!") - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - var/obj/item/paper/explosive_log/P = new(get_turf(src)) - for(var/D in logged_explosions) - var/datum/explosion_log/E = D - P.info += "\ - [E.logged_time]\ - [E.epicenter]\ - [E.actual_size_message]\ - [E.theoretical_size_message]\ - " - P.info += "
    \ - Printed at [station_time_timestamp()]." - -/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range, - var/took,var/orig_dev_range,var/orig_heavy_range,var/orig_light_range) - if(stat & NOPOWER) - return - if(z != z0) - return - - var/dx = abs(x0-x) - var/dy = abs(y0-y) - var/distance - var/direct - var/capped = FALSE - - if(dx > dy) - distance = dx - if(x0 > x) - direct = EAST - else - direct = WEST - else - distance = dy - if(y0 > y) - direct = NORTH - else - direct = SOUTH - - if(distance > 100) - return - if(!(direct & dir)) - return - - var/list/messages = list("Explosive disturbance detected.", \ - "Epicenter at: grid ([x0],[y0]). Temporal displacement of tachyons: [took] seconds.", \ - "Actual: Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range].") - - // If the bomb was capped, say its theoretical size. - if(devastation_range < orig_dev_range || heavy_impact_range < orig_heavy_range || light_impact_range < orig_light_range) - capped = TRUE - messages += "Theoretical: Epicenter radius: [orig_dev_range]. Outer radius: [orig_heavy_range]. Shockwave radius: [orig_light_range]." - logged_explosions.Insert(1, new /datum/explosion_log(station_time_timestamp(), "[x0],[y0]", "[devastation_range], [heavy_impact_range], [light_impact_range]", capped ? "[orig_dev_range], [orig_heavy_range], [orig_light_range]" : "n/a")) //Newer logs appear first - messages += "Event successfully logged in internal database." - var/miss_by = abs(explosion_target - orig_light_range) - var/tmp_tech = max_toxins_tech - miss_by - if(!miss_by) - messages += "Explosion size matches target." - else - messages += "Target ([explosion_target]) missed by : [miss_by]." - if(tmp_tech > toxins_tech.level) - toxins_tech.level = tmp_tech - messages += "Toxins technology level upgraded to [toxins_tech.level]. Swipe a technology disk to save data." - for(var/message in messages) - atom_say(message) - -/obj/machinery/doppler_array/power_change() - if(stat & BROKEN) - icon_state = "[initial(icon_state)]-broken" - else - if(powered() && anchored) - icon_state = initial(icon_state) - stat &= ~NOPOWER - else - icon_state = "[initial(icon_state)]-off" - stat |= NOPOWER - -/obj/machinery/doppler_array/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "doppler_array.tmpl", "Tachyon-doppler array", 500, 650) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/doppler_array/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - var/list/explosion_data = list() - for(var/D in logged_explosions) - var/datum/explosion_log/E = D - explosion_data += list(list( - "logged_time" = E.logged_time, - "epicenter" = E.epicenter, - "actual_size_message" = E.actual_size_message, - "theoretical_size_message" = E.theoretical_size_message, - "unique_datum_id" = E.UID())) - data["explosion_target"] = explosion_target - data["toxins_tech"] = toxins_tech.level - data["explosion_data"] = explosion_data - data["printing"] = active_timers - return data - -/obj/machinery/doppler_array/Topic(href, href_list) - if(..()) - return - if(href_list["log_to_delete"]) - var/log_to_delete = sanitize(href_list["log_to_delete"]) - for(var/D in logged_explosions) - var/datum/explosion_log/E = D - if(E.UID() == log_to_delete) - logged_explosions -= E - qdel(E) - to_chat(usr, "Log deletion successful.") - break - else if(href_list["print_logs"]) - print_explosive_logs(usr) - else - return - SSnanoui.update_uis(src) - -/obj/item/paper/explosive_log - name = "explosive log" - info = "

    Explosive Log Report

    \ - \ - \ - \ - \ - \ - \ - " //NB: the
    Time loggedEpicenterActualTheoretical
    tag is left open, it is closed later on, when the doppler array adds its data +GLOBAL_LIST_EMPTY(doppler_arrays) + +/obj/machinery/doppler_array + name = "tachyon-doppler array" + desc = "A highly precise directional sensor array which measures the release of quants from decaying tachyons. The doppler shifting of the mirror-image formed by these quants can reveal the size, location and temporal affects of energetic disturbances within a large radius ahead of the array." + icon = 'icons/obj/machines/research.dmi' + icon_state = "tdoppler" + density = 1 + anchored = 1 + atom_say_verb = "states coldly" + var/list/logged_explosions = list() + var/explosion_target + var/datum/tech/toxins/toxins_tech + var/max_toxins_tech = 7 + +/datum/explosion_log + var/logged_time + var/epicenter + var/actual_size_message + var/theoretical_size_message + +/datum/explosion_log/New(var/log_time, var/log_epicenter, var/log_actual_size_message, var/log_theoretical_size_message) + ..() + logged_time = log_time + epicenter = log_epicenter + actual_size_message = log_actual_size_message + theoretical_size_message = log_theoretical_size_message + +/obj/machinery/doppler_array/New() + ..() + GLOB.doppler_arrays += src + explosion_target = rand(8, 20) + toxins_tech = new /datum/tech/toxins(src) + +/obj/machinery/doppler_array/Destroy() + GLOB.doppler_arrays -= src + logged_explosions.Cut() + return ..() + +/obj/machinery/doppler_array/process() + return PROCESS_KILL + +/obj/machinery/doppler_array/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/disk/tech_disk)) + var/obj/item/disk/tech_disk/disk = I + disk.load_tech(toxins_tech) + to_chat(user, "You swipe the disk into [src].") + return + return ..() + +/obj/machinery/doppler_array/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!anchored && !isinspace()) + anchored = TRUE + WRENCH_ANCHOR_MESSAGE + else if(anchored) + anchored = FALSE + WRENCH_UNANCHOR_MESSAGE + power_change() + +/obj/machinery/doppler_array/attack_hand(mob/user) + if(..()) + return + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/doppler_array/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/doppler_array/AltClick(mob/user) + rotate(user) + +/obj/machinery/doppler_array/verb/rotate(mob/user) + set name = "Rotate Tachyon-doppler Dish" + set category = "Object" + set src in oview(1) + + if(user.incapacitated()) + return + if(!Adjacent(user)) + return + if(!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do that!") + return + dir = turn(dir, 90) + to_chat(user, "You rotate [src].") + +/obj/machinery/doppler_array/proc/print_explosive_logs(mob/user) + if(!logged_explosions.len) + atom_say("No logs currently stored in internal database.") + return + if(active_timers) + to_chat(user, "[src] is already printing something, please wait.") + return + atom_say("Printing explosive log. Standby...") + addtimer(CALLBACK(src, .proc/print), 50) + +/obj/machinery/doppler_array/proc/print() + visible_message("[src] prints a piece of paper!") + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + var/obj/item/paper/explosive_log/P = new(get_turf(src)) + for(var/D in logged_explosions) + var/datum/explosion_log/E = D + P.info += "\ + \ + \ + \ + \ + " + P.info += "
    [E.logged_time][E.epicenter][E.actual_size_message][E.theoretical_size_message]

    \ + Printed at [station_time_timestamp()]." + +/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range, + var/took,var/orig_dev_range,var/orig_heavy_range,var/orig_light_range) + if(stat & NOPOWER) + return + if(z != z0) + return + + var/dx = abs(x0-x) + var/dy = abs(y0-y) + var/distance + var/direct + var/capped = FALSE + + if(dx > dy) + distance = dx + if(x0 > x) + direct = EAST + else + direct = WEST + else + distance = dy + if(y0 > y) + direct = NORTH + else + direct = SOUTH + + if(distance > 100) + return + if(!(direct & dir)) + return + + var/list/messages = list("Explosive disturbance detected.", \ + "Epicenter at: grid ([x0],[y0]). Temporal displacement of tachyons: [took] seconds.", \ + "Actual: Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range].") + + // If the bomb was capped, say its theoretical size. + if(devastation_range < orig_dev_range || heavy_impact_range < orig_heavy_range || light_impact_range < orig_light_range) + capped = TRUE + messages += "Theoretical: Epicenter radius: [orig_dev_range]. Outer radius: [orig_heavy_range]. Shockwave radius: [orig_light_range]." + logged_explosions.Insert(1, new /datum/explosion_log(station_time_timestamp(), "[x0],[y0]", "[devastation_range], [heavy_impact_range], [light_impact_range]", capped ? "[orig_dev_range], [orig_heavy_range], [orig_light_range]" : "n/a")) //Newer logs appear first + messages += "Event successfully logged in internal database." + var/miss_by = abs(explosion_target - orig_light_range) + var/tmp_tech = max_toxins_tech - miss_by + if(!miss_by) + messages += "Explosion size matches target." + else + messages += "Target ([explosion_target]) missed by : [miss_by]." + if(tmp_tech > toxins_tech.level) + toxins_tech.level = tmp_tech + messages += "Toxins technology level upgraded to [toxins_tech.level]. Swipe a technology disk to save data." + for(var/message in messages) + atom_say(message) + +/obj/machinery/doppler_array/power_change() + if(stat & BROKEN) + icon_state = "[initial(icon_state)]-broken" + else + if(powered() && anchored) + icon_state = initial(icon_state) + stat &= ~NOPOWER + else + icon_state = "[initial(icon_state)]-off" + stat |= NOPOWER + +/obj/machinery/doppler_array/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "doppler_array.tmpl", "Tachyon-doppler array", 500, 650) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/doppler_array/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + var/list/explosion_data = list() + for(var/D in logged_explosions) + var/datum/explosion_log/E = D + explosion_data += list(list( + "logged_time" = E.logged_time, + "epicenter" = E.epicenter, + "actual_size_message" = E.actual_size_message, + "theoretical_size_message" = E.theoretical_size_message, + "unique_datum_id" = E.UID())) + data["explosion_target"] = explosion_target + data["toxins_tech"] = toxins_tech.level + data["explosion_data"] = explosion_data + data["printing"] = active_timers + return data + +/obj/machinery/doppler_array/Topic(href, href_list) + if(..()) + return + if(href_list["log_to_delete"]) + var/log_to_delete = sanitize(href_list["log_to_delete"]) + for(var/D in logged_explosions) + var/datum/explosion_log/E = D + if(E.UID() == log_to_delete) + logged_explosions -= E + qdel(E) + to_chat(usr, "Log deletion successful.") + break + else if(href_list["print_logs"]) + print_explosive_logs(usr) + else + return + SSnanoui.update_uis(src) + +/obj/item/paper/explosive_log + name = "explosive log" + info = "

    Explosive Log Report

    \ + \ + \ + \ + \ + \ + \ + " //NB: the
    Time loggedEpicenterActualTheoretical
    tag is left open, it is closed later on, when the doppler array adds its data diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm index b1a6f96e2eb..32924690d97 100644 --- a/code/game/machinery/embedded_controller/airlock_controllers.dm +++ b/code/game/machinery/embedded_controller/airlock_controllers.dm @@ -27,7 +27,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data = list( @@ -98,7 +98,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data = list( @@ -160,7 +160,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data = list( diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm index 670cce47785..8ad85a608f0 100644 --- a/code/game/machinery/embedded_controller/airlock_program.dm +++ b/code/game/machinery/embedded_controller/airlock_program.dm @@ -379,4 +379,4 @@ send an additional command to open the door again. #undef TARGET_NONE #undef TARGET_INOPEN -#undef TARGET_OUTOPEN \ No newline at end of file +#undef TARGET_OUTOPEN diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 62fbae71c78..2226f9f18fe 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -1,87 +1,87 @@ -/obj/machinery/embedded_controller - var/datum/computer/file/embedded_program/program //the currently executing program - - name = "Embedded Controller" - anchored = 1 - - use_power = IDLE_POWER_USE - idle_power_usage = 10 - - var/on = 1 - -/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) - return 0 - -/obj/machinery/embedded_controller/receive_signal(datum/signal/signal, receive_method, receive_param) - if(!signal || signal.encryption) return - - if(program) - program.receive_signal(signal, receive_method, receive_param) - //spawn(5) program.process() //no, program.process sends some signals and machines respond and we here again and we lag -rastaf0 - -/obj/machinery/embedded_controller/process() - if(program) - program.process() - - update_icon() - src.updateDialog() - -/obj/machinery/embedded_controller/attack_ghost(mob/user as mob) - src.ui_interact(user) - -/obj/machinery/embedded_controller/attack_ai(mob/user as mob) - src.ui_interact(user) - -/obj/machinery/embedded_controller/attack_hand(mob/user as mob) - if(!user.IsAdvancedToolUser()) - return 0 - src.ui_interact(user) - -/obj/machinery/embedded_controller/ui_interact() - return - -/obj/machinery/embedded_controller/radio - icon = 'icons/obj/airlock_machines.dmi' - icon_state = "airlock_control_standby" - power_channel = ENVIRON - density = 0 - - var/id_tag - //var/radio_power_use = 50 //power used to xmit signals - - var/frequency = 1379 - var/radio_filter = null - var/datum/radio_frequency/radio_connection - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/machinery/embedded_controller/radio/Initialize() - ..() - set_frequency(frequency) - -/obj/machinery/embedded_controller/radio/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/embedded_controller/radio/update_icon() - if(on && program) - if(program.memory["processing"]) - icon_state = "airlock_control_process" - else - icon_state = "airlock_control_standby" - else - icon_state = "airlock_control_off" - -/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/filter = null) - signal.transmission_method = TRANSMISSION_RADIO - if(radio_connection) - //use_power(radio_power_use) //neat idea, but causes way too much lag. - return radio_connection.post_signal(src, signal, filter) - else - qdel(signal) - -/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, radio_filter) +/obj/machinery/embedded_controller + var/datum/computer/file/embedded_program/program //the currently executing program + + name = "Embedded Controller" + anchored = 1 + + use_power = IDLE_POWER_USE + idle_power_usage = 10 + + var/on = 1 + +/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) + return 0 + +/obj/machinery/embedded_controller/receive_signal(datum/signal/signal, receive_method, receive_param) + if(!signal || signal.encryption) return + + if(program) + program.receive_signal(signal, receive_method, receive_param) + //spawn(5) program.process() //no, program.process sends some signals and machines respond and we here again and we lag -rastaf0 + +/obj/machinery/embedded_controller/process() + if(program) + program.process() + + update_icon() + src.updateDialog() + +/obj/machinery/embedded_controller/attack_ghost(mob/user as mob) + src.ui_interact(user) + +/obj/machinery/embedded_controller/attack_ai(mob/user as mob) + src.ui_interact(user) + +/obj/machinery/embedded_controller/attack_hand(mob/user as mob) + if(!user.IsAdvancedToolUser()) + return 0 + src.ui_interact(user) + +/obj/machinery/embedded_controller/ui_interact() + return + +/obj/machinery/embedded_controller/radio + icon = 'icons/obj/airlock_machines.dmi' + icon_state = "airlock_control_standby" + power_channel = ENVIRON + density = 0 + + var/id_tag + //var/radio_power_use = 50 //power used to xmit signals + + var/frequency = 1379 + var/radio_filter = null + var/datum/radio_frequency/radio_connection + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/machinery/embedded_controller/radio/Initialize() + ..() + set_frequency(frequency) + +/obj/machinery/embedded_controller/radio/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/embedded_controller/radio/update_icon() + if(on && program) + if(program.memory["processing"]) + icon_state = "airlock_control_process" + else + icon_state = "airlock_control_standby" + else + icon_state = "airlock_control_off" + +/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/filter = null) + signal.transmission_method = TRANSMISSION_RADIO + if(radio_connection) + //use_power(radio_power_use) //neat idea, but causes way too much lag. + return radio_connection.post_signal(src, signal, filter) + else + qdel(signal) + +/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, radio_filter) diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index 7ae268fca46..9beba3f4789 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -236,14 +236,14 @@ FIRE ALARM ui_interact(user) -/obj/machinery/firealarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) +/obj/machinery/firealarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "firealarm.tmpl", name, 400, 400, state = state) ui.open() ui.set_auto_update(1) -/obj/machinery/firealarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/firealarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/area/A = get_area(src) @@ -318,7 +318,7 @@ FIRE ALARM pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 if(is_station_contact(z) && show_alert_level) - if(security_level) + if(GLOB.security_level) overlays += image('icons/obj/monitors.dmi', "overlay_[get_security_level()]") else overlays += image('icons/obj/monitors.dmi', "overlay_green") diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 1f42a6a78c4..b7b7e35eef6 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -1,153 +1,153 @@ -// It is a gizmo that flashes a small area - -/obj/machinery/flasher - name = "Mounted flash" - desc = "A wall-mounted flashbulb device." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "mflash1" - max_integrity = 250 - integrity_failure = 100 - damage_deflection = 10 - var/id = null - var/range = 2 //this is roughly the size of brig cell - var/disable = 0 - var/last_flash = 0 //Don't want it getting spammed like regular flashes - var/strength = 5 //How weakened targets are when flashed. - var/base_state = "mflash" - anchored = 1 - -/obj/machinery/flasher/portable //Portable version of the flasher. Only flashes when anchored - name = "portable flasher" - desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." - icon_state = "pflash1" - strength = 4 - anchored = 0 - base_state = "pflash" - density = 1 - -/* -/obj/machinery/flasher/New() - sleep(4) //<--- What the fuck are you doing? D= - sd_set_light(2) -*/ -/obj/machinery/flasher/power_change() - if( powered() ) - stat &= ~NOPOWER - icon_state = "[base_state]1" -// sd_set_light(2) - else - stat |= ~NOPOWER - icon_state = "[base_state]1-p" -// sd_set_light(0) - -//Let the AI trigger them directly. -/obj/machinery/flasher/attack_ai(mob/user) - if(anchored) - return flash() - -/obj/machinery/flasher/attack_ghost(mob/user) - if(anchored && user.can_advanced_admin_interact()) - return flash() - -/obj/machinery/flasher/proc/flash() - if(!(powered())) - return - - if((disable) || (last_flash && world.time < last_flash + 150)) - return - - playsound(loc, 'sound/weapons/flash.ogg', 100, 1) - flick("[base_state]_flash", src) - last_flash = world.time - use_power(1000) - - for(var/mob/living/L in viewers(src, null)) - if(get_dist(src, L) > range) - continue - - if(L.flash_eyes(affect_silicon = 1)) - L.Weaken(strength) - if(L.weakeyes) - L.Weaken(strength * 1.5) - L.visible_message("[L] gasps and shields [L.p_their()] eyes!") - -/obj/machinery/flasher/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - if(prob(75/severity)) - flash() - ..(severity) - -/obj/machinery/flasher/portable/HasProximity(atom/movable/AM as mob|obj) - if((disable) || (last_flash && world.time < last_flash + 150)) - return - - if(istype(AM, /mob/living/carbon)) - var/mob/living/carbon/M = AM - if((M.m_intent != MOVE_INTENT_WALK) && (anchored)) - flash() - -//Don't want to render prison breaks impossible -/obj/machinery/flasher/portable/wirecutter_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - disable = !disable - if(disable) - user.visible_message("[user] has disconnected [src]'s flashbulb!", "You disconnect [src]'s flashbulb!") - if(!disable) - user.visible_message("[user] has connected [src]'s flashbulb!", "You connect [src]'s flashbulb!") - -/obj/machinery/flasher/portable/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - anchored = !anchored - if(anchored) - WRENCH_ANCHOR_MESSAGE - overlays.Cut() - else if(anchored) - WRENCH_UNANCHOR_MESSAGE - overlays += "[base_state]-s" - -// Flasher button -/obj/machinery/flasher_button - name = "flasher button" - desc = "A remote control switch for a mounted flasher." - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - var/id = null - var/active = 0 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/flasher_button/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/flasher_button/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/flasher_button/attack_hand(mob/user as mob) - if(stat & (NOPOWER|BROKEN)) - return - if(active) - return - - use_power(5) - - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/flasher/M in world) - if(M.id == id) - spawn() - M.flash() - - sleep(50) - - icon_state = "launcherbtt" - active = 0 +// It is a gizmo that flashes a small area + +/obj/machinery/flasher + name = "Mounted flash" + desc = "A wall-mounted flashbulb device." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "mflash1" + max_integrity = 250 + integrity_failure = 100 + damage_deflection = 10 + var/id = null + var/range = 2 //this is roughly the size of brig cell + var/disable = 0 + var/last_flash = 0 //Don't want it getting spammed like regular flashes + var/strength = 5 //How weakened targets are when flashed. + var/base_state = "mflash" + anchored = 1 + +/obj/machinery/flasher/portable //Portable version of the flasher. Only flashes when anchored + name = "portable flasher" + desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." + icon_state = "pflash1" + strength = 4 + anchored = 0 + base_state = "pflash" + density = 1 + +/* +/obj/machinery/flasher/New() + sleep(4) //<--- What the fuck are you doing? D= + sd_set_light(2) +*/ +/obj/machinery/flasher/power_change() + if( powered() ) + stat &= ~NOPOWER + icon_state = "[base_state]1" +// sd_set_light(2) + else + stat |= ~NOPOWER + icon_state = "[base_state]1-p" +// sd_set_light(0) + +//Let the AI trigger them directly. +/obj/machinery/flasher/attack_ai(mob/user) + if(anchored) + return flash() + +/obj/machinery/flasher/attack_ghost(mob/user) + if(anchored && user.can_advanced_admin_interact()) + return flash() + +/obj/machinery/flasher/proc/flash() + if(!(powered())) + return + + if((disable) || (last_flash && world.time < last_flash + 150)) + return + + playsound(loc, 'sound/weapons/flash.ogg', 100, 1) + flick("[base_state]_flash", src) + last_flash = world.time + use_power(1000) + + for(var/mob/living/L in viewers(src, null)) + if(get_dist(src, L) > range) + continue + + if(L.flash_eyes(affect_silicon = 1)) + L.Weaken(strength) + if(L.weakeyes) + L.Weaken(strength * 1.5) + L.visible_message("[L] gasps and shields [L.p_their()] eyes!") + +/obj/machinery/flasher/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + if(prob(75/severity)) + flash() + ..(severity) + +/obj/machinery/flasher/portable/HasProximity(atom/movable/AM as mob|obj) + if((disable) || (last_flash && world.time < last_flash + 150)) + return + + if(istype(AM, /mob/living/carbon)) + var/mob/living/carbon/M = AM + if((M.m_intent != MOVE_INTENT_WALK) && (anchored)) + flash() + +//Don't want to render prison breaks impossible +/obj/machinery/flasher/portable/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + disable = !disable + if(disable) + user.visible_message("[user] has disconnected [src]'s flashbulb!", "You disconnect [src]'s flashbulb!") + if(!disable) + user.visible_message("[user] has connected [src]'s flashbulb!", "You connect [src]'s flashbulb!") + +/obj/machinery/flasher/portable/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + anchored = !anchored + if(anchored) + WRENCH_ANCHOR_MESSAGE + overlays.Cut() + else if(anchored) + WRENCH_UNANCHOR_MESSAGE + overlays += "[base_state]-s" + +// Flasher button +/obj/machinery/flasher_button + name = "flasher button" + desc = "A remote control switch for a mounted flasher." + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + var/id = null + var/active = 0 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/flasher_button/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/flasher_button/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/flasher_button/attack_hand(mob/user as mob) + if(stat & (NOPOWER|BROKEN)) + return + if(active) + return + + use_power(5) + + active = 1 + icon_state = "launcheract" + + for(var/obj/machinery/flasher/M in world) + if(M.id == id) + spawn() + M.flash() + + sleep(50) + + icon_state = "launcherbtt" + active = 0 diff --git a/code/game/machinery/guestpass.dm b/code/game/machinery/guestpass.dm index 35d5a6546d4..165c8dbe4cb 100644 --- a/code/game/machinery/guestpass.dm +++ b/code/game/machinery/guestpass.dm @@ -193,5 +193,5 @@ /obj/machinery/computer/guestpass/hop/get_changeable_accesses() . = ..() - if(. && access_change_ids in .) + if(. && (ACCESS_CHANGE_IDS in .)) return get_all_accesses() diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index a742f6b7109..fcd6de4020c 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -1,517 +1,517 @@ -/* holograms! - * Contains: - * Holopad - * hologram - * Other stuff - */ - -/* -Revised. Original based on space ninja hologram code. Which is also mine. /N -How it works: -AI clicks on holopad in camera view. View centers on holopad. -AI clicks again on the holopad to display a hologram. hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad. -AI can use the directional keys to move the hologram around, provided the above conditions are met and the AI in question is the holopad's master. -Only one AI may project from a holopad at any given time. -AI may cancel the hologram at any time by clicking on the holopad once more. -Possible to do for anyone motivated enough: - Give an AI variable for different hologram icons. - Itegrate EMP effect to disable the unit. -*/ - - -/* - * Holopad - */ - -// HOLOPAD MODE -// 0 = RANGE BASED -// 1 = AREA BASED -#define HOLOPAD_PASSIVE_POWER_USAGE 1 -#define HOLOGRAM_POWER_USAGE 2 -#define RANGE_BASED 0 -#define AREA_BASED 1 - -var/const/HOLOPAD_MODE = RANGE_BASED - -var/list/holopads = list() - -/obj/machinery/hologram/holopad - name = "holopad" - desc = "It's a floor-mounted device for projecting holographic images." - icon_state = "holopad0" - anchored = 1 - use_power = IDLE_POWER_USE - idle_power_usage = 5 - active_power_usage = 100 - layer = TURF_LAYER+0.1 //Preventing mice and drones from sneaking under them. - plane = FLOOR_PLANE - max_integrity = 300 - armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 0) - var/list/masters = list()//List of living mobs that use the holopad - var/list/holorays = list()//Holoray-mob link. - var/last_request = 0 //to prevent request spam. ~Carn - var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. - var/temp = "" - var/list/holo_calls //array of /datum/holocalls - var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs - var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging - var/obj/effect/overlay/holoray/ray - var/ringing = FALSE - var/dialling_input = FALSE //The user is currently selecting where to send their call - -/obj/machinery/hologram/holopad/New() - ..() - holopads += src - component_parts = list() - component_parts += new /obj/item/circuitboard/holopad(null) - component_parts += new /obj/item/stock_parts/capacitor(null) - RefreshParts() - -/obj/machinery/hologram/holopad/Destroy() - if(outgoing_call) - outgoing_call.ConnectionFailure(src) - - for(var/I in holo_calls) - var/datum/holocall/HC = I - HC.ConnectionFailure(src) - - for(var/I in masters) - clear_holo(I) - holopads -= src - return ..() - -/obj/machinery/hologram/holopad/power_change() - if(powered()) - stat &= ~NOPOWER - else - stat |= NOPOWER - if(outgoing_call) - outgoing_call.ConnectionFailure(src) - -/obj/machinery/hologram/holopad/obj_break() - . = ..() - if(outgoing_call) - outgoing_call.ConnectionFailure(src) - -/obj/machinery/hologram/holopad/RefreshParts() - var/holograph_range = 4 - for(var/obj/item/stock_parts/capacitor/B in component_parts) - holograph_range += 1 * B.rating - holo_range = holograph_range - -/obj/machinery/hologram/holopad/attackby(obj/item/I, mob/user, params) - if(exchange_parts(user, I)) - return - return ..() - -/obj/machinery/hologram/holopad/screwdriver_act(mob/user, obj/item/I) - . = TRUE - default_deconstruction_screwdriver(user, "holopad_open", "holopad0", I) - - -/obj/machinery/hologram/holopad/wrench_act(mob/user, obj/item/I) - . = TRUE - default_unfasten_wrench(user, I) - -/obj/machinery/hologram/holopad/crowbar_act(mob/user, obj/item/I) - . = TRUE - default_deconstruction_crowbar(user, I) - -/obj/machinery/hologram/holopad/attack_hand(mob/living/carbon/human/user) - if(..()) - return - - if(outgoing_call) - return - - user.set_machine(src) - interact(user) - -/obj/machinery/hologram/holopad/AltClick(mob/living/carbon/human/user) - if(..()) - return - if(isAI(user)) - hangup_all_calls() - return - -//Stop ringing the AI!! -/obj/machinery/hologram/holopad/proc/hangup_all_calls() - for(var/I in holo_calls) - var/datum/holocall/HC = I - HC.Disconnect(src) - -/obj/machinery/hologram/holopad/interact(mob/living/carbon/human/user) //Carn: hologram requests. - if(!istype(user)) - return - if(!anchored) - return - - var/dat - if(temp) - dat = temp - else - dat = "Request an AI's presence.
    " - dat += "Call another holopad.
    " - - if(LAZYLEN(holo_calls)) - dat += "=====================================================
    " - - var/one_answered_call = FALSE - var/one_unanswered_call = FALSE - for(var/I in holo_calls) - var/datum/holocall/HC = I - if(HC.connected_holopad != src) - dat += "Answer call from [get_area(HC.calling_holopad)].
    " - one_unanswered_call = TRUE - else - one_answered_call = TRUE - - if(one_answered_call && one_unanswered_call) - dat += "=====================================================
    " - //we loop twice for formatting - for(var/I in holo_calls) - var/datum/holocall/HC = I - if(HC.connected_holopad == src) - dat += "Disconnect call from [HC.user].
    " - - var/area/area = get_area(src) - var/datum/browser/popup = new(user, "holopad", "[area] holopad", 400, 300) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() - -/obj/machinery/hologram/holopad/Topic(href, href_list) - if(..() || isAI(usr)) - return - add_fingerprint(usr) - if(stat & NOPOWER) - return - if(href_list["AIrequest"]) - if(last_request + 200 < world.time) - last_request = world.time - temp = "You requested an AI's presence.
    " - temp += "Main Menu" - var/area/area = get_area(src) - for(var/mob/living/silicon/ai/AI in ai_list) - if(!AI.client) - continue - to_chat(AI, "Your presence is requested at \the [area].") - else - temp = "A request for AI presence was already sent recently.
    " - temp += "Main Menu" - - else if(href_list["Holocall"]) - if(outgoing_call) - return - if(dialling_input) - to_chat(usr, "Finish dialling first!") - return - temp = "You must stand on the holopad to make a call!
    " - temp += "Main Menu" - if(usr.loc == loc) - var/list/callnames = list() - for(var/I in holopads) - var/area/A = get_area(I) - if(A) - LAZYADD(callnames[A], I) - callnames -= get_area(src) - var/list/sorted_callnames = sortAtom(callnames) - dialling_input = TRUE - var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in sorted_callnames - dialling_input = FALSE - if(QDELETED(usr) || !result || outgoing_call) - return - - if(usr.loc == loc) - temp = "Dialing...
    " - temp += "Main Menu" - new /datum/holocall(usr, src, callnames[result]) - - else if(href_list["connectcall"]) - var/datum/holocall/call_to_connect = locateUID(href_list["connectcall"]) - if(!QDELETED(call_to_connect) && (call_to_connect in holo_calls)) - call_to_connect.Answer(src) - temp = "" - - else if(href_list["disconnectcall"]) - var/datum/holocall/call_to_disconnect = locateUID(href_list["disconnectcall"]) - if(!QDELETED(call_to_disconnect)) - call_to_disconnect.Disconnect(src) - temp = "" - - else if(href_list["mainmenu"]) - temp = "" - if(outgoing_call) - outgoing_call.Disconnect() - - updateDialog() - -//do not allow AIs to answer calls or people will use it to meta the AI satellite -/obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) - if(!istype(user)) - return - if(outgoing_call) - return - /*There are pretty much only three ways to interact here. - I don't need to check for client since they're clicking on an object. - This may change in the future but for now will suffice.*/ - if(user.eyeobj.loc != loc)//Set client eye on the object if it's not already. - user.eyeobj.setLoc(get_turf(src)) - else if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one. - activate_holo(user, 1) - else//If there is a hologram, remove it. - clear_holo(user) - -/obj/machinery/hologram/holopad/process() - for(var/I in masters) - var/mob/living/master = I - if((stat & NOPOWER) || !validate_user(master) || !anchored) - clear_holo(master) - - if(outgoing_call) - outgoing_call.Check() - - ringing = FALSE - - for(var/I in holo_calls) - var/datum/holocall/HC = I - //Sanity check and skip if no longer valid call - if(!HC.Check()) - atom_say("Call was terminated at remote terminal.") - continue - - if(HC.connected_holopad != src) - if(force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2))) - HC.Answer(src) - break - if(outgoing_call) - HC.Disconnect(src)//can't answer calls while calling - else - playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring! - ringing = TRUE - - update_icon() - - -//Try to transfer hologram to another pad that can project on T -/obj/machinery/hologram/holopad/proc/transfer_to_nearby_pad(turf/T, mob/holo_owner) - if(!isAI(holo_owner)) - return - for(var/pad in holopads) - var/obj/machinery/hologram/holopad/another = pad - if(another == src) - continue - if(another.validate_location(T)) - var/obj/effect/overlay/holo_pad_hologram/h = masters[holo_owner] - unset_holo(holo_owner) - another.set_holo(holo_owner, h) - return TRUE - return FALSE - -/obj/machinery/hologram/holopad/proc/validate_user(mob/living/user) - if(QDELETED(user) || user.incapacitated() || !user.client) - return FALSE - - if(istype(user, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = user - if(!AI.current) - return FALSE - return TRUE - -//Can we display holos there -//Area check instead of line of sight check because this is a called a lot if AI wants to move around. -/obj/machinery/hologram/holopad/proc/validate_location(turf/T,check_los = FALSE) - if(T.z == z && get_dist(T, src) <= holo_range && T.loc == get_area(src)) - return TRUE - return FALSE - - -/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/user, turf/new_turf) - if(masters[user]) - var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] - var/transfered = FALSE - if(!validate_location(new_turf)) - if(!transfer_to_nearby_pad(new_turf,user)) - clear_holo(user) - return FALSE - else - transfered = TRUE - //All is good. - holo.setDir(get_dir(holo.loc, new_turf)) - holo.forceMove(new_turf) - if(!transfered) - update_holoray(user,new_turf) - return TRUE - -/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/user, var/force = 0) - var/mob/living/silicon/ai/AI = user - if(!istype(AI)) - AI = null - if(AI && !force && AI.eyeobj.loc != loc) // allows holopads to pass off holograms to the next holopad in the chain - to_chat(user, "ERROR: Unable to project hologram.") - if(!(stat & NOPOWER) && (!AI || force)) - if(AI && (istype(AI.current, /obj/machinery/hologram/holopad))) - to_chat(user, "ERROR: Image feed in progress.") - return - - var/obj/effect/overlay/holo_pad_hologram/hologram = new(loc)//Spawn a blank effect at the location. - if(isAI(user)) - hologram.icon = AI.holo_icon - else //make it like real life - hologram.icon = getHologramIcon(get_id_photo(user)) - hologram.icon_state = user.icon_state - hologram.alpha = 100 - hologram.Impersonation = user - - hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it. - hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. - hologram.anchored = 1//So space wind cannot drag it. - hologram.name = "[user.name] (hologram)"//If someone decides to right click. - hologram.set_light(2) //hologram lighting - move_hologram() - - set_holo(user, hologram) - - if(!masters[user])//If there is not already a hologram. - visible_message("A holographic image of [user] flicks to life right before your eyes!") - - return hologram - - - to_chat(user, "ERROR: Hologram Projection Malfunction.") - clear_holo(user)//safety check - -/*This is the proc for special two-way communication between AI and holopad/people talking near holopad. -For the other part of the code, check silicon say.dm. Particularly robot talk.*/ -/obj/machinery/hologram/holopad/hear_talk(atom/movable/speaker, list/message_pieces, verb) - if(speaker && masters.len)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios. - for(var/mob/living/silicon/ai/master in masters) - if(masters[master] && speaker != master) - master.relay_speech(speaker, message_pieces, verb) - - for(var/I in holo_calls) - var/datum/holocall/HC = I - if(HC.connected_holopad == src && speaker != HC.hologram) - HC.user.hear_say(message_pieces, verb, speaker = speaker) - - if(outgoing_call && speaker == outgoing_call.user) - outgoing_call.hologram.atom_say(multilingual_to_message(message_pieces)) - - - -/obj/machinery/hologram/holopad/proc/SetLightsAndPower() - var/total_users = masters.len + LAZYLEN(holo_calls) - use_power = total_users > 0 ? ACTIVE_POWER_USE : IDLE_POWER_USE - active_power_usage = HOLOPAD_PASSIVE_POWER_USAGE + (HOLOGRAM_POWER_USAGE * total_users) - if(total_users) - set_light(2) - icon_state = "holopad1" - else - set_light(0) - icon_state = "holopad0" - update_icon() - -/obj/machinery/hologram/holopad/update_icon() - var/total_users = LAZYLEN(masters) + LAZYLEN(holo_calls) - if(icon_state == "holopad_open") - return - else if(ringing) - icon_state = "holopad_ringing" - else if(total_users) - icon_state = "holopad1" - else - icon_state = "holopad0" - - -/obj/machinery/hologram/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h) - masters[user] = h - holorays[user] = new /obj/effect/overlay/holoray(loc) - var/mob/living/silicon/ai/AI = user - if(istype(AI)) - AI.current = src - SetLightsAndPower() - update_holoray(user, get_turf(loc)) - return TRUE - -/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/user) - qdel(masters[user]) // Get rid of user's hologram - unset_holo(user) - return TRUE - -/obj/machinery/hologram/holopad/proc/unset_holo(mob/living/user) - var/mob/living/silicon/ai/AI = user - if(istype(AI) && AI.current == src) - AI.current = null - masters -= user // Discard AI from the list of those who use holopad - qdel(holorays[user]) - holorays -= user - SetLightsAndPower() - return TRUE - -/obj/machinery/hologram/holopad/proc/update_holoray(mob/living/user, turf/new_turf) - var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] - var/obj/effect/overlay/holoray/ray = holorays[user] - var/disty = holo.y - ray.y - var/distx = holo.x - ray.x - var/newangle - if(!disty) - if(distx >= 0) - newangle = 90 - else - newangle = 270 - else - newangle = arctan(distx/disty) - if(disty < 0) - newangle += 180 - else if(distx < 0) - newangle += 360 - var/matrix/M = matrix() - if(get_dist(get_turf(holo), new_turf) <= 1) - animate(ray, transform = turn(M.Scale(1, sqrt(distx*distx+disty*disty)), newangle), time = 1) - else - ray.transform = turn(M.Scale(1, sqrt(distx*distx+disty*disty)), newangle) - - -/obj/effect/overlay/holo_pad_hologram - var/mob/living/Impersonation - var/datum/holocall/HC - -/obj/effect/overlay/holo_pad_hologram/Destroy() - Impersonation = null - if(!QDELETED(HC)) - HC.Disconnect(HC.calling_holopad) - return ..() - -/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0) - return 1 - -/obj/effect/overlay/holo_pad_hologram/examine(mob/user) - if(Impersonation) - . = Impersonation.examine(user) - else - . = ..() - - -/obj/effect/overlay/holoray - name = "holoray" - icon = 'icons/effects/96x96.dmi' - icon_state = "holoray" - layer = FLY_LAYER - density = FALSE - anchored = TRUE - mouse_opacity = MOUSE_OPACITY_ICON - pixel_x = -32 - pixel_y = -32 - alpha = 100 - -/* - * Other Stuff: Is this even used? - */ -/obj/machinery/hologram/projector - name = "hologram projector" - desc = "It makes a hologram appear...with magnets or something..." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "hologram0" - -#undef HOLOPAD_PASSIVE_POWER_USAGE -#undef HOLOGRAM_POWER_USAGE +/* holograms! + * Contains: + * Holopad + * hologram + * Other stuff + */ + +/* +Revised. Original based on space ninja hologram code. Which is also mine. /N +How it works: +AI clicks on holopad in camera view. View centers on holopad. +AI clicks again on the holopad to display a hologram. hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad. +AI can use the directional keys to move the hologram around, provided the above conditions are met and the AI in question is the holopad's master. +Only one AI may project from a holopad at any given time. +AI may cancel the hologram at any time by clicking on the holopad once more. +Possible to do for anyone motivated enough: + Give an AI variable for different hologram icons. + Itegrate EMP effect to disable the unit. +*/ + + +/* + * Holopad + */ + +// HOLOPAD MODE +// 0 = RANGE BASED +// 1 = AREA BASED +#define HOLOPAD_PASSIVE_POWER_USAGE 1 +#define HOLOGRAM_POWER_USAGE 2 +#define RANGE_BASED 0 +#define AREA_BASED 1 + +#define HOLOPAD_MODE RANGE_BASED + +GLOBAL_LIST_EMPTY(holopads) + +/obj/machinery/hologram/holopad + name = "holopad" + desc = "It's a floor-mounted device for projecting holographic images." + icon_state = "holopad0" + anchored = 1 + use_power = IDLE_POWER_USE + idle_power_usage = 5 + active_power_usage = 100 + layer = TURF_LAYER+0.1 //Preventing mice and drones from sneaking under them. + plane = FLOOR_PLANE + max_integrity = 300 + armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 0) + var/list/masters = list()//List of living mobs that use the holopad + var/list/holorays = list()//Holoray-mob link. + var/last_request = 0 //to prevent request spam. ~Carn + var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. + var/temp = "" + var/list/holo_calls //array of /datum/holocalls + var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs + var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging + var/obj/effect/overlay/holoray/ray + var/ringing = FALSE + var/dialling_input = FALSE //The user is currently selecting where to send their call + +/obj/machinery/hologram/holopad/New() + ..() + GLOB.holopads += src + component_parts = list() + component_parts += new /obj/item/circuitboard/holopad(null) + component_parts += new /obj/item/stock_parts/capacitor(null) + RefreshParts() + +/obj/machinery/hologram/holopad/Destroy() + if(outgoing_call) + outgoing_call.ConnectionFailure(src) + + for(var/I in holo_calls) + var/datum/holocall/HC = I + HC.ConnectionFailure(src) + + for(var/I in masters) + clear_holo(I) + GLOB.holopads -= src + return ..() + +/obj/machinery/hologram/holopad/power_change() + if(powered()) + stat &= ~NOPOWER + else + stat |= NOPOWER + if(outgoing_call) + outgoing_call.ConnectionFailure(src) + +/obj/machinery/hologram/holopad/obj_break() + . = ..() + if(outgoing_call) + outgoing_call.ConnectionFailure(src) + +/obj/machinery/hologram/holopad/RefreshParts() + var/holograph_range = 4 + for(var/obj/item/stock_parts/capacitor/B in component_parts) + holograph_range += 1 * B.rating + holo_range = holograph_range + +/obj/machinery/hologram/holopad/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + return ..() + +/obj/machinery/hologram/holopad/screwdriver_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_screwdriver(user, "holopad_open", "holopad0", I) + + +/obj/machinery/hologram/holopad/wrench_act(mob/user, obj/item/I) + . = TRUE + default_unfasten_wrench(user, I) + +/obj/machinery/hologram/holopad/crowbar_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_crowbar(user, I) + +/obj/machinery/hologram/holopad/attack_hand(mob/living/carbon/human/user) + if(..()) + return + + if(outgoing_call) + return + + user.set_machine(src) + interact(user) + +/obj/machinery/hologram/holopad/AltClick(mob/living/carbon/human/user) + if(..()) + return + if(isAI(user)) + hangup_all_calls() + return + +//Stop ringing the AI!! +/obj/machinery/hologram/holopad/proc/hangup_all_calls() + for(var/I in holo_calls) + var/datum/holocall/HC = I + HC.Disconnect(src) + +/obj/machinery/hologram/holopad/interact(mob/living/carbon/human/user) //Carn: hologram requests. + if(!istype(user)) + return + if(!anchored) + return + + var/dat + if(temp) + dat = temp + else + dat = "Request an AI's presence.
    " + dat += "Call another holopad.
    " + + if(LAZYLEN(holo_calls)) + dat += "=====================================================
    " + + var/one_answered_call = FALSE + var/one_unanswered_call = FALSE + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(HC.connected_holopad != src) + dat += "Answer call from [get_area(HC.calling_holopad)].
    " + one_unanswered_call = TRUE + else + one_answered_call = TRUE + + if(one_answered_call && one_unanswered_call) + dat += "=====================================================
    " + //we loop twice for formatting + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(HC.connected_holopad == src) + dat += "Disconnect call from [HC.user].
    " + + var/area/area = get_area(src) + var/datum/browser/popup = new(user, "holopad", "[area] holopad", 400, 300) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) + popup.open() + +/obj/machinery/hologram/holopad/Topic(href, href_list) + if(..() || isAI(usr)) + return + add_fingerprint(usr) + if(stat & NOPOWER) + return + if(href_list["AIrequest"]) + if(last_request + 200 < world.time) + last_request = world.time + temp = "You requested an AI's presence.
    " + temp += "Main Menu" + var/area/area = get_area(src) + for(var/mob/living/silicon/ai/AI in GLOB.ai_list) + if(!AI.client) + continue + to_chat(AI, "Your presence is requested at \the [area].") + else + temp = "A request for AI presence was already sent recently.
    " + temp += "Main Menu" + + else if(href_list["Holocall"]) + if(outgoing_call) + return + if(dialling_input) + to_chat(usr, "Finish dialling first!") + return + temp = "You must stand on the holopad to make a call!
    " + temp += "Main Menu" + if(usr.loc == loc) + var/list/callnames = list() + for(var/I in GLOB.holopads) + var/area/A = get_area(I) + if(A) + LAZYADD(callnames[A], I) + callnames -= get_area(src) + var/list/sorted_callnames = sortAtom(callnames) + dialling_input = TRUE + var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in sorted_callnames + dialling_input = FALSE + if(QDELETED(usr) || !result || outgoing_call) + return + + if(usr.loc == loc) + temp = "Dialing...
    " + temp += "Main Menu" + new /datum/holocall(usr, src, callnames[result]) + + else if(href_list["connectcall"]) + var/datum/holocall/call_to_connect = locateUID(href_list["connectcall"]) + if(!QDELETED(call_to_connect) && (call_to_connect in holo_calls)) + call_to_connect.Answer(src) + temp = "" + + else if(href_list["disconnectcall"]) + var/datum/holocall/call_to_disconnect = locateUID(href_list["disconnectcall"]) + if(!QDELETED(call_to_disconnect)) + call_to_disconnect.Disconnect(src) + temp = "" + + else if(href_list["mainmenu"]) + temp = "" + if(outgoing_call) + outgoing_call.Disconnect() + + updateDialog() + +//do not allow AIs to answer calls or people will use it to meta the AI satellite +/obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) + if(!istype(user)) + return + if(outgoing_call) + return + /*There are pretty much only three ways to interact here. + I don't need to check for client since they're clicking on an object. + This may change in the future but for now will suffice.*/ + if(user.eyeobj.loc != loc)//Set client eye on the object if it's not already. + user.eyeobj.setLoc(get_turf(src)) + else if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one. + activate_holo(user, 1) + else//If there is a hologram, remove it. + clear_holo(user) + +/obj/machinery/hologram/holopad/process() + for(var/I in masters) + var/mob/living/master = I + if((stat & NOPOWER) || !validate_user(master) || !anchored) + clear_holo(master) + + if(outgoing_call) + outgoing_call.Check() + + ringing = FALSE + + for(var/I in holo_calls) + var/datum/holocall/HC = I + //Sanity check and skip if no longer valid call + if(!HC.Check()) + atom_say("Call was terminated at remote terminal.") + continue + + if(HC.connected_holopad != src) + if(force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2))) + HC.Answer(src) + break + if(outgoing_call) + HC.Disconnect(src)//can't answer calls while calling + else + playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring! + ringing = TRUE + + update_icon() + + +//Try to transfer hologram to another pad that can project on T +/obj/machinery/hologram/holopad/proc/transfer_to_nearby_pad(turf/T, mob/holo_owner) + if(!isAI(holo_owner)) + return + for(var/pad in GLOB.holopads) + var/obj/machinery/hologram/holopad/another = pad + if(another == src) + continue + if(another.validate_location(T)) + var/obj/effect/overlay/holo_pad_hologram/h = masters[holo_owner] + unset_holo(holo_owner) + another.set_holo(holo_owner, h) + return TRUE + return FALSE + +/obj/machinery/hologram/holopad/proc/validate_user(mob/living/user) + if(QDELETED(user) || user.incapacitated() || !user.client) + return FALSE + + if(istype(user, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = user + if(!AI.current) + return FALSE + return TRUE + +//Can we display holos there +//Area check instead of line of sight check because this is a called a lot if AI wants to move around. +/obj/machinery/hologram/holopad/proc/validate_location(turf/T,check_los = FALSE) + if(T.z == z && get_dist(T, src) <= holo_range && T.loc == get_area(src)) + return TRUE + return FALSE + + +/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/user, turf/new_turf) + if(masters[user]) + var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] + var/transfered = FALSE + if(!validate_location(new_turf)) + if(!transfer_to_nearby_pad(new_turf,user)) + clear_holo(user) + return FALSE + else + transfered = TRUE + //All is good. + holo.setDir(get_dir(holo.loc, new_turf)) + holo.forceMove(new_turf) + if(!transfered) + update_holoray(user,new_turf) + return TRUE + +/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/user, var/force = 0) + var/mob/living/silicon/ai/AI = user + if(!istype(AI)) + AI = null + if(AI && !force && AI.eyeobj.loc != loc) // allows holopads to pass off holograms to the next holopad in the chain + to_chat(user, "ERROR: Unable to project hologram.") + if(!(stat & NOPOWER) && (!AI || force)) + if(AI && (istype(AI.current, /obj/machinery/hologram/holopad))) + to_chat(user, "ERROR: Image feed in progress.") + return + + var/obj/effect/overlay/holo_pad_hologram/hologram = new(loc)//Spawn a blank effect at the location. + if(isAI(user)) + hologram.icon = AI.holo_icon + else //make it like real life + hologram.icon = getHologramIcon(get_id_photo(user)) + hologram.icon_state = user.icon_state + hologram.alpha = 100 + hologram.Impersonation = user + + hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it. + hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. + hologram.anchored = 1//So space wind cannot drag it. + hologram.name = "[user.name] (hologram)"//If someone decides to right click. + hologram.set_light(2) //hologram lighting + move_hologram() + + set_holo(user, hologram) + + if(!masters[user])//If there is not already a hologram. + visible_message("A holographic image of [user] flicks to life right before your eyes!") + + return hologram + + + to_chat(user, "ERROR: Hologram Projection Malfunction.") + clear_holo(user)//safety check + +/*This is the proc for special two-way communication between AI and holopad/people talking near holopad. +For the other part of the code, check silicon say.dm. Particularly robot talk.*/ +/obj/machinery/hologram/holopad/hear_talk(atom/movable/speaker, list/message_pieces, verb) + if(speaker && masters.len)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios. + for(var/mob/living/silicon/ai/master in masters) + if(masters[master] && speaker != master) + master.relay_speech(speaker, message_pieces, verb) + + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(HC.connected_holopad == src && speaker != HC.hologram) + HC.user.hear_say(message_pieces, verb, speaker = speaker) + + if(outgoing_call && speaker == outgoing_call.user) + outgoing_call.hologram.atom_say(multilingual_to_message(message_pieces)) + + + +/obj/machinery/hologram/holopad/proc/SetLightsAndPower() + var/total_users = masters.len + LAZYLEN(holo_calls) + use_power = total_users > 0 ? ACTIVE_POWER_USE : IDLE_POWER_USE + active_power_usage = HOLOPAD_PASSIVE_POWER_USAGE + (HOLOGRAM_POWER_USAGE * total_users) + if(total_users) + set_light(2) + icon_state = "holopad1" + else + set_light(0) + icon_state = "holopad0" + update_icon() + +/obj/machinery/hologram/holopad/update_icon() + var/total_users = LAZYLEN(masters) + LAZYLEN(holo_calls) + if(icon_state == "holopad_open") + return + else if(ringing) + icon_state = "holopad_ringing" + else if(total_users) + icon_state = "holopad1" + else + icon_state = "holopad0" + + +/obj/machinery/hologram/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h) + masters[user] = h + holorays[user] = new /obj/effect/overlay/holoray(loc) + var/mob/living/silicon/ai/AI = user + if(istype(AI)) + AI.current = src + SetLightsAndPower() + update_holoray(user, get_turf(loc)) + return TRUE + +/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/user) + qdel(masters[user]) // Get rid of user's hologram + unset_holo(user) + return TRUE + +/obj/machinery/hologram/holopad/proc/unset_holo(mob/living/user) + var/mob/living/silicon/ai/AI = user + if(istype(AI) && AI.current == src) + AI.current = null + masters -= user // Discard AI from the list of those who use holopad + qdel(holorays[user]) + holorays -= user + SetLightsAndPower() + return TRUE + +/obj/machinery/hologram/holopad/proc/update_holoray(mob/living/user, turf/new_turf) + var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] + var/obj/effect/overlay/holoray/ray = holorays[user] + var/disty = holo.y - ray.y + var/distx = holo.x - ray.x + var/newangle + if(!disty) + if(distx >= 0) + newangle = 90 + else + newangle = 270 + else + newangle = arctan(distx/disty) + if(disty < 0) + newangle += 180 + else if(distx < 0) + newangle += 360 + var/matrix/M = matrix() + if(get_dist(get_turf(holo), new_turf) <= 1) + animate(ray, transform = turn(M.Scale(1, sqrt(distx*distx+disty*disty)), newangle), time = 1) + else + ray.transform = turn(M.Scale(1, sqrt(distx*distx+disty*disty)), newangle) + + +/obj/effect/overlay/holo_pad_hologram + var/mob/living/Impersonation + var/datum/holocall/HC + +/obj/effect/overlay/holo_pad_hologram/Destroy() + Impersonation = null + if(!QDELETED(HC)) + HC.Disconnect(HC.calling_holopad) + return ..() + +/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0) + return 1 + +/obj/effect/overlay/holo_pad_hologram/examine(mob/user) + if(Impersonation) + . = Impersonation.examine(user) + else + . = ..() + + +/obj/effect/overlay/holoray + name = "holoray" + icon = 'icons/effects/96x96.dmi' + icon_state = "holoray" + layer = FLY_LAYER + density = FALSE + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_ICON + pixel_x = -32 + pixel_y = -32 + alpha = 100 + +/* + * Other Stuff: Is this even used? + */ +/obj/machinery/hologram/projector + name = "hologram projector" + desc = "It makes a hologram appear...with magnets or something..." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "hologram0" + +#undef HOLOPAD_PASSIVE_POWER_USAGE +#undef HOLOGRAM_POWER_USAGE diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index e4bdb0d5478..c6918d795ff 100755 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -1,126 +1,126 @@ -/obj/machinery/igniter - name = "igniter" - desc = "It's useful for igniting plasma." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "igniter1" - plane = FLOOR_PLANE - max_integrity = 300 - armor = list(melee = 50, bullet = 30, laser = 70, energy = 50, bomb = 20, bio = 0, rad = 0, fire = 100, acid = 70) - resistance_flags = FIRE_PROOF - var/id = null - var/on = FALSE - anchored = TRUE - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/igniter/on - on = TRUE - -/obj/machinery/igniter/attack_ai(mob/user as mob) - return src.attack_hand(user) - - -/obj/machinery/igniter/attack_hand(mob/user as mob) - if(..()) - return - add_fingerprint(user) - - use_power(50) - src.on = !( src.on ) - src.icon_state = text("igniter[]", src.on) - return - -/obj/machinery/igniter/process() //ugh why is this even in process()? - if(src.on && !(stat & NOPOWER) ) - var/turf/location = src.loc - if(isturf(location)) - location.hotspot_expose(1000,500,1) - return 1 - -/obj/machinery/igniter/New() - ..() - icon_state = "igniter[on]" - -/obj/machinery/igniter/power_change() - if(!( stat & NOPOWER) ) - icon_state = "igniter[src.on]" - else - icon_state = "igniter0" - -// Wall mounted remote-control igniter. - -/obj/machinery/sparker - name = "Mounted igniter" - desc = "A wall-mounted ignition device." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "migniter" - resistance_flags = FIRE_PROOF - var/id = null - var/disable = FALSE - var/last_spark = FALSE - var/base_state = "migniter" - anchored = 1 - -/obj/machinery/sparker/New() - ..() - -/obj/machinery/sparker/power_change() - if( powered() && disable == 0 ) - stat &= ~NOPOWER - icon_state = "[base_state]" -// src.sd_set_light(2) - else - stat |= ~NOPOWER - icon_state = "[base_state]-p" -// src.sd_set_light(0) - -/obj/machinery/sparker/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/detective_scanner)) - return - return ..() - -/obj/machinery/sparker/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - disable = !disable - if(disable) - user.visible_message("[user] has disabled [src]!", "You disable the connection to [src].") - icon_state = "[base_state]-d" - if(!disable) - user.visible_message("[user] has reconnected [src]!", "You fix the connection to [src].") - if(powered()) - icon_state = "[base_state]" - else - icon_state = "[base_state]-p" - -/obj/machinery/sparker/attack_ai() - if(src.anchored) - return src.spark() - else - return - -/obj/machinery/sparker/proc/spark() - if(!(powered())) - return - - if((src.disable) || (src.last_spark && world.time < src.last_spark + 50)) - return - - - flick("[base_state]-spark", src) - do_sparks(2, 1, src) - src.last_spark = world.time - use_power(1000) - var/turf/location = src.loc - if(isturf(location)) - location.hotspot_expose(1000,500,1) - return 1 - -/obj/machinery/sparker/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - spark() - ..(severity) +/obj/machinery/igniter + name = "igniter" + desc = "It's useful for igniting plasma." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "igniter1" + plane = FLOOR_PLANE + max_integrity = 300 + armor = list(melee = 50, bullet = 30, laser = 70, energy = 50, bomb = 20, bio = 0, rad = 0, fire = 100, acid = 70) + resistance_flags = FIRE_PROOF + var/id = null + var/on = FALSE + anchored = TRUE + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/igniter/on + on = TRUE + +/obj/machinery/igniter/attack_ai(mob/user as mob) + return src.attack_hand(user) + + +/obj/machinery/igniter/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + + use_power(50) + src.on = !( src.on ) + src.icon_state = text("igniter[]", src.on) + return + +/obj/machinery/igniter/process() //ugh why is this even in process()? + if(src.on && !(stat & NOPOWER) ) + var/turf/location = src.loc + if(isturf(location)) + location.hotspot_expose(1000,500,1) + return 1 + +/obj/machinery/igniter/New() + ..() + icon_state = "igniter[on]" + +/obj/machinery/igniter/power_change() + if(!( stat & NOPOWER) ) + icon_state = "igniter[src.on]" + else + icon_state = "igniter0" + +// Wall mounted remote-control igniter. + +/obj/machinery/sparker + name = "Mounted igniter" + desc = "A wall-mounted ignition device." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "migniter" + resistance_flags = FIRE_PROOF + var/id = null + var/disable = FALSE + var/last_spark = FALSE + var/base_state = "migniter" + anchored = 1 + +/obj/machinery/sparker/New() + ..() + +/obj/machinery/sparker/power_change() + if( powered() && disable == 0 ) + stat &= ~NOPOWER + icon_state = "[base_state]" +// src.sd_set_light(2) + else + stat |= ~NOPOWER + icon_state = "[base_state]-p" +// src.sd_set_light(0) + +/obj/machinery/sparker/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/detective_scanner)) + return + return ..() + +/obj/machinery/sparker/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + disable = !disable + if(disable) + user.visible_message("[user] has disabled [src]!", "You disable the connection to [src].") + icon_state = "[base_state]-d" + if(!disable) + user.visible_message("[user] has reconnected [src]!", "You fix the connection to [src].") + if(powered()) + icon_state = "[base_state]" + else + icon_state = "[base_state]-p" + +/obj/machinery/sparker/attack_ai() + if(src.anchored) + return src.spark() + else + return + +/obj/machinery/sparker/proc/spark() + if(!(powered())) + return + + if((src.disable) || (src.last_spark && world.time < src.last_spark + 50)) + return + + + flick("[base_state]-spark", src) + do_sparks(2, 1, src) + src.last_spark = world.time + use_power(1000) + var/turf/location = src.loc + if(isturf(location)) + location.hotspot_expose(1000,500,1) + return 1 + +/obj/machinery/sparker/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + spark() + ..(severity) diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 204a85b426e..d7b21983792 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -1,75 +1,75 @@ -#define IV_TAKING 0 -#define IV_INJECTING 1 - -/obj/machinery/iv_drip - name = "\improper IV drip" - icon = 'icons/goonstation/objects/iv.dmi' - icon_state = "stand" - anchored = FALSE - mouse_drag_pointer = MOUSE_ACTIVE_POINTER - var/obj/item/reagent_containers/iv_bag/bag = null - -/obj/machinery/iv_drip/update_icon() - cut_overlays() - - if(bag) - add_overlay("hangingbag") - if(bag.reagents.total_volume) - var/image/filling = image('icons/goonstation/objects/iv.dmi', src, "hangingbag-fluid") - filling.icon += mix_color_from_reagents(bag.reagents.reagent_list) - add_overlay(filling) - -/obj/machinery/iv_drip/MouseDrop(mob/living/target) - if(usr.incapacitated()) - return - - if(!ishuman(usr) || !iscarbon(target)) - return - - if(Adjacent(target) && usr.Adjacent(target)) - bag.afterattack(target, usr, TRUE) - -/obj/machinery/iv_drip/attack_hand(mob/user) - if(bag) - user.put_in_hands(bag) - bag.update_icon() - bag = null - update_icon() - -/obj/machinery/iv_drip/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers/iv_bag)) - if(bag) - to_chat(user, "[src] already has an IV bag!") - return - if(!user.drop_item()) - return - - I.forceMove(src) - bag = I - to_chat(user, "You attach [I] to [src].") - update_icon() - else if (bag && istype(I, /obj/item/reagent_containers)) - bag.attackby(I) - I.afterattack(bag, usr, TRUE) - update_icon() - else - return ..() - -/obj/machinery/iv_drip/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc) - qdel(src) - -/obj/machinery/iv_drip/examine(mob/user) - . = ..() - if(bag) - . += bag.examine(user) - -/obj/machinery/iv_drip/Move(NewLoc, direct) - . = ..() - if(!.) // ..() will return 0 if we didn't actually move anywhere. - return . - playsound(loc, pick('sound/items/cartwheel1.ogg', 'sound/items/cartwheel2.ogg'), 100, 1, ignore_walls = FALSE) - -#undef IV_TAKING -#undef IV_INJECTING \ No newline at end of file +#define IV_TAKING 0 +#define IV_INJECTING 1 + +/obj/machinery/iv_drip + name = "\improper IV drip" + icon = 'icons/goonstation/objects/iv.dmi' + icon_state = "stand" + anchored = FALSE + mouse_drag_pointer = MOUSE_ACTIVE_POINTER + var/obj/item/reagent_containers/iv_bag/bag = null + +/obj/machinery/iv_drip/update_icon() + cut_overlays() + + if(bag) + add_overlay("hangingbag") + if(bag.reagents.total_volume) + var/image/filling = image('icons/goonstation/objects/iv.dmi', src, "hangingbag-fluid") + filling.icon += mix_color_from_reagents(bag.reagents.reagent_list) + add_overlay(filling) + +/obj/machinery/iv_drip/MouseDrop(mob/living/target) + if(usr.incapacitated()) + return + + if(!ishuman(usr) || !iscarbon(target)) + return + + if(Adjacent(target) && usr.Adjacent(target)) + bag.afterattack(target, usr, TRUE) + +/obj/machinery/iv_drip/attack_hand(mob/user) + if(bag) + user.put_in_hands(bag) + bag.update_icon() + bag = null + update_icon() + +/obj/machinery/iv_drip/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/reagent_containers/iv_bag)) + if(bag) + to_chat(user, "[src] already has an IV bag!") + return + if(!user.drop_item()) + return + + I.forceMove(src) + bag = I + to_chat(user, "You attach [I] to [src].") + update_icon() + else if (bag && istype(I, /obj/item/reagent_containers)) + bag.attackby(I) + I.afterattack(bag, usr, TRUE) + update_icon() + else + return ..() + +/obj/machinery/iv_drip/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc) + qdel(src) + +/obj/machinery/iv_drip/examine(mob/user) + . = ..() + if(bag) + . += bag.examine(user) + +/obj/machinery/iv_drip/Move(NewLoc, direct) + . = ..() + if(!.) // ..() will return 0 if we didn't actually move anywhere. + return . + playsound(loc, pick('sound/items/cartwheel1.ogg', 'sound/items/cartwheel2.ogg'), 100, 1, ignore_walls = FALSE) + +#undef IV_TAKING +#undef IV_INJECTING diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index 13406ac4b0f..53995f420d9 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -1,183 +1,183 @@ -// the light switch -// can have multiple per area -// can also operate on non-loc area through "otherarea" var -/obj/machinery/light_switch - name = "light switch" - desc = "It turns lights on and off. What are you, simple?" - icon = 'icons/obj/power.dmi' - icon_state = "light1" - anchored = 1.0 - var/on = 1 - var/area/area = null - var/otherarea = null - // luminosity = 1 - settagwhitelist = list("logic_id_tag") - var/light_connect = 1 //Allows the switch to control lights in its associated areas. When set to 0, using the switch won't affect the lights. - var/datum/radio_frequency/radio_connection - var/frequency = 0 - var/logic_id_tag = "default" //Defines the ID tag to send logic signals to. - var/logic_connect = 0 //Set this to allow the switch to send out logic signals. - - -/obj/machinery/light_switch/New(turf/loc, var/w_dir=null) - ..() - switch(w_dir) - if(NORTH) - pixel_y = 25 - if(SOUTH) - pixel_y = -25 - if(EAST) - pixel_x = 25 - if(WEST) - pixel_x = -25 - if(SSradio) - set_frequency(frequency) - spawn(5) - src.area = get_area(src) - - if(otherarea) - src.area = locate(text2path("/area/[otherarea]")) - - if(!name) - name = "light switch([area.name])" - - src.on = src.area.lightswitch - updateicon() - -/obj/machinery/light_switch/Initialize() - ..() - set_frequency(frequency) - -/obj/machinery/light_switch/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) - return - -/obj/machinery/light_switch/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/light_switch/proc/updateicon() - if(stat & NOPOWER) - icon_state = "light-p" - else - if(on) - icon_state = "light1" - else - icon_state = "light0" - -/obj/machinery/light_switch/examine(mob/user) - . = ..() - . += "A light switch. It is [on? "on" : "off"]." - -/obj/machinery/light_switch/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/light_switch/attack_hand(mob/user) - on = !on - updateicon() - - if(light_connect) - area.lightswitch = on - area.updateicon() - - if(logic_connect && powered(LIGHT)) //Don't bother sending a signal if we aren't set to send them or we have no power to send with. - handle_output() - - if(light_connect) - for(var/obj/machinery/light_switch/L in area) - L.on = on - L.updateicon() - - area.power_change() - -/obj/machinery/light_switch/proc/handle_output() - if(!radio_connection) //can't output without this - return - - if(logic_id_tag == null) //Don't output to an undefined id_tag - return - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - //Light switches are continuous signal sources, since they register as ON or OFF and stay that way until adjusted again - if(on) - signal.data = list( - "tag" = logic_id_tag, - "sigtype" = "logic", - "state" = LOGIC_ON, - ) - else - signal.data = list( - "tag" = logic_id_tag, - "sigtype" = "logic", - "state" = LOGIC_OFF, - ) - - radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) - if(on) - use_power(5, LIGHT) //Use a tiny bit of power every time we send an ON signal. Draws from the local APC's lighting circuit, since this is a LIGHT switch. - -/obj/machinery/light_switch/power_change() - if(!otherarea) - if(powered(LIGHT)) - stat &= ~NOPOWER - else - stat |= NOPOWER - - updateicon() - -/obj/machinery/light_switch/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - power_change() - ..(severity) - -/obj/machinery/light_switch/process() - if(logic_connect && powered(LIGHT)) //We won't send signals while unpowered, but the last signal will remain valid for anything that received it before we went dark - handle_output() - -/obj/machinery/light_switch/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/detective_scanner)) - return - return ..() - -/obj/machinery/light_switch/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - update_multitool_menu(user) - -/obj/machinery/light_switch/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - user.visible_message("[user] starts unwrenching [src] from the wall...", "You are unwrenching [src] from the wall...", "You hear ratcheting.") - . = TRUE - if(!I.use_tool(src, user, 30, volume = I.tool_volume)) - return - WRENCH_UNANCHOR_WALL_MESSAGE - new/obj/item/mounted/frame/light_switch(get_turf(src)) - qdel(src) - -/obj/machinery/light_switch/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" - "} - -/obj/machinery/light_switch/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) - ..() - if("toggle_light_connect" in href_list) - light_connect = !light_connect - if("toggle_logic" in href_list) - logic_connect = !logic_connect +// the light switch +// can have multiple per area +// can also operate on non-loc area through "otherarea" var +/obj/machinery/light_switch + name = "light switch" + desc = "It turns lights on and off. What are you, simple?" + icon = 'icons/obj/power.dmi' + icon_state = "light1" + anchored = 1.0 + var/on = 1 + var/area/area = null + var/otherarea = null + // luminosity = 1 + settagwhitelist = list("logic_id_tag") + var/light_connect = 1 //Allows the switch to control lights in its associated areas. When set to 0, using the switch won't affect the lights. + var/datum/radio_frequency/radio_connection + var/frequency = 0 + var/logic_id_tag = "default" //Defines the ID tag to send logic signals to. + var/logic_connect = 0 //Set this to allow the switch to send out logic signals. + + +/obj/machinery/light_switch/New(turf/loc, var/w_dir=null) + ..() + switch(w_dir) + if(NORTH) + pixel_y = 25 + if(SOUTH) + pixel_y = -25 + if(EAST) + pixel_x = 25 + if(WEST) + pixel_x = -25 + if(SSradio) + set_frequency(frequency) + spawn(5) + src.area = get_area(src) + + if(otherarea) + src.area = locate(text2path("/area/[otherarea]")) + + if(!name) + name = "light switch([area.name])" + + src.on = src.area.lightswitch + updateicon() + +/obj/machinery/light_switch/Initialize() + ..() + set_frequency(frequency) + +/obj/machinery/light_switch/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) + return + +/obj/machinery/light_switch/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/light_switch/proc/updateicon() + if(stat & NOPOWER) + icon_state = "light-p" + else + if(on) + icon_state = "light1" + else + icon_state = "light0" + +/obj/machinery/light_switch/examine(mob/user) + . = ..() + . += "A light switch. It is [on? "on" : "off"]." + +/obj/machinery/light_switch/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/light_switch/attack_hand(mob/user) + on = !on + updateicon() + + if(light_connect) + area.lightswitch = on + area.updateicon() + + if(logic_connect && powered(LIGHT)) //Don't bother sending a signal if we aren't set to send them or we have no power to send with. + handle_output() + + if(light_connect) + for(var/obj/machinery/light_switch/L in area) + L.on = on + L.updateicon() + + area.power_change() + +/obj/machinery/light_switch/proc/handle_output() + if(!radio_connection) //can't output without this + return + + if(logic_id_tag == null) //Don't output to an undefined id_tag + return + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + //Light switches are continuous signal sources, since they register as ON or OFF and stay that way until adjusted again + if(on) + signal.data = list( + "tag" = logic_id_tag, + "sigtype" = "logic", + "state" = LOGIC_ON, + ) + else + signal.data = list( + "tag" = logic_id_tag, + "sigtype" = "logic", + "state" = LOGIC_OFF, + ) + + radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) + if(on) + use_power(5, LIGHT) //Use a tiny bit of power every time we send an ON signal. Draws from the local APC's lighting circuit, since this is a LIGHT switch. + +/obj/machinery/light_switch/power_change() + if(!otherarea) + if(powered(LIGHT)) + stat &= ~NOPOWER + else + stat |= NOPOWER + + updateicon() + +/obj/machinery/light_switch/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + power_change() + ..(severity) + +/obj/machinery/light_switch/process() + if(logic_connect && powered(LIGHT)) //We won't send signals while unpowered, but the last signal will remain valid for anything that received it before we went dark + handle_output() + +/obj/machinery/light_switch/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/detective_scanner)) + return + return ..() + +/obj/machinery/light_switch/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + update_multitool_menu(user) + +/obj/machinery/light_switch/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + user.visible_message("[user] starts unwrenching [src] from the wall...", "You are unwrenching [src] from the wall...", "You hear ratcheting.") + . = TRUE + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + WRENCH_UNANCHOR_WALL_MESSAGE + new/obj/item/mounted/frame/light_switch(get_turf(src)) + qdel(src) + +/obj/machinery/light_switch/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" + "} + +/obj/machinery/light_switch/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) + ..() + if("toggle_light_connect" in href_list) + light_connect = !light_connect + if("toggle_logic" in href_list) + logic_connect = !logic_connect diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index f1ac4e88bbb..19dc80f9a40 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -1,596 +1,600 @@ -/* -Overview: - Used to create objects that need a per step proc call. Default definition of 'New()' - stores a reference to src machine in global 'machines list'. Default definition - of 'Del' removes reference to src machine in global 'machines list'. - -Class Variables: - use_power (num) - current state of auto power use. - Possible Values: - 0 -- no auto power use - 1 -- machine is using power at its idle power level - 2 -- machine is using power at its active power level - - active_power_usage (num) - Value for the amount of power to use when in active power mode - - idle_power_usage (num) - Value for the amount of power to use when in idle power mode - - power_channel (num) - What channel to draw from when drawing power for power mode - Possible Values: - EQUIP:0 -- Equipment Channel - LIGHT:2 -- Lighting Channel - ENVIRON:3 -- Environment Channel - - component_parts (list) - A list of component parts of machine used by frame based machines. - - uid (num) - Unique id of machine across all machines. - - gl_uid (global num) - Next uid value in sequence - - stat (bitflag) - Machine status bit flags. - Possible bit flags: - BROKEN:1 -- Machine is broken - NOPOWER:2 -- No power is being supplied to machine. - POWEROFF:4 -- tbd - MAINT:8 -- machine is currently under going maintenance. - EMPED:16 -- temporary broken by EMP pulse - - manual (num) - Currently unused. - -Class Procs: - initialize() 'game/machinery/machine.dm' - - Destroy() 'game/machinery/machine.dm' - - auto_use_power() 'game/machinery/machine.dm' - This proc determines how power mode power is deducted by the machine. - 'auto_use_power()' is called by the 'master_controller' game_controller every - tick. - - Return Value: - return:1 -- if object is powered - return:0 -- if object is not powered. - - Default definition uses 'use_power', 'power_channel', 'active_power_usage', - 'idle_power_usage', 'powered()', and 'use_power()' implement behavior. - - powered(chan = EQUIP) 'modules/power/power.dm' - Checks to see if area that contains the object has power available for power - channel given in 'chan'. - - use_power(amount, chan=EQUIP, autocalled) 'modules/power/power.dm' - Deducts 'amount' from the power channel 'chan' of the area that contains the object. - If it's autocalled then everything is normal, if something else calls use_power we are going to - need to recalculate the power two ticks in a row. - - power_change() 'modules/power/power.dm' - Called by the area that contains the object when ever that area under goes a - power state change (area runs out of power, or area channel is turned off). - - RefreshParts() 'game/machinery/machine.dm' - Called to refresh the variables in the machine that are contributed to by parts - contained in the component_parts list. (example: glass and material amounts for - the autolathe) - - Default definition does nothing. - - assign_uid() 'game/machinery/machine.dm' - Called by machine to assign a value to the uid variable. - - process() 'game/machinery/machine.dm' - Called by the 'master_controller' once per game tick for each machine that is listed in the 'machines' list. - - - Compiled by Aygar -*/ - -/obj/machinery - name = "machinery" - icon = 'icons/obj/stationobjs.dmi' - pressure_resistance = 15 - max_integrity = 200 - layer = BELOW_OBJ_LAYER - var/stat = 0 - var/emagged = 0 - var/use_power = IDLE_POWER_USE - //0 = dont run the auto - //1 = run auto, use idle - //2 = run auto, use active - var/idle_power_usage = 0 - var/active_power_usage = 0 - var/power_channel = EQUIP //EQUIP,ENVIRON or LIGHT - var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. - var/uid - var/manual = 0 - var/global/gl_uid = 1 - var/custom_aghost_alerts=0 - var/panel_open = 0 - var/area/myArea - var/interact_offline = 0 // Can the machine be interacted with while de-powered. - var/use_log = list() - var/list/settagwhitelist = list()//WHITELIST OF VARIABLES THAT THE set_tag HREF CAN MODIFY, DON'T PUT SHIT YOU DON'T NEED ON HERE, AND IF YOU'RE GONNA USE set_tag (format_tag() proc), ADD TO THIS LIST. - atom_say_verb = "beeps" - var/siemens_strength = 0.7 // how badly will it shock you? - -/obj/machinery/Initialize(mapload) - if(!armor) - armor = list(melee = 25, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) - . = ..() - GLOB.machines += src - - if(use_power) - myArea = get_area(src) - if(!speed_process) - START_PROCESSING(SSmachines, src) - else - START_PROCESSING(SSfastprocess, src) - - power_change() - -// gotta go fast -/obj/machinery/makeSpeedProcess() - if(speed_process) - return - speed_process = TRUE - STOP_PROCESSING(SSmachines, src) - START_PROCESSING(SSfastprocess, src) - -// gotta go slow -/obj/machinery/makeNormalProcess() - if(!speed_process) - return - speed_process = FALSE - STOP_PROCESSING(SSfastprocess, src) - START_PROCESSING(SSmachines, src) - -/obj/machinery/Destroy() - if(myArea) - myArea = null - GLOB.machines.Remove(src) - if(!speed_process) - STOP_PROCESSING(SSmachines, src) - else - STOP_PROCESSING(SSfastprocess, src) - return ..() - -/obj/machinery/proc/locate_machinery() - return - -/obj/machinery/process() // If you dont use process or power why are you here - return PROCESS_KILL - -/obj/machinery/proc/process_atmos() //If you dont use process why are you here - return PROCESS_KILL - -/obj/machinery/emp_act(severity) - if(use_power && !stat) - use_power(7500/severity) - new /obj/effect/temp_visual/emp(loc) - ..() -/obj/machinery/default_welder_repair(mob/user, obj/item/I) - . = ..() - if(.) - stat &= ~BROKEN - -//sets the use_power var and then forces an area power update -/obj/machinery/proc/update_use_power(var/new_use_power) - use_power = new_use_power - -/obj/machinery/proc/auto_use_power() - if(!powered(power_channel)) - return 0 - if(use_power == IDLE_POWER_USE) - use_power(idle_power_usage,power_channel, 1) - else if(use_power >= ACTIVE_POWER_USE) - use_power(active_power_usage,power_channel, 1) - return 1 - -/obj/machinery/proc/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) - if("set_id" in href_list) - if(!("id_tag" in vars)) - warning("set_id: [type] has no id_tag var.") - var/newid = copytext(reject_bad_text(input(usr, "Specify the new ID tag for this machine", src, src:id_tag) as null|text),1,MAX_MESSAGE_LEN) - if(newid) - src:id_tag = newid - return TRUE - if("set_freq" in href_list) - if(!("frequency" in vars)) - warning("set_freq: [type] has no frequency var.") - return FALSE - var/newfreq=src:frequency - if(href_list["set_freq"]!="-1") - newfreq=text2num(href_list["set_freq"]) - else - newfreq = input(usr, "Specify a new frequency (GHz). Decimals assigned automatically.", src, src:frequency) as null|num - if(newfreq) - if(findtext(num2text(newfreq), ".")) - newfreq *= 10 // shift the decimal one place - src:frequency = sanitize_frequency(newfreq, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - return TRUE - return FALSE - -/obj/machinery/proc/handle_multitool_topic(var/href, var/list/href_list, var/mob/user) - if(!allowed(user))//no, not even HREF exploits - return FALSE - var/obj/item/multitool/P = get_multitool(usr) - if(P && istype(P)) - var/update_mt_menu = FALSE - if("set_tag" in href_list) - if(!(href_list["set_tag"] in settagwhitelist))//I see you're trying Href exploits, I see you're failing, I SEE ADMIN WARNING. (seriously though, this is a powerfull HREF, I originally found this loophole, I'm not leaving it in on my PR) - message_admins("set_tag HREF (var attempted to edit: [href_list["set_tag"]]) exploit attempted by [key_name_admin(user)] on [src] (JMP)") - return FALSE - if(!(href_list["set_tag"] in vars)) - to_chat(usr, "Something went wrong: Unable to find [href_list["set_tag"]] in vars!") - return FALSE - var/current_tag = vars[href_list["set_tag"]] - var/newid = copytext(reject_bad_text(input(usr, "Specify the new value", src, current_tag) as null|text),1,MAX_MESSAGE_LEN) - if(newid) - vars[href_list["set_tag"]] = newid - update_mt_menu = TRUE - - if("unlink" in href_list) - var/idx = text2num(href_list["unlink"]) - if(!idx) - return FALSE - - var/obj/O = getLink(idx) - if(!O) - return FALSE - if(!canLink(O)) - to_chat(usr, "You can't link with that device.") - return FALSE - - if(unlinkFrom(usr, O)) - to_chat(usr, "A green light flashes on \the [P], confirming the link was removed.") - else - to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when unlinking the two devices.") - update_mt_menu = TRUE - - if("link" in href_list) - var/obj/O = P.buffer - if(!O) - return FALSE - if(!canLink(O,href_list)) - to_chat(usr, "You can't link with that device.") - return FALSE - if(isLinkedWith(O)) - to_chat(usr, "A red light flashes on \the [P]. The two devices are already linked.") - return FALSE - - if(linkWith(usr, O, href_list)) - to_chat(usr, "A green light flashes on \the [P], confirming the link was added.") - else - to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when linking the two devices.") - update_mt_menu = TRUE - - if("buffer" in href_list) - P.buffer = src - to_chat(usr, "A green light flashes, and the device appears in the multitool buffer.") - update_mt_menu = TRUE - - if("flush" in href_list) - to_chat(usr, "A green light flashes, and the device disappears from the multitool buffer.") - P.buffer = null - update_mt_menu = TRUE - - var/ret = multitool_topic(usr,href_list,P.buffer) - if(ret) - update_mt_menu = TRUE - - if(update_mt_menu) - update_multitool_menu(usr) - return TRUE - -/obj/machinery/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state = default_state) - if(..(href, href_list, nowindow, state)) - return 1 - - handle_multitool_topic(href,href_list,usr) - add_fingerprint(usr) - return 0 - -/obj/machinery/proc/operable(var/additional_flags = 0) - return !inoperable(additional_flags) - -/obj/machinery/proc/inoperable(var/additional_flags = 0) - return (stat & (NOPOWER|BROKEN|additional_flags)) - -/obj/machinery/CanUseTopic(var/mob/user) - if(!interact_offline && (stat & (NOPOWER|BROKEN))) - return STATUS_CLOSE - - return ..() - -/obj/machinery/CouldUseTopic(var/mob/user) - ..() - user.set_machine(src) - -/obj/machinery/CouldNotUseTopic(var/mob/user) - usr.unset_machine() - -/obj/machinery/proc/dropContents()//putting for swarmers, occupent code commented out, someone can use later. - var/turf/T = get_turf(src) - for(var/atom/movable/AM in contents) - AM.forceMove(T) - -//////////////////////////////////////////////////////////////////////////////////////////// - -/obj/machinery/attack_ai(mob/user) - if(isrobot(user))// For some reason attack_robot doesn't work - var/mob/living/silicon/robot/R = user - if(R.client && R.client.eye == R && !R.low_power_mode)// This is to stop robots from using cameras to remotely control machines; and from using machines when the borg has no power. - return attack_hand(user) - else - return attack_hand(user) - -/obj/machinery/attack_hand(mob/user as mob) - if(user.incapacitated()) - return TRUE - - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return TRUE - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= 60) - visible_message("[H] stares cluelessly at [src] and drools.") - return TRUE - else if(prob(H.getBrainLoss())) - to_chat(user, "You momentarily forget how to use [src].") - return TRUE - - if(panel_open) - add_fingerprint(user) - return FALSE - - if(!interact_offline && stat & (NOPOWER|BROKEN|MAINT)) - return TRUE - - add_fingerprint(user) - - return ..() - -/obj/machinery/proc/is_operational() - return !(stat & (NOPOWER|BROKEN|MAINT)) - -/obj/machinery/CheckParts(list/parts_list) - ..() - RefreshParts() - -/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. - return - return 0 - -/obj/machinery/proc/assign_uid() - uid = gl_uid - gl_uid++ - -/obj/machinery/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - on_deconstruction() - if(component_parts && component_parts.len) - spawn_frame(disassembled) - for(var/obj/item/I in component_parts) - I.forceMove(loc) - component_parts.Cut() - qdel(src) - -/obj/machinery/proc/spawn_frame(disassembled) - var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(loc) - . = M - M.anchored = anchored - if(!disassembled) - M.obj_integrity = M.max_integrity * 0.5 //the frame is already half broken - transfer_fingerprints_to(M) - M.state = 2 - M.icon_state = "box_1" - -/obj/machinery/obj_break(damage_flag) - if(!(flags & NODECONSTRUCT)) - stat |= BROKEN - -/obj/machinery/proc/default_deconstruction_crowbar(user, obj/item/I, ignore_panel = 0) - if(I.tool_behaviour != TOOL_CROWBAR) - return FALSE - if(!I.use_tool(src, user, 0, volume = 0)) - return FALSE - if((panel_open || ignore_panel) && !(flags & NODECONSTRUCT)) - deconstruct(TRUE) - to_chat(user, "You disassemble [src].") - I.play_tool_sound(user, I.tool_volume) - return 1 - return 0 - -/obj/machinery/proc/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/I) - if(I.tool_behaviour != TOOL_SCREWDRIVER) - return FALSE - if(!I.use_tool(src, user, 0, volume = 0)) - return FALSE - if(!(flags & NODECONSTRUCT)) - if(!panel_open) - panel_open = 1 - icon_state = icon_state_open - to_chat(user, "You open the maintenance hatch of [src].") - else - panel_open = 0 - icon_state = icon_state_closed - to_chat(user, "You close the maintenance hatch of [src].") - I.play_tool_sound(user, I.tool_volume) - return 1 - return 0 - -/obj/machinery/proc/default_change_direction_wrench(mob/user, obj/item/I) - if(I.tool_behaviour != TOOL_WRENCH) - return FALSE - if(!I.use_tool(src, user, 0, volume = 0)) - return FALSE - if(panel_open) - dir = turn(dir,-90) - to_chat(user, "You rotate [src].") - I.play_tool_sound(user, I.tool_volume) - return TRUE - return FALSE - -/obj/machinery/default_unfasten_wrench(mob/user, obj/item/I, time) - . = ..() - if(.) - power_change() - -/obj/machinery/proc/exchange_parts(mob/user, obj/item/storage/part_replacer/W) - var/shouldplaysound = 0 - if((flags & NODECONSTRUCT)) - return FALSE - if(istype(W) && component_parts) - if(panel_open || W.works_from_distance) - var/obj/item/circuitboard/CB = locate(/obj/item/circuitboard) in component_parts - var/P - if(W.works_from_distance) - to_chat(user, display_parts(user)) - for(var/obj/item/stock_parts/A in component_parts) - for(var/D in CB.req_components) - if(ispath(A.type, D)) - P = D - break - for(var/obj/item/stock_parts/B in W.contents) - if(istype(B, P) && istype(A, P)) - if(B.rating > A.rating) - W.remove_from_storage(B, src) - W.handle_item_insertion(A, 1) - component_parts -= A - component_parts += B - B.loc = null - to_chat(user, "[A.name] replaced with [B.name].") - shouldplaysound = 1 - break - RefreshParts() - else - to_chat(user, display_parts(user)) - if(shouldplaysound) - W.play_rped_sound() - return 1 - else - return 0 - -/obj/machinery/proc/display_parts(mob/user) - . = list("Following parts detected in the machine:") - for(var/obj/item/C in component_parts) - . += "[bicon(C)] [C.name]" - . = jointext(., "\n") - -/obj/machinery/examine(mob/user) - . = ..() - if(stat & BROKEN) - . += "It looks broken and non-functional." - if(!(resistance_flags & INDESTRUCTIBLE)) - if(resistance_flags & ON_FIRE) - . += "It's on fire!" - var/healthpercent = (obj_integrity/max_integrity) * 100 - switch(healthpercent) - if(50 to 99) - . += "It looks slightly damaged." - if(25 to 50) - . += "It appears heavily damaged." - if(0 to 25) - . += "It's falling apart!" - if(user.research_scanner && component_parts) - . += display_parts(user) - -/obj/machinery/proc/on_assess_perp(mob/living/carbon/human/perp) - return 0 - -/obj/machinery/proc/is_assess_emagged() - return emagged - -/obj/machinery/proc/assess_perp(mob/living/carbon/human/perp, var/check_access, var/auth_weapons, var/check_records, var/check_arrest) - var/threatcount = 0 //the integer returned - - if(is_assess_emagged()) - return 10 //if emagged, always return 10. - - threatcount += on_assess_perp(perp) - if(threatcount >= 10) - return threatcount - - //Agent cards lower threatlevel. - var/obj/item/card/id/id = GetIdCard(perp) - if(id && istype(id, /obj/item/card/id/syndicate)) - threatcount -= 2 - // A proper CentCom id is hard currency. - else if(id && istype(id, /obj/item/card/id/centcom)) - threatcount -= 2 - - if(check_access && !allowed(perp)) - threatcount += 4 - - if(auth_weapons && !allowed(perp)) - if(istype(perp.l_hand, /obj/item/gun) || istype(perp.l_hand, /obj/item/melee)) - threatcount += 4 - - if(istype(perp.r_hand, /obj/item/gun) || istype(perp.r_hand, /obj/item/melee)) - threatcount += 4 - - if(istype(perp.belt, /obj/item/gun) || istype(perp.belt, /obj/item/melee)) - threatcount += 2 - - if(!ishumanbasic(perp)) //beepsky so racist. - threatcount += 2 - - if(check_records || check_arrest) - var/perpname = perp.get_visible_name(TRUE) - - var/datum/data/record/R = find_security_record("name", perpname) - if(check_records && !R) - threatcount += 4 - - if(check_arrest && R && (R.fields["criminal"] == "*Arrest*")) - threatcount += 4 - - return threatcount - - -/obj/machinery/proc/shock(mob/user, prb) - if(inoperable()) - return FALSE - if(!prob(prb)) - return FALSE - do_sparks(5, 1, src) - if(electrocute_mob(user, get_area(src), src, siemens_strength, TRUE)) - return TRUE - return FALSE - -//called on machinery construction (i.e from frame to machinery) but not on initialization -/obj/machinery/proc/on_construction() - return - -/obj/machinery/proc/on_deconstruction() - return - -/obj/machinery/proc/can_be_overridden() - . = 1 - -/obj/machinery/tesla_act(power, explosive = FALSE) - ..() - if(prob(85) && explosive) - explosion(loc, 1, 2, 4, flame_range = 2, adminlog = 0, smoke = 0) - else if(prob(50)) - emp_act(EMP_LIGHT) - else - ex_act(EXPLODE_HEAVY) - -/obj/machinery/proc/adjust_item_drop_location(atom/movable/AM) // Adjust item drop location to a 3x3 grid inside the tile, returns slot id from 0 to 8 - var/md5 = md5(AM.name) // Oh, and it's deterministic too. A specific item will always drop from the same slot. - for (var/i in 1 to 32) - . += hex2num(md5[i]) - . = . % 9 - AM.pixel_x = -8 + ((.%3)*8) - AM.pixel_y = -8 + (round( . / 3)*8) +/* +Overview: + Used to create objects that need a per step proc call. Default definition of 'New()' + stores a reference to src machine in global 'machines list'. Default definition + of 'Del' removes reference to src machine in global 'machines list'. + +Class Variables: + use_power (num) + current state of auto power use. + Possible Values: + 0 -- no auto power use + 1 -- machine is using power at its idle power level + 2 -- machine is using power at its active power level + + active_power_usage (num) + Value for the amount of power to use when in active power mode + + idle_power_usage (num) + Value for the amount of power to use when in idle power mode + + power_channel (num) + What channel to draw from when drawing power for power mode + Possible Values: + EQUIP:0 -- Equipment Channel + LIGHT:2 -- Lighting Channel + ENVIRON:3 -- Environment Channel + + component_parts (list) + A list of component parts of machine used by frame based machines. + + uid (num) + Unique id of machine across all machines. + + gl_uid (global num) + Next uid value in sequence + + stat (bitflag) + Machine status bit flags. + Possible bit flags: + BROKEN:1 -- Machine is broken + NOPOWER:2 -- No power is being supplied to machine. + POWEROFF:4 -- tbd + MAINT:8 -- machine is currently under going maintenance. + EMPED:16 -- temporary broken by EMP pulse + + manual (num) + Currently unused. + +Class Procs: + initialize() 'game/machinery/machine.dm' + + Destroy() 'game/machinery/machine.dm' + + auto_use_power() 'game/machinery/machine.dm' + This proc determines how power mode power is deducted by the machine. + 'auto_use_power()' is called by the 'master_controller' game_controller every + tick. + + Return Value: + return:1 -- if object is powered + return:0 -- if object is not powered. + + Default definition uses 'use_power', 'power_channel', 'active_power_usage', + 'idle_power_usage', 'powered()', and 'use_power()' implement behavior. + + powered(chan = EQUIP) 'modules/power/power.dm' + Checks to see if area that contains the object has power available for power + channel given in 'chan'. + + use_power(amount, chan=EQUIP, autocalled) 'modules/power/power.dm' + Deducts 'amount' from the power channel 'chan' of the area that contains the object. + If it's autocalled then everything is normal, if something else calls use_power we are going to + need to recalculate the power two ticks in a row. + + power_change() 'modules/power/power.dm' + Called by the area that contains the object when ever that area under goes a + power state change (area runs out of power, or area channel is turned off). + + RefreshParts() 'game/machinery/machine.dm' + Called to refresh the variables in the machine that are contributed to by parts + contained in the component_parts list. (example: glass and material amounts for + the autolathe) + + Default definition does nothing. + + assign_uid() 'game/machinery/machine.dm' + Called by machine to assign a value to the uid variable. + + process() 'game/machinery/machine.dm' + Called by the 'master_controller' once per game tick for each machine that is listed in the 'machines' list. + + + Compiled by Aygar +*/ + +/obj/machinery + name = "machinery" + icon = 'icons/obj/stationobjs.dmi' + pressure_resistance = 15 + max_integrity = 200 + layer = BELOW_OBJ_LAYER + var/stat = 0 + var/emagged = 0 + var/use_power = IDLE_POWER_USE + //0 = dont run the auto + //1 = run auto, use idle + //2 = run auto, use active + var/idle_power_usage = 0 + var/active_power_usage = 0 + var/power_channel = EQUIP //EQUIP,ENVIRON or LIGHT + var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. + var/uid + var/manual = 0 + var/global/gl_uid = 1 + var/custom_aghost_alerts=0 + var/panel_open = 0 + var/area/myArea + var/interact_offline = 0 // Can the machine be interacted with while de-powered. + var/use_log = list() + var/list/settagwhitelist = list()//WHITELIST OF VARIABLES THAT THE set_tag HREF CAN MODIFY, DON'T PUT SHIT YOU DON'T NEED ON HERE, AND IF YOU'RE GONNA USE set_tag (format_tag() proc), ADD TO THIS LIST. + atom_say_verb = "beeps" + var/siemens_strength = 0.7 // how badly will it shock you? + +/obj/machinery/Initialize(mapload) + if(!armor) + armor = list(melee = 25, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) + . = ..() + GLOB.machines += src + + if(use_power) + myArea = get_area(src) + if(!speed_process) + START_PROCESSING(SSmachines, src) + else + START_PROCESSING(SSfastprocess, src) + + power_change() + +// gotta go fast +/obj/machinery/makeSpeedProcess() + if(speed_process) + return + speed_process = TRUE + STOP_PROCESSING(SSmachines, src) + START_PROCESSING(SSfastprocess, src) + +// gotta go slow +/obj/machinery/makeNormalProcess() + if(!speed_process) + return + speed_process = FALSE + STOP_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSmachines, src) + +/obj/machinery/Destroy() + if(myArea) + myArea = null + GLOB.machines.Remove(src) + if(!speed_process) + STOP_PROCESSING(SSmachines, src) + else + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/machinery/proc/locate_machinery() + return + +/obj/machinery/process() // If you dont use process or power why are you here + return PROCESS_KILL + +/obj/machinery/proc/process_atmos() //If you dont use process why are you here + return PROCESS_KILL + +/obj/machinery/emp_act(severity) + if(use_power && !stat) + use_power(7500/severity) + new /obj/effect/temp_visual/emp(loc) + ..() +/obj/machinery/default_welder_repair(mob/user, obj/item/I) + . = ..() + if(.) + stat &= ~BROKEN + +//sets the use_power var and then forces an area power update +/obj/machinery/proc/update_use_power(var/new_use_power) + use_power = new_use_power + +/obj/machinery/proc/auto_use_power() + if(!powered(power_channel)) + return 0 + if(use_power == IDLE_POWER_USE) + use_power(idle_power_usage,power_channel, 1) + else if(use_power >= ACTIVE_POWER_USE) + use_power(active_power_usage,power_channel, 1) + return 1 + +/obj/machinery/proc/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) + if("set_id" in href_list) + if(!("id_tag" in vars)) + warning("set_id: [type] has no id_tag var.") + var/newid = copytext(reject_bad_text(input(usr, "Specify the new ID tag for this machine", src, src:id_tag) as null|text),1,MAX_MESSAGE_LEN) + if(newid) + src:id_tag = newid + return TRUE + if("set_freq" in href_list) + if(!("frequency" in vars)) + warning("set_freq: [type] has no frequency var.") + return FALSE + var/newfreq=src:frequency + if(href_list["set_freq"]!="-1") + newfreq=text2num(href_list["set_freq"]) + else + newfreq = input(usr, "Specify a new frequency (GHz). Decimals assigned automatically.", src, src:frequency) as null|num + if(newfreq) + if(findtext(num2text(newfreq), ".")) + newfreq *= 10 // shift the decimal one place + src:frequency = sanitize_frequency(newfreq, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + return TRUE + return FALSE + +/obj/machinery/proc/handle_multitool_topic(var/href, var/list/href_list, var/mob/user) + if(!allowed(user))//no, not even HREF exploits + return FALSE + var/obj/item/multitool/P = get_multitool(usr) + if(P && istype(P)) + var/update_mt_menu = FALSE + if("set_tag" in href_list) + if(!(href_list["set_tag"] in settagwhitelist))//I see you're trying Href exploits, I see you're failing, I SEE ADMIN WARNING. (seriously though, this is a powerfull HREF, I originally found this loophole, I'm not leaving it in on my PR) + message_admins("set_tag HREF (var attempted to edit: [href_list["set_tag"]]) exploit attempted by [key_name_admin(user)] on [src] (JMP)") + return FALSE + if(!(href_list["set_tag"] in vars)) + to_chat(usr, "Something went wrong: Unable to find [href_list["set_tag"]] in vars!") + return FALSE + var/current_tag = vars[href_list["set_tag"]] + var/newid = copytext(reject_bad_text(input(usr, "Specify the new value", src, current_tag) as null|text),1,MAX_MESSAGE_LEN) + if(newid) + vars[href_list["set_tag"]] = newid + update_mt_menu = TRUE + + if("unlink" in href_list) + var/idx = text2num(href_list["unlink"]) + if(!idx) + return FALSE + + var/obj/O = getLink(idx) + if(!O) + return FALSE + if(!canLink(O)) + to_chat(usr, "You can't link with that device.") + return FALSE + + if(unlinkFrom(usr, O)) + to_chat(usr, "A green light flashes on \the [P], confirming the link was removed.") + else + to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when unlinking the two devices.") + update_mt_menu = TRUE + + if("link" in href_list) + var/obj/O = P.buffer + if(!O) + return FALSE + if(!canLink(O,href_list)) + to_chat(usr, "You can't link with that device.") + return FALSE + if(isLinkedWith(O)) + to_chat(usr, "A red light flashes on \the [P]. The two devices are already linked.") + return FALSE + + if(linkWith(usr, O, href_list)) + to_chat(usr, "A green light flashes on \the [P], confirming the link was added.") + else + to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when linking the two devices.") + update_mt_menu = TRUE + + if("buffer" in href_list) + P.buffer = src + to_chat(usr, "A green light flashes, and the device appears in the multitool buffer.") + update_mt_menu = TRUE + + if("flush" in href_list) + to_chat(usr, "A green light flashes, and the device disappears from the multitool buffer.") + P.buffer = null + update_mt_menu = TRUE + + var/ret = multitool_topic(usr,href_list,P.buffer) + if(ret) + update_mt_menu = TRUE + + if(update_mt_menu) + update_multitool_menu(usr) + return TRUE + +/obj/machinery/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state = GLOB.default_state) + if(..(href, href_list, nowindow, state)) + return 1 + + handle_multitool_topic(href,href_list,usr) + add_fingerprint(usr) + return 0 + +/obj/machinery/proc/operable(var/additional_flags = 0) + return !inoperable(additional_flags) + +/obj/machinery/proc/inoperable(var/additional_flags = 0) + return (stat & (NOPOWER|BROKEN|additional_flags)) + +/obj/machinery/CanUseTopic(var/mob/user) + if(!interact_offline && (stat & (NOPOWER|BROKEN))) + return STATUS_CLOSE + + return ..() + +/obj/machinery/CouldUseTopic(var/mob/user) + ..() + user.set_machine(src) + +/obj/machinery/CouldNotUseTopic(var/mob/user) + usr.unset_machine() + +/obj/machinery/proc/dropContents()//putting for swarmers, occupent code commented out, someone can use later. + var/turf/T = get_turf(src) + for(var/atom/movable/AM in contents) + AM.forceMove(T) + +//////////////////////////////////////////////////////////////////////////////////////////// + +/obj/machinery/attack_ai(mob/user) + if(isrobot(user))// For some reason attack_robot doesn't work + var/mob/living/silicon/robot/R = user + if(R.client && R.client.eye == R && !R.low_power_mode)// This is to stop robots from using cameras to remotely control machines; and from using machines when the borg has no power. + return attack_hand(user) + else + return attack_hand(user) + +/obj/machinery/attack_hand(mob/user as mob) + if(user.incapacitated()) + return TRUE + + if(!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return TRUE + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= 60) + visible_message("[H] stares cluelessly at [src] and drools.") + return TRUE + else if(prob(H.getBrainLoss())) + to_chat(user, "You momentarily forget how to use [src].") + return TRUE + + if(panel_open) + add_fingerprint(user) + return FALSE + + if(!interact_offline && stat & (NOPOWER|BROKEN|MAINT)) + return TRUE + + add_fingerprint(user) + + return ..() + +/obj/machinery/proc/is_operational() + return !(stat & (NOPOWER|BROKEN|MAINT)) + +/obj/machinery/CheckParts(list/parts_list) + ..() + RefreshParts() + +/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. + return + return 0 + +/obj/machinery/proc/assign_uid() + uid = gl_uid + gl_uid++ + +/obj/machinery/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + on_deconstruction() + if(component_parts && component_parts.len) + spawn_frame(disassembled) + for(var/obj/item/I in component_parts) + I.forceMove(loc) + component_parts.Cut() + qdel(src) + +/obj/machinery/proc/spawn_frame(disassembled) + var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(loc) + . = M + M.anchored = anchored + if(!disassembled) + M.obj_integrity = M.max_integrity * 0.5 //the frame is already half broken + transfer_fingerprints_to(M) + M.state = 2 + M.icon_state = "box_1" + +/obj/machinery/obj_break(damage_flag) + if(!(flags & NODECONSTRUCT)) + stat |= BROKEN + +/obj/machinery/proc/default_deconstruction_crowbar(user, obj/item/I, ignore_panel = 0) + if(I.tool_behaviour != TOOL_CROWBAR) + return FALSE + if(!I.use_tool(src, user, 0, volume = 0)) + return FALSE + if((panel_open || ignore_panel) && !(flags & NODECONSTRUCT)) + deconstruct(TRUE) + to_chat(user, "You disassemble [src].") + I.play_tool_sound(user, I.tool_volume) + return 1 + return 0 + +/obj/machinery/proc/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/I) + if(I.tool_behaviour != TOOL_SCREWDRIVER) + return FALSE + if(!I.use_tool(src, user, 0, volume = 0)) + return FALSE + if(!(flags & NODECONSTRUCT)) + if(!panel_open) + panel_open = 1 + icon_state = icon_state_open + to_chat(user, "You open the maintenance hatch of [src].") + else + panel_open = 0 + icon_state = icon_state_closed + to_chat(user, "You close the maintenance hatch of [src].") + I.play_tool_sound(user, I.tool_volume) + return 1 + return 0 + +/obj/machinery/proc/default_change_direction_wrench(mob/user, obj/item/I) + if(I.tool_behaviour != TOOL_WRENCH) + return FALSE + if(!I.use_tool(src, user, 0, volume = 0)) + return FALSE + if(panel_open) + dir = turn(dir,-90) + to_chat(user, "You rotate [src].") + I.play_tool_sound(user, I.tool_volume) + return TRUE + return FALSE + +/obj/machinery/default_unfasten_wrench(mob/user, obj/item/I, time) + . = ..() + if(.) + power_change() + +/obj/machinery/proc/exchange_parts(mob/user, obj/item/storage/part_replacer/W) + var/shouldplaysound = 0 + if((flags & NODECONSTRUCT)) + return FALSE + if(istype(W) && component_parts) + if(panel_open || W.works_from_distance) + var/obj/item/circuitboard/CB = locate(/obj/item/circuitboard) in component_parts + var/P + if(W.works_from_distance) + to_chat(user, display_parts(user)) + for(var/obj/item/stock_parts/A in component_parts) + for(var/D in CB.req_components) + if(ispath(A.type, D)) + P = D + break + for(var/obj/item/stock_parts/B in W.contents) + if(istype(B, P) && istype(A, P)) + if(B.rating > A.rating) + W.remove_from_storage(B, src) + W.handle_item_insertion(A, 1) + component_parts -= A + component_parts += B + B.loc = null + to_chat(user, "[A.name] replaced with [B.name].") + shouldplaysound = 1 + break + RefreshParts() + else + to_chat(user, display_parts(user)) + if(shouldplaysound) + W.play_rped_sound() + return 1 + else + return 0 + +/obj/machinery/proc/display_parts(mob/user) + . = list("Following parts detected in the machine:") + for(var/obj/item/C in component_parts) + . += "[bicon(C)] [C.name]" + . = jointext(., "\n") + +/obj/machinery/examine(mob/user) + . = ..() + if(stat & BROKEN) + . += "It looks broken and non-functional." + if(!(resistance_flags & INDESTRUCTIBLE)) + if(resistance_flags & ON_FIRE) + . += "It's on fire!" + var/healthpercent = (obj_integrity/max_integrity) * 100 + switch(healthpercent) + if(50 to 99) + . += "It looks slightly damaged." + if(25 to 50) + . += "It appears heavily damaged." + if(0 to 25) + . += "It's falling apart!" + if(user.research_scanner && component_parts) + . += display_parts(user) + +/obj/machinery/proc/on_assess_perp(mob/living/carbon/human/perp) + return 0 + +/obj/machinery/proc/is_assess_emagged() + return emagged + +/obj/machinery/proc/assess_perp(mob/living/carbon/human/perp, var/check_access, var/auth_weapons, var/check_records, var/check_arrest) + var/threatcount = 0 //the integer returned + + if(is_assess_emagged()) + return 10 //if emagged, always return 10. + + threatcount += on_assess_perp(perp) + if(threatcount >= 10) + return threatcount + + //Agent cards lower threatlevel. + var/obj/item/card/id/id = GetIdCard(perp) + if(id && istype(id, /obj/item/card/id/syndicate)) + threatcount -= 2 + // A proper CentCom id is hard currency. + else if(id && istype(id, /obj/item/card/id/centcom)) + threatcount -= 2 + + if(check_access && !allowed(perp)) + threatcount += 4 + + if(auth_weapons && !allowed(perp)) + if(istype(perp.l_hand, /obj/item/gun) || istype(perp.l_hand, /obj/item/melee)) + threatcount += 4 + + if(istype(perp.r_hand, /obj/item/gun) || istype(perp.r_hand, /obj/item/melee)) + threatcount += 4 + + if(istype(perp.belt, /obj/item/gun) || istype(perp.belt, /obj/item/melee)) + threatcount += 2 + + if(!ishumanbasic(perp)) //beepsky so racist. + threatcount += 2 + + if(check_records || check_arrest) + var/perpname = perp.get_visible_name(TRUE) + + var/datum/data/record/R = find_security_record("name", perpname) + if(check_records && !R) + threatcount += 4 + + if(R && R.fields["criminal"]) + switch(R.fields["criminal"]) + if(SEC_RECORD_STATUS_EXECUTE) + threatcount += 7 + if(SEC_RECORD_STATUS_ARREST) + threatcount += 5 + + return threatcount + + +/obj/machinery/proc/shock(mob/living/user, prb) + if(!istype(user) || inoperable()) + return FALSE + if(!prob(prb)) + return FALSE + do_sparks(5, 1, src) + if(electrocute_mob(user, get_area(src), src, siemens_strength, TRUE)) + return TRUE + return FALSE + +//called on machinery construction (i.e from frame to machinery) but not on initialization +/obj/machinery/proc/on_construction() + return + +/obj/machinery/proc/on_deconstruction() + return + +/obj/machinery/proc/can_be_overridden() + . = 1 + +/obj/machinery/tesla_act(power, explosive = FALSE) + ..() + if(prob(85) && explosive) + explosion(loc, 1, 2, 4, flame_range = 2, adminlog = 0, smoke = 0) + else if(prob(50)) + emp_act(EMP_LIGHT) + else + ex_act(EXPLODE_HEAVY) + +/obj/machinery/proc/adjust_item_drop_location(atom/movable/AM) // Adjust item drop location to a 3x3 grid inside the tile, returns slot id from 0 to 8 + var/md5 = md5(AM.name) // Oh, and it's deterministic too. A specific item will always drop from the same slot. + for (var/i in 1 to 32) + . += hex2num(md5[i]) + . = . % 9 + AM.pixel_x = -8 + ((.%3)*8) + AM.pixel_y = -8 + (round( . / 3)*8) diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 9b982df99fd..b73fa94259f 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -1,386 +1,386 @@ -// Magnetic attractor, creates variable magnetic fields and attraction. -// Can also be used to emit electron/proton beams to create a center of magnetism on another tile - -// tl;dr: it's magnets lol -// This was created for firing ranges, but I suppose this could have other applications - Doohl - -/obj/machinery/magnetic_module - - icon = 'icons/obj/objects.dmi' - icon_state = "floor_magnet-f" - name = "Electromagnetic Generator" - desc = "A device that uses station power to create points of magnetic energy." - level = 1 // underfloor - layer = 2.5 - anchored = 1 - use_power = IDLE_POWER_USE - idle_power_usage = 50 - - var/freq = AIRLOCK_FREQ // radio frequency - var/electricity_level = 1 // intensity of the magnetic pull - var/magnetic_field = 1 // the range of magnetic attraction - var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something - var/turf/center // the center of magnetic attraction - var/on = 0 - var/magpulling = 0 - - // x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down - var/center_x = 0 - var/center_y = 0 - var/max_dist = 20 // absolute value of center_x,y cannot exceed this integer - -/obj/machinery/magnetic_module/New() - ..() - var/turf/T = loc - hide(T.intact) - center = T - - spawn(10) // must wait for map loading to finish - if(SSradio) - SSradio.add_object(src, freq, RADIO_MAGNETS) - - spawn() - magnetic_process() - - // update the invisibility and icon -/obj/machinery/magnetic_module/hide(intact) - invisibility = intact ? 101 : 0 - updateicon() - - // update the icon_state -/obj/machinery/magnetic_module/proc/updateicon() - var/state="floor_magnet" - var/onstate="" - if(!on) - onstate="0" - - if(invisibility) - icon_state = "[state][onstate]-f" // if invisible, set icon to faded version - // in case of being revealed by T-scanner - else - icon_state = "[state][onstate]" - -/obj/machinery/magnetic_module/receive_signal(datum/signal/signal) - var/command = signal.data["command"] - var/modifier = signal.data["modifier"] - var/signal_code = signal.data["code"] - if(command && (signal_code == code)) - Cmd(command, modifier) - - - -/obj/machinery/magnetic_module/proc/Cmd(var/command, var/modifier) - if(command) - switch(command) - if("set-electriclevel") - if(modifier) electricity_level = modifier - if("set-magneticfield") - if(modifier) magnetic_field = modifier - - if("add-elec") - electricity_level++ - if(electricity_level > 12) - electricity_level = 12 - if("sub-elec") - electricity_level-- - if(electricity_level <= 0) - electricity_level = 1 - if("add-mag") - magnetic_field++ - if(magnetic_field > 4) - magnetic_field = 4 - if("sub-mag") - magnetic_field-- - if(magnetic_field <= 0) - magnetic_field = 1 - - if("set-x") - if(modifier) center_x = modifier - if("set-y") - if(modifier) center_y = modifier - - if("N") // NORTH - center_y++ - if("S") // SOUTH - center_y-- - if("E") // EAST - center_x++ - if("W") // WEST - center_x-- - if("C") // CENTER - center_x = 0 - center_y = 0 - if("R") // RANDOM - center_x = rand(-max_dist, max_dist) - center_y = rand(-max_dist, max_dist) - - if("set-code") - if(modifier) code = modifier - if("toggle-power") - on = !on - - if(on) - spawn() - magnetic_process() - -/obj/machinery/magnetic_module/process() - if(stat & NOPOWER) - on = 0 - - // Sanity checks: - if(electricity_level <= 0) - electricity_level = 1 - if(magnetic_field <= 0) - magnetic_field = 1 - - - // Limitations: - if(abs(center_x) > max_dist) - center_x = max_dist - if(abs(center_y) > max_dist) - center_y = max_dist - if(magnetic_field > 4) - magnetic_field = 4 - if(electricity_level > 12) - electricity_level = 12 - - // Update power usage: - if(on) - use_power = 2 - active_power_usage = electricity_level*15 - else - use_power = 0 - updateicon() - - -/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling - if(magpulling) return - while(on) - - magpulling = 1 - center = locate(x+center_x, y+center_y, z) - if(center) - for(var/obj/M in orange(magnetic_field, center)) - if(!M.anchored && (M.flags & CONDUCT)) - step_towards(M, center) - - for(var/mob/living/silicon/S in orange(magnetic_field, center)) - if(istype(S, /mob/living/silicon/ai)) continue - step_towards(S, center) - - use_power(electricity_level * 5) - sleep(13 - electricity_level) - - magpulling = 0 - -/obj/machinery/magnetic_controller - name = "Magnetic Control Console" - icon = 'icons/obj/airlock_machines.dmi' // uses an airlock machine icon, THINK GREEN HELP THE ENVIRONMENT - RECYCLING! - icon_state = "airlock_control_standby" - density = 1 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 45 - var/frequency = AIRLOCK_FREQ - var/code = 0 - var/list/magnets = list() - var/title = "Magnetic Control Console" - var/autolink = 0 // if set to 1, can't probe for other magnets! - - var/pathpos = 1 // position in the path - var/path = "NULL" // text path of the magnet - var/speed = 1 // lowest = 1, highest = 10 - var/list/rpath = list() // real path of the magnet, used in iterator - - var/moving = 0 // 1 if scheduled to loop - var/looping = 0 // 1 if looping - - var/datum/radio_frequency/radio_connection - - -/obj/machinery/magnetic_controller/New() - ..() - - if(autolink) - for(var/obj/machinery/magnetic_module/M in world) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - - - spawn(45) // must wait for map loading to finish - if(SSradio) - radio_connection = SSradio.add_object(src, frequency, RADIO_MAGNETS) - - - if(path) // check for default path - filter_path() // renders rpath - - -/obj/machinery/magnetic_controller/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/magnetic_controller/process() - if(magnets.len == 0 && autolink) - for(var/obj/machinery/magnetic_module/M in world) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - - -/obj/machinery/magnetic_controller/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/magnetic_controller/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - var/dat = "Magnetic Control Console

    " - if(!autolink) - dat += {" - Frequency: [frequency]
    - Code: [code]
    - Probe Generators
    - "} - - if(magnets.len >= 1) - - dat += "Magnets confirmed:
    " - var/i = 0 - for(var/obj/machinery/magnetic_module/M in magnets) - i++ - dat += "     < \[[i]\] ([M.on ? "On":"Off"]) | Electricity level: - [M.electricity_level] +; Magnetic field: - [M.magnetic_field] +
    " - - dat += "
    Speed: - [speed] +
    " - dat += "Path: {[path]}
    " - dat += "Moving: [moving ? "Enabled":"Disabled"]" - - - user << browse(dat, "window=magnet;size=400x500") - onclose(user, "magnet") - -/obj/machinery/magnetic_controller/Topic(href, href_list) - if(stat & (BROKEN|NOPOWER)) - return - usr.set_machine(src) - src.add_fingerprint(usr) - - if(href_list["radio-op"]) - - // Prepare signal beforehand, because this is a radio operation - var/datum/signal/signal = new - signal.transmission_method = 1 // radio transmission - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - // Apply any necessary commands - switch(href_list["radio-op"]) - if("togglepower") - signal.data["command"] = "toggle-power" - - if("minuselec") - signal.data["command"] = "sub-elec" - if("pluselec") - signal.data["command"] = "add-elec" - - if("minusmag") - signal.data["command"] = "sub-mag" - if("plusmag") - signal.data["command"] = "add-mag" - - - // Broadcast the signal - - radio_connection.post_signal(src, signal, filter = RADIO_MAGNETS) - - spawn(1) - updateUsrDialog() // pretty sure this increases responsiveness - - if(href_list["operation"]) - switch(href_list["operation"]) - if("plusspeed") - speed++ - if(speed > 10) - speed = 10 - if("minusspeed") - speed -- - if(speed <= 0) - speed = 1 - if("setpath") - var/newpath = sanitize(copytext(input(usr, "Please define a new path!",,path) as text|null,1,MAX_MESSAGE_LEN)) - if(newpath && newpath != "") - moving = 0 // stop moving - path = newpath - pathpos = 1 // reset position - filter_path() // renders rpath - - if("togglemoving") - moving = !moving - if(moving) - spawn() MagnetMove() - - - updateUsrDialog() - -/obj/machinery/magnetic_controller/proc/MagnetMove() - if(looping) return - - while(moving && rpath.len >= 1) - - if(stat & (BROKEN|NOPOWER)) - break - - looping = 1 - - // Prepare the radio signal - var/datum/signal/signal = new - signal.transmission_method = 1 // radio transmission - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - if(pathpos > rpath.len) // if the position is greater than the length, we just loop through the list! - pathpos = 1 - - var/nextmove = uppertext(rpath[pathpos]) // makes it un-case-sensitive - - if(!(nextmove in list("N","S","E","W","C","R"))) - // N, S, E, W are directional - // C is center - // R is random (in magnetic field's bounds) - qdel(signal) - break // break the loop if the character located is invalid - - signal.data["command"] = nextmove - - - pathpos++ // increase iterator - - // Broadcast the signal - spawn() - radio_connection.post_signal(src, signal, filter = RADIO_MAGNETS) - - if(speed == 10) - sleep(1) - else - sleep(12-speed) - - looping = 0 - - -/obj/machinery/magnetic_controller/proc/filter_path() - // Generates the rpath variable using the path string, think of this as "string2list" - // Doesn't use params2list() because of the akward way it stacks entities - rpath = list() // clear rpath - var/maximum_character = min( 50, length(path) ) // chooses the maximum length of the iterator. 50 max length - - for(var/i=1, i<=maximum_character, i++) // iterates through all characters in path - - var/nextchar = copytext(path, i, i+1) // find next character - - if(!(nextchar in list(";", "&", "*", " "))) // if char is a separator, ignore - rpath += copytext(path, i, i+1) // else, add to list - - // there doesn't HAVE to be separators but it makes paths syntatically visible +// Magnetic attractor, creates variable magnetic fields and attraction. +// Can also be used to emit electron/proton beams to create a center of magnetism on another tile + +// tl;dr: it's magnets lol +// This was created for firing ranges, but I suppose this could have other applications - Doohl + +/obj/machinery/magnetic_module + + icon = 'icons/obj/objects.dmi' + icon_state = "floor_magnet-f" + name = "Electromagnetic Generator" + desc = "A device that uses station power to create points of magnetic energy." + level = 1 // underfloor + layer = 2.5 + anchored = 1 + use_power = IDLE_POWER_USE + idle_power_usage = 50 + + var/freq = AIRLOCK_FREQ // radio frequency + var/electricity_level = 1 // intensity of the magnetic pull + var/magnetic_field = 1 // the range of magnetic attraction + var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something + var/turf/center // the center of magnetic attraction + var/on = 0 + var/magpulling = 0 + + // x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down + var/center_x = 0 + var/center_y = 0 + var/max_dist = 20 // absolute value of center_x,y cannot exceed this integer + +/obj/machinery/magnetic_module/New() + ..() + var/turf/T = loc + hide(T.intact) + center = T + + spawn(10) // must wait for map loading to finish + if(SSradio) + SSradio.add_object(src, freq, RADIO_MAGNETS) + + spawn() + magnetic_process() + + // update the invisibility and icon +/obj/machinery/magnetic_module/hide(intact) + invisibility = intact ? 101 : 0 + updateicon() + + // update the icon_state +/obj/machinery/magnetic_module/proc/updateicon() + var/state="floor_magnet" + var/onstate="" + if(!on) + onstate="0" + + if(invisibility) + icon_state = "[state][onstate]-f" // if invisible, set icon to faded version + // in case of being revealed by T-scanner + else + icon_state = "[state][onstate]" + +/obj/machinery/magnetic_module/receive_signal(datum/signal/signal) + var/command = signal.data["command"] + var/modifier = signal.data["modifier"] + var/signal_code = signal.data["code"] + if(command && (signal_code == code)) + Cmd(command, modifier) + + + +/obj/machinery/magnetic_module/proc/Cmd(var/command, var/modifier) + if(command) + switch(command) + if("set-electriclevel") + if(modifier) electricity_level = modifier + if("set-magneticfield") + if(modifier) magnetic_field = modifier + + if("add-elec") + electricity_level++ + if(electricity_level > 12) + electricity_level = 12 + if("sub-elec") + electricity_level-- + if(electricity_level <= 0) + electricity_level = 1 + if("add-mag") + magnetic_field++ + if(magnetic_field > 4) + magnetic_field = 4 + if("sub-mag") + magnetic_field-- + if(magnetic_field <= 0) + magnetic_field = 1 + + if("set-x") + if(modifier) center_x = modifier + if("set-y") + if(modifier) center_y = modifier + + if("N") // NORTH + center_y++ + if("S") // SOUTH + center_y-- + if("E") // EAST + center_x++ + if("W") // WEST + center_x-- + if("C") // CENTER + center_x = 0 + center_y = 0 + if("R") // RANDOM + center_x = rand(-max_dist, max_dist) + center_y = rand(-max_dist, max_dist) + + if("set-code") + if(modifier) code = modifier + if("toggle-power") + on = !on + + if(on) + spawn() + magnetic_process() + +/obj/machinery/magnetic_module/process() + if(stat & NOPOWER) + on = 0 + + // Sanity checks: + if(electricity_level <= 0) + electricity_level = 1 + if(magnetic_field <= 0) + magnetic_field = 1 + + + // Limitations: + if(abs(center_x) > max_dist) + center_x = max_dist + if(abs(center_y) > max_dist) + center_y = max_dist + if(magnetic_field > 4) + magnetic_field = 4 + if(electricity_level > 12) + electricity_level = 12 + + // Update power usage: + if(on) + use_power = 2 + active_power_usage = electricity_level*15 + else + use_power = 0 + updateicon() + + +/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling + if(magpulling) return + while(on) + + magpulling = 1 + center = locate(x+center_x, y+center_y, z) + if(center) + for(var/obj/M in orange(magnetic_field, center)) + if(!M.anchored && (M.flags & CONDUCT)) + step_towards(M, center) + + for(var/mob/living/silicon/S in orange(magnetic_field, center)) + if(istype(S, /mob/living/silicon/ai)) continue + step_towards(S, center) + + use_power(electricity_level * 5) + sleep(13 - electricity_level) + + magpulling = 0 + +/obj/machinery/magnetic_controller + name = "Magnetic Control Console" + icon = 'icons/obj/airlock_machines.dmi' // uses an airlock machine icon, THINK GREEN HELP THE ENVIRONMENT - RECYCLING! + icon_state = "airlock_control_standby" + density = 1 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 45 + var/frequency = AIRLOCK_FREQ + var/code = 0 + var/list/magnets = list() + var/title = "Magnetic Control Console" + var/autolink = 0 // if set to 1, can't probe for other magnets! + + var/pathpos = 1 // position in the path + var/path = "NULL" // text path of the magnet + var/speed = 1 // lowest = 1, highest = 10 + var/list/rpath = list() // real path of the magnet, used in iterator + + var/moving = 0 // 1 if scheduled to loop + var/looping = 0 // 1 if looping + + var/datum/radio_frequency/radio_connection + + +/obj/machinery/magnetic_controller/New() + ..() + + if(autolink) + for(var/obj/machinery/magnetic_module/M in world) + if(M.freq == frequency && M.code == code) + magnets.Add(M) + + + spawn(45) // must wait for map loading to finish + if(SSradio) + radio_connection = SSradio.add_object(src, frequency, RADIO_MAGNETS) + + + if(path) // check for default path + filter_path() // renders rpath + + +/obj/machinery/magnetic_controller/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/magnetic_controller/process() + if(magnets.len == 0 && autolink) + for(var/obj/machinery/magnetic_module/M in world) + if(M.freq == frequency && M.code == code) + magnets.Add(M) + + +/obj/machinery/magnetic_controller/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/magnetic_controller/attack_hand(mob/user as mob) + if(stat & (BROKEN|NOPOWER)) + return + user.set_machine(src) + var/dat = "Magnetic Control Console

    " + if(!autolink) + dat += {" + Frequency: [frequency]
    + Code: [code]
    + Probe Generators
    + "} + + if(magnets.len >= 1) + + dat += "Magnets confirmed:
    " + var/i = 0 + for(var/obj/machinery/magnetic_module/M in magnets) + i++ + dat += "     < \[[i]\] ([M.on ? "On":"Off"]) | Electricity level: - [M.electricity_level] +; Magnetic field: - [M.magnetic_field] +
    " + + dat += "
    Speed: - [speed] +
    " + dat += "Path: {[path]}
    " + dat += "Moving: [moving ? "Enabled":"Disabled"]" + + + user << browse(dat, "window=magnet;size=400x500") + onclose(user, "magnet") + +/obj/machinery/magnetic_controller/Topic(href, href_list) + if(stat & (BROKEN|NOPOWER)) + return + usr.set_machine(src) + src.add_fingerprint(usr) + + if(href_list["radio-op"]) + + // Prepare signal beforehand, because this is a radio operation + var/datum/signal/signal = new + signal.transmission_method = 1 // radio transmission + signal.source = src + signal.frequency = frequency + signal.data["code"] = code + + // Apply any necessary commands + switch(href_list["radio-op"]) + if("togglepower") + signal.data["command"] = "toggle-power" + + if("minuselec") + signal.data["command"] = "sub-elec" + if("pluselec") + signal.data["command"] = "add-elec" + + if("minusmag") + signal.data["command"] = "sub-mag" + if("plusmag") + signal.data["command"] = "add-mag" + + + // Broadcast the signal + + radio_connection.post_signal(src, signal, filter = RADIO_MAGNETS) + + spawn(1) + updateUsrDialog() // pretty sure this increases responsiveness + + if(href_list["operation"]) + switch(href_list["operation"]) + if("plusspeed") + speed++ + if(speed > 10) + speed = 10 + if("minusspeed") + speed -- + if(speed <= 0) + speed = 1 + if("setpath") + var/newpath = sanitize(copytext(input(usr, "Please define a new path!",,path) as text|null,1,MAX_MESSAGE_LEN)) + if(newpath && newpath != "") + moving = 0 // stop moving + path = newpath + pathpos = 1 // reset position + filter_path() // renders rpath + + if("togglemoving") + moving = !moving + if(moving) + spawn() MagnetMove() + + + updateUsrDialog() + +/obj/machinery/magnetic_controller/proc/MagnetMove() + if(looping) return + + while(moving && rpath.len >= 1) + + if(stat & (BROKEN|NOPOWER)) + break + + looping = 1 + + // Prepare the radio signal + var/datum/signal/signal = new + signal.transmission_method = 1 // radio transmission + signal.source = src + signal.frequency = frequency + signal.data["code"] = code + + if(pathpos > rpath.len) // if the position is greater than the length, we just loop through the list! + pathpos = 1 + + var/nextmove = uppertext(rpath[pathpos]) // makes it un-case-sensitive + + if(!(nextmove in list("N","S","E","W","C","R"))) + // N, S, E, W are directional + // C is center + // R is random (in magnetic field's bounds) + qdel(signal) + break // break the loop if the character located is invalid + + signal.data["command"] = nextmove + + + pathpos++ // increase iterator + + // Broadcast the signal + spawn() + radio_connection.post_signal(src, signal, filter = RADIO_MAGNETS) + + if(speed == 10) + sleep(1) + else + sleep(12-speed) + + looping = 0 + + +/obj/machinery/magnetic_controller/proc/filter_path() + // Generates the rpath variable using the path string, think of this as "string2list" + // Doesn't use params2list() because of the akward way it stacks entities + rpath = list() // clear rpath + var/maximum_character = min( 50, length(path) ) // chooses the maximum length of the iterator. 50 max length + + for(var/i=1, i<=maximum_character, i++) // iterates through all characters in path + + var/nextchar = copytext(path, i, i+1) // find next character + + if(!(nextchar in list(";", "&", "*", " "))) // if char is a separator, ignore + rpath += copytext(path, i, i+1) // else, add to list + + // there doesn't HAVE to be separators but it makes paths syntatically visible diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index 9e6f0d672cc..fc6e61be76d 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -1,211 +1,211 @@ -/obj/machinery/mass_driver - name = "mass driver" - desc = "Shoots things into space." - icon = 'icons/obj/objects.dmi' - icon_state = "mass_driver" - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 50 - - var/power = 1.0 - var/code = 1.0 - var/id_tag = "default" - settagwhitelist = list("id_tag") - var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess. - -/obj/machinery/mass_driver/attackby(obj/item/W, mob/user as mob) - - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(istype(W, /obj/item/screwdriver)) - to_chat(user, "You begin to unscrew the bolts off the [src]...") - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 30 * W.toolspeed, target = src)) - var/obj/machinery/mass_driver_frame/F = new(get_turf(src)) - F.dir = src.dir - F.anchored = 1 - F.build = 4 - F.update_icon() - qdel(src) - return 1 - - return ..() - -/obj/machinery/mass_driver/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" -
      -
    • [format_tag("ID Tag","id_tag")]
    • -
    "} - -/obj/machinery/mass_driver/proc/drive(amount) - if(stat & (BROKEN|NOPOWER)) - return - use_power(500*power) - var/O_limit = 0 - var/atom/target = get_edge_target_turf(src, dir) - for(var/atom/movable/O in loc) - if(!O.anchored||istype(O, /obj/mecha))//Mechs need their launch platforms. - O_limit++ - if(O_limit >= 20)//so no more than 20 items are sent at a time, probably for counter-lag purposes - break - use_power(500) - spawn() - var/coef = 1 - if(emagged) - coef = 5 - O.throw_at(target, drive_range * power * coef, power * coef) - flick("mass_driver1", src) - return - -/obj/machinery/mass_driver/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - drive() - ..(severity) - -/obj/machinery/mass_driver/emag_act(mob/user) - if(!emagged) - emagged = 1 - to_chat(user, "You hack the Mass Driver, radically increasing the force at which it'll throw things. Better not stand in its way.") - return 1 - return -1 - -////////////////MASS BUMPER/////////////////// - -/obj/machinery/mass_driver/bumper - name = "mass bumper" - desc = "Now you're here, now you're over there." - density = 1 - -/obj/machinery/mass_driver/bumper/Bumped(M as mob|obj) - density = 0 - step(M, get_dir(M,src)) - spawn(1) - density = 1 - drive() - return - -////////////////MASS DRIVER FRAME/////////////////// - -/obj/machinery/mass_driver_frame - name = "mass driver frame" - icon = 'icons/obj/objects.dmi' - icon_state = "mass_driver_b0" - density = 0 - anchored = 0 - var/build = 0 - -/obj/machinery/mass_driver_frame/attackby(var/obj/item/W as obj, var/mob/user as mob) - switch(build) - if(0) // Loose frame - if(istype(W, /obj/item/wrench)) - to_chat(user, "You begin to anchor \the [src] on the floor.") - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 10 * W.toolspeed, target = src) && (build == 0)) - to_chat(user, "You anchor \the [src]!") - anchored = 1 - build++ - update_icon() - return 1 - return - if(1) // Fixed to the floor - if(istype(W, /obj/item/wrench)) - to_chat(user, "You begin to de-anchor \the [src] from the floor.") - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 10 * W.toolspeed, target = src) && (build == 1)) - build-- - update_icon() - anchored = 0 - to_chat(user, "You de-anchored \the [src]!") - return 1 - if(2) // Welded to the floor - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - to_chat(user, "You start adding cables to \the [src]...") - playsound(get_turf(src), C.usesound, 50, 1) - if(do_after(user, 20 * C.toolspeed, target = src) && (C.amount >= 2) && (build == 2)) - C.use(2) - to_chat(user, "You've added cables to \the [src].") - build++ - update_icon() - return - if(3) // Wired - if(istype(W, /obj/item/wirecutters)) - to_chat(user, "You begin to remove the wiring from \the [src].") - if(do_after(user, 10 * W.toolspeed, target = src) && (build == 3)) - new /obj/item/stack/cable_coil(loc,2) - playsound(get_turf(src), W.usesound, 50, 1) - to_chat(user, "You've removed the cables from \the [src].") - build-- - update_icon() - return 1 - if(istype(W, /obj/item/stack/rods)) - var/obj/item/stack/rods/R = W - to_chat(user, "You begin to complete \the [src]...") - playsound(get_turf(src), R.usesound, 50, 1) - if(do_after(user, 20 * R.toolspeed, target = src) && (R.amount >= 2) && (build == 3)) - R.use(2) - to_chat(user, "You've added the grille to \the [src].") - build++ - update_icon() - return 1 - return - if(4) // Grille in place - if(istype(W, /obj/item/crowbar)) - to_chat(user, "You begin to pry off the grille from \the [src]...") - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 30 * W.toolspeed, target = src) && (build == 4)) - new /obj/item/stack/rods(loc,2) - build-- - update_icon() - return 1 - if(istype(W, /obj/item/screwdriver)) - to_chat(user, "You finalize the Mass Driver...") - playsound(get_turf(src), W.usesound, 50, 1) - var/obj/machinery/mass_driver/M = new(get_turf(src)) - M.dir = src.dir - qdel(src) - return 1 - return - return ..() - -/obj/machinery/mass_driver_frame/welder_act(mob/user, obj/item/I) - if(build != 0 && build != 1 && build != 2) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(build == 0) //can deconstruct - WELDER_ATTEMPT_SLICING_MESSAGE - if(I.use_tool(src, user, 30, volume = I.tool_volume)) - WELDER_SLICING_SUCCESS_MESSAGE - new /obj/item/stack/sheet/plasteel(drop_location(),3) - qdel(src) - else if(build == 1) //wrenched but not welded down - WELDER_ATTEMPT_FLOOR_WELD_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume) && build == 1) - WELDER_FLOOR_WELD_SUCCESS_MESSAGE - build = 2 - else if(build == 2) //welded down - WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume) && build == 2) - WELDER_FLOOR_SLICE_SUCCESS_MESSAGE - build = 1 - update_icon() - -/obj/machinery/mass_driver_frame/update_icon() - icon_state = "mass_driver_b[build]" - -/obj/machinery/mass_driver_frame/verb/rotate() - set category = "Object" - set name = "Rotate Frame" - set src in view(1) - - if( usr.stat || usr.restrained() || (usr.status_flags & FAKEDEATH)) - return - - src.dir = turn(src.dir, -90) - return +/obj/machinery/mass_driver + name = "mass driver" + desc = "Shoots things into space." + icon = 'icons/obj/objects.dmi' + icon_state = "mass_driver" + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 50 + + var/power = 1.0 + var/code = 1.0 + var/id_tag = "default" + settagwhitelist = list("id_tag") + var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess. + +/obj/machinery/mass_driver/attackby(obj/item/W, mob/user as mob) + + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(istype(W, /obj/item/screwdriver)) + to_chat(user, "You begin to unscrew the bolts off the [src]...") + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 30 * W.toolspeed, target = src)) + var/obj/machinery/mass_driver_frame/F = new(get_turf(src)) + F.dir = src.dir + F.anchored = 1 + F.build = 4 + F.update_icon() + qdel(src) + return 1 + + return ..() + +/obj/machinery/mass_driver/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" +
      +
    • [format_tag("ID Tag","id_tag")]
    • +
    "} + +/obj/machinery/mass_driver/proc/drive(amount) + if(stat & (BROKEN|NOPOWER)) + return + use_power(500*power) + var/O_limit = 0 + var/atom/target = get_edge_target_turf(src, dir) + for(var/atom/movable/O in loc) + if(!O.anchored||istype(O, /obj/mecha))//Mechs need their launch platforms. + O_limit++ + if(O_limit >= 20)//so no more than 20 items are sent at a time, probably for counter-lag purposes + break + use_power(500) + spawn() + var/coef = 1 + if(emagged) + coef = 5 + O.throw_at(target, drive_range * power * coef, power * coef) + flick("mass_driver1", src) + return + +/obj/machinery/mass_driver/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + drive() + ..(severity) + +/obj/machinery/mass_driver/emag_act(mob/user) + if(!emagged) + emagged = 1 + to_chat(user, "You hack the Mass Driver, radically increasing the force at which it'll throw things. Better not stand in its way.") + return 1 + return -1 + +////////////////MASS BUMPER/////////////////// + +/obj/machinery/mass_driver/bumper + name = "mass bumper" + desc = "Now you're here, now you're over there." + density = 1 + +/obj/machinery/mass_driver/bumper/Bumped(M as mob|obj) + density = 0 + step(M, get_dir(M,src)) + spawn(1) + density = 1 + drive() + return + +////////////////MASS DRIVER FRAME/////////////////// + +/obj/machinery/mass_driver_frame + name = "mass driver frame" + icon = 'icons/obj/objects.dmi' + icon_state = "mass_driver_b0" + density = 0 + anchored = 0 + var/build = 0 + +/obj/machinery/mass_driver_frame/attackby(var/obj/item/W as obj, var/mob/user as mob) + switch(build) + if(0) // Loose frame + if(istype(W, /obj/item/wrench)) + to_chat(user, "You begin to anchor \the [src] on the floor.") + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 10 * W.toolspeed, target = src) && (build == 0)) + to_chat(user, "You anchor \the [src]!") + anchored = 1 + build++ + update_icon() + return 1 + return + if(1) // Fixed to the floor + if(istype(W, /obj/item/wrench)) + to_chat(user, "You begin to de-anchor \the [src] from the floor.") + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 10 * W.toolspeed, target = src) && (build == 1)) + build-- + update_icon() + anchored = 0 + to_chat(user, "You de-anchored \the [src]!") + return 1 + if(2) // Welded to the floor + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + to_chat(user, "You start adding cables to \the [src]...") + playsound(get_turf(src), C.usesound, 50, 1) + if(do_after(user, 20 * C.toolspeed, target = src) && (C.amount >= 2) && (build == 2)) + C.use(2) + to_chat(user, "You've added cables to \the [src].") + build++ + update_icon() + return + if(3) // Wired + if(istype(W, /obj/item/wirecutters)) + to_chat(user, "You begin to remove the wiring from \the [src].") + if(do_after(user, 10 * W.toolspeed, target = src) && (build == 3)) + new /obj/item/stack/cable_coil(loc,2) + playsound(get_turf(src), W.usesound, 50, 1) + to_chat(user, "You've removed the cables from \the [src].") + build-- + update_icon() + return 1 + if(istype(W, /obj/item/stack/rods)) + var/obj/item/stack/rods/R = W + to_chat(user, "You begin to complete \the [src]...") + playsound(get_turf(src), R.usesound, 50, 1) + if(do_after(user, 20 * R.toolspeed, target = src) && (R.amount >= 2) && (build == 3)) + R.use(2) + to_chat(user, "You've added the grille to \the [src].") + build++ + update_icon() + return 1 + return + if(4) // Grille in place + if(istype(W, /obj/item/crowbar)) + to_chat(user, "You begin to pry off the grille from \the [src]...") + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 30 * W.toolspeed, target = src) && (build == 4)) + new /obj/item/stack/rods(loc,2) + build-- + update_icon() + return 1 + if(istype(W, /obj/item/screwdriver)) + to_chat(user, "You finalize the Mass Driver...") + playsound(get_turf(src), W.usesound, 50, 1) + var/obj/machinery/mass_driver/M = new(get_turf(src)) + M.dir = src.dir + qdel(src) + return 1 + return + return ..() + +/obj/machinery/mass_driver_frame/welder_act(mob/user, obj/item/I) + if(build != 0 && build != 1 && build != 2) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(build == 0) //can deconstruct + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 30, volume = I.tool_volume)) + WELDER_SLICING_SUCCESS_MESSAGE + new /obj/item/stack/sheet/plasteel(drop_location(),3) + qdel(src) + else if(build == 1) //wrenched but not welded down + WELDER_ATTEMPT_FLOOR_WELD_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume) && build == 1) + WELDER_FLOOR_WELD_SUCCESS_MESSAGE + build = 2 + else if(build == 2) //welded down + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume) && build == 2) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + build = 1 + update_icon() + +/obj/machinery/mass_driver_frame/update_icon() + icon_state = "mass_driver_b[build]" + +/obj/machinery/mass_driver_frame/verb/rotate() + set category = "Object" + set name = "Rotate Frame" + set src in view(1) + + if( usr.stat || usr.restrained() || (usr.status_flags & FAKEDEATH)) + return + + src.dir = turn(src.dir, -90) + return diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index c93c4e099b2..48d87d0f330 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -1,222 +1,222 @@ -// Navigation beacon for AI robots -// No longer exists on the radio controller, it is managed by a global list. - -/obj/machinery/navbeacon - - icon = 'icons/obj/objects.dmi' - icon_state = "navbeacon0-f" - name = "navigation beacon" - desc = "A radio beacon used for bot navigation." - level = 1 // underfloor - layer = 2.5 - anchored = 1 - max_integrity = 500 - armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) - var/open = 0 // true if cover is open - var/locked = 1 // true if controls are locked - var/location = "" // location response text - var/list/codes // assoc. list of transponder codes - var/codes_txt = "" // codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" - - req_access = list(access_engine, access_robotics) - -/obj/machinery/navbeacon/New() - ..() - - set_codes() - - var/turf/T = loc - hide(T.intact) - if(!codes || !codes.len) - log_runtime(EXCEPTION("Empty codes datum at ([x],[y],[z])"), src, list("codes_txt: '[codes_txt]'")) - if("patrol" in codes) - if(!GLOB.navbeacons["[z]"]) - GLOB.navbeacons["[z]"] = list() - GLOB.navbeacons["[z]"] += src //Register with the patrol list! - if("delivery" in codes) - GLOB.deliverybeacons += src - GLOB.deliverybeacontags += location - -/obj/machinery/navbeacon/Destroy() - GLOB.navbeacons["[z]"] -= src //Remove from beacon list, if in one. - GLOB.deliverybeacons -= src - return ..() - -/obj/machinery/navbeacon/serialize() - var/list/data = ..() - data["codes"] = codes - return data - -/obj/machinery/navbeacon/deserialize(list/data) - codes = data["codes"] - ..() - -// set the transponder codes assoc list from codes_txt -/obj/machinery/navbeacon/proc/set_codes() - if(!codes_txt) - return - - codes = new() - - var/list/entries = splittext(codes_txt, ";") // entries are separated by semicolons - - for(var/e in entries) - var/index = findtext(e, "=") // format is "key=value" - if(index) - var/key = copytext(e, 1, index) - var/val = copytext(e, index+1) - codes[key] = val - else - codes[e] = "1" - - -// called when turf state changes -// hide the object if turf is intact -/obj/machinery/navbeacon/hide(intact) - invisibility = intact ? INVISIBILITY_MAXIMUM : 0 - updateicon() - -// update the icon_state -/obj/machinery/navbeacon/proc/updateicon() - var/state="navbeacon[open]" - - if(invisibility) - icon_state = "[state]-f" // if invisible, set icon to faded version - // in case revealed by T-scanner - else - icon_state = "[state]" - -/obj/machinery/navbeacon/attackby(obj/item/I, mob/user, params) - var/turf/T = loc - if(T.intact) - return // prevent intraction when T-scanner revealed - - if(istype(I, /obj/item/screwdriver)) - open = !open - - user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") - - updateicon() - - else if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda)) - if(open) - if(allowed(user)) - locked = !locked - to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") - else - to_chat(user, "Access denied.") - updateDialog() - else - to_chat(user, "You must open the cover first!") - else - return ..() - -/obj/machinery/navbeacon/attack_ai(mob/user) - interact(user, 1) - -/obj/machinery/navbeacon/attack_hand(mob/user) - interact(user, 0) - -/obj/machinery/navbeacon/interact(mob/user, ai = 0) - var/turf/T = loc - if(T.intact) - return // prevent intraction when T-scanner revealed - - if(!open && !ai) // can't alter controls if not open, unless you're an AI - to_chat(user, "The beacon's control cover is closed!") - return - - - var/t - - if(locked && !ai) - t = {"Navigation Beacon

    -(swipe card to unlock controls)
    -Location: [location ? location : "(none)"]
    -Transponder Codes:
      "} - - for(var/key in codes) - t += "
    • [key] ... [codes[key]]" - t+= "
        " - - else - - t = {"Navigation Beacon

        -(swipe card to lock controls)
        - -
        -Location: [location ? location : "None"]
        -Transponder Codes:
          "} - - for(var/key in codes) - t += "
        • [key] ... [codes[key]]" - t += " Edit" - t += " Delete
          " - t += " Add New
          " - t+= "
            " - - var/datum/browser/popup = new(user, "navbeacon", "Navigation Beacon", 300, 400) - popup.set_content(t) - popup.open() - return - -/obj/machinery/navbeacon/Topic(href, href_list) - if(..()) - return - if(open && !locked) - usr.set_machine(src) - - if(href_list["locedit"]) - var/newloc = copytext(sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null),1,MAX_MESSAGE_LEN) - if(newloc) - location = newloc - updateDialog() - - else if(href_list["edit"]) - var/codekey = href_list["code"] - - var/newkey = stripped_input(usr, "Enter Transponder Code Key", "Navigation Beacon", codekey) - if(!newkey) - return - - var/codeval = codes[codekey] - var/newval = stripped_input(usr, "Enter Transponder Code Value", "Navigation Beacon", codeval) - if(!newval) - newval = codekey - return - - codes.Remove(codekey) - codes[newkey] = newval - - updateDialog() - - else if(href_list["delete"]) - var/codekey = href_list["code"] - codes.Remove(codekey) - updateDialog() - - else if(href_list["add"]) - - var/newkey = stripped_input(usr, "Enter New Transponder Code Key", "Navigation Beacon") - if(!newkey) - return - - var/newval = stripped_input(usr, "Enter New Transponder Code Value", "Navigation Beacon") - if(!newval) - newval = "1" - return - - if(!codes) - codes = new() - - codes[newkey] = newval - - updateDialog() - - -/obj/machinery/navbeacon/invisible - invisibility = INVISIBILITY_MAXIMUM - -/obj/machinery/navbeacon/invisible/hide(intact) - invisibility = INVISIBILITY_MAXIMUM - updateicon() \ No newline at end of file +// Navigation beacon for AI robots +// No longer exists on the radio controller, it is managed by a global list. + +/obj/machinery/navbeacon + + icon = 'icons/obj/objects.dmi' + icon_state = "navbeacon0-f" + name = "navigation beacon" + desc = "A radio beacon used for bot navigation." + level = 1 // underfloor + layer = 2.5 + anchored = 1 + max_integrity = 500 + armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) + var/open = 0 // true if cover is open + var/locked = 1 // true if controls are locked + var/location = "" // location response text + var/list/codes // assoc. list of transponder codes + var/codes_txt = "" // codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" + + req_access = list(ACCESS_ENGINE, ACCESS_ROBOTICS) + +/obj/machinery/navbeacon/New() + ..() + + set_codes() + + var/turf/T = loc + hide(T.intact) + if(!codes || !codes.len) + log_runtime(EXCEPTION("Empty codes datum at ([x],[y],[z])"), src, list("codes_txt: '[codes_txt]'")) + if("patrol" in codes) + if(!GLOB.navbeacons["[z]"]) + GLOB.navbeacons["[z]"] = list() + GLOB.navbeacons["[z]"] += src //Register with the patrol list! + if("delivery" in codes) + GLOB.deliverybeacons += src + GLOB.deliverybeacontags += location + +/obj/machinery/navbeacon/Destroy() + GLOB.navbeacons["[z]"] -= src //Remove from beacon list, if in one. + GLOB.deliverybeacons -= src + return ..() + +/obj/machinery/navbeacon/serialize() + var/list/data = ..() + data["codes"] = codes + return data + +/obj/machinery/navbeacon/deserialize(list/data) + codes = data["codes"] + ..() + +// set the transponder codes assoc list from codes_txt +/obj/machinery/navbeacon/proc/set_codes() + if(!codes_txt) + return + + codes = new() + + var/list/entries = splittext(codes_txt, ";") // entries are separated by semicolons + + for(var/e in entries) + var/index = findtext(e, "=") // format is "key=value" + if(index) + var/key = copytext(e, 1, index) + var/val = copytext(e, index+1) + codes[key] = val + else + codes[e] = "1" + + +// called when turf state changes +// hide the object if turf is intact +/obj/machinery/navbeacon/hide(intact) + invisibility = intact ? INVISIBILITY_MAXIMUM : 0 + updateicon() + +// update the icon_state +/obj/machinery/navbeacon/proc/updateicon() + var/state="navbeacon[open]" + + if(invisibility) + icon_state = "[state]-f" // if invisible, set icon to faded version + // in case revealed by T-scanner + else + icon_state = "[state]" + +/obj/machinery/navbeacon/attackby(obj/item/I, mob/user, params) + var/turf/T = loc + if(T.intact) + return // prevent intraction when T-scanner revealed + + if(istype(I, /obj/item/screwdriver)) + open = !open + + user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") + + updateicon() + + else if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda)) + if(open) + if(allowed(user)) + locked = !locked + to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") + else + to_chat(user, "Access denied.") + updateDialog() + else + to_chat(user, "You must open the cover first!") + else + return ..() + +/obj/machinery/navbeacon/attack_ai(mob/user) + interact(user, 1) + +/obj/machinery/navbeacon/attack_hand(mob/user) + interact(user, 0) + +/obj/machinery/navbeacon/interact(mob/user, ai = 0) + var/turf/T = loc + if(T.intact) + return // prevent intraction when T-scanner revealed + + if(!open && !ai) // can't alter controls if not open, unless you're an AI + to_chat(user, "The beacon's control cover is closed!") + return + + + var/t + + if(locked && !ai) + t = {"Navigation Beacon

            +(swipe card to unlock controls)
            +Location: [location ? location : "(none)"]
            +Transponder Codes:
              "} + + for(var/key in codes) + t += "
            • [key] ... [codes[key]]" + t+= "
                " + + else + + t = {"Navigation Beacon

                +(swipe card to lock controls)
                + +
                +Location: [location ? location : "None"]
                +Transponder Codes:
                  "} + + for(var/key in codes) + t += "
                • [key] ... [codes[key]]" + t += " Edit" + t += " Delete
                  " + t += " Add New
                  " + t+= "
                    " + + var/datum/browser/popup = new(user, "navbeacon", "Navigation Beacon", 300, 400) + popup.set_content(t) + popup.open() + return + +/obj/machinery/navbeacon/Topic(href, href_list) + if(..()) + return + if(open && !locked) + usr.set_machine(src) + + if(href_list["locedit"]) + var/newloc = copytext(sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null),1,MAX_MESSAGE_LEN) + if(newloc) + location = newloc + updateDialog() + + else if(href_list["edit"]) + var/codekey = href_list["code"] + + var/newkey = stripped_input(usr, "Enter Transponder Code Key", "Navigation Beacon", codekey) + if(!newkey) + return + + var/codeval = codes[codekey] + var/newval = stripped_input(usr, "Enter Transponder Code Value", "Navigation Beacon", codeval) + if(!newval) + newval = codekey + return + + codes.Remove(codekey) + codes[newkey] = newval + + updateDialog() + + else if(href_list["delete"]) + var/codekey = href_list["code"] + codes.Remove(codekey) + updateDialog() + + else if(href_list["add"]) + + var/newkey = stripped_input(usr, "Enter New Transponder Code Key", "Navigation Beacon") + if(!newkey) + return + + var/newval = stripped_input(usr, "Enter New Transponder Code Value", "Navigation Beacon") + if(!newval) + newval = "1" + return + + if(!codes) + codes = new() + + codes[newkey] = newval + + updateDialog() + + +/obj/machinery/navbeacon/invisible + invisibility = INVISIBILITY_MAXIMUM + +/obj/machinery/navbeacon/invisible/hide(intact) + invisibility = INVISIBILITY_MAXIMUM + updateicon() diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 61d96602343..c2396ab1d9f 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -55,9 +55,9 @@ var/list/datum/feed_channel/network_channels = list() var/datum/feed_message/wanted_issue -var/datum/feed_network/news_network = new /datum/feed_network //The global news-network, which is coincidentally a global list. +GLOBAL_DATUM_INIT(news_network, /datum/feed_network, new()) //The global news-network, which is coincidentally a global list. -var/list/obj/machinery/newscaster/allCasters = list() //Global list that will contain reference to all newscasters in existence. +GLOBAL_LIST_EMPTY(allNewscasters) //Global list that will contain reference to all newscasters in existence. #define NEWSCASTER_MAIN 0 // Main menu #define NEWSCASTER_FC_LIST 1 // Feed channel list @@ -128,13 +128,13 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co securityCaster = 1 /obj/machinery/newscaster/New() - allCasters += src - unit_no = allCasters.len + GLOB.allNewscasters += src + unit_no = GLOB.allNewscasters.len update_icon() //for any custom ones on the map... ..() /obj/machinery/newscaster/Destroy() - allCasters -= src + GLOB.allNewscasters -= src viewing_channel = null QDEL_NULL(photo) return ..() @@ -144,7 +144,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co if(inoperable()) icon_state = "newscaster_off" else - if(!news_network.wanted_issue) //wanted icon state, there can be no overlays on it as it's a priority message + if(!GLOB.news_network.wanted_issue) //wanted icon state, there can be no overlays on it as it's a priority message icon_state = "newscaster_normal" if(alert) //new message alert overlay add_overlay("newscaster_alert") @@ -194,7 +194,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co switch(screen) if(0) - data["wanted_issue"] = news_network.wanted_issue ? 1 : 0 + data["wanted_issue"] = GLOB.news_network.wanted_issue ? 1 : 0 data["silence"] = silence data["securityCaster"] = securityCaster if(securityCaster) @@ -202,7 +202,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co if(1, 6, 7) var/list/channels = list() data["channels"] = channels - for(var/datum/feed_channel/C in news_network.network_channels) + for(var/datum/feed_channel/C in GLOB.news_network.network_channels) channels[++channels.len] = list("name" = C.channel_name, "ref" = "\ref[C]", "censored" = C.censored, "admin" = C.is_admin_channel) if(2) data["scanned_user"] = scanned_user @@ -215,10 +215,10 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co data["msg"] = msg data["photo"] = photo ? 1 : 0 if(4) - var/total_num = length(news_network.network_channels) + var/total_num = length(GLOB.news_network.network_channels) var/active_num = total_num var/message_num=0 - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(!FC.censored) message_num += length(FC.messages) else @@ -254,7 +254,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co if(10) var/wanted_already = 0 var/end_param = 1 - if(news_network.wanted_issue) + if(GLOB.news_network.wanted_issue) wanted_already = 1 end_param = 2 data["wanted_already"] = wanted_already @@ -263,16 +263,16 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co data["msg"] = msg data["photo"] = photo ? 1 : 0 if(wanted_already) - data["author"] = news_network.wanted_issue.backup_author + data["author"] = GLOB.news_network.wanted_issue.backup_author else data["scanned_user"] = scanned_user if(11) - data["author"] = news_network.wanted_issue.backup_author - data["criminal"] = news_network.wanted_issue.author - data["description"] = news_network.wanted_issue.body - if(news_network.wanted_issue.img) - user << browse_rsc(news_network.wanted_issue.img, "tmp_photow.png") - data["photo"] = news_network.wanted_issue.img ? news_network.wanted_issue.img : 0 + data["author"] = GLOB.news_network.wanted_issue.backup_author + data["criminal"] = GLOB.news_network.wanted_issue.author + data["description"] = GLOB.news_network.wanted_issue.body + if(GLOB.news_network.wanted_issue.img) + user << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png") + data["photo"] = GLOB.news_network.wanted_issue.img ? GLOB.news_network.wanted_issue.img : 0 if(12) var/list/jobs = list() data["jobs"] = jobs @@ -295,13 +295,13 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co else if(href_list["submit_new_channel"]) var/list/existing_authors = list() - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.author == REDACTED) existing_authors += FC.backup_author else existing_authors += FC.author var/check = 0 - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == channel_name) check = 1 break @@ -325,13 +325,13 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co newChannel.author = scanned_user newChannel.locked = c_locked feedback_inc("newscaster_channels", 1) - news_network.network_channels += newChannel //Adding channel to the global network + GLOB.news_network.network_channels += newChannel //Adding channel to the global network temp = "Feed channel '[channel_name]' created successfully." temp_back_screen = NEWSCASTER_MAIN else if(href_list["set_channel_receiving"]) var/list/available_channels = list() - for(var/datum/feed_channel/F in news_network.network_channels) + for(var/datum/feed_channel/F in GLOB.news_network.network_channels) if((!F.locked || F.author == scanned_user) && !F.censored) available_channels += F.channel_name channel_name = strip_html_simple(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels) @@ -366,14 +366,14 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co newMsg.img = photo.img feedback_inc("newscaster_stories",1) var/announcement = "" - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == channel_name) FC.messages += newMsg //Adding message to the network's appropriate feed_channel announcement = FC.announce_news(msg_title) break temp = "Feed story successfully submitted to [channel_name]." temp_back_screen = NEWSCASTER_MAIN - for(var/obj/machinery/newscaster/NC in allCasters) + for(var/obj/machinery/newscaster/NC in GLOB.allNewscasters) NC.newsAlert(announcement) else if(href_list["create_channel"]) @@ -405,12 +405,12 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co else if(href_list["menu_wanted"]) var/already_wanted = 0 - if(news_network.wanted_issue) + if(GLOB.news_network.wanted_issue) already_wanted = 1 if(already_wanted) - channel_name = news_network.wanted_issue.author - msg = news_network.wanted_issue.body + channel_name = GLOB.news_network.wanted_issue.author + msg = GLOB.news_network.wanted_issue.body screen = NEWSCASTER_W_ISSUE_H else if(href_list["set_wanted_name"]) @@ -441,32 +441,32 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co W.backup_author = scanned_user //I know, a bit wacky if(photo) W.img = photo.img - news_network.wanted_issue = W - for(var/obj/machinery/newscaster/NS in allCasters) + GLOB.news_network.wanted_issue = W + for(var/obj/machinery/newscaster/NS in GLOB.allNewscasters) NS.newsAlert() NS.update_icon() temp = "Wanted issue for [channel_name] is now in Network Circulation." temp_back_screen = NEWSCASTER_MAIN else - if(news_network.wanted_issue.is_admin_message) + if(GLOB.news_network.wanted_issue.is_admin_message) alert("The wanted issue has been distributed by a Nanotrasen higherup. You cannot edit it.","Ok") return - news_network.wanted_issue.author = channel_name - news_network.wanted_issue.body = msg - news_network.wanted_issue.backup_author = scanned_user + GLOB.news_network.wanted_issue.author = channel_name + GLOB.news_network.wanted_issue.body = msg + GLOB.news_network.wanted_issue.backup_author = scanned_user if(photo) - news_network.wanted_issue.img = photo.img + GLOB.news_network.wanted_issue.img = photo.img temp = "Wanted issue for [channel_name] successfully edited." temp_back_screen = NEWSCASTER_MAIN else if(href_list["cancel_wanted"]) - if(news_network.wanted_issue.is_admin_message) + if(GLOB.news_network.wanted_issue.is_admin_message) alert("The wanted issue has been distributed by a Nanotrasen higherup. You cannot take it down.", "Ok") return var/choice = alert("Please confirm wanted issue removal", "Network Security Handler", "Confirm", "Cancel") if(choice == "Confirm") - news_network.wanted_issue = null - for(var/obj/machinery/newscaster/NC in allCasters) + GLOB.news_network.wanted_issue = null + for(var/obj/machinery/newscaster/NC in GLOB.allNewscasters) NC.update_icon() temp = "Wanted Issue successfully deleted from Circulation" temp_back_screen = NEWSCASTER_MAIN @@ -826,10 +826,10 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co /obj/machinery/newscaster/proc/print_paper() feedback_inc("newscaster_newspapers_printed",1) var/obj/item/newspaper/NEWSPAPER = new /obj/item/newspaper - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) NEWSPAPER.news_content += FC - if(news_network.wanted_issue) - NEWSPAPER.important_message = news_network.wanted_issue + if(GLOB.news_network.wanted_issue) + NEWSPAPER.important_message = GLOB.news_network.wanted_issue NEWSPAPER.loc = get_turf(src) paper_remaining-- return diff --git a/code/game/machinery/overview.dm b/code/game/machinery/overview.dm index e77eef869dc..7165cceb1ff 100644 --- a/code/game/machinery/overview.dm +++ b/code/game/machinery/overview.dm @@ -1,362 +1,362 @@ -//#define AMAP - -/obj/machinery/computer/security/verb/station_map() - set name = ".map" - set category = "Object" - set src in view(1) - usr.set_machine(src) - if(!mapping) return - - log_game("[usr]([usr.key]) used station map L[z] in [src.loc.loc]") - - src.drawmap(usr) - -/obj/machinery/computer/security/proc/drawmap(var/mob/user as mob) - - var/icx = round(world.maxx/16) + 1 - var/icy = round(world.maxy/16) + 1 - - var/xoff = round( (icx*16-world.maxx)-2) - var/yoff = round( (icy*16-world.maxy)-2) - - var/icount = icx * icy - - - var/list/imap = list() - -#ifdef AMAP - - for(var/i = 0; i
    - - - - -
    - [left_part] - - [list_queue()] -
    "} - var/datum/browser/popup = new(user, "mecha_fabricator", name, 1000, 600) - popup.set_content(dat) - popup.open(0) - onclose(user, "mecha_fabricator") - return - -/obj/machinery/mecha_part_fabricator/Topic(href, href_list) - if(..()) - return 1 - var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) - if(href_list["part_set"]) - var/tpart_set = afilter.getStr("part_set") - if(tpart_set) - if(tpart_set=="clear") - part_set = null - else - part_set = tpart_set - screen = "parts" - if(href_list["part"]) - var/T = afilter.getStr("part") - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(D.build_type & fabricator_type) - if(D.id == T) - if(!processing_queue) - build_part(D) - else - add_to_queue(D) - break - if(href_list["add_to_queue"]) - var/T = afilter.getStr("add_to_queue") - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(D.build_type & fabricator_type) - if(D.id == T) - add_to_queue(D) - break - return update_queue_on_page() - if(href_list["remove_from_queue"]) - remove_from_queue(afilter.getNum("remove_from_queue")) - return update_queue_on_page() - if(href_list["partset_to_queue"]) - add_part_set_to_queue(afilter.get("partset_to_queue")) - return update_queue_on_page() - if(href_list["process_queue"]) - spawn(0) - if(processing_queue || being_built) - return FALSE - processing_queue = 1 - process_queue() - processing_queue = 0 - if(href_list["clear_temp"]) - temp = null - if(href_list["screen"]) - screen = href_list["screen"] - if(href_list["queue_move"] && href_list["index"]) - var/index = afilter.getNum("index") - var/new_index = index + afilter.getNum("queue_move") - if(isnum(index) && isnum(new_index)) - if(IsInRange(new_index,1,queue.len)) - queue.Swap(index,new_index) - return update_queue_on_page() - if(href_list["clear_queue"]) - queue = list() - return update_queue_on_page() - if(href_list["sync"]) - sync() - if(href_list["part_desc"]) - var/T = afilter.getStr("part_desc") - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(D.build_type & fabricator_type) - if(D.id == T) - var/obj/part = D.build_path - temp = {"

    [initial(part.name)] description:

    - [initial(part.desc)]
    - Return - "} - break - - if(href_list["remove_mat"] && href_list["material"]) - GET_COMPONENT(materials, /datum/component/material_container) - materials.retrieve_sheets(text2num(href_list["remove_mat"]), href_list["material"]) - - updateUsrDialog() - return - -/obj/machinery/mecha_part_fabricator/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) - var/stack_name = material2name(id_inserted) - overlays += "fab-load-[stack_name]" - sleep(10) - overlays -= "fab-load-[stack_name]" - updateUsrDialog() - -/obj/machinery/mecha_part_fabricator/attackby(obj/item/W, mob/user, params) - if(default_deconstruction_screwdriver(user, "fab-o", "fab-idle", W)) - return - - if(exchange_parts(user, W)) - return - - if(default_deconstruction_crowbar(user, W)) - return TRUE - - else - return ..() - -/obj/machinery/mecha_part_fabricator/proc/material2name(ID) - return copytext(ID,2) - -/obj/machinery/mecha_part_fabricator/proc/is_insertion_ready(mob/user) - if(panel_open) - to_chat(user, "You can't load [src] while it's opened!") - return FALSE - if(being_built) - to_chat(user, "\The [src] is currently processing! Please wait until completion.") - return FALSE - - return TRUE - -/obj/machinery/mecha_part_fabricator/spacepod - name = "spacepod fabricator" - fabricator_type = PODFAB - part_sets = list( "Pod_Weaponry", - "Pod_Armor", - "Pod_Cargo", - "Pod_Parts", - "Pod_Frame", - "Misc") - req_access = list(access_mechanic) - -/obj/machinery/mecha_part_fabricator/spacepod/New() - ..() - QDEL_LIST(component_parts) - component_parts = list() - component_parts += new /obj/item/circuitboard/podfab(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stack/sheet/glass(null) - RefreshParts() - -/obj/machinery/mecha_part_fabricator/robot - name = "Robotic Fabricator" - part_sets = list("Cyborg") +/obj/machinery/mecha_part_fabricator + icon = 'icons/obj/robotics.dmi' + icon_state = "fab-idle" + name = "exosuit fabricator" + desc = "Nothing is being built." + density = TRUE + anchored = TRUE + use_power = IDLE_POWER_USE + idle_power_usage = 20 + active_power_usage = 5000 + var/time_coeff = 1 + var/component_coeff = 1 + var/datum/research/files + var/fabricator_type = MECHFAB + var/id + var/sync = 0 + var/part_set + var/datum/design/being_built + var/list/queue = list() + var/processing_queue = 0 + var/screen = "main" + var/temp + var/list/part_sets = list( + "Cyborg", + "Cyborg Repair", + "Ripley", + "Firefighter", + "Odysseus", + "Gygax", + "Durand", + "H.O.N.K", + "Reticence", + "Phazon", + "Exosuit Equipment", + "Cyborg Upgrade Modules", + "Medical", + "Misc" + ) + +/obj/machinery/mecha_part_fabricator/New() + var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, + list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_BLUESPACE), 0, + FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) + materials.precise_insertion = TRUE + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/mechfab(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + files = new /datum/research(src) //Setup the research data holder. + +/obj/machinery/mecha_part_fabricator/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/mechfab(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + +/obj/machinery/mecha_part_fabricator/Destroy() + GET_COMPONENT(materials, /datum/component/material_container) + materials.retrieve_all() + return ..() + +/obj/machinery/mecha_part_fabricator/RefreshParts() + var/T = 0 + + //maximum stocking amount (default 300000, 600000 at T4) + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + T += M.rating + GET_COMPONENT(materials, /datum/component/material_container) + materials.max_amount = (200000 + (T*50000)) + + //resources adjustment coefficient (1 -> 0.85 -> 0.7 -> 0.55) + T = 1.15 + for(var/obj/item/stock_parts/micro_laser/Ma in component_parts) + T -= Ma.rating*0.15 + component_coeff = T + + //building time adjustment coefficient (1 -> 0.8 -> 0.6) + T = -1 + for(var/obj/item/stock_parts/manipulator/Ml in component_parts) + T += Ml.rating + time_coeff = round(initial(time_coeff) - (initial(time_coeff)*(T))/5,0.01) + + +/obj/machinery/mecha_part_fabricator/proc/output_parts_list(set_name) + var/output = "" + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(!(set_name in D.category)) + continue + output += "
    [output_part_info(D)]
    \[" + if(check_resources(D)) + output += "Build | " + output += "Add to queue\]\[?\]
    " + return output + +/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D) + var/output = "[initial(D.name)] (Cost: [output_part_cost(D)]) [get_construction_time_w_coeff(D)/10] seconds" + return output + +/obj/machinery/mecha_part_fabricator/proc/output_part_cost(datum/design/D) + var/i = 0 + var/output + for(var/c in D.materials) + output += "[i?" | ":null][get_resource_cost_w_coeff(D, c)] [material2name(c)]" + i++ + return output + +/obj/machinery/mecha_part_fabricator/proc/output_available_resources() + var/output + GET_COMPONENT(materials, /datum/component/material_container) + for(var/mat_id in materials.materials) + var/datum/material/M = materials.materials[mat_id] + output += "[M.name]: [M.amount] cm³" + if(M.amount >= MINERAL_MATERIAL_AMOUNT) + output += "- Remove \[1\]" + if(M.amount >= (MINERAL_MATERIAL_AMOUNT * 10)) + output += " | \[10\]" + output += " | \[All\]" + output += "
    " + return output + +/obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D) + var/list/resources = list() + for(var/R in D.materials) + resources[R] = get_resource_cost_w_coeff(D, R) + return resources + +/obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D) + if(D.reagents_list.len) // No reagents storage - no reagent designs. + return FALSE + GET_COMPONENT(materials, /datum/component/material_container) + if(materials.has_materials(get_resources_w_coeff(D))) + return TRUE + return FALSE + +/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D) + being_built = D + desc = "It's building \a [initial(D.name)]." + var/list/res_coef = get_resources_w_coeff(D) + + GET_COMPONENT(materials, /datum/component/material_container) + materials.use_amount(res_coef) + overlays += "fab-active" + use_power = ACTIVE_POWER_USE + updateUsrDialog() + sleep(get_construction_time_w_coeff(D)) + use_power = IDLE_POWER_USE + overlays -= "fab-active" + desc = initial(desc) + + var/obj/item/I = new D.build_path(loc) + if(D.locked) + var/obj/item/storage/lockbox/research/large/L = new /obj/item/storage/lockbox/research/large(get_step(src, SOUTH)) + I.forceMove(L) + L.name += " ([I.name])" + L.origin_tech = I.origin_tech + else + I.forceMove(get_step(src, SOUTH)) + if(istype(I)) + I.materials = res_coef + atom_say("[I] is complete.") + being_built = null + + updateUsrDialog() + return TRUE + +/obj/machinery/mecha_part_fabricator/proc/update_queue_on_page() + send_byjax(usr,"mecha_fabricator.browser","queue",list_queue()) + return + +/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(set_name) + if(set_name in part_sets) + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(set_name in D.category) + add_to_queue(D) + +/obj/machinery/mecha_part_fabricator/proc/add_to_queue(D) + if(!istype(queue)) + queue = list() + if(D) + queue[++queue.len] = D + return queue.len + +/obj/machinery/mecha_part_fabricator/proc/remove_from_queue(index) + if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len)) + return FALSE + queue.Cut(index,++index) + return TRUE + +/obj/machinery/mecha_part_fabricator/proc/process_queue() + var/datum/design/D = queue[1] + if(!D) + remove_from_queue(1) + if(queue.len) + return process_queue() + else + return + temp = null + while(D) + if(stat&(NOPOWER|BROKEN)) + return FALSE + if(!check_resources(D)) + atom_say("Not enough resources. Queue processing stopped.") + temp = {"Not enough resources to build next part.
    + Try again | Return"} + return FALSE + remove_from_queue(1) + build_part(D) + D = listgetindex(queue, 1) + atom_say("Queue processing finished successfully.") + +/obj/machinery/mecha_part_fabricator/proc/list_queue() + var/output = "Queue contains:" + if(!istype(queue) || !queue.len) + output += "
    Nothing" + else + output += "
      " + var/i = 0 + for(var/datum/design/D in queue) + i++ + var/obj/part = D.build_path + output += "" + output += initial(part.name) + " - " + output += "[i>1?"":null] " + output += "[i↓":null] " + output += "Remove" + + output += "
    " + output += "\[Process queue | Clear queue\]" + return output + +/obj/machinery/mecha_part_fabricator/proc/sync() + temp = "Updating local R&D database..." + updateUsrDialog() + sleep(30) //only sleep if called by user + var/area/localarea = get_area(src) + + for(var/obj/machinery/computer/rdconsole/RDC in localarea.contents) + if(!RDC.sync) + continue + RDC.files.push_data(files) + temp = "Processed equipment designs.
    " + //check if the tech coefficients have changed + temp += "Return" + + updateUsrDialog() + atom_say("Successfully synchronized with R&D server.") + return + + temp = "Unable to connect to local R&D Database.
    Please check your connections and try again.
    Return" + updateUsrDialog() + return + +/obj/machinery/mecha_part_fabricator/proc/get_resource_cost_w_coeff(datum/design/D, resource, roundto = 1) + return round(D.materials[resource]*component_coeff, roundto) + +/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(datum/design/D, roundto = 1) //aran + return round(initial(D.construction_time)*time_coeff, roundto) + +/obj/machinery/mecha_part_fabricator/attack_ghost(mob/user) + interact(user) + +/obj/machinery/mecha_part_fabricator/attack_hand(mob/user) + if(..()) + return 1 + if(!allowed(user) && !isobserver(user)) + to_chat(user, "Access denied.") + return 1 + return interact(user) + +/obj/machinery/mecha_part_fabricator/interact(mob/user) + var/dat, left_part + if(..()) + return + user.set_machine(src) + var/turf/exit = get_step(src,(dir)) + if(exit.density) + atom_say("Error! Part outlet is obstructed.") + return + if(temp) + left_part = temp + else if(being_built) + var/obj/I = being_built.build_path + left_part = {"Building [initial(I.name)].
    + Please wait until completion...
    "} + else + switch(screen) + if("main") + left_part = output_available_resources()+"
    " + left_part += "Sync with R&D servers
    " + for(var/part_set in part_sets) + left_part += "[part_set] - \[Add all parts to queue\]
    " + if("parts") + left_part += output_parts_list(part_set) + left_part += "
    Return" + dat = {" + + [name] + + + + + + + + +
    + [left_part] + + [list_queue()] +
    "} + var/datum/browser/popup = new(user, "mecha_fabricator", name, 1000, 600) + popup.set_content(dat) + popup.open(0) + onclose(user, "mecha_fabricator") + return + +/obj/machinery/mecha_part_fabricator/Topic(href, href_list) + if(..()) + return 1 + var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) + if(href_list["part_set"]) + var/tpart_set = afilter.getStr("part_set") + if(tpart_set) + if(tpart_set=="clear") + part_set = null + else + part_set = tpart_set + screen = "parts" + if(href_list["part"]) + var/T = afilter.getStr("part") + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(D.id == T) + if(!processing_queue) + build_part(D) + else + add_to_queue(D) + break + if(href_list["add_to_queue"]) + var/T = afilter.getStr("add_to_queue") + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(D.id == T) + add_to_queue(D) + break + return update_queue_on_page() + if(href_list["remove_from_queue"]) + remove_from_queue(afilter.getNum("remove_from_queue")) + return update_queue_on_page() + if(href_list["partset_to_queue"]) + add_part_set_to_queue(afilter.get("partset_to_queue")) + return update_queue_on_page() + if(href_list["process_queue"]) + spawn(0) + if(processing_queue || being_built) + return FALSE + processing_queue = 1 + process_queue() + processing_queue = 0 + if(href_list["clear_temp"]) + temp = null + if(href_list["screen"]) + screen = href_list["screen"] + if(href_list["queue_move"] && href_list["index"]) + var/index = afilter.getNum("index") + var/new_index = index + afilter.getNum("queue_move") + if(isnum(index) && isnum(new_index)) + if(IsInRange(new_index,1,queue.len)) + queue.Swap(index,new_index) + return update_queue_on_page() + if(href_list["clear_queue"]) + queue = list() + return update_queue_on_page() + if(href_list["sync"]) + sync() + if(href_list["part_desc"]) + var/T = afilter.getStr("part_desc") + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(D.id == T) + var/obj/part = D.build_path + temp = {"

    [initial(part.name)] description:

    + [initial(part.desc)]
    + Return + "} + break + + if(href_list["remove_mat"] && href_list["material"]) + GET_COMPONENT(materials, /datum/component/material_container) + materials.retrieve_sheets(text2num(href_list["remove_mat"]), href_list["material"]) + + updateUsrDialog() + return + +/obj/machinery/mecha_part_fabricator/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) + var/stack_name = material2name(id_inserted) + overlays += "fab-load-[stack_name]" + sleep(10) + overlays -= "fab-load-[stack_name]" + updateUsrDialog() + +/obj/machinery/mecha_part_fabricator/attackby(obj/item/W, mob/user, params) + if(default_deconstruction_screwdriver(user, "fab-o", "fab-idle", W)) + return + + if(exchange_parts(user, W)) + return + + if(default_deconstruction_crowbar(user, W)) + return TRUE + + else + return ..() + +/obj/machinery/mecha_part_fabricator/proc/material2name(ID) + return copytext(ID,2) + +/obj/machinery/mecha_part_fabricator/proc/is_insertion_ready(mob/user) + if(panel_open) + to_chat(user, "You can't load [src] while it's opened!") + return FALSE + if(being_built) + to_chat(user, "\The [src] is currently processing! Please wait until completion.") + return FALSE + + return TRUE + +/obj/machinery/mecha_part_fabricator/spacepod + name = "spacepod fabricator" + fabricator_type = PODFAB + part_sets = list( "Pod_Weaponry", + "Pod_Armor", + "Pod_Cargo", + "Pod_Parts", + "Pod_Frame", + "Misc") + req_access = list(ACCESS_MECHANIC) + +/obj/machinery/mecha_part_fabricator/spacepod/New() + ..() + QDEL_LIST(component_parts) + component_parts = list() + component_parts += new /obj/item/circuitboard/podfab(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + +/obj/machinery/mecha_part_fabricator/robot + name = "Robotic Fabricator" + part_sets = list("Cyborg") diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 7c4aee9781c..7efa81863b6 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -1,1521 +1,1526 @@ -/obj/mecha - name = "Mecha" - desc = "Exosuit" - icon = 'icons/mecha/mecha.dmi' - density = 1 //Dense. To raise the heat. - opacity = 1 ///opaque. Menacing. - anchored = 1 //no pulling around. - resistance_flags = FIRE_PROOF | ACID_PROOF - layer = MOB_LAYER //icon draw layer - infra_luminosity = 15 //byond implementation is bugged. - force = 5 - max_integrity = 300 //max_integrity is base health - armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100) - var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) - var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. - var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) - var/can_move = 0 // time of next allowed movement - var/mob/living/carbon/occupant = null - var/step_in = 10 //make a step in step_in/10 sec. - var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. - var/normal_step_energy_drain = 10 - var/step_energy_drain = 10 - var/melee_energy_drain = 15 - var/overload_step_energy_drain_min = 100 - var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. - var/obj/item/stock_parts/cell/cell - var/state = 0 - var/list/log = new - var/last_message = 0 - var/add_req_access = 1 - var/maint_access = 1 - var/dna //dna-locking the mech - var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference - var/datum/effect_system/spark_spread/spark_system = new - var/lights = 0 - var/lights_power = 6 - var/emagged = FALSE - - //inner atmos - var/use_internal_tank = 0 - var/internal_tank_valve = ONE_ATMOSPHERE - var/obj/machinery/portable_atmospherics/canister/internal_tank - var/datum/gas_mixture/cabin_air - var/obj/machinery/atmospherics/unary/portables_connector/connected_port = null - - var/obj/item/radio/radio = null - var/list/trackers = list() - - var/max_temperature = 25000 - var/internal_damage_threshold = 50 //health percentage below which internal damage is possible - var/internal_damage = 0 //contains bitflags - - var/list/operation_req_access = list()//required access level for mecha operation - var/list/internals_req_access = list(access_engine,access_robotics)//required access level to open cell compartment - - var/wreckage - - var/list/equipment = new - var/obj/item/mecha_parts/mecha_equipment/selected - var/max_equip = 3 - var/datum/events/events - var/turf/crashing = null - var/occupant_sight_flags = 0 - - var/stepsound = 'sound/mecha/mechstep.ogg' - var/turnsound = 'sound/mecha/mechturn.ogg' - var/nominalsound = 'sound/mecha/nominal.ogg' - var/zoomsound = 'sound/mecha/imag_enh.ogg' - var/critdestrsound = 'sound/mecha/critdestr.ogg' - var/weapdestrsound = 'sound/mecha/weapdestr.ogg' - var/lowpowersound = 'sound/mecha/lowpower.ogg' - var/longactivationsound = 'sound/mecha/nominal.ogg' - var/starting_voice = /obj/item/mecha_modkit/voice - var/activated = FALSE - var/power_warned = FALSE - - var/destruction_sleep_duration = 1 //Time that mech pilot is put to sleep for if mech is destroyed - - var/melee_cooldown = 10 - var/melee_can_hit = 1 - - // Action vars - var/defence_mode = FALSE - var/defence_mode_deflect_chance = 35 - var/leg_overload_mode = FALSE - var/leg_overload_coeff = 100 - var/thrusters_active = FALSE - var/smoke = 5 - var/smoke_ready = 1 - var/smoke_cooldown = 100 - var/zoom_mode = FALSE - var/phasing = FALSE - var/phasing_energy_drain = 200 - var/phase_state = "" //icon_state when phasing - - hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) - -/obj/mecha/Initialize() - . = ..() - events = new - icon_state += "-open" - add_radio() - add_cabin() - add_airtank() - spark_system.set_up(2, 0, src) - spark_system.attach(src) - smoke_system.set_up(3, src) - smoke_system.attach(src) - add_cell() - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - log_message("[src] created.") - GLOB.mechas_list += src //global mech list - prepare_huds() - for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) - diag_hud.add_to_hud(src) - diag_hud_set_mechhealth() - diag_hud_set_mechcell() - diag_hud_set_mechstat() - diag_hud_set_mechtracking() - - var/obj/item/mecha_modkit/voice/V = new starting_voice(src) - V.install(src) - qdel(V) - -//////////////////////// -////// Helpers ///////// -//////////////////////// - -/obj/mecha/get_cell() - return cell - -/obj/mecha/proc/add_airtank() - internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) - return internal_tank - -/obj/mecha/proc/add_cell(var/obj/item/stock_parts/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new/obj/item/stock_parts/cell/high/plus(src) - -/obj/mecha/proc/add_cabin() - cabin_air = new - cabin_air.temperature = T20C - cabin_air.volume = 200 - cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) - cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) - return cabin_air - -/obj/mecha/proc/add_radio() - radio = new(src) - radio.name = "[src] radio" - radio.icon = icon - radio.icon_state = icon_state - radio.subspace_transmission = 1 - -/obj/mecha/examine(mob/user) - . = ..() - var/integrity = obj_integrity * 100 / max_integrity - switch(integrity) - if(85 to 100) - . += "It's fully intact." - if(65 to 85) - . += "It's slightly damaged." - if(45 to 65) - . += "It's badly damaged." - if(25 to 45) - . += "It's heavily damaged." - else - . += "It's falling apart." - if(equipment && equipment.len) - . += "It's equipped with:" - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - . += "[bicon(ME)] [ME]" - -/obj/mecha/hear_talk(mob/M, list/message_pieces) - if(M == occupant && radio.broadcasting) - radio.talk_into(M, message_pieces) - -/obj/mecha/proc/click_action(atom/target, mob/user, params) - if(!occupant || occupant != user ) - return - if(user.incapacitated()) - return - if(phasing) - occupant_message("Unable to interact with objects while phasing.") - return - if(state) - occupant_message("Maintenance protocols in effect.") - return - if(!get_charge()) - return - if(src == target) - return - - var/dir_to_target = get_dir(src, target) - if(dir_to_target && !(dir_to_target & dir))//wrong direction - return - - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) - target = safepick(view(3,target)) - if(!target) - return - - var/mob/living/L = user - if(!target.Adjacent(src)) - if(selected && selected.is_ranged()) - if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful) - to_chat(L, "You don't want to harm other living beings!") - return - selected.action(target, params) - else if(selected && selected.is_melee()) - if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM)) - to_chat(user, "You don't want to harm other living beings!") - return - selected.action(target, params) - else - if(internal_damage & MECHA_INT_CONTROL_LOST) - target = safepick(oview(1, src)) - if(!melee_can_hit || !isatom(target)) - return - target.mech_melee_attack(src) - melee_can_hit = 0 - spawn(melee_cooldown) - melee_can_hit = 1 - -/obj/mecha/proc/mech_toxin_damage(mob/living/target) - playsound(src, 'sound/effects/spray2.ogg', 50, 1) - if(target.reagents) - if(target.reagents.get_reagent_amount("atropine") + force < force*2) - target.reagents.add_reagent("atropine", force/2) - if(target.reagents.get_reagent_amount("toxin") + force < force*2) - target.reagents.add_reagent("toxin", force/2.5) - -/obj/mecha/proc/range_action(atom/target) - return - - -////////////////////////////////// -//////// Movement procs //////// -////////////////////////////////// - -/obj/mecha/Move(atom/newLoc, direct) - . = ..() - if(.) - events.fireEvent("onMove",get_turf(src)) - -/obj/mecha/Process_Spacemove(var/movement_dir = 0) - . = ..() - if(.) - return 1 - if(thrusters_active && movement_dir && use_power(step_energy_drain)) - return 1 - - var/atom/movable/backup = get_spacemove_backup() - if(backup) - if(istype(backup) && movement_dir && !backup.anchored) - if(backup.newtonian_move(turn(movement_dir, 180))) - if(occupant) - to_chat(occupant, "You push off of [backup] to propel yourself.") - return 1 - -/obj/mecha/relaymove(mob/user, direction) - if(!direction) - return - if(user != occupant) //While not "realistic", this piece is player friendly. - user.forceMove(get_turf(src)) - to_chat(user, "You climb out from [src].") - return 0 - if(connected_port) - if(world.time - last_message > 20) - occupant_message("Unable to move while connected to the air system port!") - last_message = world.time - return 0 - if(state) - occupant_message("Maintenance protocols in effect.") - return - return domove(direction) - -/obj/mecha/proc/domove(direction) - if(can_move >= world.time) - return 0 - if(!Process_Spacemove(direction)) - return 0 - if(!has_charge(step_energy_drain)) - return 0 - if(defence_mode) - if(world.time - last_message > 20) - occupant_message("Unable to move while in defence mode.") - last_message = world.time - return 0 - if(zoom_mode) - if(world.time - last_message > 20) - occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 - - var/move_result = 0 - var/move_type = 0 - if(internal_damage & MECHA_INT_CONTROL_LOST) - move_result = mechsteprand() - move_type = MECHAMOVE_RAND - else if(dir != direction) - move_result = mechturn(direction) - move_type = MECHAMOVE_TURN - else - move_result = mechstep(direction) - move_type = MECHAMOVE_STEP - - if(move_result && move_type) - aftermove(move_type) - can_move = world.time + step_in - return TRUE - return FALSE - -/obj/mecha/proc/aftermove(move_type) - use_power(step_energy_drain) - if(move_type & (MECHAMOVE_RAND | MECHAMOVE_STEP) && occupant) - var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector) in loc - if(possible_port) - var/obj/screen/alert/mech_port_available/A = occupant.throw_alert("mechaport", /obj/screen/alert/mech_port_available, override = TRUE) - if(A) - A.target = possible_port - else - occupant.clear_alert("mechaport") - if(leg_overload_mode) - if(obj_integrity < max_integrity - max_integrity / 3) - leg_overload_mode = FALSE - step_in = initial(step_in) - step_energy_drain = initial(step_energy_drain) - occupant_message("Leg actuators damage threshold exceded. Disabling overload.") - -/obj/mecha/proc/mechturn(direction) - dir = direction - if(turnsound) - playsound(src,turnsound,40,1) - return 1 - -/obj/mecha/proc/mechstep(direction) - . = step(src, direction) - if(!.) - if(phasing && get_charge() >= phasing_energy_drain) - if(can_move < world.time) - . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself - flick("phazon-phase", src) - forceMove(get_step(src, direction)) - use_power(phasing_energy_drain) - playsound(src, stepsound, 40, 1) - can_move = world.time + (step_in * 3) - else if(stepsound) - playsound(src, stepsound, 40, 1) - -/obj/mecha/proc/mechsteprand() - . = step_rand(src) - if(. && stepsound) - playsound(src, stepsound, 40, 1) - -/obj/mecha/Bump(var/atom/obstacle, bump_allowed) - if(throwing) //high velocity mechas in your face! - var/breakthrough = 0 - if(istype(obstacle, /obj/structure/window)) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/grille/)) - var/obj/structure/grille/G = obstacle - G.obj_break() - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/table)) - var/obj/structure/table/T = obstacle - qdel(T) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/rack)) - new /obj/item/rack_parts(obstacle.loc) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank)) - obstacle.ex_act(1) - - else if(isliving(obstacle)) - var/mob/living/L = obstacle - var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') - if(L.flags & GODMODE) - return - L.take_overall_damage(5,0) - if(L.buckled) - L.buckled = 0 - L.Stun(5) - L.Weaken(5) - L.apply_effect(STUTTER, 5) - playsound(src, pick(hit_sound), 50, 0, 0) - breakthrough = 1 - - else - if(throwing) - throwing.finalize(FALSE) - crashing = null - - ..() - - if(breakthrough) - if(crashing) - spawn(1) - throw_at(crashing, 50, throw_speed) - else - spawn(1) - crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. - throw_at(crashing, 50, throw_speed) - - else - if(bump_allowed) - if(..()) - return - if(isobj(obstacle)) - var/obj/O = obstacle - if(istype(O, /obj/effect/portal)) //derpfix - anchored = 0 - O.Bumped(src) - spawn(0) //countering portal teleport spawn(0), hurr - anchored = 1 - else if(!O.anchored) - step(obstacle, dir) - else if(ismob(obstacle)) - step(obstacle, dir) - - -/////////////////////////////////// -//////// Internal damage //////// -/////////////////////////////////// - -/obj/mecha/proc/check_for_internal_damage(list/possible_int_damage, ignore_threshold=null) - if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) - return - if(prob(20)) - if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) - for(var/T in possible_int_damage) - if(internal_damage & T) - possible_int_damage -= T - var/int_dam_flag = safepick(possible_int_damage) - if(int_dam_flag) - setInternalDamage(int_dam_flag) - if(prob(5)) - if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) - var/obj/item/mecha_parts/mecha_equipment/ME = safepick(equipment) - if(ME) - qdel(ME) - -/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) - return int_dam_flag ? internal_damage&int_dam_flag : internal_damage - - -/obj/mecha/proc/setInternalDamage(int_dam_flag) - internal_damage |= int_dam_flag - log_append_to_last("Internal damage of type [int_dam_flag].",1) - occupant << sound('sound/machines/warning-buzzer.ogg',wait=0) - diag_hud_set_mechstat() - -/obj/mecha/proc/clearInternalDamage(int_dam_flag) - internal_damage &= ~int_dam_flag - switch(int_dam_flag) - if(MECHA_INT_TEMP_CONTROL) - occupant_message("Life support system reactivated.") - if(MECHA_INT_FIRE) - occupant_message("Internal fire extinquished.") - if(MECHA_INT_TANK_BREACH) - occupant_message("Damaged internal tank has been sealed.") - diag_hud_set_mechstat() - - -//////////////////////////////////////// -//////// Health related procs //////// -//////////////////////////////////////// - -/obj/mecha/proc/get_armour_facing(relative_dir) - switch(relative_dir) - if(0) // BACKSTAB! - return facing_modifiers[MECHA_BACK_ARMOUR] - if(45, 90, 270, 315) - return facing_modifiers[MECHA_SIDE_ARMOUR] - if(225, 180, 135) - return facing_modifiers[MECHA_FRONT_ARMOUR] - return 1 //always return non-0 - -/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - spark_system.start() - switch(damage_flag) - if("fire") - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL)) - if("melee") - check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - else - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) - if(. >= 5 || prob(33)) - occupant_message("Taking damage!") - log_message("Took [damage_amount] points of damage. Damage type: [damage_type]") - -/obj/mecha/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - . = ..() - if(!damage_amount) - return 0 - var/booster_deflection_modifier = 1 - var/booster_damage_modifier = 1 - if(damage_flag == "bullet" || damage_flag == "laser" || damage_flag == "energy") - for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) - if(B.projectile_react()) - booster_deflection_modifier = B.deflect_coeff - booster_damage_modifier = B.damage_coeff - break - else if(damage_flag == "melee") - for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) - if(B.attack_react()) - booster_deflection_modifier *= B.deflect_coeff - booster_damage_modifier *= B.damage_coeff - break - - if(attack_dir) - var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src)) - booster_damage_modifier /= facing_modifier - booster_deflection_modifier *= facing_modifier - if(prob(deflect_chance * booster_deflection_modifier)) - visible_message("[src]'s armour deflects the attack!") - log_message("Armor saved.") - return 0 - if(.) - . *= booster_damage_modifier - -/obj/mecha/attack_hand(mob/living/user) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) - playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1) - user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") - log_message("Attack by hand/paw. Attacker - [user].") - - -/obj/mecha/attack_alien(mob/living/user) - log_message("Attack by alien. Attacker - [user].", color = "red") - playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) - attack_generic(user, 15, BRUTE, "melee", 0) - -/obj/mecha/attack_animal(mob/living/simple_animal/user) - log_message("Attack by simple animal. Attacker - [user].") - if(!user.melee_damage_upper && !user.obj_damage) - user.custom_emote(1, "[user.friendly] [src].") - return FALSE - else - var/play_soundeffect = 1 - if(user.environment_smash) - play_soundeffect = 0 - playsound(src, 'sound/effects/bang.ogg', 50, TRUE) - var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper) - if(user.obj_damage) - animal_damage = user.obj_damage - animal_damage = min(animal_damage, 20*user.environment_smash) - user.create_attack_log("attacked [name]") - attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) - return TRUE - -/obj/mecha/hulk_damage() - return 15 - -/obj/mecha/attack_hulk(mob/living/carbon/human/user) - . = ..() - if(.) - log_message("Attack by hulk. Attacker - [user].", 1) - add_attack_logs(user, src, "Punched with hulk powers") - -/obj/mecha/blob_act(obj/structure/blob/B) - log_message("Attack by blob. Attacker - [B].") - take_damage(30, BRUTE, "melee", 0, get_dir(src, B)) - -/obj/mecha/attack_tk() - return - -/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper - log_message("Hit by [AM].") - . = ..() - -/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper - log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).") - ..() - -/obj/mecha/ex_act(severity, target) - log_message("Affected by explosion of severity: [severity].") - if(prob(deflect_chance)) - severity++ - log_message("Armor saved, changing severity to [severity]") - ..() - severity++ - for(var/X in equipment) - var/obj/item/mecha_parts/mecha_equipment/ME = X - ME.ex_act(severity) - for(var/Y in trackers) - var/obj/item/mecha_parts/mecha_tracking/MT = Y - MT.ex_act(severity) - if(occupant) - occupant.ex_act(severity) - -/obj/mecha/handle_atom_del(atom/A) - if(A == occupant) - occupant = null - icon_state = initial(icon_state)+"-open" - setDir(dir_in) - -/obj/mecha/Destroy() - if(occupant) - occupant.SetSleeping(destruction_sleep_duration) - go_out() - var/mob/living/silicon/ai/AI - for(var/mob/M in src) //Let's just be ultra sure - if(isAI(M)) - occupant = null - AI = M //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. They can be recovered with an AI card from the wreck. - else - M.forceMove(loc) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - E.detach(loc) - qdel(E) - equipment.Cut() - QDEL_NULL(cell) - QDEL_NULL(internal_tank) - if(AI) - AI.gib() //No wreck, no AI to recover - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - if(loc) - loc.assume_air(cabin_air) - air_update_turf() - else - qdel(cabin_air) - cabin_air = null - QDEL_NULL(spark_system) - QDEL_NULL(smoke_system) - - GLOB.mechas_list -= src //global mech list - return ..() - -//TODO -/obj/mecha/emp_act(severity) - if(get_charge()) - use_power((cell.charge/3)/(severity*2)) - take_damage(30 / severity, BURN, "energy", 1) - log_message("EMP detected", 1) - check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL, MECHA_INT_CONTROL_LOST, MECHA_INT_SHORT_CIRCUIT), 1) - -/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > max_temperature) - log_message("Exposed to dangerous temperature.", 1) - take_damage(5, BURN, 0, 1) - check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) - -////////////////////// -////// AttackBy ////// -////////////////////// - -/obj/mecha/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/mmi)) - if(mmi_move_inside(W,user)) - to_chat(user, "[src]-MMI interface initialized successfuly") - else - to_chat(user, "[src]-MMI interface initialization failed.") - return - - if(istype(W, /obj/item/mecha_parts/mecha_equipment)) - var/obj/item/mecha_parts/mecha_equipment/E = W - spawn() - if(E.can_attach(src)) - if(!user.drop_item()) - return - E.attach(src) - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - else - to_chat(user, "You were unable to attach [W] to [src]!") - return - - if(W.GetID()) - if(add_req_access || maint_access) - if(internals_access_allowed(usr)) - var/obj/item/card/id/id_card - if(istype(W, /obj/item/card/id)) - id_card = W - else - var/obj/item/pda/pda = W - id_card = pda.id - output_maintenance_dialog(id_card, user) - return - else - to_chat(user, "Invalid ID: Access denied.") - else - to_chat(user, "Maintenance protocols disabled by operator.") - - else if(istype(W, /obj/item/stack/cable_coil)) - if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - var/obj/item/stack/cable_coil/CC = W - if(CC.amount > 1) - CC.use(2) - clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) - to_chat(user, "You replace the fused wires.") - else - to_chat(user, "There's not enough wire to finish the task.") - return - - else if(istype(W, /obj/item/stock_parts/cell)) - if(state==4) - if(!cell) - if(!user.drop_item()) - return - to_chat(user, "You install the powercell.") - W.forceMove(src) - cell = W - log_message("Powercell installed") - else - to_chat(user, "There's already a powercell installed.") - return - - else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) - if(!user.unEquip(W)) - to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") - return - W.forceMove(src) - trackers += W - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - diag_hud_set_mechtracking() - return - - else if(istype(W, /obj/item/paintkit)) - if(occupant) - to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") - return - - var/obj/item/paintkit/P = W - var/found = null - - for(var/type in P.allowed_types) - if(type == initial_icon) - found = 1 - break - - if(!found) - to_chat(user, "That kit isn't meant for use on this class of exosuit.") - return - - user.visible_message("[user] opens [P] and spends some quality time customising [src].") - - name = P.new_name - desc = P.new_desc - initial_icon = P.new_icon - reset_icon() - - user.drop_item() - qdel(P) - - else if(istype(W, /obj/item/mecha_modkit)) - if(occupant) - to_chat(user, "You can't access the mech's modification port while it is occupied.") - return - var/obj/item/mecha_modkit/M = W - if(do_after_once(user, M.install_time, target = src)) - M.install(src, user) - else - to_chat(user, "You stop installing [M].") - - else - return ..() - - -/obj/mecha/crowbar_act(mob/user, obj/item/I) - if(state != 2 && state != 3 && !(state == 4 && pilot_is_mmi())) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(state == 2) - state = 3 - to_chat(user, "You open the hatch to the power unit") - else if(state == 3) - state = 2 - to_chat(user, "You close the hatch to the power unit") - else - // Since having maint protocols available is controllable by the MMI, I see this as a consensual way to remove an MMI without destroying the mech - user.visible_message("[user] begins levering out the MMI from the [src].", "You begin to lever out the MMI from the [src].") - to_chat(occupant, "[user] is prying you out of the exosuit!") - if(I.use_tool(src, user, 80, volume = I.tool_volume) && pilot_is_mmi()) - user.visible_message("[user] pries the MMI out of the [src]!", "You finish removing the MMI from the [src]!") - go_out() - -/obj/mecha/screwdriver_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - if(!(state==3 && cell) && !(state==4 && cell)) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) - clearInternalDamage(MECHA_INT_TEMP_CONTROL) - to_chat(user, "You repair the damaged temperature controller.") - else if(state==3 && cell) - cell.forceMove(loc) - cell = null - state = 4 - to_chat(user, "You unscrew and pry out the powercell.") - log_message("Powercell removed") - else if(state==4 && cell) - state=3 - to_chat(user, "You screw the cell in place.") - -/obj/mecha/wrench_act(mob/user, obj/item/I) - if(state != 1 && state != 2) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(state==1) - state = 2 - to_chat(user, "You undo the securing bolts.") - else - state = 1 - to_chat(user, "You tighten the securing bolts.") - -/obj/mecha/welder_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if((obj_integrity >= max_integrity) && !internal_damage) - to_chat(user, "[src] is at full integrity!") - return - WELDER_ATTEMPT_REPAIR_MESSAGE - if(I.use_tool(src, user, 15, volume = I.tool_volume)) - if(internal_damage & MECHA_INT_TANK_BREACH) - clearInternalDamage(MECHA_INT_TANK_BREACH) - user.visible_message("[user] repairs the damaged gas tank.", "You repair the damaged gas tank.") - else if(obj_integrity < max_integrity) - user.visible_message("[user] repairs some damage to [name].", "You repair some damage to [name].") - obj_integrity += min(10, max_integrity - obj_integrity) - else - to_chat(user, "[src] is at full integrity!") - -/obj/mecha/mech_melee_attack(obj/mecha/M) - if(!has_charge(melee_energy_drain)) - return 0 - use_power(melee_energy_drain) - if(M.damtype == BRUTE || M.damtype == BURN) - add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") - . = ..() - -/obj/mecha/emag_act(mob/user) - to_chat(user, "[src]'s ID slot rejects the card.") - return - - -///////////////////////////////////// -//////////// AI piloting //////////// -///////////////////////////////////// - -/obj/mecha/attack_ai(mob/living/silicon/ai/user) - if(!isAI(user)) - return - //Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot. - if(user.can_dominate_mechs) - examine(user) //Get diagnostic information! - for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) - to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") - to_chat(user, "[B.get_mecha_info_text()]") - break - //Nothing like a big, red link to make the player feel powerful! - to_chat(user, "ASSUME DIRECT CONTROL?
    ") - else - examine(user) - if(occupant) - user << "This exosuit has a pilot and cannot be controlled." - return - var/can_control_mech = FALSE - for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers) - can_control_mech = TRUE - to_chat(user, "[bicon(src)] Status of [name]:\n\ - [A.get_mecha_info_text()]") - break - if(!can_control_mech) - to_chat(user, "You cannot control exosuits without AI control beacons installed.") - return - to_chat(user, "Take control of exosuit?
    ") - -/obj/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) - if(!..()) - return - - //Transfer from core or card to mech. Proc is called by mech. - switch(interaction) - if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. - if(!maint_access) //Mech must be in maint mode to allow carding. - to_chat(user, "[name] must have maintenance protocols active in order to allow a transfer.") - return - AI = occupant - if(!AI || !isAI(occupant)) //Mech does not have an AI for a pilot - to_chat(user, "No AI detected in the [name] onboard computer.") - return - if(AI.mind.special_role) //Malf AIs cannot leave mechs. Except through death. - to_chat(user, "ACCESS DENIED.") - return - AI.aiRestorePowerRoutine = 0//So the AI initially has power. - AI.control_disabled = 1 - AI.aiRadio.disabledAi = 1 - AI.loc = card - occupant = null - AI.controlled_mech = null - AI.remote_control = null - icon_state = initial(icon_state)+"-open" - to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") - - if(AI_MECH_HACK) //Called by AIs on the mech - AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) - if(AI.can_dominate_mechs) - if(occupant) //Oh, I am sorry, were you using that? - to_chat(AI, "Pilot detected! Forced ejection initiated!") - to_chat(occupant, "You have been forcibly ejected!") - go_out(1) //IT IS MINE, NOW. SUCK IT, RD! - ai_enter_mech(AI, interaction) - - if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. - AI = locate(/mob/living/silicon/ai) in card - if(!AI) - to_chat(user, "There is no AI currently installed on this device.") - return - else if(AI.stat || !AI.client) - to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") - return - else if(occupant || dna) //Normal AIs cannot steal mechs! - to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") - return - AI.control_disabled = 0 - AI.aiRadio.disabledAi = 0 - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") - ai_enter_mech(AI, interaction) - -//Hack and From Card interactions share some code, so leave that here for both to use. -/obj/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) - AI.aiRestorePowerRoutine = 0 - AI.loc = src - occupant = AI - icon_state = initial(icon_state) - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!hasInternalDamage()) - occupant << sound(nominalsound, volume = 50) - AI.cancel_camera() - AI.controlled_mech = src - AI.remote_control = src - AI.canmove = 1 //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. - AI.can_shunt = 0 //ONE AI ENTERS. NO AI LEAVES. - to_chat(AI, "[AI.can_dominate_mechs ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ - : "You have been uploaded to a mech's onboard computer."]") - to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") - if(interaction == AI_TRANS_FROM_CARD) - GrantActions(AI, FALSE) - else - GrantActions(AI, !AI.can_dominate_mechs) - -///////////////////////////////////// -//////// Atmospheric stuff //////// -///////////////////////////////////// - -/obj/mecha/proc/get_turf_air() - var/turf/T = get_turf(src) - if(T) - . = T.return_air() - -/obj/mecha/remove_air(amount) - if(use_internal_tank) - return cabin_air.remove(amount) - else - var/turf/T = get_turf(src) - if(T) - return T.remove_air(amount) - -/obj/mecha/return_air() - if(use_internal_tank) - return cabin_air - return get_turf_air() - -/obj/mecha/proc/return_pressure() - var/datum/gas_mixture/t_air = return_air() - if(t_air) - . = t_air.return_pressure() - -//skytodo: //No idea what you want me to do here, mate. -/obj/mecha/proc/return_temperature() - var/datum/gas_mixture/t_air = return_air() - if(t_air) - . = t_air.return_temperature() - -/obj/mecha/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !istype(new_port) || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - connected_port.parent.reconcile_air() - - if(occupant) - occupant.clear_alert("mechaport") - occupant.throw_alert("mechaport_d", /obj/screen/alert/mech_port_disconnect) - - log_message("Connected to gas port.") - return 1 - -/obj/mecha/proc/disconnect() - if(!connected_port) - return 0 - - connected_port.connected_device = null - connected_port = null - log_message("Disconnected from gas port.") - if(occupant) - occupant.clear_alert("mechaport_d") - return 1 - -/obj/mecha/portableConnectorReturnAir() - return internal_tank.return_air() - -/obj/mecha/proc/toggle_lights() - lights = !lights - if(lights) - set_light(light_range + lights_power) - else - set_light(light_range - lights_power) - occupant_message("Toggled lights [lights ? "on" : "off"].") - log_message("Toggled lights [lights ? "on" : "off"].") - -/obj/mecha/proc/toggle_internal_tank() - use_internal_tank = !use_internal_tank - occupant_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") - log_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") - -/obj/mecha/MouseDrop_T(mob/M, mob/user) - if(user.incapacitated()) - return - if(user != M) - return - log_message("[user] tries to move in.") - if(occupant) - to_chat(user, "The [src] is already occupied!") - log_append_to_last("Permission denied.") - return - var/passed - if(dna) - if(ishuman(user)) - if(user.dna.unique_enzymes == dna) - passed = 1 - else if(operation_allowed(user)) - passed = 1 - if(!passed) - to_chat(user, "Access denied.") - log_append_to_last("Permission denied.") - return - if(user.buckled) - to_chat(user, "You are currently buckled and cannot move.") - log_append_to_last("Permission denied.") - return - if(user.has_buckled_mobs()) //mob attached to us - to_chat(user, "You can't enter the exosuit with other creatures attached to you!") - return - - visible_message("[user] starts to climb into [src]") - - if(do_after(user, 40, target = src)) - if(obj_integrity <= 0) - to_chat(user, "You cannot get in the [name], it has been destroyed!") - else if(occupant) - to_chat(user, "[occupant] was faster! Try better next time, loser.") - else if(user.buckled) - to_chat(user, "You can't enter the exosuit while buckled.") - else if(user.has_buckled_mobs()) - to_chat(user, "You can't enter the exosuit with other creatures attached to you!") - else - moved_inside(user) - else - to_chat(user, "You stop entering the exosuit!") - -/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) - if(H && H.client && H in range(1)) - occupant = H - H.stop_pulling() - H.forceMove(src) - H.reset_perspective(src) - add_fingerprint(H) - GrantActions(H, human_occupant = 1) - forceMove(loc) - log_append_to_last("[H] moved in as pilot.") - icon_state = reset_icon() - dir = dir_in - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!activated) - occupant << sound(longactivationsound, volume = 50) - activated = TRUE - else if(!hasInternalDamage()) - occupant << sound(nominalsound, volume = 50) - if(state) - H.throw_alert("locked", /obj/screen/alert/mech_maintenance) - return 1 - else - return 0 - -/obj/mecha/proc/mmi_move_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected!") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level!") - return 0 - else if(occupant) - to_chat(user, "Occupant detected!") - return 0 - else if(dna && dna != mmi_as_oc.brainmob.dna.unique_enzymes) - to_chat(user, "Access denied. [name] is secured with a DNA lock.") - return 0 - - if(do_after(user, 40, target = src)) - if(!occupant) - return mmi_moved_inside(mmi_as_oc,user) - else - to_chat(user, "Occupant detected!") - else - to_chat(user, "You stop inserting the MMI.") - return 0 - -/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/mmi_as_oc,mob/user) - if(mmi_as_oc && user in range(1)) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected.") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level.") - return 0 - if(!user.unEquip(mmi_as_oc)) - to_chat(user, "\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]") - return 0 - var/mob/brainmob = mmi_as_oc.brainmob - brainmob.reset_perspective(src) - occupant = brainmob - brainmob.forceMove(src) //should allow relaymove - brainmob.canmove = 1 - if(istype(mmi_as_oc, /obj/item/mmi/robotic_brain)) - var/obj/item/mmi/robotic_brain/R = mmi_as_oc - if(R.imprinted_master) - to_chat(brainmob, "Your imprint to [R.imprinted_master] has been temporarily disabled. You should help the crew and not commit harm.") - mmi_as_oc.loc = src - mmi_as_oc.mecha = src - Entered(mmi_as_oc) - Move(loc) - icon_state = reset_icon() - dir = dir_in - log_message("[mmi_as_oc] moved in as pilot.") - if(!hasInternalDamage()) - to_chat(occupant, sound(nominalsound, volume=50)) - GrantActions(brainmob) - return 1 - else - return 0 - -/obj/mecha/proc/pilot_is_mmi() - var/atom/movable/mob_container - if(istype(occupant, /mob/living/carbon/brain)) - var/mob/living/carbon/brain/brain = occupant - mob_container = brain.container - if(istype(mob_container, /obj/item/mmi)) - return 1 - return 0 - -/obj/mecha/proc/pilot_mmi_hud(var/mob/living/carbon/brain/pilot) - return - -/obj/mecha/Exited(atom/movable/M, atom/newloc) - if(occupant && occupant == M) // The occupant exited the mech without calling go_out() - go_out(1, newloc) - -/obj/mecha/proc/go_out(forced, atom/newloc = loc) - if(!occupant) - return - var/atom/movable/mob_container - occupant.clear_alert("charge") - occupant.clear_alert("locked") - occupant.clear_alert("mech damage") - occupant.clear_alert("mechaport") - occupant.clear_alert("mechaport_d") - if(ishuman(occupant)) - mob_container = occupant - RemoveActions(occupant, human_occupant = 1) - else if(isbrain(occupant)) - var/mob/living/carbon/brain/brain = occupant - RemoveActions(brain) - mob_container = brain.container - else if(isAI(occupant)) - var/mob/living/silicon/ai/AI = occupant - if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. - RemoveActions(occupant) - occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. - occupant = null - return - else - if(!AI.linked_core || QDELETED(AI.linked_core)) - to_chat(AI, "Inactive core destroyed. Unable to return.") - AI.linked_core = null - return - to_chat(AI, "Returning to core...") - AI.controlled_mech = null - AI.remote_control = null - RemoveActions(occupant, 1) - mob_container = AI - newloc = get_turf(AI.linked_core) - qdel(AI.linked_core) - else - return - var/mob/living/L = occupant - occupant = null //we need it null when forceMove calls Exited(). - if(mob_container.forceMove(newloc))//ejecting mob container - log_message("[mob_container] moved out.") - L << browse(null, "window=exosuit") - - if(istype(mob_container, /obj/item/mmi)) - var/obj/item/mmi/mmi = mob_container - if(mmi.brainmob) - L.loc = mmi - L.reset_perspective() - mmi.mecha = null - mmi.update_icon() - L.canmove = 0 - if(istype(mmi, /obj/item/mmi/robotic_brain)) - var/obj/item/mmi/robotic_brain/R = mmi - if(R.imprinted_master) - to_chat(L, "Imprint re-enabled, you are once again bound to [R.imprinted_master]'s commands.") - icon_state = initial(icon_state)+"-open" - dir = dir_in - - if(L && L.client) - L.client.RemoveViewMod("mecha") - zoom_mode = FALSE - -///////////////////////// -////// Access stuff ///// -///////////////////////// - -/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) - if(!ishuman(H)) - return 0 - for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(check_access(ID, operation_req_access)) - return 1 - return 0 - - -/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) - for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(check_access(ID, internals_req_access)) - return 1 - return 0 - - -/obj/mecha/check_access(obj/item/card/id/I, list/access_list) - if(!istype(access_list)) - return 1 - if(!access_list.len) //no requirements - return 1 - if(istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if(!istype(I) || !I.access) //not ID or no access - return 0 - if(access_list==operation_req_access) - for(var/req in access_list) - if(!(req in I.access)) //doesn't have this access - return 0 - else if(access_list==internals_req_access) - for(var/req in access_list) - if(req in I.access) - return 1 - return 1 - -/////////////////////// -///// Power stuff ///// -/////////////////////// - -/obj/mecha/proc/has_charge(amount) - return (get_charge()>=amount) - -/obj/mecha/proc/get_charge() - for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) - var/relay_charge = R.get_charge() - if(relay_charge) - return relay_charge - if(cell) - return max(0, cell.charge) - -/obj/mecha/proc/use_power(amount) - if(get_charge()) - cell.use(amount) - if(occupant) - update_cell() - return 1 - return 0 - -/obj/mecha/proc/give_power(amount) - if(!isnull(get_charge())) - cell.give(amount) - if(occupant) - update_cell() - return 1 - return 0 - -/obj/mecha/proc/update_cell() - if(cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - occupant.clear_alert("charge") - if(0.5 to 0.75) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 1) - if(0.25 to 0.5) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 2) - if(power_warned) - power_warned = FALSE - if(0.01 to 0.25) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 3) - if(!power_warned) - occupant << sound(lowpowersound, volume = 50) - power_warned = TRUE - else - occupant.throw_alert("charge", /obj/screen/alert/mech_emptycell) - else - occupant.throw_alert("charge", /obj/screen/alert/mech_nocell) - -/obj/mecha/proc/reset_icon() - if(initial_icon) - icon_state = initial_icon - else - icon_state = initial(icon_state) - return icon_state - -////////////////////////////////////////// -//////// Mecha global iterators //////// -////////////////////////////////////////// - -/obj/mecha/process() - process_internal_damage() - regulate_temp() - give_air() - update_huds() - -/obj/mecha/proc/process_internal_damage() - if(!internal_damage) - return - - if(internal_damage & MECHA_INT_FIRE) - if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5)) - clearInternalDamage(MECHA_INT_FIRE) - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH)) - setInternalDamage(MECHA_INT_TANK_BREACH) - - if(int_tank_air && int_tank_air.return_volume() > 0) - int_tank_air.temperature = min(6000 + T0C, cabin_air.return_temperature() + rand(10, 15)) - - if(cabin_air && cabin_air.return_volume()>0) - cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15)) - if(cabin_air.return_temperature() > max_temperature/2) - take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0) - - if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) - if(loc) - loc.assume_air(leaked_gas) - air_update_turf() - else - qdel(leaked_gas) - - if(internal_damage & MECHA_INT_SHORT_CIRCUIT) - if(get_charge()) - spark_system.start() - cell.charge -= min(20,cell.charge) - cell.maxcharge -= min(20,cell.maxcharge) - -/obj/mecha/proc/regulate_temp() - if(internal_damage & MECHA_INT_TEMP_CONTROL) - return - - if(cabin_air && cabin_air.return_volume() > 0) - var/delta = cabin_air.temperature - T20C - cabin_air.temperature -= max(-10, min(10, round(delta / 4, 0.1))) - -/obj/mecha/proc/give_air() - if(!internal_tank) - return - - var/datum/gas_mixture/tank_air = internal_tank.return_air() - - var/release_pressure = internal_tank_valve - var/cabin_pressure = cabin_air.return_pressure() - var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) - var/transfer_moles = 0 - if(pressure_delta > 0) //cabin pressure lower than release pressure - if(tank_air.return_temperature() > 0) - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) - cabin_air.merge(removed) - else if(pressure_delta < 0) //cabin pressure higher than release pressure - var/datum/gas_mixture/t_air = return_air() - pressure_delta = cabin_pressure - release_pressure - if(t_air) - pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) - if(pressure_delta > 0) //if location pressure is lower than cabin pressure - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) - if(t_air) - t_air.merge(removed) - else //just delete the cabin gas, we're in space or some shit - qdel(removed) - -/obj/mecha/proc/update_huds() - diag_hud_set_mechhealth() - diag_hud_set_mechcell() - diag_hud_set_mechstat() - diag_hud_set_mechtracking() - - -/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30) - -/obj/mecha/update_remote_sight(mob/living/user) - if(occupant_sight_flags) - if(user == occupant) - user.sight |= occupant_sight_flags - - ..() - -/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) - if(!no_effect) - if(selected) - used_item = selected - else if(!visual_effect_icon) - visual_effect_icon = ATTACK_EFFECT_SMASH - if(damtype == BURN) - visual_effect_icon = ATTACK_EFFECT_MECHFIRE - else if(damtype == TOX) - visual_effect_icon = ATTACK_EFFECT_MECHTOXIN - ..() - -/obj/mecha/obj_destruction() - if(wreckage) - var/mob/living/silicon/ai/AI - if(isAI(occupant)) - AI = occupant - occupant = null - var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - if(E.salvageable && prob(30)) - WR.crowbar_salvage += E - E.detach(WR) //detaches from src into WR - E.equip_ready = 1 - else - E.detach(loc) - qdel(E) - if(cell) - WR.crowbar_salvage += cell - cell.forceMove(WR) - cell.charge = rand(0, cell.charge) - cell = null - if(internal_tank) - WR.crowbar_salvage += internal_tank - internal_tank.forceMove(WR) - cell = null - . = ..() - -/obj/mecha/CtrlClick(mob/living/L) - if(occupant != L || !istype(L)) - return ..() - - var/list/choices = list("Cancel / No Change" = mutable_appearance(icon = 'icons/mob/screen_gen.dmi', icon_state = "x")) - var/list/choices_to_refs = list() - - for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) - if(!MT.selectable || selected == MT) - continue - var/mutable_appearance/clean/MA = new(MT) - choices[MT.name] = MA - choices_to_refs[MT.name] = MT - - var/choice = show_radial_menu(L, src, choices, radius = 48, custom_check = CALLBACK(src, .proc/check_menu, L)) - if(!check_menu(L) || choice == "Cancel / No Change") - return - - var/obj/item/mecha_parts/mecha_equipment/new_sel = LAZYACCESS(choices_to_refs, choice) - if(istype(new_sel)) - selected = new_sel - occupant_message("You switch to [selected].") - visible_message("[src] raises [selected]") - send_byjax(occupant, "exosuit.browser", "eq_list", get_equipment_list()) - -/obj/mecha/proc/check_menu(mob/living/L) - if(L != occupant || !istype(L)) - return FALSE - if(L.incapacitated()) - return FALSE - return TRUE \ No newline at end of file +/obj/mecha + name = "Mecha" + desc = "Exosuit" + icon = 'icons/mecha/mecha.dmi' + density = 1 //Dense. To raise the heat. + opacity = 1 ///opaque. Menacing. + anchored = 1 //no pulling around. + resistance_flags = FIRE_PROOF | ACID_PROOF + layer = MOB_LAYER //icon draw layer + infra_luminosity = 15 //byond implementation is bugged. + force = 5 + max_integrity = 300 //max_integrity is base health + armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100) + var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) + var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. + var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) + var/can_move = 0 // time of next allowed movement + var/mob/living/carbon/occupant = null + var/step_in = 10 //make a step in step_in/10 sec. + var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. + var/normal_step_energy_drain = 10 + var/step_energy_drain = 10 + var/melee_energy_drain = 15 + var/overload_step_energy_drain_min = 100 + var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. + var/obj/item/stock_parts/cell/cell + var/state = 0 + var/list/log = new + var/last_message = 0 + var/add_req_access = 1 + var/maint_access = 1 + var/dna //dna-locking the mech + var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference + var/datum/effect_system/spark_spread/spark_system = new + var/lights = 0 + var/lights_power = 6 + var/emagged = FALSE + var/frozen = FALSE + + //inner atmos + var/use_internal_tank = 0 + var/internal_tank_valve = ONE_ATMOSPHERE + var/obj/machinery/portable_atmospherics/canister/internal_tank + var/datum/gas_mixture/cabin_air + var/obj/machinery/atmospherics/unary/portables_connector/connected_port = null + + var/obj/item/radio/radio = null + var/list/trackers = list() + + var/max_temperature = 25000 + var/internal_damage_threshold = 50 //health percentage below which internal damage is possible + var/internal_damage = 0 //contains bitflags + + var/list/operation_req_access = list()//required access level for mecha operation + var/list/internals_req_access = list(ACCESS_ENGINE,ACCESS_ROBOTICS)//required access level to open cell compartment + + var/wreckage + + var/list/equipment = new + var/obj/item/mecha_parts/mecha_equipment/selected + var/max_equip = 3 + var/datum/events/events + var/turf/crashing = null + var/occupant_sight_flags = 0 + + var/stepsound = 'sound/mecha/mechstep.ogg' + var/turnsound = 'sound/mecha/mechturn.ogg' + var/nominalsound = 'sound/mecha/nominal.ogg' + var/zoomsound = 'sound/mecha/imag_enh.ogg' + var/critdestrsound = 'sound/mecha/critdestr.ogg' + var/weapdestrsound = 'sound/mecha/weapdestr.ogg' + var/lowpowersound = 'sound/mecha/lowpower.ogg' + var/longactivationsound = 'sound/mecha/nominal.ogg' + var/starting_voice = /obj/item/mecha_modkit/voice + var/activated = FALSE + var/power_warned = FALSE + + var/destruction_sleep_duration = 1 //Time that mech pilot is put to sleep for if mech is destroyed + + var/melee_cooldown = 10 + var/melee_can_hit = 1 + + // Action vars + var/defence_mode = FALSE + var/defence_mode_deflect_chance = 35 + var/leg_overload_mode = FALSE + var/leg_overload_coeff = 100 + var/thrusters_active = FALSE + var/smoke = 5 + var/smoke_ready = 1 + var/smoke_cooldown = 100 + var/zoom_mode = FALSE + var/phasing = FALSE + var/phasing_energy_drain = 200 + var/phase_state = "" //icon_state when phasing + + hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) + +/obj/mecha/Initialize() + . = ..() + events = new + icon_state += "-open" + add_radio() + add_cabin() + add_airtank() + spark_system.set_up(2, 0, src) + spark_system.attach(src) + smoke_system.set_up(3, src) + smoke_system.attach(src) + add_cell() + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + log_message("[src] created.") + GLOB.mechas_list += src //global mech list + prepare_huds() + for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) + diag_hud.add_to_hud(src) + diag_hud_set_mechhealth() + diag_hud_set_mechcell() + diag_hud_set_mechstat() + diag_hud_set_mechtracking() + + var/obj/item/mecha_modkit/voice/V = new starting_voice(src) + V.install(src) + qdel(V) + +//////////////////////// +////// Helpers ///////// +//////////////////////// + +/obj/mecha/get_cell() + return cell + +/obj/mecha/proc/add_airtank() + internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) + return internal_tank + +/obj/mecha/proc/add_cell(var/obj/item/stock_parts/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new/obj/item/stock_parts/cell/high/plus(src) + +/obj/mecha/proc/add_cabin() + cabin_air = new + cabin_air.temperature = T20C + cabin_air.volume = 200 + cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + return cabin_air + +/obj/mecha/proc/add_radio() + radio = new(src) + radio.name = "[src] radio" + radio.icon = icon + radio.icon_state = icon_state + radio.subspace_transmission = 1 + +/obj/mecha/examine(mob/user) + . = ..() + var/integrity = obj_integrity * 100 / max_integrity + switch(integrity) + if(85 to 100) + . += "It's fully intact." + if(65 to 85) + . += "It's slightly damaged." + if(45 to 65) + . += "It's badly damaged." + if(25 to 45) + . += "It's heavily damaged." + else + . += "It's falling apart." + if(equipment && equipment.len) + . += "It's equipped with:" + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + . += "[bicon(ME)] [ME]" + +/obj/mecha/hear_talk(mob/M, list/message_pieces) + if(M == occupant && radio.broadcasting) + radio.talk_into(M, message_pieces) + +/obj/mecha/proc/click_action(atom/target, mob/user, params) + if(!occupant || occupant != user ) + return + if(user.incapacitated()) + return + if(phasing) + occupant_message("Unable to interact with objects while phasing.") + return + if(state) + occupant_message("Maintenance protocols in effect.") + return + if(!get_charge()) + return + if(src == target) + return + + var/dir_to_target = get_dir(src, target) + if(dir_to_target && !(dir_to_target & dir))//wrong direction + return + + if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) + target = safepick(view(3,target)) + if(!target) + return + + var/mob/living/L = user + if(!target.Adjacent(src)) + if(selected && selected.is_ranged()) + if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful) + to_chat(L, "You don't want to harm other living beings!") + return + selected.action(target, params) + else if(selected && selected.is_melee()) + if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM)) + to_chat(user, "You don't want to harm other living beings!") + return + selected.action(target, params) + else + if(internal_damage & MECHA_INT_CONTROL_LOST) + target = safepick(oview(1, src)) + if(!melee_can_hit || !isatom(target)) + return + target.mech_melee_attack(src) + melee_can_hit = 0 + spawn(melee_cooldown) + melee_can_hit = 1 + +/obj/mecha/proc/mech_toxin_damage(mob/living/target) + playsound(src, 'sound/effects/spray2.ogg', 50, 1) + if(target.reagents) + if(target.reagents.get_reagent_amount("atropine") + force < force*2) + target.reagents.add_reagent("atropine", force/2) + if(target.reagents.get_reagent_amount("toxin") + force < force*2) + target.reagents.add_reagent("toxin", force/2.5) + +/obj/mecha/proc/range_action(atom/target) + return + + +////////////////////////////////// +//////// Movement procs //////// +////////////////////////////////// + +/obj/mecha/Move(atom/newLoc, direct) + . = ..() + if(.) + events.fireEvent("onMove",get_turf(src)) + +/obj/mecha/Process_Spacemove(var/movement_dir = 0) + . = ..() + if(.) + return 1 + if(thrusters_active && movement_dir && use_power(step_energy_drain)) + return 1 + + var/atom/movable/backup = get_spacemove_backup() + if(backup) + if(istype(backup) && movement_dir && !backup.anchored) + if(backup.newtonian_move(turn(movement_dir, 180))) + if(occupant) + to_chat(occupant, "You push off of [backup] to propel yourself.") + return 1 + +/obj/mecha/relaymove(mob/user, direction) + if(!direction || frozen) + return + if(user != occupant) //While not "realistic", this piece is player friendly. + user.forceMove(get_turf(src)) + to_chat(user, "You climb out from [src].") + return 0 + if(connected_port) + if(world.time - last_message > 20) + occupant_message("Unable to move while connected to the air system port!") + last_message = world.time + return 0 + if(state) + occupant_message("Maintenance protocols in effect.") + return + return domove(direction) + +/obj/mecha/proc/domove(direction) + if(can_move >= world.time) + return 0 + if(!Process_Spacemove(direction)) + return 0 + if(!has_charge(step_energy_drain)) + return 0 + if(defence_mode) + if(world.time - last_message > 20) + occupant_message("Unable to move while in defence mode.") + last_message = world.time + return 0 + if(zoom_mode) + if(world.time - last_message > 20) + occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 + + var/move_result = 0 + var/move_type = 0 + if(internal_damage & MECHA_INT_CONTROL_LOST) + move_result = mechsteprand() + move_type = MECHAMOVE_RAND + else if(dir != direction) + move_result = mechturn(direction) + move_type = MECHAMOVE_TURN + else + move_result = mechstep(direction) + move_type = MECHAMOVE_STEP + + if(move_result && move_type) + aftermove(move_type) + can_move = world.time + step_in + return TRUE + return FALSE + +/obj/mecha/proc/aftermove(move_type) + use_power(step_energy_drain) + if(move_type & (MECHAMOVE_RAND | MECHAMOVE_STEP) && occupant) + var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector) in loc + if(possible_port) + var/obj/screen/alert/mech_port_available/A = occupant.throw_alert("mechaport", /obj/screen/alert/mech_port_available, override = TRUE) + if(A) + A.target = possible_port + else + occupant.clear_alert("mechaport") + if(leg_overload_mode) + if(obj_integrity < max_integrity - max_integrity / 3) + leg_overload_mode = FALSE + step_in = initial(step_in) + step_energy_drain = initial(step_energy_drain) + occupant_message("Leg actuators damage threshold exceded. Disabling overload.") + +/obj/mecha/proc/mechturn(direction) + dir = direction + if(turnsound) + playsound(src,turnsound,40,1) + return 1 + +/obj/mecha/proc/mechstep(direction) + . = step(src, direction) + if(!.) + if(phasing && get_charge() >= phasing_energy_drain) + if(can_move < world.time) + . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself + flick("phazon-phase", src) + forceMove(get_step(src, direction)) + use_power(phasing_energy_drain) + playsound(src, stepsound, 40, 1) + can_move = world.time + (step_in * 3) + else if(stepsound) + playsound(src, stepsound, 40, 1) + +/obj/mecha/proc/mechsteprand() + . = step_rand(src) + if(. && stepsound) + playsound(src, stepsound, 40, 1) + +/obj/mecha/Bump(var/atom/obstacle, bump_allowed) + if(throwing) //high velocity mechas in your face! + var/breakthrough = 0 + if(istype(obstacle, /obj/structure/window)) + qdel(obstacle) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/grille/)) + var/obj/structure/grille/G = obstacle + G.obj_break() + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/table)) + var/obj/structure/table/T = obstacle + qdel(T) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/rack)) + new /obj/item/rack_parts(obstacle.loc) + qdel(obstacle) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank)) + obstacle.ex_act(1) + + else if(isliving(obstacle)) + var/mob/living/L = obstacle + var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') + if(L.flags & GODMODE) + return + L.take_overall_damage(5,0) + if(L.buckled) + L.buckled = 0 + L.Stun(5) + L.Weaken(5) + L.apply_effect(STUTTER, 5) + playsound(src, pick(hit_sound), 50, 0, 0) + breakthrough = 1 + + else + if(throwing) + throwing.finalize(FALSE) + crashing = null + + ..() + + if(breakthrough) + if(crashing) + spawn(1) + throw_at(crashing, 50, throw_speed) + else + spawn(1) + crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. + throw_at(crashing, 50, throw_speed) + + else + if(bump_allowed) + if(..()) + return + if(isobj(obstacle)) + var/obj/O = obstacle + if(istype(O, /obj/effect/portal)) //derpfix + anchored = 0 + O.Bumped(src) + spawn(0) //countering portal teleport spawn(0), hurr + anchored = 1 + else if(!O.anchored) + step(obstacle, dir) + else if(ismob(obstacle)) + step(obstacle, dir) + + +/////////////////////////////////// +//////// Internal damage //////// +/////////////////////////////////// + +/obj/mecha/proc/check_for_internal_damage(list/possible_int_damage, ignore_threshold=null) + if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) + return + if(prob(20)) + if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) + for(var/T in possible_int_damage) + if(internal_damage & T) + possible_int_damage -= T + var/int_dam_flag = safepick(possible_int_damage) + if(int_dam_flag) + setInternalDamage(int_dam_flag) + if(prob(5)) + if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) + var/obj/item/mecha_parts/mecha_equipment/ME = safepick(equipment) + if(ME) + qdel(ME) + +/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) + return int_dam_flag ? internal_damage&int_dam_flag : internal_damage + + +/obj/mecha/proc/setInternalDamage(int_dam_flag) + internal_damage |= int_dam_flag + log_append_to_last("Internal damage of type [int_dam_flag].",1) + occupant << sound('sound/machines/warning-buzzer.ogg',wait=0) + diag_hud_set_mechstat() + +/obj/mecha/proc/clearInternalDamage(int_dam_flag) + internal_damage &= ~int_dam_flag + switch(int_dam_flag) + if(MECHA_INT_TEMP_CONTROL) + occupant_message("Life support system reactivated.") + if(MECHA_INT_FIRE) + occupant_message("Internal fire extinquished.") + if(MECHA_INT_TANK_BREACH) + occupant_message("Damaged internal tank has been sealed.") + diag_hud_set_mechstat() + + +//////////////////////////////////////// +//////// Health related procs //////// +//////////////////////////////////////// + +/obj/mecha/proc/get_armour_facing(relative_dir) + switch(relative_dir) + if(0) // BACKSTAB! + return facing_modifiers[MECHA_BACK_ARMOUR] + if(45, 90, 270, 315) + return facing_modifiers[MECHA_SIDE_ARMOUR] + if(225, 180, 135) + return facing_modifiers[MECHA_FRONT_ARMOUR] + return 1 //always return non-0 + +/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + spark_system.start() + switch(damage_flag) + if("fire") + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL)) + if("melee") + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + else + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) + if(. >= 5 || prob(33)) + occupant_message("Taking damage!") + log_message("Took [damage_amount] points of damage. Damage type: [damage_type]") + +/obj/mecha/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + . = ..() + if(!damage_amount) + return 0 + var/booster_deflection_modifier = 1 + var/booster_damage_modifier = 1 + if(damage_flag == "bullet" || damage_flag == "laser" || damage_flag == "energy") + for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) + if(B.projectile_react()) + booster_deflection_modifier = B.deflect_coeff + booster_damage_modifier = B.damage_coeff + break + else if(damage_flag == "melee") + for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) + if(B.attack_react()) + booster_deflection_modifier *= B.deflect_coeff + booster_damage_modifier *= B.damage_coeff + break + + if(attack_dir) + var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src)) + booster_damage_modifier /= facing_modifier + booster_deflection_modifier *= facing_modifier + if(prob(deflect_chance * booster_deflection_modifier)) + visible_message("[src]'s armour deflects the attack!") + log_message("Armor saved.") + return 0 + if(.) + . *= booster_damage_modifier + +/obj/mecha/attack_hand(mob/living/user) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) + playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1) + user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") + log_message("Attack by hand/paw. Attacker - [user].") + + +/obj/mecha/attack_alien(mob/living/user) + log_message("Attack by alien. Attacker - [user].", TRUE) + playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) + attack_generic(user, 15, BRUTE, "melee", 0) + +/obj/mecha/attack_animal(mob/living/simple_animal/user) + log_message("Attack by simple animal. Attacker - [user].") + if(!user.melee_damage_upper && !user.obj_damage) + user.custom_emote(1, "[user.friendly] [src].") + return FALSE + else + var/play_soundeffect = 1 + if(user.environment_smash) + play_soundeffect = 0 + playsound(src, 'sound/effects/bang.ogg', 50, TRUE) + var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper) + if(user.obj_damage) + animal_damage = user.obj_damage + animal_damage = min(animal_damage, 20*user.environment_smash) + user.create_attack_log("attacked [name]") + add_attack_logs(user, src, "Attacked") + attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) + return TRUE + +/obj/mecha/hulk_damage() + return 15 + +/obj/mecha/attack_hulk(mob/living/carbon/human/user) + . = ..() + if(.) + log_message("Attack by hulk. Attacker - [user].", 1) + add_attack_logs(user, src, "Punched with hulk powers") + +/obj/mecha/blob_act(obj/structure/blob/B) + log_message("Attack by blob. Attacker - [B].") + take_damage(30, BRUTE, "melee", 0, get_dir(src, B)) + +/obj/mecha/attack_tk() + return + +/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper + log_message("Hit by [AM].") + . = ..() + +/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper + log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).") + ..() + +/obj/mecha/ex_act(severity, target) + log_message("Affected by explosion of severity: [severity].") + if(prob(deflect_chance)) + severity++ + log_message("Armor saved, changing severity to [severity]") + ..() + severity++ + for(var/X in equipment) + var/obj/item/mecha_parts/mecha_equipment/ME = X + ME.ex_act(severity) + for(var/Y in trackers) + var/obj/item/mecha_parts/mecha_tracking/MT = Y + MT.ex_act(severity) + if(occupant) + occupant.ex_act(severity) + +/obj/mecha/handle_atom_del(atom/A) + if(A == occupant) + occupant = null + icon_state = initial(icon_state)+"-open" + setDir(dir_in) + +/obj/mecha/Destroy() + if(occupant) + occupant.SetSleeping(destruction_sleep_duration) + go_out() + var/mob/living/silicon/ai/AI + for(var/mob/M in src) //Let's just be ultra sure + if(isAI(M)) + occupant = null + AI = M //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. They can be recovered with an AI card from the wreck. + else + M.forceMove(loc) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + E.detach(loc) + qdel(E) + equipment.Cut() + QDEL_NULL(cell) + QDEL_NULL(internal_tank) + if(AI) + AI.gib() //No wreck, no AI to recover + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + if(loc) + loc.assume_air(cabin_air) + air_update_turf() + else + qdel(cabin_air) + cabin_air = null + QDEL_NULL(spark_system) + QDEL_NULL(smoke_system) + + GLOB.mechas_list -= src //global mech list + return ..() + +//TODO +/obj/mecha/emp_act(severity) + if(get_charge()) + use_power((cell.charge/3)/(severity*2)) + take_damage(30 / severity, BURN, "energy", 1) + log_message("EMP detected", 1) + check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL, MECHA_INT_CONTROL_LOST, MECHA_INT_SHORT_CIRCUIT), 1) + +/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > max_temperature) + log_message("Exposed to dangerous temperature.", 1) + take_damage(5, BURN, 0, 1) + check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) + +////////////////////// +////// AttackBy ////// +////////////////////// + +/obj/mecha/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/mmi)) + if(mmi_move_inside(W,user)) + to_chat(user, "[src]-MMI interface initialized successfuly") + else + to_chat(user, "[src]-MMI interface initialization failed.") + return + + if(istype(W, /obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = W + spawn() + if(E.can_attach(src)) + if(!user.drop_item()) + return + E.attach(src) + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + else + to_chat(user, "You were unable to attach [W] to [src]!") + return + + if(W.GetID()) + if(add_req_access || maint_access) + if(internals_access_allowed(usr)) + var/obj/item/card/id/id_card + if(istype(W, /obj/item/card/id)) + id_card = W + else + var/obj/item/pda/pda = W + id_card = pda.id + output_maintenance_dialog(id_card, user) + return + else + to_chat(user, "Invalid ID: Access denied.") + else + to_chat(user, "Maintenance protocols disabled by operator.") + + else if(istype(W, /obj/item/stack/cable_coil)) + if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) + var/obj/item/stack/cable_coil/CC = W + if(CC.amount > 1) + CC.use(2) + clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) + to_chat(user, "You replace the fused wires.") + else + to_chat(user, "There's not enough wire to finish the task.") + return + + else if(istype(W, /obj/item/stock_parts/cell)) + if(state==4) + if(!cell) + if(!user.drop_item()) + return + to_chat(user, "You install the powercell.") + W.forceMove(src) + cell = W + log_message("Powercell installed") + else + to_chat(user, "There's already a powercell installed.") + return + + else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) + if(!user.unEquip(W)) + to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") + return + W.forceMove(src) + trackers += W + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + diag_hud_set_mechtracking() + return + + else if(istype(W, /obj/item/paintkit)) + if(occupant) + to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") + return + + var/obj/item/paintkit/P = W + var/found = null + + for(var/type in P.allowed_types) + if(type == initial_icon) + found = 1 + break + + if(!found) + to_chat(user, "That kit isn't meant for use on this class of exosuit.") + return + + user.visible_message("[user] opens [P] and spends some quality time customising [src].") + + name = P.new_name + desc = P.new_desc + initial_icon = P.new_icon + reset_icon() + + user.drop_item() + qdel(P) + + else if(istype(W, /obj/item/mecha_modkit)) + if(occupant) + to_chat(user, "You can't access the mech's modification port while it is occupied.") + return + var/obj/item/mecha_modkit/M = W + if(do_after_once(user, M.install_time, target = src)) + M.install(src, user) + else + to_chat(user, "You stop installing [M].") + + else + return ..() + + +/obj/mecha/crowbar_act(mob/user, obj/item/I) + if(state != 2 && state != 3 && !(state == 4 && pilot_is_mmi())) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(state == 2) + state = 3 + to_chat(user, "You open the hatch to the power unit") + else if(state == 3) + state = 2 + to_chat(user, "You close the hatch to the power unit") + else + // Since having maint protocols available is controllable by the MMI, I see this as a consensual way to remove an MMI without destroying the mech + user.visible_message("[user] begins levering out the MMI from the [src].", "You begin to lever out the MMI from the [src].") + to_chat(occupant, "[user] is prying you out of the exosuit!") + if(I.use_tool(src, user, 80, volume = I.tool_volume) && pilot_is_mmi()) + user.visible_message("[user] pries the MMI out of the [src]!", "You finish removing the MMI from the [src]!") + go_out() + +/obj/mecha/screwdriver_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + if(!(state==3 && cell) && !(state==4 && cell)) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) + clearInternalDamage(MECHA_INT_TEMP_CONTROL) + to_chat(user, "You repair the damaged temperature controller.") + else if(state==3 && cell) + cell.forceMove(loc) + cell = null + state = 4 + to_chat(user, "You unscrew and pry out the powercell.") + log_message("Powercell removed") + else if(state==4 && cell) + state=3 + to_chat(user, "You screw the cell in place.") + +/obj/mecha/wrench_act(mob/user, obj/item/I) + if(state != 1 && state != 2) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(state==1) + state = 2 + to_chat(user, "You undo the securing bolts.") + else + state = 1 + to_chat(user, "You tighten the securing bolts.") + +/obj/mecha/welder_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if((obj_integrity >= max_integrity) && !internal_damage) + to_chat(user, "[src] is at full integrity!") + return + WELDER_ATTEMPT_REPAIR_MESSAGE + if(I.use_tool(src, user, 15, volume = I.tool_volume)) + if(internal_damage & MECHA_INT_TANK_BREACH) + clearInternalDamage(MECHA_INT_TANK_BREACH) + user.visible_message("[user] repairs the damaged gas tank.", "You repair the damaged gas tank.") + else if(obj_integrity < max_integrity) + user.visible_message("[user] repairs some damage to [name].", "You repair some damage to [name].") + obj_integrity += min(10, max_integrity - obj_integrity) + else + to_chat(user, "[src] is at full integrity!") + +/obj/mecha/mech_melee_attack(obj/mecha/M) + if(!has_charge(melee_energy_drain)) + return 0 + use_power(melee_energy_drain) + if(M.damtype == BRUTE || M.damtype == BURN) + add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + . = ..() + +/obj/mecha/emag_act(mob/user) + to_chat(user, "[src]'s ID slot rejects the card.") + return + + +///////////////////////////////////// +//////////// AI piloting //////////// +///////////////////////////////////// + +/obj/mecha/attack_ai(mob/living/silicon/ai/user) + if(!isAI(user)) + return + //Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot. + if(user.can_dominate_mechs) + examine(user) //Get diagnostic information! + for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) + to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") + to_chat(user, "[B.get_mecha_info_text()]") + break + //Nothing like a big, red link to make the player feel powerful! + to_chat(user, "ASSUME DIRECT CONTROL?
    ") + else + examine(user) + if(occupant) + user << "This exosuit has a pilot and cannot be controlled." + return + var/can_control_mech = FALSE + for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers) + can_control_mech = TRUE + to_chat(user, "[bicon(src)] Status of [name]:\n\ + [A.get_mecha_info_text()]") + break + if(!can_control_mech) + to_chat(user, "You cannot control exosuits without AI control beacons installed.") + return + to_chat(user, "Take control of exosuit?
    ") + +/obj/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) + if(!..()) + return + + //Transfer from core or card to mech. Proc is called by mech. + switch(interaction) + if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. + if(!maint_access) //Mech must be in maint mode to allow carding. + to_chat(user, "[name] must have maintenance protocols active in order to allow a transfer.") + return + AI = occupant + if(!AI || !isAI(occupant)) //Mech does not have an AI for a pilot + to_chat(user, "No AI detected in the [name] onboard computer.") + return + if(AI.mind.special_role) //Malf AIs cannot leave mechs. Except through death. + to_chat(user, "ACCESS DENIED.") + return + AI.aiRestorePowerRoutine = 0//So the AI initially has power. + AI.control_disabled = 1 + AI.aiRadio.disabledAi = 1 + AI.loc = card + occupant = null + AI.controlled_mech = null + AI.remote_control = null + icon_state = initial(icon_state)+"-open" + to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") + + if(AI_MECH_HACK) //Called by AIs on the mech + AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) + if(AI.can_dominate_mechs) + if(occupant) //Oh, I am sorry, were you using that? + to_chat(AI, "Pilot detected! Forced ejection initiated!") + to_chat(occupant, "You have been forcibly ejected!") + go_out(1) //IT IS MINE, NOW. SUCK IT, RD! + ai_enter_mech(AI, interaction) + + if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. + AI = locate(/mob/living/silicon/ai) in card + if(!AI) + to_chat(user, "There is no AI currently installed on this device.") + return + else if(AI.stat || !AI.client) + to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") + return + else if(occupant || dna) //Normal AIs cannot steal mechs! + to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") + return + AI.control_disabled = 0 + AI.aiRadio.disabledAi = 0 + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") + ai_enter_mech(AI, interaction) + +//Hack and From Card interactions share some code, so leave that here for both to use. +/obj/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) + AI.aiRestorePowerRoutine = 0 + AI.loc = src + occupant = AI + icon_state = initial(icon_state) + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + if(!hasInternalDamage()) + occupant << sound(nominalsound, volume = 50) + AI.cancel_camera() + AI.controlled_mech = src + AI.remote_control = src + AI.canmove = 1 //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. + AI.can_shunt = 0 //ONE AI ENTERS. NO AI LEAVES. + to_chat(AI, "[AI.can_dominate_mechs ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ + : "You have been uploaded to a mech's onboard computer."]") + to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") + if(interaction == AI_TRANS_FROM_CARD) + GrantActions(AI, FALSE) + else + GrantActions(AI, !AI.can_dominate_mechs) + +///////////////////////////////////// +//////// Atmospheric stuff //////// +///////////////////////////////////// + +/obj/mecha/proc/get_turf_air() + var/turf/T = get_turf(src) + if(T) + . = T.return_air() + +/obj/mecha/remove_air(amount) + if(use_internal_tank) + return cabin_air.remove(amount) + else + var/turf/T = get_turf(src) + if(T) + return T.remove_air(amount) + +/obj/mecha/return_air() + if(use_internal_tank) + return cabin_air + return get_turf_air() + +/obj/mecha/proc/return_pressure() + var/datum/gas_mixture/t_air = return_air() + if(t_air) + . = t_air.return_pressure() + +//skytodo: //No idea what you want me to do here, mate. +/obj/mecha/proc/return_temperature() + var/datum/gas_mixture/t_air = return_air() + if(t_air) + . = t_air.return_temperature() + +/obj/mecha/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !istype(new_port) || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + connected_port.parent.reconcile_air() + + if(occupant) + occupant.clear_alert("mechaport") + occupant.throw_alert("mechaport_d", /obj/screen/alert/mech_port_disconnect) + + log_message("Connected to gas port.") + return 1 + +/obj/mecha/proc/disconnect() + if(!connected_port) + return 0 + + connected_port.connected_device = null + connected_port = null + log_message("Disconnected from gas port.") + if(occupant) + occupant.clear_alert("mechaport_d") + return 1 + +/obj/mecha/portableConnectorReturnAir() + return internal_tank.return_air() + +/obj/mecha/proc/toggle_lights() + lights = !lights + if(lights) + set_light(light_range + lights_power) + else + set_light(light_range - lights_power) + occupant_message("Toggled lights [lights ? "on" : "off"].") + log_message("Toggled lights [lights ? "on" : "off"].") + +/obj/mecha/proc/toggle_internal_tank() + use_internal_tank = !use_internal_tank + occupant_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") + log_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") + +/obj/mecha/MouseDrop_T(mob/M, mob/user) + if(frozen) + to_chat(user, "Do not enter Admin-Frozen mechs.") + return + if(user.incapacitated()) + return + if(user != M) + return + log_message("[user] tries to move in.") + if(occupant) + to_chat(user, "The [src] is already occupied!") + log_append_to_last("Permission denied.") + return + var/passed + if(dna) + if(ishuman(user)) + if(user.dna.unique_enzymes == dna) + passed = 1 + else if(operation_allowed(user)) + passed = 1 + if(!passed) + to_chat(user, "Access denied.") + log_append_to_last("Permission denied.") + return + if(user.buckled) + to_chat(user, "You are currently buckled and cannot move.") + log_append_to_last("Permission denied.") + return + if(user.has_buckled_mobs()) //mob attached to us + to_chat(user, "You can't enter the exosuit with other creatures attached to you!") + return + + visible_message("[user] starts to climb into [src]") + + if(do_after(user, 40, target = src)) + if(obj_integrity <= 0) + to_chat(user, "You cannot get in the [name], it has been destroyed!") + else if(occupant) + to_chat(user, "[occupant] was faster! Try better next time, loser.") + else if(user.buckled) + to_chat(user, "You can't enter the exosuit while buckled.") + else if(user.has_buckled_mobs()) + to_chat(user, "You can't enter the exosuit with other creatures attached to you!") + else + moved_inside(user) + else + to_chat(user, "You stop entering the exosuit!") + +/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) + if(H && H.client && (H in range(1))) + occupant = H + H.stop_pulling() + H.forceMove(src) + H.reset_perspective(src) + add_fingerprint(H) + GrantActions(H, human_occupant = 1) + forceMove(loc) + log_append_to_last("[H] moved in as pilot.") + icon_state = reset_icon() + dir = dir_in + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + if(!activated) + occupant << sound(longactivationsound, volume = 50) + activated = TRUE + else if(!hasInternalDamage()) + occupant << sound(nominalsound, volume = 50) + if(state) + H.throw_alert("locked", /obj/screen/alert/mech_maintenance) + return 1 + else + return 0 + +/obj/mecha/proc/mmi_move_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected!") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level!") + return 0 + else if(occupant) + to_chat(user, "Occupant detected!") + return 0 + else if(dna && dna != mmi_as_oc.brainmob.dna.unique_enzymes) + to_chat(user, "Access denied. [name] is secured with a DNA lock.") + return 0 + + if(do_after(user, 40, target = src)) + if(!occupant) + return mmi_moved_inside(mmi_as_oc,user) + else + to_chat(user, "Occupant detected!") + else + to_chat(user, "You stop inserting the MMI.") + return 0 + +/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/mmi_as_oc,mob/user) + if(mmi_as_oc && (user in range(1))) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected.") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level.") + return 0 + if(!user.unEquip(mmi_as_oc)) + to_chat(user, "\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]") + return 0 + var/mob/brainmob = mmi_as_oc.brainmob + brainmob.reset_perspective(src) + occupant = brainmob + brainmob.forceMove(src) //should allow relaymove + brainmob.canmove = 1 + if(istype(mmi_as_oc, /obj/item/mmi/robotic_brain)) + var/obj/item/mmi/robotic_brain/R = mmi_as_oc + if(R.imprinted_master) + to_chat(brainmob, "Your imprint to [R.imprinted_master] has been temporarily disabled. You should help the crew and not commit harm.") + mmi_as_oc.loc = src + mmi_as_oc.mecha = src + Entered(mmi_as_oc) + Move(loc) + icon_state = reset_icon() + dir = dir_in + log_message("[mmi_as_oc] moved in as pilot.") + if(!hasInternalDamage()) + to_chat(occupant, sound(nominalsound, volume=50)) + GrantActions(brainmob) + return 1 + else + return 0 + +/obj/mecha/proc/pilot_is_mmi() + var/atom/movable/mob_container + if(istype(occupant, /mob/living/carbon/brain)) + var/mob/living/carbon/brain/brain = occupant + mob_container = brain.container + if(istype(mob_container, /obj/item/mmi)) + return 1 + return 0 + +/obj/mecha/proc/pilot_mmi_hud(var/mob/living/carbon/brain/pilot) + return + +/obj/mecha/Exited(atom/movable/M, atom/newloc) + if(occupant && occupant == M) // The occupant exited the mech without calling go_out() + go_out(1, newloc) + +/obj/mecha/proc/go_out(forced, atom/newloc = loc) + if(!occupant) + return + var/atom/movable/mob_container + occupant.clear_alert("charge") + occupant.clear_alert("locked") + occupant.clear_alert("mech damage") + occupant.clear_alert("mechaport") + occupant.clear_alert("mechaport_d") + if(ishuman(occupant)) + mob_container = occupant + RemoveActions(occupant, human_occupant = 1) + else if(isbrain(occupant)) + var/mob/living/carbon/brain/brain = occupant + RemoveActions(brain) + mob_container = brain.container + else if(isAI(occupant)) + var/mob/living/silicon/ai/AI = occupant + if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. + RemoveActions(occupant) + occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. + occupant = null + return + else + if(!AI.linked_core || QDELETED(AI.linked_core)) + to_chat(AI, "Inactive core destroyed. Unable to return.") + AI.linked_core = null + return + to_chat(AI, "Returning to core...") + AI.controlled_mech = null + AI.remote_control = null + RemoveActions(occupant, 1) + mob_container = AI + newloc = get_turf(AI.linked_core) + qdel(AI.linked_core) + else + return + var/mob/living/L = occupant + occupant = null //we need it null when forceMove calls Exited(). + if(mob_container.forceMove(newloc))//ejecting mob container + log_message("[mob_container] moved out.") + L << browse(null, "window=exosuit") + + if(istype(mob_container, /obj/item/mmi)) + var/obj/item/mmi/mmi = mob_container + if(mmi.brainmob) + L.loc = mmi + L.reset_perspective() + mmi.mecha = null + mmi.update_icon() + L.canmove = 0 + if(istype(mmi, /obj/item/mmi/robotic_brain)) + var/obj/item/mmi/robotic_brain/R = mmi + if(R.imprinted_master) + to_chat(L, "Imprint re-enabled, you are once again bound to [R.imprinted_master]'s commands.") + icon_state = initial(icon_state)+"-open" + dir = dir_in + + if(L && L.client) + L.client.RemoveViewMod("mecha") + zoom_mode = FALSE + +///////////////////////// +////// Access stuff ///// +///////////////////////// + +/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) + if(!ishuman(H)) + return 0 + for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(check_access(ID, operation_req_access)) + return 1 + return 0 + + +/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) + for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(check_access(ID, internals_req_access)) + return 1 + return 0 + + +/obj/mecha/check_access(obj/item/card/id/I, list/access_list) + if(!istype(access_list)) + return 1 + if(!access_list.len) //no requirements + return 1 + if(istype(I, /obj/item/pda)) + var/obj/item/pda/pda = I + I = pda.id + if(!istype(I) || !I.access) //not ID or no access + return 0 + if(access_list==operation_req_access) + for(var/req in access_list) + if(!(req in I.access)) //doesn't have this access + return 0 + else if(access_list==internals_req_access) + for(var/req in access_list) + if(req in I.access) + return 1 + return 1 + +/////////////////////// +///// Power stuff ///// +/////////////////////// + +/obj/mecha/proc/has_charge(amount) + return (get_charge()>=amount) + +/obj/mecha/proc/get_charge() + for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) + var/relay_charge = R.get_charge() + if(relay_charge) + return relay_charge + if(cell) + return max(0, cell.charge) + +/obj/mecha/proc/use_power(amount) + if(get_charge()) + cell.use(amount) + if(occupant) + update_cell() + return 1 + return 0 + +/obj/mecha/proc/give_power(amount) + if(!isnull(get_charge())) + cell.give(amount) + if(occupant) + update_cell() + return 1 + return 0 + +/obj/mecha/proc/update_cell() + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + occupant.clear_alert("charge") + if(0.5 to 0.75) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 1) + if(0.25 to 0.5) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 2) + if(power_warned) + power_warned = FALSE + if(0.01 to 0.25) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 3) + if(!power_warned) + occupant << sound(lowpowersound, volume = 50) + power_warned = TRUE + else + occupant.throw_alert("charge", /obj/screen/alert/mech_emptycell) + else + occupant.throw_alert("charge", /obj/screen/alert/mech_nocell) + +/obj/mecha/proc/reset_icon() + if(initial_icon) + icon_state = initial_icon + else + icon_state = initial(icon_state) + return icon_state + +////////////////////////////////////////// +//////// Mecha global iterators //////// +////////////////////////////////////////// + +/obj/mecha/process() + process_internal_damage() + regulate_temp() + give_air() + update_huds() + +/obj/mecha/proc/process_internal_damage() + if(!internal_damage) + return + + if(internal_damage & MECHA_INT_FIRE) + if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5)) + clearInternalDamage(MECHA_INT_FIRE) + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH)) + setInternalDamage(MECHA_INT_TANK_BREACH) + + if(int_tank_air && int_tank_air.return_volume() > 0) + int_tank_air.temperature = min(6000 + T0C, cabin_air.return_temperature() + rand(10, 15)) + + if(cabin_air && cabin_air.return_volume()>0) + cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15)) + if(cabin_air.return_temperature() > max_temperature/2) + take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0) + + if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) + if(loc) + loc.assume_air(leaked_gas) + air_update_turf() + else + qdel(leaked_gas) + + if(internal_damage & MECHA_INT_SHORT_CIRCUIT) + if(get_charge()) + spark_system.start() + cell.charge -= min(20,cell.charge) + cell.maxcharge -= min(20,cell.maxcharge) + +/obj/mecha/proc/regulate_temp() + if(internal_damage & MECHA_INT_TEMP_CONTROL) + return + + if(cabin_air && cabin_air.return_volume() > 0) + var/delta = cabin_air.temperature - T20C + cabin_air.temperature -= max(-10, min(10, round(delta / 4, 0.1))) + +/obj/mecha/proc/give_air() + if(!internal_tank) + return + + var/datum/gas_mixture/tank_air = internal_tank.return_air() + + var/release_pressure = internal_tank_valve + var/cabin_pressure = cabin_air.return_pressure() + var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) + var/transfer_moles = 0 + if(pressure_delta > 0) //cabin pressure lower than release pressure + if(tank_air.return_temperature() > 0) + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) + cabin_air.merge(removed) + else if(pressure_delta < 0) //cabin pressure higher than release pressure + var/datum/gas_mixture/t_air = return_air() + pressure_delta = cabin_pressure - release_pressure + if(t_air) + pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) + if(pressure_delta > 0) //if location pressure is lower than cabin pressure + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) + if(t_air) + t_air.merge(removed) + else //just delete the cabin gas, we're in space or some shit + qdel(removed) + +/obj/mecha/proc/update_huds() + diag_hud_set_mechhealth() + diag_hud_set_mechcell() + diag_hud_set_mechstat() + diag_hud_set_mechtracking() + + +/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) + flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30) + +/obj/mecha/update_remote_sight(mob/living/user) + if(occupant_sight_flags) + if(user == occupant) + user.sight |= occupant_sight_flags + + ..() + +/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) + if(!no_effect) + if(selected) + used_item = selected + else if(!visual_effect_icon) + visual_effect_icon = ATTACK_EFFECT_SMASH + if(damtype == BURN) + visual_effect_icon = ATTACK_EFFECT_MECHFIRE + else if(damtype == TOX) + visual_effect_icon = ATTACK_EFFECT_MECHTOXIN + ..() + +/obj/mecha/obj_destruction() + if(wreckage) + var/mob/living/silicon/ai/AI + if(isAI(occupant)) + AI = occupant + occupant = null + var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + if(E.salvageable && prob(30)) + WR.crowbar_salvage += E + E.detach(WR) //detaches from src into WR + E.equip_ready = 1 + else + E.detach(loc) + qdel(E) + if(cell) + WR.crowbar_salvage += cell + cell.forceMove(WR) + cell.charge = rand(0, cell.charge) + cell = null + if(internal_tank) + WR.crowbar_salvage += internal_tank + internal_tank.forceMove(WR) + cell = null + . = ..() + +/obj/mecha/CtrlClick(mob/living/L) + if(occupant != L || !istype(L)) + return ..() + + var/list/choices = list("Cancel / No Change" = mutable_appearance(icon = 'icons/mob/screen_gen.dmi', icon_state = "x")) + var/list/choices_to_refs = list() + + for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) + if(!MT.selectable || selected == MT) + continue + var/mutable_appearance/clean/MA = new(MT) + choices[MT.name] = MA + choices_to_refs[MT.name] = MT + + var/choice = show_radial_menu(L, src, choices, radius = 48, custom_check = CALLBACK(src, .proc/check_menu, L)) + if(!check_menu(L) || choice == "Cancel / No Change") + return + + var/obj/item/mecha_parts/mecha_equipment/new_sel = LAZYACCESS(choices_to_refs, choice) + if(istype(new_sel)) + selected = new_sel + occupant_message("You switch to [selected].") + visible_message("[src] raises [selected]") + send_byjax(occupant, "exosuit.browser", "eq_list", get_equipment_list()) + +/obj/mecha/proc/check_menu(mob/living/L) + if(L != occupant || !istype(L)) + return FALSE + if(L.incapacitated()) + return FALSE + return TRUE diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index 3c71db19b72..8ff3d4ebd5c 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -225,4 +225,4 @@ chassis.damtype = new_damtype button_icon_state = "mech_damtype_[new_damtype]" playsound(src, 'sound/mecha/mechmove01.ogg', 50, 1) - UpdateButtonIcon() \ No newline at end of file + UpdateButtonIcon() diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm index 64eac560d39..c9dc5b11e97 100644 --- a/code/game/mecha/mecha_construction_paths.dm +++ b/code/game/mecha/mecha_construction_paths.dm @@ -1,1727 +1,1727 @@ -#define STANDARD_STACK_AMOUNT 5 - -//////////////////////////////// -///// Construction datums ////// -//////////////////////////////// - -/datum/construction/mecha/custom_action(step, atom/used_atom, mob/user) - if(istype(used_atom, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = used_atom - if(C.use(4)) - playsound(holder, C.usesound, 50, 1) - else - to_chat(user, ("There's not enough cable to finish the task.")) - return 0 - else if(istype(used_atom, /obj/item/stack)) - var/obj/item/stack/S = used_atom - if(S.amount < STANDARD_STACK_AMOUNT) - to_chat(user, ("There's not enough material in this stack.")) - return 0 - else - S.use(STANDARD_STACK_AMOUNT) - else - return ..() - -/datum/construction/reversible/mecha/custom_action(index as num, diff as num, atom/used_atom, mob/user as mob) - if(istype(used_atom, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = used_atom - if(C.use(4)) - playsound(holder, C.usesound, 50, 1) - else - to_chat(user, ("There's not enough cable to finish the task.")) - return 0 - else if(istype(used_atom, /obj/item/stack)) - var/obj/item/stack/S = used_atom - if(S.amount < STANDARD_STACK_AMOUNT) - to_chat(user, ("There's not enough material in this stack.")) - return 0 - else - S.use(STANDARD_STACK_AMOUNT) - else if(isitem(used_atom)) - var/obj/item/I = used_atom - if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) - if(!I.use_tool(holder, user, 0, volume = I.tool_volume)) - return 0 - return 1 - - -/datum/construction/mecha/ripley_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 - ) - -/datum/construction/mecha/ripley_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/ripley_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/ripley_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "ripley0" - const_holder.density = 1 - const_holder.overlays.len = 0 - qdel(src) - return - - -/datum/construction/reversible/mecha/ripley - result = "/obj/mecha/working/ripley" - taskpath = /datum/job_objective/make_ripley - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //5 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //7 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //8 - list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //9 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //10 - list("key"=/obj/item/circuitboard/mecha/ripley/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //11 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/ripley/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/ripley/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "ripley2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "ripley0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "ripley3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "ripley4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "ripley2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "ripley5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "ripley3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "ripley6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "ripley4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "ripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "ripley5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "ripley8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "ripley6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "ripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "ripley7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "ripley10" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "ripley8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "ripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "ripley9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") - holder.icon_state = "ripley12" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "ripley10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") - holder.icon_state = "ripley13" - else - user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "ripley11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "ripley12" - return 1 - -/datum/construction/reversible/mecha/ripley/spawn_result() - ..() - feedback_inc("mecha_ripley_created",1) - return - - - -/datum/construction/mecha/gygax_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 - list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/gygax_head) - ) - -/datum/construction/mecha/gygax_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/gygax_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/gygax_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "gygax0" - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/reversible/mecha/gygax - result = "/obj/mecha/combat/gygax" - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/gygax_armour, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //5 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Advanced capacitor is secured."), - //7 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Advanced capacitor is installed."), - //8 - list("key"=/obj/item/stock_parts/capacitor/adv, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Advanced scanner module is secured."), - //9 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Advanced scanner module is installed."), - //10 - list("key"=/obj/item/stock_parts/scanning_module/adv, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Scanning module is secured."), - //11 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Scanning module is installed."), - //12 - list("key"=/obj/item/circuitboard/mecha/gygax/targeting, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //14 - list("key"=/obj/item/circuitboard/mecha/gygax/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //15 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //16 - list("key"=/obj/item/circuitboard/mecha/gygax/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //17 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/gygax/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/gygax/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "gygax2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "gygax0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "gygax3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "gygax4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "gygax2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "gygax5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "gygax3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "gygax6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/gygax/main(get_turf(holder)) - holder.icon_state = "gygax4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "gygax7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "gygax5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "gygax8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/gygax/peripherals(get_turf(holder)) - holder.icon_state = "gygax6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - holder.icon_state = "gygax9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "gygax7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "gygax10" - else - user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") - new /obj/item/circuitboard/mecha/gygax/targeting(get_turf(holder)) - holder.icon_state = "gygax8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs advanced scanner module to the [holder].", "You install advanced scanner module to the [holder].") - qdel(used_atom) - holder.icon_state = "gygax11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "gygax9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") - holder.icon_state = "gygax12" - else - user.visible_message("[user] removes the advanced scanner module from the [holder].", "You remove the advanced scanner module from the [holder].") - new /obj/item/stock_parts/scanning_module/adv(get_turf(holder)) - holder.icon_state = "gygax10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs advanced capacitor to the [holder].", "You install advanced capacitor to the [holder].") - qdel(used_atom) - holder.icon_state = "gygax13" - else - user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") - holder.icon_state = "gygax11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") - holder.icon_state = "gygax14" - else - user.visible_message("[user] removes the advanced capacitor from the [holder].", "You remove the advanced capacitor from the [holder].") - new /obj/item/stock_parts/capacitor/adv(get_turf(holder)) - holder.icon_state = "gygax12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "gygax15" - else - user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") - holder.icon_state = "gygax13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "gygax16" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "gygax14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "gygax17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "gygax15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Gygax Armor Plates to the [holder].", "You install Gygax Armor Plates to the [holder].") - qdel(used_atom) - holder.icon_state = "gygax18" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "gygax16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Gygax Armor Plates.", "You secure Gygax Armor Plates.") - holder.icon_state = "gygax19" - else - user.visible_message("[user] pries Gygax Armor Plates from the [holder].", "You pry Gygax Armor Plates from the [holder].") - new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) - holder.icon_state = "gygax17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Gygax Armor Plates to the [holder].", "You weld Gygax Armor Plates to the [holder].") - else - user.visible_message("[user] unfastens Gygax Armor Plates.", "You unfasten Gygax Armor Plates.") - holder.icon_state = "gygax18" - return 1 - -/datum/construction/reversible/mecha/gygax/spawn_result() - ..() - feedback_inc("mecha_gygax_created",1) - return - -/datum/construction/mecha/firefighter_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 - list("key"=/obj/item/clothing/suit/fire)//6 - ) - -/datum/construction/mecha/firefighter_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/firefighter_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/firefighter_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "fireripley0" - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/reversible/mecha/firefighter - result = "/obj/mecha/working/ripley/firefighter" - taskpath = /datum/job_objective/make_ripley - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is being installed."), - //4 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //5 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //6 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - - //7 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //8 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //9 - list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //10 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //11 - list("key"=/obj/item/circuitboard/mecha/ripley/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //12 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //13 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //14 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //15 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/firefighter/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(15) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(14) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "fireripley2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "fireripley0" - if(13) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "fireripley3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "fireripley4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "fireripley2" - if(11) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "fireripley5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "fireripley3" - if(10) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "fireripley6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "fireripley4" - if(9) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "fireripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "fireripley5" - if(8) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "fireripley8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "fireripley6" - if(7) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "fireripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "fireripley7" - - if(6) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "fireripley10" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "fireripley8" - if(5) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "fireripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "fireripley9" - if(4) - if(diff==FORWARD) - user.visible_message("[user] starts to install the external armor layer to the [holder].", "You start to install the external armor layer to the [holder].") - holder.icon_state = "fireripley12" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "fireripley10" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") - holder.icon_state = "fireripley13" - else - user.visible_message("[user] removes the external armor from the [holder].", "You remove the external armor from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "fireripley11" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") - holder.icon_state = "fireripley14" - else - user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "fireripley12" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "fireripley13" - return 1 - -/datum/construction/reversible/mecha/firefighter/spawn_result() - ..() - feedback_inc("mecha_firefighter_created",1) - return - - - -/datum/construction/mecha/honker_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/honker_torso),//1 - list("key"=/obj/item/mecha_parts/part/honker_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/honker_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/honker_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/honker_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/honker_head) - ) - -/datum/construction/mecha/honker_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/honker_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/honker_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/mecha/honker(const_holder) - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/mecha/honker - result = "/obj/mecha/combat/honker" - steps = list(list("key"=/obj/item/bikehorn),//1 - list("key"=/obj/item/clothing/shoes/clown_shoes),//2 - list("key"=/obj/item/bikehorn),//3 - list("key"=/obj/item/clothing/mask/gas/clown_hat),//4 - list("key"=/obj/item/bikehorn),//5 - list("key"=/obj/item/circuitboard/mecha/honker/targeting),//6 - list("key"=/obj/item/bikehorn),//7 - list("key"=/obj/item/circuitboard/mecha/honker/peripherals),//8 - list("key"=/obj/item/bikehorn),//9 - list("key"=/obj/item/circuitboard/mecha/honker/main),//10 - list("key"=/obj/item/bikehorn),//11 - ) - -/datum/construction/mecha/honker/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/mecha/honker/custom_action(step, atom/used_atom, mob/user) - if(!..()) - return 0 - - if(istype(used_atom, /obj/item/bikehorn)) - playsound(holder, 'sound/items/bikehorn.ogg', 50, 1) - user.visible_message("HONK!") - - //TODO: better messages. - switch(step) - if(10) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") - qdel(used_atom) - if(8) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - if(6) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - if(4) - user.visible_message("[user] puts clown wig and mask on the [holder].", "You put clown wig and mask on the [holder].") - qdel(used_atom) - if(2) - user.visible_message("[user] puts clown boots on the [holder].", "You put clown boots on the [holder].") - qdel(used_atom) - return 1 - -/datum/construction/mecha/honker/spawn_result() - ..() - feedback_inc("mecha_honker_created",1) - return - -/datum/construction/mecha/reticence_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/reticence_torso),//1 - list("key"=/obj/item/mecha_parts/part/reticence_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/reticence_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/reticence_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/reticence_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/reticence_head) - ) - -/datum/construction/mecha/reticence_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/reticence_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state + "+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/reticence_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/mecha/reticence(const_holder) - const_holder.density = 1 - qdel(src) - return - -/datum/construction/mecha/reticence - result = "/obj/mecha/combat/reticence" - steps = list(list("key"=/obj/effect/dummy/mecha_emote_step),//1 - list("key"=/obj/item/clothing/suit/suspenders),//2 - list("key"=/obj/effect/dummy/mecha_emote_step),//3 - list("key"=/obj/item/clothing/mask/gas/mime),//4 - list("key"=/obj/effect/dummy/mecha_emote_step),//5 - list("key"=/obj/item/clothing/head/beret),//6 - list("key"=/obj/item/circuitboard/mecha/reticence/targeting),//7 - list("key"=/obj/item/circuitboard/mecha/reticence/peripherals),//8 - list("key"=/obj/item/circuitboard/mecha/reticence/main),//9 - ) - -/datum/construction/mecha/reticence/action(atom/used_atom,mob/user) - return check_step(used_atom,user) - -/datum/construction/mecha/reticence/custom_action(step, atom/used_atom, mob/user) - if(!..()) - return 0 - - if(istype(used_atom, /obj/effect/dummy/mecha_emote_step)) - var/obj/effect/dummy/mecha_emote_step/E = used_atom - holder.visible_message("[holder] likewise [E.emote]") - qdel(used_atom) - - //TODO: better messages. - switch(step) - if(9) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") - qdel(used_atom) - if(8) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - if(7) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - if(6) - user.visible_message("[user] puts beret on the [holder].", "You put beret on the [holder].") - qdel(used_atom) - if(4) - user.visible_message("[user] puts mime mask on the [holder].", "You put mime mask on the [holder].") - qdel(used_atom) - if(2) - user.visible_message("[user] puts suspenders on the [holder].", "You put suspenders on the [holder].") - qdel(used_atom) - return 1 - -/datum/construction/mecha/reticence/spawn_result() - ..() - feedback_inc("mecha_reticence_created",1) - return - -/datum/construction/mecha/durand_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 - list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/durand_head) - ) - -/datum/construction/mecha/durand_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/durand_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/durand_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "durand0" - const_holder.density = 1 - qdel(src) - return - -/datum/construction/reversible/mecha/durand - result = "/obj/mecha/combat/durand" - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/durand_armor, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //5 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Super capacitor is secured."), - //7 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Super capacitor is installed."), - //8 - list("key"=/obj/item/stock_parts/capacitor/super, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Phasic scanner module is secured."), - //9 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Phasic scanner module is installed."), - //10 - list("key"=/obj/item/stock_parts/scanning_module/phasic, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Scanning module is secured."), - //11 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Scanning module is installed."), - //12 - list("key"=/obj/item/circuitboard/mecha/durand/targeting, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //14 - list("key"=/obj/item/circuitboard/mecha/durand/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //15 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //16 - list("key"=/obj/item/circuitboard/mecha/durand/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //17 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - - -/datum/construction/reversible/mecha/durand/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/durand/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "durand2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "durand0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "durand3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "durand4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "durand2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "durand5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "durand3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "durand6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/durand/main(get_turf(holder)) - holder.icon_state = "durand4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "durand7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "durand5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "durand8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/durand/peripherals(get_turf(holder)) - holder.icon_state = "durand6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - holder.icon_state = "durand9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "durand7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "durand10" - else - user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") - new /obj/item/circuitboard/mecha/durand/targeting(get_turf(holder)) - holder.icon_state = "durand8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") - qdel(used_atom) - holder.icon_state = "durand11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "durand9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") - holder.icon_state = "durand12" - else - user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") - new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) - holder.icon_state = "durand10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") - qdel(used_atom) - holder.icon_state = "durand13" - else - user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") - holder.icon_state = "durand11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") - holder.icon_state = "durand14" - else - user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") - new /obj/item/stock_parts/capacitor/super(get_turf(holder)) - holder.icon_state = "durand12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "durand15" - else - user.visible_message("[user] unfastens the super capacitor.", "You unfasten the super capacitor.") - holder.icon_state = "durand13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "durand16" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "durand14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "durand17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "durand15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Durand Armor Plates to the [holder].", "You install Durand Armor Plates to the [holder].") - qdel(used_atom) - holder.icon_state = "durand18" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "durand16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Durand Armor Plates.", "You secure Durand Armor Plates.") - holder.icon_state = "durand19" - else - user.visible_message("[user] pries Durand Armor Plates from the [holder].", "You pry Durand Armor Plates from the [holder].") - new /obj/item/mecha_parts/part/durand_armor(get_turf(holder)) - holder.icon_state = "durand17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Durand Armor Plates to the [holder].", "You weld Durand Armor Plates to the [holder].") - else - user.visible_message("[user] unfastens Durand Armor Plates.", "You unfasten Durand Armor Plates.") - holder.icon_state = "durand18" - return 1 - -/datum/construction/reversible/mecha/durand/spawn_result() - ..() - feedback_inc("mecha_durand_created",1) - return - -//PHAZON - -/datum/construction/mecha/phazon_chassis - result = "/obj/mecha/combat/phazon" - steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 - list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/phazon_head) - ) - -/datum/construction/mecha/phazon_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/phazon_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/phazon_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "phazon0" - const_holder.density = 1 - qdel(src) - return - -/datum/construction/reversible/mecha/phazon - result = "/obj/mecha/combat/phazon" - steps = list( - //1 - list("key"=/obj/item/assembly/signaler/anomaly, - "backkey"=null, //Cannot remove the anomaly core once it's in - "desc"="Anomaly core socket is open and awaiting connection."), - //2 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //3 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //4 - list("key"=/obj/item/mecha_parts/part/phazon_armor, - "backkey"=TOOL_WELDER, - "desc"="Phase armor is welded."), - //5 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Phase armor is wrenched."), - //6 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Phase armor is installed."), - //7 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The bluespace crystal is engaged."), - //8 - list("key"=TOOL_SCREWDRIVER, - "backkey"=/obj/item/wirecutters, - "desc"="The bluespace crystal is connected."), - //9 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_CROWBAR, - "desc"="The bluespace crystal is installed."), - //10 - list("key"=/obj/item/stack/ore/bluespace_crystal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Super capacitor is secured."), - //11 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Super capacitor is installed."), - //12 - list("key"=/obj/item/stock_parts/capacitor/super, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Phasic scanner module is secured."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Phasic scanner module is installed."), - //14 - list("key"=/obj/item/stock_parts/scanning_module/phasic, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Scanning module is secured."), - //15 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Scanning module is installed."), - //16 - list("key"=/obj/item/circuitboard/mecha/phazon/targeting, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //17 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed"), - //18 - list("key"=/obj/item/circuitboard/mecha/phazon/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //19 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //20 - list("key"=/obj/item/circuitboard/mecha/phazon/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //21 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //22 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //23 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //24 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - - -/datum/construction/reversible/mecha/phazon/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/phazon/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(24) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(23) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "phazon2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "phazon0" - if(22) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "phazon3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(21) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "phazon4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "phazon2" - if(20) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "phazon5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "phazon3" - if(19) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "phazon6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/phazon/main(get_turf(holder)) - holder.icon_state = "phazon4" - if(18) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "phazon7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "phazon5" - if(17) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "phazon8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/phazon/peripherals(get_turf(holder)) - holder.icon_state = "phazon6" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - holder.icon_state = "phazon9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "phazon7" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "phazon10" - else - user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") - new /obj/item/circuitboard/mecha/phazon/targeting(get_turf(holder)) - holder.icon_state = "phazon8" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") - qdel(used_atom) - holder.icon_state = "phazon11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "phazon9" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") - holder.icon_state = "phazon12" - else - user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") - new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) - holder.icon_state = "phazon10" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") - qdel(used_atom) - holder.icon_state = "phazon13" - else - user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") - holder.icon_state = "phazon11" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") - holder.icon_state = "phazon14" - else - user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") - new /obj/item/stock_parts/capacitor/super(get_turf(holder)) - holder.icon_state = "phazon12" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the bluespace crystal.", "You install the bluespace crystals.") - holder.icon_state = "phazon15" - else - user.visible_message("[user] unsecures the super capacitor from the [holder].", "You unsecure the super capacitor from the [holder].") - holder.icon_state = "phazon13" - if(9) - if(diff==FORWARD) - user.visible_message("[user] connects the bluespace crystal.", "You connect the bluespace crystals.") - holder.icon_state = "phazon16" - else - user.visible_message("[user] removes the bluespace crystal from the [holder].", "You remove the bluespace crystal from the [holder].") - new /obj/item/stack/ore/bluespace_crystal(get_turf(holder), new_amount = 5) - holder.icon_state = "phazon14" - if(8) - if(diff==FORWARD) - user.visible_message("[user] engages the bluespace crystal.", "You engage the bluespace crystals.") - holder.icon_state = "phazon17" - else - user.visible_message("[user] disconnects the bluespace crystal from the [holder].", "You disconnect the bluespace crystal from the [holder].") - holder.icon_state = "phazon15" - if(7) - if(diff==FORWARD) - user.visible_message("[user] installs the phase armor layer to the [holder].", "You install the phase armor layer to the [holder].") - holder.icon_state = "phazon18" - else - user.visible_message("[user] disengages the bluespace crystal.", "You disengage the bluespace crystals.") - holder.icon_state = "phazon16" - if(6) - if(diff==FORWARD) - user.visible_message("[user] secures the phase armor layer.", "You secure the phase armor layer.") - holder.icon_state = "phazon19" - else - user.visible_message("[user] pries the phase armor layer from the [holder].", "You pry the phase armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "phazon17" - if(5) - if(diff==FORWARD) - user.visible_message("[user] welds the phase armor layer to the [holder].", "You weld the phase armor layer to the [holder].") - holder.icon_state = "phazon20" - else - user.visible_message("[user] unfastens the phase armor layer.", "You unfasten the phase armor layer.") - holder.icon_state = "phazon18" - if(4) - if(diff==FORWARD) - user.visible_message("[user] installs Phazon Armor Plates to the [holder].", "You install Phazon Armor Plates to the [holder].") - qdel(used_atom) - holder.icon_state = "phazon21" - else - user.visible_message("[user] cuts phase armor layer from the [holder].", "You cut the phase armor layer from the [holder].") - holder.icon_state = "phazon19" - if(3) - if(diff==FORWARD) - user.visible_message("[user] secures Phazon Armor Plates.", "You secure Phazon Armor Plates.") - holder.icon_state = "phazon22" - else - user.visible_message("[user] pries Phazon Armor Plates from the [holder].", "You pry Phazon Armor Plates from the [holder].") - new /obj/item/mecha_parts/part/phazon_armor(get_turf(holder)) - holder.icon_state = "phazon20" - if(2) - if(diff==FORWARD) - user.visible_message("[user] welds Phazon Armor Plates to the [holder].", "You weld Phazon Armor Plates to the [holder].") - else - user.visible_message("[user] unfastens Phazon Armor Plates.", "You unfasten Phazon Armor Plates.") - holder.icon_state = "phazon21" - if(1) - if(diff==FORWARD) - user.visible_message("[user] carefully inserts the anomaly core into \the [holder] and secures it.", "You slowly place the anomaly core into its socket and close its chamber.") - qdel(used_atom) - return 1 - -/datum/construction/reversible/mecha/phazon/spawn_result() - ..() - feedback_inc("mecha_phazon_created",1) - return - -//ODYSSEUS - -/datum/construction/mecha/odysseus_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 - list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 - list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 - list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 - list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 - list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 - ) - -/datum/construction/mecha/odysseus_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/odysseus_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/odysseus_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "odysseus0" - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/reversible/mecha/odysseus - result = "/obj/mecha/medical/odysseus" - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //5 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //7 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //8 - list("key"=/obj/item/circuitboard/mecha/odysseus/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //9 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //10 - list("key"=/obj/item/circuitboard/mecha/odysseus/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //11 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/odysseus/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "odysseus2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "odysseus0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "odysseus3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "odysseus4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "odysseus2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "odysseus5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "odysseus3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "odysseus6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/odysseus/main(get_turf(holder)) - holder.icon_state = "odysseus4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "odysseus7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "odysseus5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "odysseus8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) - holder.icon_state = "odysseus6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "odysseus9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "odysseus7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "odysseus10" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "odysseus8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "odysseus11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "odysseus9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs [used_atom] layer to the [holder].", "You install the external reinforced armor layer to the [holder].") - - holder.icon_state = "odysseus12" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "odysseus10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") - holder.icon_state = "odysseus13" - else - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - user.visible_message("[user] pries [MS] from the [holder].", "You pry [MS] from the [holder].") - holder.icon_state = "odysseus11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") - holder.icon_state = "odysseus14" - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "odysseus12" - return 1 - -/datum/construction/reversible/mecha/odysseus/spawn_result() - ..() - feedback_inc("mecha_odysseus_created",1) - return +#define STANDARD_STACK_AMOUNT 5 + +//////////////////////////////// +///// Construction datums ////// +//////////////////////////////// + +/datum/construction/mecha/custom_action(step, atom/used_atom, mob/user) + if(istype(used_atom, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = used_atom + if(C.use(4)) + playsound(holder, C.usesound, 50, 1) + else + to_chat(user, ("There's not enough cable to finish the task.")) + return 0 + else if(istype(used_atom, /obj/item/stack)) + var/obj/item/stack/S = used_atom + if(S.amount < STANDARD_STACK_AMOUNT) + to_chat(user, ("There's not enough material in this stack.")) + return 0 + else + S.use(STANDARD_STACK_AMOUNT) + else + return ..() + +/datum/construction/reversible/mecha/custom_action(index as num, diff as num, atom/used_atom, mob/user as mob) + if(istype(used_atom, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = used_atom + if(C.use(4)) + playsound(holder, C.usesound, 50, 1) + else + to_chat(user, ("There's not enough cable to finish the task.")) + return 0 + else if(istype(used_atom, /obj/item/stack)) + var/obj/item/stack/S = used_atom + if(S.amount < STANDARD_STACK_AMOUNT) + to_chat(user, ("There's not enough material in this stack.")) + return 0 + else + S.use(STANDARD_STACK_AMOUNT) + else if(isitem(used_atom)) + var/obj/item/I = used_atom + if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) + if(!I.use_tool(holder, user, 0, volume = I.tool_volume)) + return 0 + return 1 + + +/datum/construction/mecha/ripley_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 + ) + +/datum/construction/mecha/ripley_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/ripley_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/ripley_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "ripley0" + const_holder.density = 1 + const_holder.overlays.len = 0 + qdel(src) + return + + +/datum/construction/reversible/mecha/ripley + result = "/obj/mecha/working/ripley" + taskpath = /datum/job_objective/make_ripley + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //8 + list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //10 + list("key"=/obj/item/circuitboard/mecha/ripley/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //11 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/ripley/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/ripley/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "ripley2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "ripley0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "ripley3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "ripley4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "ripley2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "ripley5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "ripley3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "ripley6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "ripley4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "ripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "ripley5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "ripley8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "ripley6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "ripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "ripley7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "ripley10" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "ripley8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "ripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "ripley9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") + holder.icon_state = "ripley12" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "ripley10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") + holder.icon_state = "ripley13" + else + user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "ripley11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "ripley12" + return 1 + +/datum/construction/reversible/mecha/ripley/spawn_result() + ..() + feedback_inc("mecha_ripley_created",1) + return + + + +/datum/construction/mecha/gygax_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 + list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/gygax_head) + ) + +/datum/construction/mecha/gygax_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/gygax_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/gygax_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "gygax0" + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/reversible/mecha/gygax + result = "/obj/mecha/combat/gygax" + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/gygax_armour, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Advanced capacitor is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Advanced capacitor is installed."), + //8 + list("key"=/obj/item/stock_parts/capacitor/adv, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Advanced scanner module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Advanced scanner module is installed."), + //10 + list("key"=/obj/item/stock_parts/scanning_module/adv, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Scanning module is secured."), + //11 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Scanning module is installed."), + //12 + list("key"=/obj/item/circuitboard/mecha/gygax/targeting, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //14 + list("key"=/obj/item/circuitboard/mecha/gygax/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //15 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //16 + list("key"=/obj/item/circuitboard/mecha/gygax/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //17 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/gygax/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/gygax/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "gygax2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "gygax0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "gygax3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "gygax4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "gygax2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "gygax5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "gygax3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "gygax6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/gygax/main(get_turf(holder)) + holder.icon_state = "gygax4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "gygax7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "gygax5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "gygax8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "gygax6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + holder.icon_state = "gygax9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "gygax7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "gygax10" + else + user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") + new /obj/item/circuitboard/mecha/gygax/targeting(get_turf(holder)) + holder.icon_state = "gygax8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to the [holder].", "You install advanced scanner module to the [holder].") + qdel(used_atom) + holder.icon_state = "gygax11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "gygax9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "gygax12" + else + user.visible_message("[user] removes the advanced scanner module from the [holder].", "You remove the advanced scanner module from the [holder].") + new /obj/item/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "gygax10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to the [holder].", "You install advanced capacitor to the [holder].") + qdel(used_atom) + holder.icon_state = "gygax13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "gygax11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "gygax14" + else + user.visible_message("[user] removes the advanced capacitor from the [holder].", "You remove the advanced capacitor from the [holder].") + new /obj/item/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "gygax12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "gygax15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "gygax13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "gygax16" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "gygax14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "gygax17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "gygax15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Gygax Armor Plates to the [holder].", "You install Gygax Armor Plates to the [holder].") + qdel(used_atom) + holder.icon_state = "gygax18" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "gygax16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Gygax Armor Plates.", "You secure Gygax Armor Plates.") + holder.icon_state = "gygax19" + else + user.visible_message("[user] pries Gygax Armor Plates from the [holder].", "You pry Gygax Armor Plates from the [holder].") + new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) + holder.icon_state = "gygax17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Gygax Armor Plates to the [holder].", "You weld Gygax Armor Plates to the [holder].") + else + user.visible_message("[user] unfastens Gygax Armor Plates.", "You unfasten Gygax Armor Plates.") + holder.icon_state = "gygax18" + return 1 + +/datum/construction/reversible/mecha/gygax/spawn_result() + ..() + feedback_inc("mecha_gygax_created",1) + return + +/datum/construction/mecha/firefighter_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 + list("key"=/obj/item/clothing/suit/fire)//6 + ) + +/datum/construction/mecha/firefighter_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/firefighter_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/firefighter_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "fireripley0" + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/reversible/mecha/firefighter + result = "/obj/mecha/working/ripley/firefighter" + taskpath = /datum/job_objective/make_ripley + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is being installed."), + //4 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //5 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //6 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + + //7 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //8 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //9 + list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //10 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //11 + list("key"=/obj/item/circuitboard/mecha/ripley/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //12 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //13 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //14 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //15 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/firefighter/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(15) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(14) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "fireripley2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "fireripley0" + if(13) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "fireripley3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "fireripley4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "fireripley2" + if(11) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "fireripley5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "fireripley3" + if(10) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "fireripley6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "fireripley4" + if(9) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "fireripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "fireripley5" + if(8) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "fireripley8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "fireripley6" + if(7) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "fireripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "fireripley7" + + if(6) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "fireripley10" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "fireripley8" + if(5) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "fireripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "fireripley9" + if(4) + if(diff==FORWARD) + user.visible_message("[user] starts to install the external armor layer to the [holder].", "You start to install the external armor layer to the [holder].") + holder.icon_state = "fireripley12" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "fireripley10" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") + holder.icon_state = "fireripley13" + else + user.visible_message("[user] removes the external armor from the [holder].", "You remove the external armor from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "fireripley11" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") + holder.icon_state = "fireripley14" + else + user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "fireripley12" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "fireripley13" + return 1 + +/datum/construction/reversible/mecha/firefighter/spawn_result() + ..() + feedback_inc("mecha_firefighter_created",1) + return + + + +/datum/construction/mecha/honker_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/honker_torso),//1 + list("key"=/obj/item/mecha_parts/part/honker_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/honker_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/honker_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/honker_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/honker_head) + ) + +/datum/construction/mecha/honker_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/honker_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/honker_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/mecha/honker(const_holder) + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/mecha/honker + result = "/obj/mecha/combat/honker" + steps = list(list("key"=/obj/item/bikehorn),//1 + list("key"=/obj/item/clothing/shoes/clown_shoes),//2 + list("key"=/obj/item/bikehorn),//3 + list("key"=/obj/item/clothing/mask/gas/clown_hat),//4 + list("key"=/obj/item/bikehorn),//5 + list("key"=/obj/item/circuitboard/mecha/honker/targeting),//6 + list("key"=/obj/item/bikehorn),//7 + list("key"=/obj/item/circuitboard/mecha/honker/peripherals),//8 + list("key"=/obj/item/bikehorn),//9 + list("key"=/obj/item/circuitboard/mecha/honker/main),//10 + list("key"=/obj/item/bikehorn),//11 + ) + +/datum/construction/mecha/honker/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/mecha/honker/custom_action(step, atom/used_atom, mob/user) + if(!..()) + return 0 + + if(istype(used_atom, /obj/item/bikehorn)) + playsound(holder, 'sound/items/bikehorn.ogg', 50, 1) + user.visible_message("HONK!") + + //TODO: better messages. + switch(step) + if(10) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") + qdel(used_atom) + if(8) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + if(6) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + if(4) + user.visible_message("[user] puts clown wig and mask on the [holder].", "You put clown wig and mask on the [holder].") + qdel(used_atom) + if(2) + user.visible_message("[user] puts clown boots on the [holder].", "You put clown boots on the [holder].") + qdel(used_atom) + return 1 + +/datum/construction/mecha/honker/spawn_result() + ..() + feedback_inc("mecha_honker_created",1) + return + +/datum/construction/mecha/reticence_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/reticence_torso),//1 + list("key"=/obj/item/mecha_parts/part/reticence_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/reticence_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/reticence_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/reticence_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/reticence_head) + ) + +/datum/construction/mecha/reticence_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/reticence_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state + "+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/reticence_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/mecha/reticence(const_holder) + const_holder.density = 1 + qdel(src) + return + +/datum/construction/mecha/reticence + result = "/obj/mecha/combat/reticence" + steps = list(list("key"=/obj/effect/dummy/mecha_emote_step),//1 + list("key"=/obj/item/clothing/suit/suspenders),//2 + list("key"=/obj/effect/dummy/mecha_emote_step),//3 + list("key"=/obj/item/clothing/mask/gas/mime),//4 + list("key"=/obj/effect/dummy/mecha_emote_step),//5 + list("key"=/obj/item/clothing/head/beret),//6 + list("key"=/obj/item/circuitboard/mecha/reticence/targeting),//7 + list("key"=/obj/item/circuitboard/mecha/reticence/peripherals),//8 + list("key"=/obj/item/circuitboard/mecha/reticence/main),//9 + ) + +/datum/construction/mecha/reticence/action(atom/used_atom,mob/user) + return check_step(used_atom,user) + +/datum/construction/mecha/reticence/custom_action(step, atom/used_atom, mob/user) + if(!..()) + return 0 + + if(istype(used_atom, /obj/effect/dummy/mecha_emote_step)) + var/obj/effect/dummy/mecha_emote_step/E = used_atom + holder.visible_message("[holder] likewise [E.emote]") + qdel(used_atom) + + //TODO: better messages. + switch(step) + if(9) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") + qdel(used_atom) + if(8) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + if(7) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + if(6) + user.visible_message("[user] puts beret on the [holder].", "You put beret on the [holder].") + qdel(used_atom) + if(4) + user.visible_message("[user] puts mime mask on the [holder].", "You put mime mask on the [holder].") + qdel(used_atom) + if(2) + user.visible_message("[user] puts suspenders on the [holder].", "You put suspenders on the [holder].") + qdel(used_atom) + return 1 + +/datum/construction/mecha/reticence/spawn_result() + ..() + feedback_inc("mecha_reticence_created",1) + return + +/datum/construction/mecha/durand_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 + list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/durand_head) + ) + +/datum/construction/mecha/durand_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/durand_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/durand_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "durand0" + const_holder.density = 1 + qdel(src) + return + +/datum/construction/reversible/mecha/durand + result = "/obj/mecha/combat/durand" + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/durand_armor, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Super capacitor is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Super capacitor is installed."), + //8 + list("key"=/obj/item/stock_parts/capacitor/super, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Phasic scanner module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Phasic scanner module is installed."), + //10 + list("key"=/obj/item/stock_parts/scanning_module/phasic, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Scanning module is secured."), + //11 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Scanning module is installed."), + //12 + list("key"=/obj/item/circuitboard/mecha/durand/targeting, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //14 + list("key"=/obj/item/circuitboard/mecha/durand/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //15 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //16 + list("key"=/obj/item/circuitboard/mecha/durand/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //17 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + + +/datum/construction/reversible/mecha/durand/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/durand/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "durand2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "durand0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "durand3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "durand4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "durand2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "durand5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "durand3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "durand6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/durand/main(get_turf(holder)) + holder.icon_state = "durand4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "durand7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "durand5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "durand8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/durand/peripherals(get_turf(holder)) + holder.icon_state = "durand6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + holder.icon_state = "durand9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "durand7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "durand10" + else + user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") + new /obj/item/circuitboard/mecha/durand/targeting(get_turf(holder)) + holder.icon_state = "durand8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") + qdel(used_atom) + holder.icon_state = "durand11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "durand9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") + holder.icon_state = "durand12" + else + user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") + new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) + holder.icon_state = "durand10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") + qdel(used_atom) + holder.icon_state = "durand13" + else + user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") + holder.icon_state = "durand11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") + holder.icon_state = "durand14" + else + user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") + new /obj/item/stock_parts/capacitor/super(get_turf(holder)) + holder.icon_state = "durand12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "durand15" + else + user.visible_message("[user] unfastens the super capacitor.", "You unfasten the super capacitor.") + holder.icon_state = "durand13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "durand16" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "durand14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "durand17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "durand15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Durand Armor Plates to the [holder].", "You install Durand Armor Plates to the [holder].") + qdel(used_atom) + holder.icon_state = "durand18" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "durand16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Durand Armor Plates.", "You secure Durand Armor Plates.") + holder.icon_state = "durand19" + else + user.visible_message("[user] pries Durand Armor Plates from the [holder].", "You pry Durand Armor Plates from the [holder].") + new /obj/item/mecha_parts/part/durand_armor(get_turf(holder)) + holder.icon_state = "durand17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Durand Armor Plates to the [holder].", "You weld Durand Armor Plates to the [holder].") + else + user.visible_message("[user] unfastens Durand Armor Plates.", "You unfasten Durand Armor Plates.") + holder.icon_state = "durand18" + return 1 + +/datum/construction/reversible/mecha/durand/spawn_result() + ..() + feedback_inc("mecha_durand_created",1) + return + +//PHAZON + +/datum/construction/mecha/phazon_chassis + result = "/obj/mecha/combat/phazon" + steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 + list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/phazon_head) + ) + +/datum/construction/mecha/phazon_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/phazon_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/phazon_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "phazon0" + const_holder.density = 1 + qdel(src) + return + +/datum/construction/reversible/mecha/phazon + result = "/obj/mecha/combat/phazon" + steps = list( + //1 + list("key"=/obj/item/assembly/signaler/anomaly, + "backkey"=null, //Cannot remove the anomaly core once it's in + "desc"="Anomaly core socket is open and awaiting connection."), + //2 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //3 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //4 + list("key"=/obj/item/mecha_parts/part/phazon_armor, + "backkey"=TOOL_WELDER, + "desc"="Phase armor is welded."), + //5 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Phase armor is wrenched."), + //6 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Phase armor is installed."), + //7 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The bluespace crystal is engaged."), + //8 + list("key"=TOOL_SCREWDRIVER, + "backkey"=/obj/item/wirecutters, + "desc"="The bluespace crystal is connected."), + //9 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_CROWBAR, + "desc"="The bluespace crystal is installed."), + //10 + list("key"=/obj/item/stack/ore/bluespace_crystal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Super capacitor is secured."), + //11 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Super capacitor is installed."), + //12 + list("key"=/obj/item/stock_parts/capacitor/super, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Phasic scanner module is secured."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Phasic scanner module is installed."), + //14 + list("key"=/obj/item/stock_parts/scanning_module/phasic, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Scanning module is secured."), + //15 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Scanning module is installed."), + //16 + list("key"=/obj/item/circuitboard/mecha/phazon/targeting, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //17 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed"), + //18 + list("key"=/obj/item/circuitboard/mecha/phazon/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //19 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //20 + list("key"=/obj/item/circuitboard/mecha/phazon/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //21 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //22 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //23 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //24 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + + +/datum/construction/reversible/mecha/phazon/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/phazon/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(24) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(23) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "phazon2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "phazon0" + if(22) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "phazon3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(21) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "phazon4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "phazon2" + if(20) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "phazon5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "phazon3" + if(19) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "phazon6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/phazon/main(get_turf(holder)) + holder.icon_state = "phazon4" + if(18) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "phazon7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "phazon5" + if(17) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "phazon8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/phazon/peripherals(get_turf(holder)) + holder.icon_state = "phazon6" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + holder.icon_state = "phazon9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "phazon7" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "phazon10" + else + user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") + new /obj/item/circuitboard/mecha/phazon/targeting(get_turf(holder)) + holder.icon_state = "phazon8" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") + qdel(used_atom) + holder.icon_state = "phazon11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "phazon9" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") + holder.icon_state = "phazon12" + else + user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") + new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) + holder.icon_state = "phazon10" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") + qdel(used_atom) + holder.icon_state = "phazon13" + else + user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") + holder.icon_state = "phazon11" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") + holder.icon_state = "phazon14" + else + user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") + new /obj/item/stock_parts/capacitor/super(get_turf(holder)) + holder.icon_state = "phazon12" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the bluespace crystal.", "You install the bluespace crystals.") + holder.icon_state = "phazon15" + else + user.visible_message("[user] unsecures the super capacitor from the [holder].", "You unsecure the super capacitor from the [holder].") + holder.icon_state = "phazon13" + if(9) + if(diff==FORWARD) + user.visible_message("[user] connects the bluespace crystal.", "You connect the bluespace crystals.") + holder.icon_state = "phazon16" + else + user.visible_message("[user] removes the bluespace crystal from the [holder].", "You remove the bluespace crystal from the [holder].") + new /obj/item/stack/ore/bluespace_crystal(get_turf(holder), new_amount = 5) + holder.icon_state = "phazon14" + if(8) + if(diff==FORWARD) + user.visible_message("[user] engages the bluespace crystal.", "You engage the bluespace crystals.") + holder.icon_state = "phazon17" + else + user.visible_message("[user] disconnects the bluespace crystal from the [holder].", "You disconnect the bluespace crystal from the [holder].") + holder.icon_state = "phazon15" + if(7) + if(diff==FORWARD) + user.visible_message("[user] installs the phase armor layer to the [holder].", "You install the phase armor layer to the [holder].") + holder.icon_state = "phazon18" + else + user.visible_message("[user] disengages the bluespace crystal.", "You disengage the bluespace crystals.") + holder.icon_state = "phazon16" + if(6) + if(diff==FORWARD) + user.visible_message("[user] secures the phase armor layer.", "You secure the phase armor layer.") + holder.icon_state = "phazon19" + else + user.visible_message("[user] pries the phase armor layer from the [holder].", "You pry the phase armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "phazon17" + if(5) + if(diff==FORWARD) + user.visible_message("[user] welds the phase armor layer to the [holder].", "You weld the phase armor layer to the [holder].") + holder.icon_state = "phazon20" + else + user.visible_message("[user] unfastens the phase armor layer.", "You unfasten the phase armor layer.") + holder.icon_state = "phazon18" + if(4) + if(diff==FORWARD) + user.visible_message("[user] installs Phazon Armor Plates to the [holder].", "You install Phazon Armor Plates to the [holder].") + qdel(used_atom) + holder.icon_state = "phazon21" + else + user.visible_message("[user] cuts phase armor layer from the [holder].", "You cut the phase armor layer from the [holder].") + holder.icon_state = "phazon19" + if(3) + if(diff==FORWARD) + user.visible_message("[user] secures Phazon Armor Plates.", "You secure Phazon Armor Plates.") + holder.icon_state = "phazon22" + else + user.visible_message("[user] pries Phazon Armor Plates from the [holder].", "You pry Phazon Armor Plates from the [holder].") + new /obj/item/mecha_parts/part/phazon_armor(get_turf(holder)) + holder.icon_state = "phazon20" + if(2) + if(diff==FORWARD) + user.visible_message("[user] welds Phazon Armor Plates to the [holder].", "You weld Phazon Armor Plates to the [holder].") + else + user.visible_message("[user] unfastens Phazon Armor Plates.", "You unfasten Phazon Armor Plates.") + holder.icon_state = "phazon21" + if(1) + if(diff==FORWARD) + user.visible_message("[user] carefully inserts the anomaly core into \the [holder] and secures it.", "You slowly place the anomaly core into its socket and close its chamber.") + qdel(used_atom) + return 1 + +/datum/construction/reversible/mecha/phazon/spawn_result() + ..() + feedback_inc("mecha_phazon_created",1) + return + +//ODYSSEUS + +/datum/construction/mecha/odysseus_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 + list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 + list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 + list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 + list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 + list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 + ) + +/datum/construction/mecha/odysseus_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/odysseus_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/odysseus_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "odysseus0" + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/reversible/mecha/odysseus + result = "/obj/mecha/medical/odysseus" + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //8 + list("key"=/obj/item/circuitboard/mecha/odysseus/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //10 + list("key"=/obj/item/circuitboard/mecha/odysseus/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //11 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/odysseus/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "odysseus2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "odysseus0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "odysseus3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "odysseus4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "odysseus2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "odysseus5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "odysseus3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "odysseus6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/odysseus/main(get_turf(holder)) + holder.icon_state = "odysseus4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "odysseus7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "odysseus5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "odysseus8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) + holder.icon_state = "odysseus6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "odysseus9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "odysseus7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "odysseus10" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "odysseus8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "odysseus11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "odysseus9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs [used_atom] layer to the [holder].", "You install the external reinforced armor layer to the [holder].") + + holder.icon_state = "odysseus12" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "odysseus10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") + holder.icon_state = "odysseus13" + else + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + user.visible_message("[user] pries [MS] from the [holder].", "You pry [MS] from the [holder].") + holder.icon_state = "odysseus11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") + holder.icon_state = "odysseus14" + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "odysseus12" + return 1 + +/datum/construction/reversible/mecha/odysseus/spawn_result() + ..() + feedback_inc("mecha_odysseus_created",1) + return diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index 99e6ca72367..9a3dffb0c3b 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -1,161 +1,161 @@ -/obj/machinery/computer/mecha - name = "exosuit control console" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "rd_key" - icon_screen = "mecha" - light_color = LIGHT_COLOR_FADEDPURPLE - req_access = list(access_robotics) - circuit = /obj/item/circuitboard/mecha_control - var/list/located = list() - var/screen = 0 - var/stored_data - -/obj/machinery/computer/mecha/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/computer/mecha/attack_hand(mob/user) - ui_interact(user) - -/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "exosuit_control.tmpl", "Exosuit Control Console", 420, 500) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/mecha/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["screen"] = screen - if(screen == 0) - var/list/mechas[0] - for(var/obj/item/mecha_parts/mecha_tracking/TR in world) - var/answer = TR.get_mecha_info() - if(answer) - mechas[++mechas.len] = answer - data["mechas"] = mechas - if(screen == 1) - data["log"] = stored_data - return data - -/obj/machinery/computer/mecha/Topic(href, href_list) - if(..()) - return 1 - - var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) - if(href_list["send_message"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("send_message") - var/message = strip_html_simple(input(usr,"Input message","Transmit message") as text) - if(!trim(message) || ..()) - return 1 - var/obj/mecha/M = MT.in_mecha() - if(M) - M.occupant_message(message) - - if(href_list["shock"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("shock") - MT.shock() - - if(href_list["get_log"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("get_log") - stored_data = MT.get_mecha_log() - screen = 1 - - if(href_list["return"]) - screen = 0 - - SSnanoui.update_uis(src) - return - -/obj/item/mecha_parts/mecha_tracking - name = "Exosuit tracking beacon" - desc = "Device used to transmit exosuit data." - icon = 'icons/obj/device.dmi' - icon_state = "motion2" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "programming=2;magnets=2" - var/ai_beacon = FALSE //If this beacon allows for AI control. Exists to avoid using istype() on checking. - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info() - if(!in_mecha()) - return FALSE - var/obj/mecha/M = loc - var/list/answer[0] - answer["reference"] = "\ref[src]" - answer["name"] = sanitize(replacetext(M.name,"\"","'")) // Double apostrophes break JSON - if(M.cell) - answer["cell"] = 1 - answer["cell_capacity"] = M.cell.maxcharge - answer["cell_current"] = M.get_charge() - answer["cell_percentage"] = round(M.cell.percent()) - else - answer["cell"] = 0 - answer["integrity"] = round((M.obj_integrity/M.max_integrity*100), 0.01) - answer["airtank"] = M.return_pressure() - answer["pilot"] = "[M.occupant||"None"]" - var/area/area = get_area(M) - answer["location"] = "[sanitize(area.name)||"Unknown"]" - answer["equipment"] = "[M.selected||"None"]" - if(istype(M, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/RM = M - answer["hascargo"] = 1 - answer["cargo"] = RM.cargo.len/RM.cargo_capacity*100 - - return answer - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info_text() - if(!in_mecha()) - return FALSE - var/obj/mecha/M = loc - var/cell_charge = M.get_charge() - var/area/A = get_area(M) - var/answer = {"Name: [M.name] - Integrity: [M.obj_integrity / M.max_integrity * 100]% - Cell charge: [isnull(cell_charge)?"Not found":"[M.cell.percent()]%"] - Airtank: [M.return_pressure()]kPa - Pilot: [M.occupant||"None"] - Location: [sanitize(A.name)||"Unknown"] - Active equipment: [M.selected||"None"]
    "} - if(istype(M, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/RM = M - answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
    " - - return answer - -/obj/item/mecha_parts/mecha_tracking/emp_act() - qdel(src) - -/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() - if(istype(loc, /obj/mecha)) - return loc - return FALSE - -/obj/item/mecha_parts/mecha_tracking/proc/shock() - var/obj/mecha/M = in_mecha() - if(M) - M.emp_act(2) - qdel(src) - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() - if(!in_mecha()) - return 0 - var/obj/mecha/M = loc - return M.get_log_html() - -/obj/item/mecha_parts/mecha_tracking/ai_control - name = "exosuit AI control beacon" - desc = "A device used to transmit exosuit data. Also allows active AI units to take control of said exosuit." - origin_tech = "programming=3;magnets=2;engineering=2" - ai_beacon = TRUE - -/obj/item/storage/box/mechabeacons - name = "Exosuit Tracking Beacons" - -/obj/item/storage/box/mechabeacons/New() - ..() - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) +/obj/machinery/computer/mecha + name = "exosuit control console" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "rd_key" + icon_screen = "mecha" + light_color = LIGHT_COLOR_FADEDPURPLE + req_access = list(ACCESS_ROBOTICS) + circuit = /obj/item/circuitboard/mecha_control + var/list/located = list() + var/screen = 0 + var/stored_data + +/obj/machinery/computer/mecha/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/computer/mecha/attack_hand(mob/user) + ui_interact(user) + +/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "exosuit_control.tmpl", "Exosuit Control Console", 420, 500) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/mecha/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["screen"] = screen + if(screen == 0) + var/list/mechas[0] + for(var/obj/item/mecha_parts/mecha_tracking/TR in world) + var/answer = TR.get_mecha_info() + if(answer) + mechas[++mechas.len] = answer + data["mechas"] = mechas + if(screen == 1) + data["log"] = stored_data + return data + +/obj/machinery/computer/mecha/Topic(href, href_list) + if(..()) + return 1 + + var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) + if(href_list["send_message"]) + var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("send_message") + var/message = strip_html_simple(input(usr,"Input message","Transmit message") as text) + if(!trim(message) || ..()) + return 1 + var/obj/mecha/M = MT.in_mecha() + if(M) + M.occupant_message(message) + + if(href_list["shock"]) + var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("shock") + MT.shock() + + if(href_list["get_log"]) + var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("get_log") + stored_data = MT.get_mecha_log() + screen = 1 + + if(href_list["return"]) + screen = 0 + + SSnanoui.update_uis(src) + return + +/obj/item/mecha_parts/mecha_tracking + name = "Exosuit tracking beacon" + desc = "Device used to transmit exosuit data." + icon = 'icons/obj/device.dmi' + icon_state = "motion2" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "programming=2;magnets=2" + var/ai_beacon = FALSE //If this beacon allows for AI control. Exists to avoid using istype() on checking. + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info() + if(!in_mecha()) + return FALSE + var/obj/mecha/M = loc + var/list/answer[0] + answer["reference"] = "\ref[src]" + answer["name"] = sanitize(replacetext(M.name,"\"","'")) // Double apostrophes break JSON + if(M.cell) + answer["cell"] = 1 + answer["cell_capacity"] = M.cell.maxcharge + answer["cell_current"] = M.get_charge() + answer["cell_percentage"] = round(M.cell.percent()) + else + answer["cell"] = 0 + answer["integrity"] = round((M.obj_integrity/M.max_integrity*100), 0.01) + answer["airtank"] = M.return_pressure() + answer["pilot"] = "[M.occupant||"None"]" + var/area/area = get_area(M) + answer["location"] = "[sanitize(area.name)||"Unknown"]" + answer["equipment"] = "[M.selected||"None"]" + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + answer["hascargo"] = 1 + answer["cargo"] = RM.cargo.len/RM.cargo_capacity*100 + + return answer + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info_text() + if(!in_mecha()) + return FALSE + var/obj/mecha/M = loc + var/cell_charge = M.get_charge() + var/area/A = get_area(M) + var/answer = {"Name: [M.name] + Integrity: [M.obj_integrity / M.max_integrity * 100]% + Cell charge: [isnull(cell_charge)?"Not found":"[M.cell.percent()]%"] + Airtank: [M.return_pressure()]kPa + Pilot: [M.occupant||"None"] + Location: [sanitize(A.name)||"Unknown"] + Active equipment: [M.selected||"None"]
    "} + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
    " + + return answer + +/obj/item/mecha_parts/mecha_tracking/emp_act() + qdel(src) + +/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() + if(istype(loc, /obj/mecha)) + return loc + return FALSE + +/obj/item/mecha_parts/mecha_tracking/proc/shock() + var/obj/mecha/M = in_mecha() + if(M) + M.emp_act(2) + qdel(src) + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() + if(!in_mecha()) + return 0 + var/obj/mecha/M = loc + return M.get_log_html() + +/obj/item/mecha_parts/mecha_tracking/ai_control + name = "exosuit AI control beacon" + desc = "A device used to transmit exosuit data. Also allows active AI units to take control of said exosuit." + origin_tech = "programming=3;magnets=2;engineering=2" + ai_beacon = TRUE + +/obj/item/storage/box/mechabeacons + name = "Exosuit Tracking Beacons" + +/obj/item/storage/box/mechabeacons/New() + ..() + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) diff --git a/code/game/mecha/mecha_modkit.dm b/code/game/mecha/mecha_modkit.dm index 9a75fc75077..9d07e760c79 100644 --- a/code/game/mecha/mecha_modkit.dm +++ b/code/game/mecha/mecha_modkit.dm @@ -75,4 +75,4 @@ critdestrsound = null weapdestrsound = null lowpowersound = null - longactivationsound = null \ No newline at end of file + longactivationsound = null diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm index 6aff10eb423..86fd9cb6821 100644 --- a/code/game/mecha/mecha_parts.dm +++ b/code/game/mecha/mecha_parts.dm @@ -1,457 +1,457 @@ -///////////////////////// -////// Mecha Parts ////// -///////////////////////// - -/obj/item/mecha_parts - name = "mecha part" - icon = 'icons/mecha/mech_construct.dmi' - icon_state = "blank" - w_class = WEIGHT_CLASS_GIGANTIC - flags = CONDUCT - origin_tech = "programming=2;materials=2;engineering=2" - - -/obj/item/mecha_parts/chassis - name="Mecha Chassis" - icon_state = "backbone" - var/datum/construction/construct - flags = CONDUCT - -/obj/item/mecha_parts/chassis/Destroy() - QDEL_NULL(construct) - return ..() - -/obj/item/mecha_parts/chassis/attackby(obj/item/W, mob/user, params) - if(!construct || !construct.action(W, user)) - return ..() - -/obj/item/mecha_parts/chassis/attack_hand() - return - -/////////// Ripley - -/obj/item/mecha_parts/chassis/ripley - name = "Ripley Chassis" - -/obj/item/mecha_parts/chassis/ripley/New() - ..() - construct = new /datum/construction/mecha/ripley_chassis(src) - -/obj/item/mecha_parts/part/ripley_torso - name="Ripley Torso" - desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." - icon_state = "ripley_harness" - origin_tech = "programming=2;materials=2;biotech=2;engineering=2" - -/obj/item/mecha_parts/part/ripley_left_arm - name="Ripley Left Arm" - desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_l_arm" - -/obj/item/mecha_parts/part/ripley_right_arm - name="Ripley Right Arm" - desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_r_arm" - -/obj/item/mecha_parts/part/ripley_left_leg - name="Ripley Left Leg" - desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_l_leg" - -/obj/item/mecha_parts/part/ripley_right_leg - name="Ripley Right Leg" - desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_r_leg" - -///////// Gygax - -/obj/item/mecha_parts/chassis/gygax - name = "Gygax Chassis" - -/obj/item/mecha_parts/chassis/gygax/New() - ..() - construct = new /datum/construction/mecha/gygax_chassis(src) - -/obj/item/mecha_parts/part/gygax_torso - name="Gygax Torso" - desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." - icon_state = "gygax_harness" - origin_tech = "programming=2;materials=4;biotech=3;engineering=3" - -/obj/item/mecha_parts/part/gygax_head - name="Gygax Head" - desc="A Gygax head. Houses advanced surveilance and targeting sensors." - icon_state = "gygax_head" - origin_tech = "programming=2;materials=4;magnets=3;engineering=3" - -/obj/item/mecha_parts/part/gygax_left_arm - name="Gygax Left Arm" - desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_l_arm" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_right_arm - name="Gygax Right Arm" - desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_r_arm" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_left_leg - name="Gygax Left Leg" - icon_state = "gygax_l_leg" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_right_leg - name="Gygax Right Leg" - icon_state = "gygax_r_leg" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_armour - name="Gygax Armour Plates" - icon_state = "gygax_armour" - origin_tech = "materials=6;combat=4;engineering=4" - - -//////////// Durand - -/obj/item/mecha_parts/chassis/durand - name = "Durand Chassis" - -/obj/item/mecha_parts/chassis/durand/New() - ..() - construct = new /datum/construction/mecha/durand_chassis(src) - -/obj/item/mecha_parts/part/durand_torso - name="Durand Torso" - icon_state = "durand_harness" - origin_tech = "programming=2;materials=3;biotech=3;engineering=3" - -/obj/item/mecha_parts/part/durand_head - name="Durand Head" - icon_state = "durand_head" - origin_tech = "programming=2;materials=3;magnets=3;engineering=3" - -/obj/item/mecha_parts/part/durand_left_arm - name="Durand Left Arm" - icon_state = "durand_l_arm" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_right_arm - name="Durand Right Arm" - icon_state = "durand_r_arm" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_left_leg - name="Durand Left Leg" - icon_state = "durand_l_leg" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_right_leg - name="Durand Right Leg" - icon_state = "durand_r_leg" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_armor - name="Durand Armour Plates" - icon_state = "durand_armor" - origin_tech = "materials=5;combat=4;engineering=4" - - - -////////// Firefighter - -/obj/item/mecha_parts/chassis/firefighter - name = "Firefighter Chassis" - -/obj/item/mecha_parts/chassis/firefighter/New() - ..() - construct = new /datum/construction/mecha/firefighter_chassis(src) - -////////// HONK - -/obj/item/mecha_parts/chassis/honker - name = "H.O.N.K Chassis" - -/obj/item/mecha_parts/chassis/honker/New() - ..() - construct = new /datum/construction/mecha/honker_chassis(src) - -/obj/item/mecha_parts/part/honker_torso - name="H.O.N.K Torso" - icon_state = "honker_harness" - -/obj/item/mecha_parts/part/honker_head - name="H.O.N.K Head" - icon_state = "honker_head" - -/obj/item/mecha_parts/part/honker_left_arm - name="H.O.N.K Left Arm" - icon_state = "honker_l_arm" - -/obj/item/mecha_parts/part/honker_right_arm - name="H.O.N.K Right Arm" - icon_state = "honker_r_arm" - -/obj/item/mecha_parts/part/honker_left_leg - name="H.O.N.K Left Leg" - icon_state = "honker_l_leg" - -/obj/item/mecha_parts/part/honker_right_leg - name="H.O.N.K Right Leg" - icon_state = "honker_r_leg" - - -////////// Reticence - -/obj/item/mecha_parts/chassis/reticence - name = "Reticence Chassis" - -/obj/item/mecha_parts/chassis/reticence/New() - ..() - construct = new /datum/construction/mecha/reticence_chassis(src) - -/obj/effect/dummy/mecha_emote_step - var/emote - -/obj/effect/dummy/mecha_emote_step/New(e) - emote = e - -/obj/item/mecha_parts/chassis/reticence/hear_message(mob/living/M, msg) - if(!istype(M) || !istype(construct, /datum/construction/mecha/reticence)) - return - // is the current step the dummy emote object? - var/list/steps = construct.steps - if(steps[steps.len]["key"] == /obj/effect/dummy/mecha_emote_step) - construct.action(new /obj/effect/dummy/mecha_emote_step(msg), M) - -/obj/item/mecha_parts/part/reticence_torso - name = "Reticence Torso" - icon_state = "reticence_harness" - -/obj/item/mecha_parts/part/reticence_head - name = "Reticence Head" - icon_state = "reticence_head" - -/obj/item/mecha_parts/part/reticence_left_arm - name = "Reticence Left Arm" - icon_state = "reticence_l_arm" - -/obj/item/mecha_parts/part/reticence_right_arm - name = "Reticence Right Arm" - icon_state = "reticence_r_arm" - -/obj/item/mecha_parts/part/reticence_left_leg - name = "Reticence Left Leg" - icon_state = "reticence_l_leg" - -/obj/item/mecha_parts/part/reticence_right_leg - name = "Reticence Right Leg" - icon_state = "reticence_r_leg" - - -////////// Phazon - -/obj/item/mecha_parts/chassis/phazon - name = "Phazon Chassis" - -/obj/item/mecha_parts/chassis/phazon/New() - ..() - construct = new /datum/construction/mecha/phazon_chassis(src) - -/obj/item/mecha_parts/part/phazon_torso - name="Phazon Torso" - icon_state = "phazon_harness" - origin_tech = "programming=4;materials=4;bluespace=4;plasmatech=5" - -/obj/item/mecha_parts/part/phazon_head - name="Phazon Head" - icon_state = "phazon_head" - origin_tech = "programming=3;materials=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_left_arm - name="Phazon Left Arm" - icon_state = "phazon_l_arm" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_right_arm - name="Phazon Right Arm" - icon_state = "phazon_r_arm" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_left_leg - name="Phazon Left Leg" - icon_state = "phazon_l_leg" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_right_leg - name="Phazon Right Leg" - icon_state = "phazon_r_leg" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_armor - name="Phazon armor" - desc="Phazon armor plates. They are layered with plasma to protect the pilot from the stress of phasing and have unusual properties." - icon_state = "phazon_armor" - origin_tech = "materials=4;bluespace=4;plasmatech=5" - -///////// Odysseus -/obj/item/mecha_parts/chassis/odysseus - name = "Odysseus Chassis" - -/obj/item/mecha_parts/chassis/odysseus/New() - ..() - construct = new /datum/construction/mecha/odysseus_chassis(src) - -/obj/item/mecha_parts/part/odysseus_head - name="Odysseus Head" - icon_state = "odysseus_head" - -/obj/item/mecha_parts/part/odysseus_torso - name="Odysseus Torso" - desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." - icon_state = "odysseus_torso" - origin_tech = "programming=2;materials=2;biotech=2;engineering=2" - -/obj/item/mecha_parts/part/odysseus_left_arm - name="Odysseus Left Arm" - desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_l_arm" - -/obj/item/mecha_parts/part/odysseus_right_arm - name="Odysseus Right Arm" - desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_r_arm" - -/obj/item/mecha_parts/part/odysseus_left_leg - name="Odysseus Left Leg" - desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_l_leg" - -/obj/item/mecha_parts/part/odysseus_right_leg - name="Odysseus Right Leg" - desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_r_leg" - -/*/obj/item/mecha_parts/part/odysseus_armour - name="Odysseus Carapace" - icon_state = "odysseus_armour" - origin_tech = "materials=3;engineering=3")*/ - - -///////// Circuitboards - -/obj/item/circuitboard/mecha - name = "Exosuit Circuit board" - icon = 'icons/obj/module.dmi' - icon_state = "std_mod" - item_state = "electronic" - board_type = "other" - flags = CONDUCT - force = 5.0 - w_class = WEIGHT_CLASS_SMALL - throwforce = 5.0 - throw_speed = 3 - throw_range = 15 - -/obj/item/circuitboard/mecha/ripley - origin_tech = "programming=2" - -/obj/item/circuitboard/mecha/ripley/peripherals - name = "Circuit board (Ripley Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/ripley/main - name = "Circuit board (Ripley Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/gygax - origin_tech = "programming=4;combat=3;engineering=3" - -/obj/item/circuitboard/mecha/gygax/peripherals - name = "Circuit board (Gygax Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/gygax/targeting - name = "Circuit board (Gygax Weapon Control and Targeting module)" - icon_state = "mcontroller" - origin_tech = "programming=4;combat=4" - -/obj/item/circuitboard/mecha/gygax/main - name = "Circuit board (Gygax Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/durand - origin_tech = "programming=4;combat=3;engineering=3" - -/obj/item/circuitboard/mecha/durand/peripherals - name = "Circuit board (Durand Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/durand/targeting - name = "Circuit board (Durand Weapon Control and Targeting module)" - icon_state = "mcontroller" - origin_tech = "programming=4;combat=4;engineering=3" - -/obj/item/circuitboard/mecha/durand/main - name = "Circuit board (Durand Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/phazon - origin_tech = "programming=5;plasmatech=4" - -/obj/item/circuitboard/mecha/phazon/peripherals - name = "Circuit board (Phazon Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/phazon/targeting - name = "Circuit board (Phazon Weapon Control and Targeting module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/phazon/main - name = "Circuit board (Phazon Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/honker - origin_tech = "programming=3;engineering=3" - -/obj/item/circuitboard/mecha/honker/peripherals - name = "Circuit board (H.O.N.K Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/honker/targeting - name = "Circuit board (H.O.N.K Weapon Control and Targeting module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/honker/main - name = "Circuit board (H.O.N.K Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/reticence - origin_tech = "programming=3;engineering=3" - -/obj/item/circuitboard/mecha/reticence/peripherals - name = "circuit board (Reticence Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/reticence/targeting - name = "circuit board (Reticence Weapon Control and Targeting module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/reticence/main - name = "circuit board (Reticence Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/odysseus - origin_tech = "programming=3;biotech=3" - -/obj/item/circuitboard/mecha/odysseus/peripherals - name = "Circuit board (Odysseus Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/odysseus/main - name = "Circuit board (Odysseus Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/pod - name = "Circuit board (Space Pod Mainboard)" - icon_state = "mainboard" +///////////////////////// +////// Mecha Parts ////// +///////////////////////// + +/obj/item/mecha_parts + name = "mecha part" + icon = 'icons/mecha/mech_construct.dmi' + icon_state = "blank" + w_class = WEIGHT_CLASS_GIGANTIC + flags = CONDUCT + origin_tech = "programming=2;materials=2;engineering=2" + + +/obj/item/mecha_parts/chassis + name="Mecha Chassis" + icon_state = "backbone" + var/datum/construction/construct + flags = CONDUCT + +/obj/item/mecha_parts/chassis/Destroy() + QDEL_NULL(construct) + return ..() + +/obj/item/mecha_parts/chassis/attackby(obj/item/W, mob/user, params) + if(!construct || !construct.action(W, user)) + return ..() + +/obj/item/mecha_parts/chassis/attack_hand() + return + +/////////// Ripley + +/obj/item/mecha_parts/chassis/ripley + name = "Ripley Chassis" + +/obj/item/mecha_parts/chassis/ripley/New() + ..() + construct = new /datum/construction/mecha/ripley_chassis(src) + +/obj/item/mecha_parts/part/ripley_torso + name="Ripley Torso" + desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." + icon_state = "ripley_harness" + origin_tech = "programming=2;materials=2;biotech=2;engineering=2" + +/obj/item/mecha_parts/part/ripley_left_arm + name="Ripley Left Arm" + desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_l_arm" + +/obj/item/mecha_parts/part/ripley_right_arm + name="Ripley Right Arm" + desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_r_arm" + +/obj/item/mecha_parts/part/ripley_left_leg + name="Ripley Left Leg" + desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_l_leg" + +/obj/item/mecha_parts/part/ripley_right_leg + name="Ripley Right Leg" + desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_r_leg" + +///////// Gygax + +/obj/item/mecha_parts/chassis/gygax + name = "Gygax Chassis" + +/obj/item/mecha_parts/chassis/gygax/New() + ..() + construct = new /datum/construction/mecha/gygax_chassis(src) + +/obj/item/mecha_parts/part/gygax_torso + name="Gygax Torso" + desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." + icon_state = "gygax_harness" + origin_tech = "programming=2;materials=4;biotech=3;engineering=3" + +/obj/item/mecha_parts/part/gygax_head + name="Gygax Head" + desc="A Gygax head. Houses advanced surveilance and targeting sensors." + icon_state = "gygax_head" + origin_tech = "programming=2;materials=4;magnets=3;engineering=3" + +/obj/item/mecha_parts/part/gygax_left_arm + name="Gygax Left Arm" + desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_l_arm" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_right_arm + name="Gygax Right Arm" + desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_r_arm" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_left_leg + name="Gygax Left Leg" + icon_state = "gygax_l_leg" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_right_leg + name="Gygax Right Leg" + icon_state = "gygax_r_leg" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_armour + name="Gygax Armour Plates" + icon_state = "gygax_armour" + origin_tech = "materials=6;combat=4;engineering=4" + + +//////////// Durand + +/obj/item/mecha_parts/chassis/durand + name = "Durand Chassis" + +/obj/item/mecha_parts/chassis/durand/New() + ..() + construct = new /datum/construction/mecha/durand_chassis(src) + +/obj/item/mecha_parts/part/durand_torso + name="Durand Torso" + icon_state = "durand_harness" + origin_tech = "programming=2;materials=3;biotech=3;engineering=3" + +/obj/item/mecha_parts/part/durand_head + name="Durand Head" + icon_state = "durand_head" + origin_tech = "programming=2;materials=3;magnets=3;engineering=3" + +/obj/item/mecha_parts/part/durand_left_arm + name="Durand Left Arm" + icon_state = "durand_l_arm" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_right_arm + name="Durand Right Arm" + icon_state = "durand_r_arm" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_left_leg + name="Durand Left Leg" + icon_state = "durand_l_leg" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_right_leg + name="Durand Right Leg" + icon_state = "durand_r_leg" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_armor + name="Durand Armour Plates" + icon_state = "durand_armor" + origin_tech = "materials=5;combat=4;engineering=4" + + + +////////// Firefighter + +/obj/item/mecha_parts/chassis/firefighter + name = "Firefighter Chassis" + +/obj/item/mecha_parts/chassis/firefighter/New() + ..() + construct = new /datum/construction/mecha/firefighter_chassis(src) + +////////// HONK + +/obj/item/mecha_parts/chassis/honker + name = "H.O.N.K Chassis" + +/obj/item/mecha_parts/chassis/honker/New() + ..() + construct = new /datum/construction/mecha/honker_chassis(src) + +/obj/item/mecha_parts/part/honker_torso + name="H.O.N.K Torso" + icon_state = "honker_harness" + +/obj/item/mecha_parts/part/honker_head + name="H.O.N.K Head" + icon_state = "honker_head" + +/obj/item/mecha_parts/part/honker_left_arm + name="H.O.N.K Left Arm" + icon_state = "honker_l_arm" + +/obj/item/mecha_parts/part/honker_right_arm + name="H.O.N.K Right Arm" + icon_state = "honker_r_arm" + +/obj/item/mecha_parts/part/honker_left_leg + name="H.O.N.K Left Leg" + icon_state = "honker_l_leg" + +/obj/item/mecha_parts/part/honker_right_leg + name="H.O.N.K Right Leg" + icon_state = "honker_r_leg" + + +////////// Reticence + +/obj/item/mecha_parts/chassis/reticence + name = "Reticence Chassis" + +/obj/item/mecha_parts/chassis/reticence/New() + ..() + construct = new /datum/construction/mecha/reticence_chassis(src) + +/obj/effect/dummy/mecha_emote_step + var/emote + +/obj/effect/dummy/mecha_emote_step/New(e) + emote = e + +/obj/item/mecha_parts/chassis/reticence/hear_message(mob/living/M, msg) + if(!istype(M) || !istype(construct, /datum/construction/mecha/reticence)) + return + // is the current step the dummy emote object? + var/list/steps = construct.steps + if(steps[steps.len]["key"] == /obj/effect/dummy/mecha_emote_step) + construct.action(new /obj/effect/dummy/mecha_emote_step(msg), M) + +/obj/item/mecha_parts/part/reticence_torso + name = "Reticence Torso" + icon_state = "reticence_harness" + +/obj/item/mecha_parts/part/reticence_head + name = "Reticence Head" + icon_state = "reticence_head" + +/obj/item/mecha_parts/part/reticence_left_arm + name = "Reticence Left Arm" + icon_state = "reticence_l_arm" + +/obj/item/mecha_parts/part/reticence_right_arm + name = "Reticence Right Arm" + icon_state = "reticence_r_arm" + +/obj/item/mecha_parts/part/reticence_left_leg + name = "Reticence Left Leg" + icon_state = "reticence_l_leg" + +/obj/item/mecha_parts/part/reticence_right_leg + name = "Reticence Right Leg" + icon_state = "reticence_r_leg" + + +////////// Phazon + +/obj/item/mecha_parts/chassis/phazon + name = "Phazon Chassis" + +/obj/item/mecha_parts/chassis/phazon/New() + ..() + construct = new /datum/construction/mecha/phazon_chassis(src) + +/obj/item/mecha_parts/part/phazon_torso + name="Phazon Torso" + icon_state = "phazon_harness" + origin_tech = "programming=4;materials=4;bluespace=4;plasmatech=5" + +/obj/item/mecha_parts/part/phazon_head + name="Phazon Head" + icon_state = "phazon_head" + origin_tech = "programming=3;materials=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_left_arm + name="Phazon Left Arm" + icon_state = "phazon_l_arm" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_right_arm + name="Phazon Right Arm" + icon_state = "phazon_r_arm" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_left_leg + name="Phazon Left Leg" + icon_state = "phazon_l_leg" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_right_leg + name="Phazon Right Leg" + icon_state = "phazon_r_leg" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_armor + name="Phazon armor" + desc="Phazon armor plates. They are layered with plasma to protect the pilot from the stress of phasing and have unusual properties." + icon_state = "phazon_armor" + origin_tech = "materials=4;bluespace=4;plasmatech=5" + +///////// Odysseus +/obj/item/mecha_parts/chassis/odysseus + name = "Odysseus Chassis" + +/obj/item/mecha_parts/chassis/odysseus/New() + ..() + construct = new /datum/construction/mecha/odysseus_chassis(src) + +/obj/item/mecha_parts/part/odysseus_head + name="Odysseus Head" + icon_state = "odysseus_head" + +/obj/item/mecha_parts/part/odysseus_torso + name="Odysseus Torso" + desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." + icon_state = "odysseus_torso" + origin_tech = "programming=2;materials=2;biotech=2;engineering=2" + +/obj/item/mecha_parts/part/odysseus_left_arm + name="Odysseus Left Arm" + desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_l_arm" + +/obj/item/mecha_parts/part/odysseus_right_arm + name="Odysseus Right Arm" + desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_r_arm" + +/obj/item/mecha_parts/part/odysseus_left_leg + name="Odysseus Left Leg" + desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_l_leg" + +/obj/item/mecha_parts/part/odysseus_right_leg + name="Odysseus Right Leg" + desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_r_leg" + +/*/obj/item/mecha_parts/part/odysseus_armour + name="Odysseus Carapace" + icon_state = "odysseus_armour" + origin_tech = "materials=3;engineering=3")*/ + + +///////// Circuitboards + +/obj/item/circuitboard/mecha + name = "Exosuit Circuit board" + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + item_state = "electronic" + board_type = "other" + flags = CONDUCT + force = 5.0 + w_class = WEIGHT_CLASS_SMALL + throwforce = 5.0 + throw_speed = 3 + throw_range = 15 + +/obj/item/circuitboard/mecha/ripley + origin_tech = "programming=2" + +/obj/item/circuitboard/mecha/ripley/peripherals + name = "Circuit board (Ripley Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/ripley/main + name = "Circuit board (Ripley Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/gygax + origin_tech = "programming=4;combat=3;engineering=3" + +/obj/item/circuitboard/mecha/gygax/peripherals + name = "Circuit board (Gygax Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/gygax/targeting + name = "Circuit board (Gygax Weapon Control and Targeting module)" + icon_state = "mcontroller" + origin_tech = "programming=4;combat=4" + +/obj/item/circuitboard/mecha/gygax/main + name = "Circuit board (Gygax Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/durand + origin_tech = "programming=4;combat=3;engineering=3" + +/obj/item/circuitboard/mecha/durand/peripherals + name = "Circuit board (Durand Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/durand/targeting + name = "Circuit board (Durand Weapon Control and Targeting module)" + icon_state = "mcontroller" + origin_tech = "programming=4;combat=4;engineering=3" + +/obj/item/circuitboard/mecha/durand/main + name = "Circuit board (Durand Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/phazon + origin_tech = "programming=5;plasmatech=4" + +/obj/item/circuitboard/mecha/phazon/peripherals + name = "Circuit board (Phazon Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/phazon/targeting + name = "Circuit board (Phazon Weapon Control and Targeting module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/phazon/main + name = "Circuit board (Phazon Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/honker + origin_tech = "programming=3;engineering=3" + +/obj/item/circuitboard/mecha/honker/peripherals + name = "Circuit board (H.O.N.K Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/honker/targeting + name = "Circuit board (H.O.N.K Weapon Control and Targeting module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/honker/main + name = "Circuit board (H.O.N.K Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/reticence + origin_tech = "programming=3;engineering=3" + +/obj/item/circuitboard/mecha/reticence/peripherals + name = "circuit board (Reticence Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/reticence/targeting + name = "circuit board (Reticence Weapon Control and Targeting module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/reticence/main + name = "circuit board (Reticence Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/odysseus + origin_tech = "programming=3;biotech=3" + +/obj/item/circuitboard/mecha/odysseus/peripherals + name = "Circuit board (Odysseus Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/odysseus/main + name = "Circuit board (Odysseus Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/pod + name = "Circuit board (Space Pod Mainboard)" + icon_state = "mainboard" diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm index 9598d328d7a..fc8f38a971c 100644 --- a/code/game/mecha/mecha_topic.dm +++ b/code/game/mecha/mecha_topic.dm @@ -18,8 +18,8 @@ .hidden {display: none;} - - - -
    - - - - -"} - - for(var/adm_ckey in admin_datums) - var/datum/admins/D = admin_datums[adm_ckey] - if(!D) continue - var/rank = D.rank ? D.rank : "*none*" - var/rights = rights2text(D.rights," ") - if(!rights) rights = "*none*" - output += {" - - - -"} - - /*output += "" - output += "" - output += "" - output += "" - output += ""*/ - - output += {" -
    CKEY \[+\]RANKPERMISSIONS
    [adm_ckey] \[-\][rank][rights]
    [adm_ckey] \[-\][rank][rights]
    -
    Search:
    - -"} - - usr << browse(output,"window=editrights;size=600x500") - -/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) - if(config.admin_legacy_system) return - - if(!usr.client) - return - - if(!check_rights(R_PERMISSIONS)) - return - - establish_db_connection() - - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - if(!adm_ckey || !new_rank) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(!istext(adm_ckey) || !istext(new_rank)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/new_admin = 1 - var/admin_id - while(select_query.NextRow()) - new_admin = 0 - admin_id = text2num(select_query.item[1]) - - flag_account_for_forum_sync(adm_ckey) - if(new_admin) - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin")] (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") - log_query.Execute() - to_chat(usr, "New admin added.") - else - if(!isnull(admin_id) && isnum(admin_id)) - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") - log_query.Execute() - to_chat(usr, "Admin rank changed.") - -/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) - if(config.admin_legacy_system) - return - - if(!usr.client) - return - - if(!check_rights(R_PERMISSIONS)) - return - - establish_db_connection() - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - if(!adm_ckey || !new_permission) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(istext(new_permission)) - new_permission = text2num(new_permission) - - if(!istext(adm_ckey) || !isnum(new_permission)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/admin_id - var/admin_rights - while(select_query.NextRow()) - admin_id = text2num(select_query.item[1]) - admin_rights = text2num(select_query.item[2]) - - if(!admin_id) - return - - flag_account_for_forum_sync(adm_ckey) - if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") - log_query.Execute() - to_chat(usr, "Permission removed.") - else //This admin doesn't have this permission, so we are adding it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") - log_query.Execute() - to_chat(usr, "Permission added.") - -/datum/admins/proc/updateranktodb(ckey,newrank) - establish_db_connection() - if(!dbcon.IsConnected()) - return - if(!check_rights(R_PERMISSIONS)) - return - var/sql_ckey = sanitizeSQL(ckey) - var/sql_admin_rank = sanitizeSQL(newrank) - - var/DBQuery/query_update = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'") - query_update.Execute() - flag_account_for_forum_sync(sql_ckey) \ No newline at end of file +/client/proc/edit_admin_permissions() + set category = "Admin" + set name = "Permissions Panel" + set desc = "Edit admin permissions" + if(!check_rights(R_PERMISSIONS)) + return + usr.client.holder.edit_admin_permissions() + +/datum/admins/proc/edit_admin_permissions() + if(!check_rights(R_PERMISSIONS)) + return + + var/output = {" + + +Permissions Panel + + + + +
    + + + + +"} + + for(var/adm_ckey in GLOB.admin_datums) + var/datum/admins/D = GLOB.admin_datums[adm_ckey] + if(!D) continue + var/rank = D.rank ? D.rank : "*none*" + var/rights = rights2text(D.rights," ") + if(!rights) rights = "*none*" + output += {" + + + +"} + + /*output += "" + output += "" + output += "" + output += "" + output += ""*/ + + output += {" +
    CKEY \[+\]RANKPERMISSIONS
    [adm_ckey] \[-\][rank][rights]
    [adm_ckey] \[-\][rank][rights]
    +
    Search:
    + +"} + + usr << browse(output,"window=editrights;size=600x500") + +/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) + if(config.admin_legacy_system) return + + if(!usr.client) + return + + if(!check_rights(R_PERMISSIONS)) + return + + establish_db_connection() + + if(!GLOB.dbcon.IsConnected()) + to_chat(usr, "Failed to establish database connection") + return + + if(!adm_ckey || !new_rank) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(!istext(adm_ckey) || !istext(new_rank)) + return + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/new_admin = 1 + var/admin_id + while(select_query.NextRow()) + new_admin = 0 + admin_id = text2num(select_query.item[1]) + + flag_account_for_forum_sync(adm_ckey) + if(new_admin) + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin")] (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") + insert_query.Execute() + var/DBQuery/log_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") + log_query.Execute() + to_chat(usr, "New admin added.") + else + if(!isnull(admin_id) && isnum(admin_id)) + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") + log_query.Execute() + to_chat(usr, "Admin rank changed.") + +/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) + if(config.admin_legacy_system) + return + + if(!usr.client) + return + + if(!check_rights(R_PERMISSIONS)) + return + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + to_chat(usr, "Failed to establish database connection") + return + + if(!adm_ckey || !new_permission) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(istext(new_permission)) + new_permission = text2num(new_permission) + + if(!istext(adm_ckey) || !isnum(new_permission)) + return + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/admin_id + var/admin_rights + while(select_query.NextRow()) + admin_id = text2num(select_query.item[1]) + admin_rights = text2num(select_query.item[2]) + + if(!admin_id) + return + + flag_account_for_forum_sync(adm_ckey) + if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") + log_query.Execute() + to_chat(usr, "Permission removed.") + else //This admin doesn't have this permission, so we are adding it. + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") + log_query.Execute() + to_chat(usr, "Permission added.") + +/datum/admins/proc/updateranktodb(ckey,newrank) + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + return + if(!check_rights(R_PERMISSIONS)) + return + var/sql_ckey = sanitizeSQL(ckey) + var/sql_admin_rank = sanitizeSQL(newrank) + + var/DBQuery/query_update = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'") + query_update.Execute() + flag_account_for_forum_sync(sql_ckey) diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 9ed6f0cd997..2a1d03b79aa 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -392,11 +392,12 @@ var/logout_status logout_status = M.client ? "" : " (logged out)" var/dname = M.real_name + var/area/A = get_area(M) if(!dname) dname = M - return {"[dname][caption][logout_status][M.stat == 2 ? " (DEAD)" : ""] - PM[close ? "" : ""]"} + return {"[dname][caption][logout_status][istype(A, /area/security/permabrig) ? " (PERMA) " : ""][M.stat == 2 ? " (DEAD)" : ""] + PM [ADMIN_FLW(M, "FLW")] [close ? "" : ""]"} /datum/admins/proc/check_antagonists() if(!check_rights(R_ADMIN)) return @@ -465,7 +466,7 @@ if(GAMEMODE_IS_BLOB) var/datum/game_mode/blob/mode = SSticker.mode dat += "
    " - dat += "" + dat += "" for(var/datum/mind/blob in mode.infected_crew) var/mob/M = blob.current @@ -475,7 +476,7 @@ else dat += "" dat += "
    Blob
    Progress: [blobs.len]/[mode.blobwincount]
    Progress: [GLOB.blobs.len]/[mode.blobwincount]
    Blob not found!
    " - + if(SSticker.mode.blob_overminds.len) dat += check_role_table("Blob Overminds", SSticker.mode.blob_overminds) @@ -492,7 +493,7 @@ dat += check_role_table("Ninjas", ticker.mode.ninjas)*/ if(SSticker.mode.cult.len) - dat += check_role_table("Cultists", SSticker.mode.cult, 0) + dat += check_role_table("Cultists", SSticker.mode.cult) dat += "
    use Cult Mindspeak" if(GAMEMODE_IS_CULT) var/datum/game_mode/cult/cult_round = SSticker.mode @@ -541,9 +542,9 @@ if(SSticker.mode.eventmiscs.len) dat += check_role_table("Event Roles", SSticker.mode.eventmiscs) - if(ts_spiderlist.len) + if(GLOB.ts_spiderlist.len) var/list/spider_minds = list() - for(var/mob/living/simple_animal/hostile/poison/terror_spider/S in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/S in GLOB.ts_spiderlist) if(S.ckey) spider_minds += S.mind if(spider_minds.len) @@ -551,10 +552,10 @@ var/count_eggs = 0 var/count_spiderlings = 0 - for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in ts_egg_list) + for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in GLOB.ts_egg_list) if(is_station_level(E.z)) count_eggs += E.spiderling_number - for(var/obj/structure/spider/spiderling/terror_spiderling/L in ts_spiderling_list) + for(var/obj/structure/spider/spiderling/terror_spiderling/L in GLOB.ts_spiderling_list) if(!L.stillborn && is_station_level(L.z)) count_spiderlings += 1 dat += "
    Growing TS on-station: [count_eggs] egg[count_eggs != 1 ? "s" : ""], [count_spiderlings] spiderling[count_spiderlings != 1 ? "s" : ""].
    " diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index fe487ae6431..cbda6699694 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -27,8 +27,8 @@ Bombs
    [check_rights(R_SERVER, 0) ? "  Toggle bomb cap
    " : "
    "] Lists
    - Show last [length(lastsignalers)] signalers   - Show last [length(lawchanges)] law changes
    + Show last [length(GLOB.lastsignalers)] signalers   + Show last [length(GLOB.lawchanges)] law changes
    List DNA (Blood)   List Fingerprints
    Power
    @@ -49,7 +49,7 @@ Security Level Elevated

    Change all maintenance doors to engie/brig access only
    - Change all maintenance doors to brig access only
    + Change all maintenance doors to brig access only
    Remove cap on security officers  
    Coder Secrets
    @@ -105,7 +105,7 @@ Turn all humans into monkeys
    Make all items look like guns
    Warp all Players to Prison
    - Make all players retarded
    + Make all players stupid
    Misc
    Remove firesuits, grilles, and pods   Triple AI mode (needs to be used in the lobby)
    diff --git a/code/modules/admin/sql_notes.dm b/code/modules/admin/sql_notes.dm index a6ac31a4114..238c380f417 100644 --- a/code/modules/admin/sql_notes.dm +++ b/code/modules/admin/sql_notes.dm @@ -1,7 +1,7 @@ /proc/add_note(target_ckey, notetext, timestamp, adminckey, logged = 1, server, checkrights = 1) if(checkrights && !check_rights(R_ADMIN|R_MOD)) return - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) to_chat(usr, "Failed to establish database connection.") return @@ -13,7 +13,7 @@ else target_ckey = ckey(target_ckey) - var/DBQuery/query_find_ckey = dbcon.NewQuery("SELECT ckey, exp FROM [format_table_name("player")] WHERE ckey = '[target_ckey]'") + var/DBQuery/query_find_ckey = GLOB.dbcon.NewQuery("SELECT ckey, exp FROM [format_table_name("player")] WHERE ckey = '[target_ckey]'") if(!query_find_ckey.Execute()) var/err = query_find_ckey.ErrorMsg() log_game("SQL ERROR obtaining ckey from player table. Error : \[[err]\]\n") @@ -46,7 +46,7 @@ if(config && config.server_name) server = config.server_name server = sanitizeSQL(server) - var/DBQuery/query_noteadd = dbcon.NewQuery("INSERT INTO [format_table_name("notes")] (ckey, timestamp, notetext, adminckey, server, crew_playtime) VALUES ('[target_ckey]', '[timestamp]', '[notetext]', '[admin_sql_ckey]', '[server]', '[crew_number]')") + var/DBQuery/query_noteadd = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("notes")] (ckey, timestamp, notetext, adminckey, server, crew_playtime) VALUES ('[target_ckey]', '[timestamp]', '[notetext]', '[admin_sql_ckey]', '[server]', '[crew_number]')") if(!query_noteadd.Execute()) var/err = query_noteadd.ErrorMsg() log_game("SQL ERROR adding new note to table. Error : \[[err]\]\n") @@ -62,13 +62,13 @@ var/ckey var/notetext var/adminckey - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) to_chat(usr, "Failed to establish database connection.") return if(!note_id) return note_id = text2num(note_id) - var/DBQuery/query_find_note_del = dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]") + var/DBQuery/query_find_note_del = GLOB.dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]") if(!query_find_note_del.Execute()) var/err = query_find_note_del.ErrorMsg() log_game("SQL ERROR obtaining ckey, notetext, adminckey from player table. Error : \[[err]\]\n") @@ -77,7 +77,7 @@ ckey = query_find_note_del.item[1] notetext = query_find_note_del.item[2] adminckey = query_find_note_del.item[3] - var/DBQuery/query_del_note = dbcon.NewQuery("DELETE FROM [format_table_name("notes")] WHERE id = [note_id]") + var/DBQuery/query_del_note = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("notes")] WHERE id = [note_id]") if(!query_del_note.Execute()) var/err = query_del_note.ErrorMsg() log_game("SQL ERROR removing note from table. Error : \[[err]\]\n") @@ -89,7 +89,7 @@ /proc/edit_note(note_id) if(!check_rights(R_ADMIN|R_MOD)) return - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) to_chat(usr, "Failed to establish database connection.") return if(!note_id) @@ -97,7 +97,7 @@ note_id = text2num(note_id) var/target_ckey var/sql_ckey = usr.ckey - var/DBQuery/query_find_note_edit = dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]") + var/DBQuery/query_find_note_edit = GLOB.dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]") if(!query_find_note_edit.Execute()) var/err = query_find_note_edit.ErrorMsg() log_game("SQL ERROR obtaining notetext from notes table. Error : \[[err]\]\n") @@ -112,7 +112,7 @@ new_note = sanitizeSQL(new_note) var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from \"[old_note]\" to \"[new_note]\"
    " edit_text = sanitizeSQL(edit_text) - var/DBQuery/query_update_note = dbcon.NewQuery("UPDATE [format_table_name("notes")] SET notetext = '[new_note]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [note_id]") + var/DBQuery/query_update_note = GLOB.dbcon.NewQuery("UPDATE [format_table_name("notes")] SET notetext = '[new_note]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [note_id]") if(!query_update_note.Execute()) var/err = query_update_note.ErrorMsg() log_game("SQL ERROR editing note. Error : \[[err]\]\n") @@ -139,7 +139,7 @@ output = navbar if(target_ckey) var/target_sql_ckey = ckey(target_ckey) - var/DBQuery/query_get_notes = dbcon.NewQuery("SELECT id, timestamp, notetext, adminckey, last_editor, server, crew_playtime FROM [format_table_name("notes")] WHERE ckey = '[target_sql_ckey]' ORDER BY timestamp") + var/DBQuery/query_get_notes = GLOB.dbcon.NewQuery("SELECT id, timestamp, notetext, adminckey, last_editor, server, crew_playtime FROM [format_table_name("notes")] WHERE ckey = '[target_sql_ckey]' ORDER BY timestamp") if(!query_get_notes.Execute()) var/err = query_get_notes.ErrorMsg() log_game("SQL ERROR obtaining ckey, notetext, adminckey, last_editor, server, crew_playtime from notes table. Error : \[[err]\]\n") @@ -181,7 +181,7 @@ search = "^\[^\[:alpha:\]\]" else search = "^[index]" - var/DBQuery/query_list_notes = dbcon.NewQuery("SELECT DISTINCT ckey FROM [format_table_name("notes")] WHERE ckey REGEXP '[search]' ORDER BY ckey") + var/DBQuery/query_list_notes = GLOB.dbcon.NewQuery("SELECT DISTINCT ckey FROM [format_table_name("notes")] WHERE ckey REGEXP '[search]' ORDER BY ckey") if(!query_list_notes.Execute()) var/err = query_list_notes.ErrorMsg() log_game("SQL ERROR obtaining ckey from notes table. Error : \[[err]\]\n") @@ -196,7 +196,7 @@ /proc/show_player_info_irc(var/key as text) var/target_sql_ckey = ckey(key) - var/DBQuery/query_get_notes = dbcon.NewQuery("SELECT timestamp, notetext, adminckey, server, crew_playtime FROM [format_table_name("notes")] WHERE ckey = '[target_sql_ckey]' ORDER BY timestamp") + var/DBQuery/query_get_notes = GLOB.dbcon.NewQuery("SELECT timestamp, notetext, adminckey, server, crew_playtime FROM [format_table_name("notes")] WHERE ckey = '[target_sql_ckey]' ORDER BY timestamp") if(!query_get_notes.Execute()) var/err = query_get_notes.ErrorMsg() log_game("SQL ERROR obtaining timestamp, notetext, adminckey, server, crew_playtime from notes table. Error : \[[err]\]\n") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 2d98626a1c3..7826a005e7d 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1,3491 +1,3500 @@ -/datum/admins/Topic(href, href_list) - ..() - - if(usr.client != src.owner || !check_rights(0)) - log_admin("[key_name(usr)] tried to use the admin panel without authorization.") - message_admins("[key_name_admin(usr)] has attempted to override the admin panel!") - return - - if(SSticker.mode && SSticker.mode.check_antagonists_topic(href, href_list)) - check_antagonists() - return - - if(href_list["rejectadminhelp"]) - if(!check_rights(R_ADMIN|R_MOD)) - return - var/client/C = locateUID(href_list["rejectadminhelp"]) - if(!C) - return - - C << 'sound/effects/adminhelp.ogg' - - to_chat(C, "- AdminHelp Rejected! -") - to_chat(C, "Your admin help was rejected.") - to_chat(C, "Please try to be calm, clear, and descriptive in admin helps, do not assume the admin has seen any related events, and clearly state the names of anybody you are reporting. If you asked a question, please ensure it was clear what you were asking.") - - message_admins("[key_name_admin(usr)] rejected [key_name_admin(C.mob)]'s admin help") - log_admin("[key_name(usr)] rejected [key_name(C.mob)]'s admin help") - - if(href_list["openadminticket"]) - if(!check_rights(R_ADMIN)) - return - var/ticketID = text2num(href_list["openadminticket"]) - SStickets.showDetailUI(usr, ticketID) - - if(href_list["openmentorticket"]) - if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) - return - var/ticketID = text2num(href_list["openmentorticket"]) - SSmentor_tickets.showDetailUI(usr, ticketID) - - if(href_list["stickyban"]) - stickyban(href_list["stickyban"],href_list) - - if(href_list["makeAntag"]) - switch(href_list["makeAntag"]) - if("1") - log_admin("[key_name(usr)] has spawned a traitor.") - if(!makeTraitors()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("2") - log_admin("[key_name(usr)] has spawned a changeling.") - if(!makeChangelings()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("3") - log_admin("[key_name(usr)] has spawned revolutionaries.") - if(!makeRevs()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("4") - log_admin("[key_name(usr)] has spawned a cultists.") - if(!makeCult()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("5") - log_admin("[key_name(usr)] has spawned a wizard.") - if(!makeWizard()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("6") - log_admin("[key_name(usr)] has spawned vampires.") - if(!makeVampires()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("7") - log_admin("[key_name(usr)] has spawned vox raiders.") - if(!makeVoxRaiders()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("8") - log_admin("[key_name(usr)] has spawned an abductor team.") - if(!makeAbductorTeam()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - - else if(href_list["dbsearchckey"] || href_list["dbsearchadmin"] || href_list["dbsearchip"] || href_list["dbsearchcid"] || href_list["dbsearchbantype"]) - var/adminckey = href_list["dbsearchadmin"] - var/playerckey = href_list["dbsearchckey"] - var/playerip = href_list["dbsearchip"] - var/playercid = href_list["dbsearchcid"] - var/dbbantype = text2num(href_list["dbsearchbantype"]) - var/match = 0 - - if("dbmatch" in href_list) - match = 1 - - DB_ban_panel(playerckey, adminckey, playerip, playercid, dbbantype, match) - return - - else if(href_list["dbbanedit"]) - var/banedit = href_list["dbbanedit"] - var/banid = text2num(href_list["dbbanid"]) - if(!banedit || !banid) - return - - DB_ban_edit(banid, banedit) - return - - else if(href_list["dbbanaddtype"]) - - var/bantype = text2num(href_list["dbbanaddtype"]) - var/banckey = href_list["dbbanaddckey"] - var/banip = href_list["dbbanaddip"] - var/bancid = href_list["dbbanaddcid"] - var/banduration = text2num(href_list["dbbaddduration"]) - var/banjob = href_list["dbbanaddjob"] - var/banreason = href_list["dbbanreason"] - - banckey = ckey(banckey) - - switch(bantype) - if(BANTYPE_PERMA) - if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") - return - banduration = null - banjob = null - if(BANTYPE_TEMP) - if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") - return - banjob = null - if(BANTYPE_JOB_PERMA) - if(!banckey || !banreason || !banjob) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") - return - banduration = null - if(BANTYPE_JOB_TEMP) - if(!banckey || !banreason || !banjob || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") - return - if(BANTYPE_APPEARANCE) - if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") - return - banduration = null - banjob = null - if(BANTYPE_ADMIN_PERMA) - if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") - return - banduration = null - banjob = null - if(BANTYPE_ADMIN_TEMP) - if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") - return - banjob = null - - var/mob/playermob - - for(var/mob/M in GLOB.player_list) - if(M.ckey == banckey) - playermob = M - break - - - banreason = "(MANUAL BAN) "+banreason - - if(!playermob) - if(banip) - banreason = "[banreason] (CUSTOM IP)" - if(bancid) - banreason = "[banreason] (CUSTOM CID)" - else - message_admins("Ban process: A mob matching [playermob.ckey] was found at location [playermob.x], [playermob.y], [playermob.z]. Custom IP and computer id fields replaced with the IP and computer id from the located mob") - - DB_ban_record(bantype, playermob, banduration, banreason, banjob, null, banckey, banip, bancid ) - - - else if(href_list["editrights"]) - if(!check_rights(R_PERMISSIONS)) - message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.") - log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.") - return - - var/adm_ckey - - var/task = href_list["editrights"] - if(task == "add") - var/new_ckey = ckey(clean_input("New admin's ckey","Admin ckey", null)) - if(!new_ckey) return - if(new_ckey in admin_datums) - to_chat(usr, "Error: Topic 'editrights': [new_ckey] is already an admin") - return - adm_ckey = new_ckey - task = "rank" - else if(task != "show") - adm_ckey = ckey(href_list["ckey"]) - if(!adm_ckey) - to_chat(usr, "Error: Topic 'editrights': No valid ckey") - return - - var/datum/admins/D = admin_datums[adm_ckey] - - if(task == "remove") - if(alert("Are you sure you want to remove [adm_ckey]?","Message","Yes","Cancel") == "Yes") - if(!D) return - admin_datums -= adm_ckey - D.disassociate() - - updateranktodb(adm_ckey, "player") - message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list") - log_admin("[key_name(usr)] removed [adm_ckey] from the admins list") - log_admin_rank_modification(adm_ckey, "Removed") - - else if(task == "rank") - var/new_rank - if(admin_ranks.len) - new_rank = input("Please select a rank", "New rank", null, null) as null|anything in (admin_ranks|"*New Rank*") - else - new_rank = input("Please select a rank", "New rank", null, null) as null|anything in list("Mentor", "Trial Admin", "Game Admin", "*New Rank*") - - var/rights = 0 - if(D) - rights = D.rights - switch(new_rank) - if(null,"") return - if("*New Rank*") - new_rank = input("Please input a new rank", "New custom rank", null, null) as null|text - if(config.admin_legacy_system) - new_rank = ckeyEx(new_rank) - if(!new_rank) - to_chat(usr, "Error: Topic 'editrights': Invalid rank") - return - if(config.admin_legacy_system) - if(admin_ranks.len) - if(new_rank in admin_ranks) - rights = admin_ranks[new_rank] //we typed a rank which already exists, use its rights - else - admin_ranks[new_rank] = 0 //add the new rank to admin_ranks - else - if(config.admin_legacy_system) - new_rank = ckeyEx(new_rank) - rights = admin_ranks[new_rank] //we input an existing rank, use its rights - - if(D) - D.disassociate() //remove adminverbs and unlink from client - D.rank = new_rank //update the rank - D.rights = rights //update the rights based on admin_ranks (default: 0) - else - D = new /datum/admins(new_rank, rights, adm_ckey) - - var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in) - D.associate(C) //link up with the client and add verbs - - updateranktodb(adm_ckey, new_rank) - message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]") - log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]") - log_admin_rank_modification(adm_ckey, new_rank) - - else if(task == "permissions") - if(!D) return - while(TRUE) - var/list/permissionlist = list() - for(var/i=1, i<=R_MAXPERMISSION, i<<=1) //that <<= is shorthand for i = i << 1. Which is a left bitshift - permissionlist[rights2text(i)] = i - var/new_permission = input("Select a permission to turn on/off", adm_ckey + "'s Permissions", null, null) as null|anything in permissionlist - if(!new_permission) - return - var/oldrights = D.rights - var/toggleresult = "ON" - D.rights ^= permissionlist[new_permission] - if(oldrights > D.rights) - toggleresult = "OFF" - - message_admins("[key_name_admin(usr)] toggled the [new_permission] permission of [adm_ckey] to [toggleresult]") - log_admin("[key_name(usr)] toggled the [new_permission] permission of [adm_ckey] to [toggleresult]") - log_admin_permission_modification(adm_ckey, permissionlist[new_permission]) - - - edit_admin_permissions() - - else if(href_list["call_shuttle"]) - if(!check_rights(R_ADMIN)) return - - - switch(href_list["call_shuttle"]) - if("1") - if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) - return - SSshuttle.emergency.request() - log_admin("[key_name(usr)] called the Emergency Shuttle") - message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station") - - if("2") - if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) - return - switch(SSshuttle.emergency.mode) - if(SHUTTLE_CALL) - SSshuttle.emergency.cancel() - log_admin("[key_name(usr)] sent the Emergency Shuttle back") - message_admins("[key_name_admin(usr)] sent the Emergency Shuttle back") - else - SSshuttle.emergency.cancel() - log_admin("[key_name(usr)] called the Emergency Shuttle") - message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station") - - - href_list["secrets"] = "check_antagonist" - - else if(href_list["edit_shuttle_time"]) - if(!check_rights(R_SERVER)) return - - var/timer = input("Enter new shuttle duration (seconds):","Edit Shuttle Timeleft", SSshuttle.emergency.timeLeft() ) as num - SSshuttle.emergency.setTimer(timer*10) - log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds") - minor_announcement.Announce("The emergency shuttle will reach its destination in [round(SSshuttle.emergency.timeLeft(600))] minutes.") - message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds") - href_list["secrets"] = "check_antagonist" - - else if(href_list["delay_round_end"]) - if(!check_rights(R_SERVER)) return - - SSticker.delay_end = !SSticker.delay_end - log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].") - message_admins("[key_name_admin(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) - href_list["secretsadmin"] = "check_antagonist" - - else if(href_list["simplemake"]) - if(!check_rights(R_SPAWN)) return - - var/mob/M = locateUID(href_list["mob"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - var/delmob = 0 - switch(alert("Delete old mob?","Message","Yes","No","Cancel")) - if("Cancel") return - if("Yes") delmob = 1 - - switch(href_list["simplemake"]) - if("observer") M.change_mob_type( /mob/dead/observer , null, null, delmob, 1 ) - if("drone") M.change_mob_type( /mob/living/carbon/alien/humanoid/drone , null, null, delmob, 1 ) - if("hunter") M.change_mob_type( /mob/living/carbon/alien/humanoid/hunter , null, null, delmob, 1 ) - if("queen") M.change_mob_type( /mob/living/carbon/alien/humanoid/queen/large , null, null, delmob, 1 ) - if("sentinel") M.change_mob_type( /mob/living/carbon/alien/humanoid/sentinel , null, null, delmob, 1 ) - if("larva") M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob, 1 ) - if("human") - var/posttransformoutfit = usr.client.robust_dress_shop() - var/mob/living/carbon/human/newmob = M.change_mob_type(/mob/living/carbon/human, null, null, delmob, 1) - if(posttransformoutfit && istype(newmob)) - newmob.equipOutfit(posttransformoutfit) - if("slime") M.change_mob_type( /mob/living/simple_animal/slime , null, null, delmob, 1 ) - if("monkey") M.change_mob_type( /mob/living/carbon/human/monkey , null, null, delmob, 1 ) - if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob, 1 ) - if("cat") M.change_mob_type( /mob/living/simple_animal/pet/cat , null, null, delmob, 1 ) - if("runtime") M.change_mob_type( /mob/living/simple_animal/pet/cat/Runtime , null, null, delmob, 1 ) - if("corgi") M.change_mob_type( /mob/living/simple_animal/pet/dog/corgi , null, null, delmob, 1 ) - if("crab") M.change_mob_type( /mob/living/simple_animal/crab , null, null, delmob, 1 ) - if("coffee") M.change_mob_type( /mob/living/simple_animal/crab/Coffee , null, null, delmob, 1 ) - if("parrot") M.change_mob_type( /mob/living/simple_animal/parrot , null, null, delmob, 1 ) - if("polyparrot") M.change_mob_type( /mob/living/simple_animal/parrot/Poly , null, null, delmob, 1 ) - if("constructarmoured") M.change_mob_type( /mob/living/simple_animal/hostile/construct/armoured , null, null, delmob, 1 ) - if("constructbuilder") M.change_mob_type( /mob/living/simple_animal/hostile/construct/builder , null, null, delmob, 1 ) - if("constructwraith") M.change_mob_type( /mob/living/simple_animal/hostile/construct/wraith , null, null, delmob, 1 ) - if("shade") M.change_mob_type( /mob/living/simple_animal/shade , null, null, delmob, 1 ) - - log_admin("[key_name(usr)] has used rudimentary transformation on [key_name(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]") - message_admins("[key_name_admin(usr)] has used rudimentary transformation on [key_name_admin(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]", 1) - - - /////////////////////////////////////new ban stuff - else if(href_list["unbanf"]) - if(!check_rights(R_BAN)) return - - var/banfolder = href_list["unbanf"] - Banlist.cd = "/base/[banfolder]" - var/key = Banlist["key"] - if(alert(usr, "Are you sure you want to unban [key]?", "Confirmation", "Yes", "No") == "Yes") - if(RemoveBan(banfolder)) - unbanpanel() - else - alert(usr, "This ban has already been lifted / does not exist.", "Error", "Ok") - unbanpanel() - - else if(href_list["warn"]) - usr.client.warn(href_list["warn"]) - - else if(href_list["unbane"]) - if(!check_rights(R_BAN)) return - - UpdateTime() - var/reason - - var/banfolder = href_list["unbane"] - Banlist.cd = "/base/[banfolder]" - var/reason2 = Banlist["reason"] - var/temp = Banlist["temp"] - - var/minutes = Banlist["minutes"] - - var/banned_key = Banlist["key"] - Banlist.cd = "/base" - - var/duration - - switch(alert("Temporary Ban?",,"Yes","No")) - if("Yes") - temp = 1 - var/mins = 0 - if(minutes > CMinutes) - mins = minutes - CMinutes - mins = input(usr,"How long (in minutes)? (Default: 1440)","Ban time",mins ? mins : 1440) as num|null - if(!mins) return - mins = min(525599,mins) - minutes = CMinutes + mins - duration = GetExp(minutes) - reason = input(usr,"Please state the reason","Reason",reason2) as message|null - if(!reason) return - if("No") - temp = 0 - duration = "Perma" - reason = input(usr,"Please state the reason","Reason",reason2) as message|null - if(!reason) return - - log_admin("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") - ban_unban_log_save("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") - message_admins("[key_name_admin(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]", 1) - Banlist.cd = "/base/[banfolder]" - to_chat(Banlist["reason"], reason) - to_chat(Banlist["temp"], temp) - to_chat(Banlist["minutes"], minutes) - to_chat(Banlist["bannedby"], usr.ckey) - Banlist.cd = "/base" - feedback_inc("ban_edit",1) - unbanpanel() - - /////////////////////////////////////new ban stuff - - else if(href_list["appearanceban"]) - if(!check_rights(R_BAN)) - return - var/mob/M = locateUID(href_list["appearanceban"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") - return - var/ban_ckey_param = href_list["dbbanaddckey"] - - var/banreason = appearance_isbanned(M) - if(banreason) - /* if(!config.ban_legacy_system) - to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") - DB_ban_panel(M.ckey) - return */ - switch(alert("Reason: '[banreason]' Remove appearance ban?","Please Confirm","Yes","No")) - if("Yes") - ban_unban_log_save("[key_name(usr)] removed [key_name(M)]'s appearance ban") - log_admin("[key_name(usr)] removed [key_name(M)]'s appearance ban") - feedback_inc("ban_appearance_unban", 1) - DB_ban_unban(M.ckey, BANTYPE_APPEARANCE) - appearance_unban(M) - message_admins("[key_name_admin(usr)] removed [key_name_admin(M)]'s appearance ban", 1) - to_chat(M, "[usr.client.ckey] has removed your appearance ban.") - - else switch(alert("Appearance ban [M.ckey]?",,"Yes","No", "Cancel")) - if("Yes") - var/reason = input(usr,"Please state the reason","Reason") as message|null - if(!reason) - return - M = admin_ban_mobsearch(M, ban_ckey_param, usr) - ban_unban_log_save("[key_name(usr)] appearance banned [key_name(M)]. reason: [reason]") - log_admin("[key_name(usr)] appearance banned [key_name(M)]. \nReason: [reason]") - feedback_inc("ban_appearance",1) - DB_ban_record(BANTYPE_APPEARANCE, M, -1, reason) - appearance_fullban(M, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") - add_note(M.ckey, "Appearance banned - [reason]", null, usr.ckey, 0) - message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)]", 1) - to_chat(M, "You have been appearance banned by [usr.client.ckey].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "Appearance ban can be lifted only upon request.") - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - if("No") - return - - else if(href_list["jobban2"]) -// if(!check_rights(R_BAN)) return - - var/mob/M = locateUID(href_list["jobban2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") - return - if(!SSjobs) - to_chat(usr, "SSjobs has not been setup!") - return - - var/dat = "" - var/header = "Job-Ban Panel: [M.name]" - var/body - var/jobs = "" - - /***********************************WARNING!************************************ - The jobban stuff looks mangled and disgusting - But it looks beautiful in-game - -Nodrak - ************************************WARNING!***********************************/ - var/counter = 0 -//Regular jobs - //Command (Blue) - jobs += "" - jobs += "" - for(var/jobPos in command_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 6) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Command Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Security (Red) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in security_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Security Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Engineering (Yellow) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in engineering_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Engineering Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Medical (White) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in medical_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Medical Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Science (Purple) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in science_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Science Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Support (Grey) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in support_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Support Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Non-Human (Green) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in nonhuman_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - //Drone - if(jobban_isbanned(M, "Drone")) - jobs += "" - else - jobs += "" - - //pAI - if(jobban_isbanned(M, "pAI")) - jobs += "" - else - jobs += "" - - jobs += "
    Non-human Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    DroneDronepAIpAI
    " - - //Antagonist (Orange) - var/isbanned_dept = jobban_isbanned(M, "Syndicate") - jobs += "" - jobs += "" - - counter = 0 - for(var/role in antag_roles) - if(jobban_isbanned(M, role) || isbanned_dept) - jobs += "" - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Antagonist Positions
    [replacetext(role, " ", " ")][replacetext(role, " ", " ")]
    " - - //Other races (BLUE, because I have no idea what other color to make this) - jobs += "" - jobs += "" - - counter = 0 - for(var/role in other_roles) - if(jobban_isbanned(M, role) || isbanned_dept) - jobs += "" - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Other
    [replacetext(role, " ", " ")][replacetext(role, " ", " ")]
    " - - //Whitelisted positions - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in whitelisted_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Whitelisted Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - body = "[jobs]" - dat = "[header][body]" - usr << browse(dat, "window=jobban2;size=800x490") - return - - //JOBBAN'S INNARDS - else if(href_list["jobban3"]) - if(!check_rights(R_BAN)) return - - var/mob/M = locateUID(href_list["jobban4"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(M != usr) //we can jobban ourselves - if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) //they can ban too. So we can't ban them - alert("You cannot perform this action. You must be of a higher administrative rank!") - return - - var/ban_ckey_param = href_list["dbbanaddckey"] - - if(!SSjobs) - to_chat(usr, "SSjobs has not been setup!") - return - - //get jobs for department if specified, otherwise just returnt he one job in a list. - var/list/joblist = list() - switch(href_list["jobban3"]) - if("commanddept") - for(var/jobPos in command_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("securitydept") - for(var/jobPos in security_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("engineeringdept") - for(var/jobPos in engineering_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("medicaldept") - for(var/jobPos in medical_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("sciencedept") - for(var/jobPos in science_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("supportdept") - for(var/jobPos in support_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("nonhumandept") - joblist += "pAI" - for(var/jobPos in nonhuman_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("whitelistdept") - for(var/jobPos in whitelisted_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - else - joblist += href_list["jobban3"] - - //Create a list of unbanned jobs within joblist - var/list/notbannedlist = list() - for(var/job in joblist) - if(!jobban_isbanned(M, job)) - notbannedlist += job - - //Banning comes first - if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. - switch(alert("Temporary Ban of [M.ckey]?",,"Yes","No", "Cancel")) - if("Yes") - if(config.ban_legacy_system) - to_chat(usr, "Your server is using the legacy banning system, which does not support temporary job bans. Consider upgrading. Aborting ban.") - return - var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null - if(!mins) - return - var/reason = input(usr,"Please state the reason","Reason","") as message|null - if(!reason) - return - - var/msg - M = admin_ban_mobsearch(M, ban_ckey_param, usr) - for(var/job in notbannedlist) - ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]") - log_admin("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes") - feedback_inc("ban_job_tmp",1) - DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job) - feedback_add_details("ban_job_tmp","- [job]") - jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") //Legacy banning does not support temporary jobbans. - if(!msg) - msg = job - else - msg += ", [job]" - add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) - message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "This jobban will be lifted in [mins] minutes.") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - if("No") - var/reason = input(usr,"Please state the reason","Reason","") as message|null - if(reason) - var/msg - M = admin_ban_mobsearch(M, ban_ckey_param, usr) - for(var/job in notbannedlist) - ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]") - log_admin("[key_name(usr)] perma-banned [key_name(M)] from [job]") - feedback_inc("ban_job",1) - DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, job) - feedback_add_details("ban_job","- [job]") - jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") - if(!msg) msg = job - else msg += ", [job]" - add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) - message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "Jobban can be lifted only upon request.") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - if("Cancel") - return - - //Unbanning joblist - //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) - if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. - if(!config.ban_legacy_system) - to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") - DB_ban_panel(M.ckey) - return - var/msg - for(var/job in joblist) - var/reason = jobban_isbanned(M, job) - if(!reason) continue //skip if it isn't jobbanned anyway - switch(alert("Job: '[job]' Reason: '[reason]' Un-jobban?","Please Confirm","Yes","No")) - if("Yes") - ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") - log_admin("[key_name(usr)] unbanned [key_name(M)] from [job]") - DB_ban_unban(M.ckey, BANTYPE_JOB_PERMA, job) - feedback_inc("ban_job_unban",1) - feedback_add_details("ban_job_unban","- [job]") - jobban_unban(M, job) - if(!msg) msg = job - else msg += ", [job]" - else - continue - if(msg) - message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - return 0 //we didn't do anything! - - else if(href_list["boot2"]) - var/mob/M = locateUID(href_list["boot2"]) - if(ismob(M)) - if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) - to_chat(usr, "[key_name_admin(M)] cannot be kicked from the server.") - return - to_chat(M, "You have been kicked from the server") - log_admin("[key_name(usr)] booted [key_name(M)].") - message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1) - //M.client = null - del(M.client) - - //Player Notes - else if(href_list["addnote"]) - var/target_ckey = href_list["addnote"] - add_note(target_ckey) - - else if(href_list["addnoteempty"]) - add_note() - - else if(href_list["removenote"]) - var/note_id = href_list["removenote"] - remove_note(note_id) - - else if(href_list["editnote"]) - var/note_id = href_list["editnote"] - edit_note(note_id) - - else if(href_list["shownote"]) - var/target = href_list["shownote"] - show_note(index = target) - - else if(href_list["nonalpha"]) - var/target = href_list["nonalpha"] - target = text2num(target) - show_note(index = target) - - else if(href_list["webtools"]) - var/target_ckey = href_list["webtools"] - if(config.forum_playerinfo_url) - var/url_to_open = config.forum_playerinfo_url + target_ckey - if(alert("Open [url_to_open]",,"Yes","No")=="Yes") - usr.client << link(url_to_open) - - else if(href_list["shownoteckey"]) - var/target_ckey = href_list["shownoteckey"] - show_note(target_ckey) - - else if(href_list["notessearch"]) - var/target = href_list["notessearch"] - show_note(index = target) - - else if(href_list["noteedits"]) - var/note_id = sanitizeSQL("[href_list["noteedits"]]") - var/DBQuery/query_noteedits = dbcon.NewQuery("SELECT edits FROM [format_table_name("notes")] WHERE id = '[note_id]'") - if(!query_noteedits.Execute()) - var/err = query_noteedits.ErrorMsg() - log_game("SQL ERROR obtaining edits from notes table. Error : \[[err]\]\n") - return - if(query_noteedits.NextRow()) - var/edit_log = query_noteedits.item[1] - usr << browse(edit_log,"window=noteedits") - - else if(href_list["removejobban"]) - if(!check_rights(R_BAN)) return - - var/t = href_list["removejobban"] - if(t) - if((alert("Do you want to unjobban [t]?","Unjobban confirmation", "Yes", "No") == "Yes") && t) //No more misclicks! Unless you do it twice. - log_admin("[key_name(usr)] removed [t]") - message_admins("[key_name_admin(usr)] removed [t]", 1) - jobban_remove(t) - href_list["ban"] = 1 // lets it fall through and refresh - var/t_split = splittext(t, " - ") - var/key = t_split[1] - var/job = t_split[2] - DB_ban_unban(ckey(key), BANTYPE_JOB_PERMA, job) - - else if(href_list["newban"]) - if(!check_rights(R_BAN)) return - - var/mob/M = locateUID(href_list["newban"]) - if(!ismob(M)) - return - var/ban_ckey_param = href_list["dbbanaddckey"] - - switch(alert("Temporary Ban of [M.ckey] / [ban_ckey_param]?",,"Yes","No", "Cancel")) - if("Yes") - var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null - if(!mins) - return - if(mins >= 525600) mins = 525599 - var/reason = input(usr,"Please state the reason","Reason") as message|null - if(!reason) - return - M = admin_ban_mobsearch(M, ban_ckey_param, usr) - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) - ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") - feedback_inc("ban_tmp",1) - DB_ban_record(BANTYPE_TEMP, M, mins, reason) - feedback_inc("ban_tmp_mins",mins) - if(M.client) - M.client.link_forum_account(TRUE) - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - - del(M.client) - //qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends. - if("No") - var/reason = input(usr,"Please state the reason","Reason") as message|null - if(!reason) - return - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This ban does not expire automatically and must be appealed.") - if(M.client) - M.client.link_forum_account(TRUE) - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - ban_unban_log_save("[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This ban does not expire automatically and must be appealed.") - log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis ban does not expire automatically and must be appealed.") - message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis ban does not expire automatically and must be appealed.") - feedback_inc("ban_perma",1) - DB_ban_record(BANTYPE_PERMA, M, -1, reason) - - del(M.client) - //qdel(M) - if("Cancel") - return - - - //Watchlist - else if(href_list["watchadd"]) - var/target_ckey = href_list["watchadd"] - usr.client.watchlist_add(target_ckey) - - else if(href_list["watchremove"]) - var/target_ckey = href_list["watchremove"] - var/confirm = alert("Are you sure you want to remove [target_ckey] from the watchlist?", "Confirm Watchlist Removal", "Yes", "No") - if(confirm == "Yes") - usr.client.watchlist_remove(target_ckey) - - else if(href_list["watchedit"]) - var/target_ckey = href_list["watchedit"] - usr.client.watchlist_edit(target_ckey) - - else if(href_list["watchaddbrowse"]) - usr.client.watchlist_add(null, 1) - - else if(href_list["watchremovebrowse"]) - var/target_ckey = href_list["watchremovebrowse"] - usr.client.watchlist_remove(target_ckey, 1) - - else if(href_list["watcheditbrowse"]) - var/target_ckey = href_list["watcheditbrowse"] - usr.client.watchlist_edit(target_ckey, 1) - - else if(href_list["watchsearch"]) - var/target_ckey = href_list["watchsearch"] - usr.client.watchlist_show(target_ckey) - - else if(href_list["watchshow"]) - usr.client.watchlist_show() - - else if(href_list["watcheditlog"]) - var/target_ckey = sanitizeSQL("[href_list["watcheditlog"]]") - var/DBQuery/query_watchedits = dbcon.NewQuery("SELECT edits FROM [format_table_name("watch")] WHERE ckey = '[target_ckey]'") - if(!query_watchedits.Execute()) - var/err = query_watchedits.ErrorMsg() - log_game("SQL ERROR obtaining edits from watch table. Error : \[[err]\]\n") - return - if(query_watchedits.NextRow()) - var/edit_log = query_watchedits.item[1] - usr << browse(edit_log,"window=watchedits") - - else if(href_list["mute"]) - if(!check_rights(R_ADMIN|R_MOD)) - return - - var/mob/M = locateUID(href_list["mute"]) - if(!ismob(M)) return - if(!M.client) return - - var/mute_type = href_list["mute_type"] - if(istext(mute_type)) mute_type = text2num(mute_type) - if(!isnum(mute_type)) return - - cmd_admin_mute(M, mute_type) - - else if(href_list["c_mode"]) - if(!check_rights(R_ADMIN)) return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - var/dat = {"What mode do you wish to play?
    "} - for(var/mode in config.modes) - dat += {"[config.mode_names[mode]]
    "} - dat += {"Secret
    "} - dat += {"Random
    "} - dat += {"Now: [master_mode]"} - usr << browse(dat, "window=c_mode") - - else if(href_list["f_secret"]) - if(!check_rights(R_ADMIN)) return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(master_mode != "secret") - return alert(usr, "The game mode has to be secret!", null, null, null, null) - var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
    "} - for(var/mode in config.modes) - dat += {"[config.mode_names[mode]]
    "} - dat += {"Random (default)
    "} - dat += {"Now: [secret_force_mode]"} - usr << browse(dat, "window=f_secret") - - else if(href_list["c_mode2"]) - if(!check_rights(R_ADMIN|R_SERVER)) return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - master_mode = href_list["c_mode2"] - log_admin("[key_name(usr)] set the mode as [master_mode].") - message_admins("[key_name_admin(usr)] set the mode as [master_mode].", 1) - to_chat(world, "The mode is now: [master_mode]") - Game() // updates the main game menu - world.save_mode(master_mode) - .(href, list("c_mode"=1)) - - else if(href_list["f_secret2"]) - if(!check_rights(R_ADMIN|R_SERVER)) return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(master_mode != "secret") - return alert(usr, "The game mode has to be secret!", null, null, null, null) - secret_force_mode = href_list["f_secret2"] - log_admin("[key_name(usr)] set the forced secret mode as [secret_force_mode].") - message_admins("[key_name_admin(usr)] set the forced secret mode as [secret_force_mode].", 1) - Game() // updates the main game menu - .(href, list("f_secret"=1)) - - else if(href_list["monkeyone"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["monkeyone"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make monkey?",, "Yes", "No") != "Yes") - return - - log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)]") - message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(H)]", 1) - H.monkeyize() - - - else if(href_list["corgione"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["corgione"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - if(alert(usr, "Confirm make corgi?",, "Yes", "No") != "Yes") - return - - log_admin("[key_name(usr)] attempting to corgize [key_name(H)]") - message_admins("[key_name_admin(usr)] attempting to corgize [key_name_admin(H)]", 1) - H.corgize() - - else if(href_list["makePAI"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makePAI"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make pai?",, "Yes", "No") != "Yes") - return - - var/painame = "Default" - var/name = "" - if(alert(usr, "Do you want to set their name or let them choose their own name?", "Name Choice", "Set Name", "Let them choose") == "Set Name") - name = sanitize(copytext(input(usr, "Enter a name for the new pAI. Default name is [painame].", "pAI Name", painame),1,MAX_NAME_LEN)) - else - name = sanitize(copytext(input(H, "An admin wants to make you into a pAI. Choose a name. Default is [painame].", "pAI Name", painame),1,MAX_NAME_LEN)) - - if(!name) - name = painame - - log_admin("[key_name(usr)] attempting to pAIze [key_name(H)]") - message_admins("[key_name_admin(usr)] attempting to pAIze [key_name_admin(H)]", 1) - H.paize(name) - - else if(href_list["forcespeech"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/M = locateUID(href_list["forcespeech"]) - if(!ismob(M)) - to_chat(usr, "this can only be used on instances of type /mob") - - var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. - if(!speech) return - M.say(speech) - speech = sanitize(speech) // Nah, we don't trust them - log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]") - message_admins("[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]") - - else if(href_list["sendtoprison"]) - if(!check_rights(R_ADMIN)) return - - if(alert(usr, "Send to admin prison for the round?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["sendtoprison"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - var/turf/prison_cell = pick(prisonwarp) - if(!prison_cell) return - - var/obj/structure/closet/secure_closet/brig/locker = new /obj/structure/closet/secure_closet/brig(prison_cell) - locker.opened = 0 - locker.locked = 1 - - //strip their stuff and stick it in the crate - for(var/obj/item/I in M) - if(M.unEquip(I)) - I.loc = locker - I.layer = initial(I.layer) - I.plane = initial(I.plane) - I.dropped(M) - M.update_icons() - - //so they black out before warping - M.Paralyse(5) - sleep(5) - if(!M) return - - M.loc = prison_cell - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/prisoner = M - prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) - prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) - - to_chat(M, "You have been sent to the prison station!") - log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") - message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) - - else if(href_list["sendbacktolobby"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locateUID(href_list["sendbacktolobby"]) - - if(!isobserver(M)) - to_chat(usr, "You can only send ghost players back to the Lobby.") - return - - if(!M.client) - to_chat(usr, "[M] doesn't seem to have an active client.") - return - - if(alert(usr, "Send [key_name(M)] back to Lobby?", "Message", "Yes", "No") != "Yes") - return - - log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] back to the Lobby.") - - var/mob/new_player/NP = new() - GLOB.non_respawnable_keys -= M.ckey - NP.ckey = M.ckey - qdel(M) - - else if(href_list["tdome1"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["tdome1"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - for(var/obj/item/I in M) - M.unEquip(I) - if(I) - I.loc = M.loc - I.layer = initial(I.layer) - I.plane = initial(I.plane) - I.dropped(M) - - M.Paralyse(5) - sleep(5) - M.loc = pick(tdome1) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 1)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 1)", 1) - - else if(href_list["tdome2"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["tdome2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - for(var/obj/item/I in M) - M.unEquip(I) - if(I) - I.loc = M.loc - I.layer = initial(I.layer) - I.plane = initial(I.plane) - I.dropped(M) - - M.Paralyse(5) - sleep(5) - M.loc = pick(tdome2) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 2)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 2)", 1) - - else if(href_list["tdomeadmin"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["tdomeadmin"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - M.Paralyse(5) - sleep(5) - M.loc = pick(tdomeadmin) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Admin.)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Admin.)", 1) - - else if(href_list["tdomeobserve"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["tdomeobserve"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - for(var/obj/item/I in M) - M.unEquip(I) - if(I) - I.loc = M.loc - I.layer = initial(I.layer) - I.plane = initial(I.plane) - I.dropped(M) - - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/observer = M - observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket(observer), slot_w_uniform) - observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(observer), slot_shoes) - M.Paralyse(5) - sleep(5) - M.loc = pick(tdomeobserve) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Observer.)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Observer.)", 1) - - else if(href_list["aroomwarp"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["aroomwarp"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - M.Paralyse(5) - sleep(5) - M.loc = pick(aroomwarp) - spawn(50) - to_chat(M, "You have been sent to the Admin Room!.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the Admin Room") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the Admin Room", 1) - - - else if(href_list["revive"]) - if(!check_rights(R_REJUVINATE)) return - - var/mob/living/L = locateUID(href_list["revive"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - - L.revive() - message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(L)]!", 1) - log_admin("[key_name(usr)] healed / revived [key_name(L)]") - - else if(href_list["makeai"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - if(alert(usr, "Confirm make ai?",, "Yes", "No") != "Yes") - return - - message_admins("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!", 1) - log_admin("[key_name(usr)] AIized [key_name(H)]") - var/mob/living/silicon/ai/ai_character = H.AIize() - ai_character.moveToAILandmark() - - else if(href_list["makealien"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make alien?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_alienize(H) - - else if(href_list["makeslime"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make slime?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_slimeize(H) - - else if(href_list["makesuper"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - if(alert(usr, "Confirm make superhero?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_super(H) - - else if(href_list["makerobot"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make robot?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_robotize(H) - - else if(href_list["makeanimal"]) - if(!check_rights(R_SPAWN)) return - - var/mob/M = locateUID(href_list["makeanimal"]) - if(istype(M, /mob/new_player)) - to_chat(usr, "This cannot be used on instances of type /mob/new_player") - return - if(alert(usr, "Confirm make animal?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_animalize(M) - - else if(href_list["incarn_ghost"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/dead/observer/G = locateUID(href_list["incarn_ghost"]) - if(!istype(G)) - to_chat(usr, "This will only work on /mob/dead/observer") - - var/posttransformoutfit = usr.client.robust_dress_shop() - - var/mob/living/carbon/human/H = G.incarnate_ghost() - - if(posttransformoutfit && istype(H)) - H.equipOutfit(posttransformoutfit) - - log_admin("[key_name(G)] was incarnated by [key_name(owner)]") - message_admins("[key_name_admin(G)] was incarnated by [key_name_admin(owner)]") - - else if(href_list["togmutate"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["togmutate"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - var/block=text2num(href_list["block"]) - //testing("togmutate([href_list["block"]] -> [block])") - usr.client.cmd_admin_toggle_block(H,block) - show_player_panel(H) - //H.regenerate_icons() - - else if(href_list["adminplayeropts"]) - var/mob/M = locateUID(href_list["adminplayeropts"]) - show_player_panel(M) - - else if(href_list["adminplayerobservefollow"]) - if(!check_rights(R_ADMIN|R_MOD)) - return - - var/mob/M = locateUID(href_list["adminplayerobservefollow"]) - - var/client/C = usr.client - if(!isobserver(usr)) C.admin_ghost() - var/mob/dead/observer/A = C.mob - sleep(2) - A.ManualFollow(M) - - else if(href_list["check_antagonist"]) - check_antagonists() - - else if(href_list["take_question"]) - var/index = text2num(href_list["take_question"]) - - if(href_list["is_mhelp"]) - SSmentor_tickets.takeTicket(index) - else //Ahelp - SStickets.takeTicket(index) - - else if(href_list["resolve"]) - var/index = text2num(href_list["resolve"]) - if(href_list["is_mhelp"]) - SSmentor_tickets.resolveTicket(index) - else //Ahelp - SStickets.resolveTicket(index) - - else if(href_list["autorespond"]) - var/index = text2num(href_list["autorespond"]) - if(!check_rights(R_ADMIN|R_MOD)) - return - SStickets.autoRespond(index) - - else if(href_list["cult_nextobj"]) - if(alert(usr, "Validate the current Cult objective and unlock the next one?", "Cult Cheat Code", "Yes", "No") != "Yes") - return - - if(!GAMEMODE_IS_CULT) - alert("Couldn't locate cult mode datum! This shouldn't ever happen, tell a coder!") - return - - var/datum/game_mode/cult/cult_round = SSticker.mode - cult_round.bypass_phase() - message_admins("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.") - log_admin("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.") - - else if(href_list["cult_mindspeak"]) - var/input = stripped_input(usr, "Communicate to all the cultists with the voice of [SSticker.cultdat.entity_name]", "Voice of [SSticker.cultdat.entity_name]", "") - if(!input) - return - - for(var/datum/mind/H in SSticker.mode.cult) - if (H.current) - to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]
    ") - - for(var/mob/dead/observer/O in GLOB.player_list) - to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]
    ") - - message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [SSticker.cultdat.entity_name].") - log_admin("[key_name(usr)] Voice of [SSticker.cultdat.entity_name]: [input]") - - else if(href_list["adminplayerobservecoodjump"]) - if(!check_rights(R_ADMIN)) return - - var/x = text2num(href_list["X"]) - var/y = text2num(href_list["Y"]) - var/z = text2num(href_list["Z"]) - - var/client/C = usr.client - if(!isobserver(usr)) C.admin_ghost() - sleep(2) - C.jumptocoord(x,y,z) - - else if(href_list["adminchecklaws"]) - output_ai_laws() - - else if(href_list["adminmoreinfo"]) - var/mob/M = locateUID(href_list["adminmoreinfo"]) - admin_mob_info(M) - - else if(href_list["adminspawncookie"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - var/mob/living/carbon/human/H = locateUID(href_list["adminspawncookie"]) - if(!ishuman(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_l_hand ) - if(!(istype(H.l_hand,/obj/item/reagent_containers/food/snacks/cookie))) - H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_r_hand ) - if(!(istype(H.r_hand,/obj/item/reagent_containers/food/snacks/cookie))) - log_admin("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") - message_admins("[key_name_admin(H)] has [H.p_their()] hands full, so [H.p_they()] did not receive [H.p_their()] cookie, spawned by [key_name_admin(src.owner)].") - return - else - H.update_inv_r_hand()//To ensure the icon appears in the HUD - else - H.update_inv_l_hand() - log_admin("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") - message_admins("[key_name_admin(H)] got [H.p_their()] cookie, spawned by [key_name_admin(src.owner)]") - feedback_inc("admin_cookies_spawned",1) - to_chat(H, "Your prayers have been answered!! You received the best cookie!") - - else if(href_list["BlueSpaceArtillery"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - var/mob/living/M = locateUID(href_list["BlueSpaceArtillery"]) - if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - - if(alert(owner, "Are you sure you wish to hit [key_name(M)] with Bluespace Artillery?", "Confirm Firing?" , "Yes" , "No") != "Yes") - return - - if(BSACooldown) - to_chat(owner, "Standby. Reload cycle in progress. Gunnery crews ready in five seconds!") - return - - BSACooldown = 1 - spawn(50) - BSACooldown = 0 - - to_chat(M, "You've been hit by bluespace artillery!") - log_admin("[key_name(M)] has been hit by Bluespace Artillery fired by [key_name(owner)]") - message_admins("[key_name_admin(M)] has been hit by Bluespace Artillery fired by [key_name_admin(owner)]") - - var/turf/simulated/floor/T = get_turf(M) - if(istype(T)) - if(prob(80)) - T.break_tile_to_plating() - else - T.break_tile() - - if(M.health <= 1) - M.gib() - else - M.adjustBruteLoss(min(99,(M.health - 1))) - M.Stun(20) - M.Weaken(20) - M.Stuttering(20) - - else if(href_list["CentcommReply"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locateUID(href_list["CentcommReply"]) - usr.client.admin_headset_message(M, "Centcomm") - - else if(href_list["SyndicateReply"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locateUID(href_list["SyndicateReply"]) - usr.client.admin_headset_message(M, "Syndicate") - - else if(href_list["HeadsetMessage"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locateUID(href_list["HeadsetMessage"]) - usr.client.admin_headset_message(M) - - else if(href_list["EvilFax"]) - if(!check_rights(R_ADMIN)) - return - var/mob/living/carbon/human/H = locateUID(href_list["EvilFax"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - var/etypes = list("Borgification", "Corgification", "Death By Fire", "Total Brain Death", "Honk Tumor", "Cluwne", "Demote", "Demote with Bot", "Revoke Fax Access", "Angry Fax Machine") - var/eviltype = input(src.owner, "Which type of evil fax do you wish to send [H]?","Its good to be baaaad...", "") as null|anything in etypes - if(!(eviltype in etypes)) - return - var/customname = clean_input("Pick a title for the evil fax.", "Fax Title", , owner) - if(!customname) - customname = "paper" - var/obj/item/paper/evilfax/P = new /obj/item/paper/evilfax(null) - var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) - - P.name = "Central Command - [customname]" - P.info = "You really should've known better." - P.myeffect = eviltype - P.mytarget = H - if(alert("Do you want the Evil Fax to activate automatically if [H] tries to ignore it?",,"Yes", "No") == "Yes") - P.activate_on_timeout = 1 - P.x = rand(-2, 0) - P.y = rand(-1, 2) - P.offset_x += P.x - P.offset_y += P.y - P.update_icon() - var/stampvalue = "cent" - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.icon_state = "paper_stamp-[stampvalue]" - stampoverlay.pixel_x = P.x - stampoverlay.pixel_y = P.y - P.stamped = list() - P.stamped += /obj/item/stamp/centcom - if(!P.ico) - P.ico = new - P.ico += "paper_stamp-[stampvalue]" - P.overlays += stampoverlay - P.stamps += "
    " - P.update_icon() - P.faxmachineid = fax.UID() - P.loc = fax.loc // Do not use fax.receivefax(P) here, as it won't preserve the type. Physically teleporting the fax paper is required. - if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") - to_chat(src.owner, "You sent a [eviltype] fax to [H]") - log_admin("[key_name(src.owner)] sent [key_name(H)] a [eviltype] fax") - message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a [eviltype] fax") - else if(href_list["Bless"]) - if(!check_rights(R_EVENT)) - return - var/mob/living/M = locateUID(href_list["Bless"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - var/btypes = list("To Arrivals", "Moderate Heal") - var/mob/living/carbon/human/H - if(ishuman(M)) - H = M - btypes += "Heal Over Time" - btypes += "Permanent Regeneration" - btypes += "Super Powers" - btypes += "Scarab Guardian" - btypes += "Human Protector" - btypes += "Sentient Pet" - btypes += "All Access" - var/blessing = input(owner, "How would you like to bless [M]?", "Its good to be good...", "") as null|anything in btypes - if(!(blessing in btypes)) - return - var/logmsg = null - switch(blessing) - if("To Arrivals") - M.forceMove(pick(latejoin)) - to_chat(M, "You are abruptly pulled through space!") - logmsg = "a teleport to arrivals." - if("Moderate Heal") - M.adjustBruteLoss(-25) - M.adjustFireLoss(-25) - M.adjustToxLoss(-25) - M.adjustOxyLoss(-25) - to_chat(M,"You feel invigorated!") - logmsg = "a moderate heal." - if("Heal Over Time") - H.reagents.add_reagent("salglu_solution", 30) - H.reagents.add_reagent("salbutamol", 20) - H.reagents.add_reagent("spaceacillin", 20) - logmsg = "a heal over time." - if("Permanent Regeneration") - H.dna.SetSEState(REGENERATEBLOCK, 1) - genemutcheck(H, REGENERATEBLOCK, null, MUTCHK_FORCED) - H.update_mutations() - H.gene_stability = 100 - logmsg = "permanent regeneration." - if("Super Powers") - var/list/default_genes = list(REGENERATEBLOCK, BREATHLESSBLOCK, COLDBLOCK) - for(var/gene in default_genes) - H.dna.SetSEState(gene, 1) - genemutcheck(H, gene, null, MUTCHK_FORCED) - H.update_mutations() - H.gene_stability = 100 - logmsg = "superpowers." - if("Scarab Guardian") - var/obj/item/guardiancreator/biological/scarab = new /obj/item/guardiancreator/biological(H) - var/list/possible_guardians = list("Chaos", "Standard", "Ranged", "Support", "Explosive", "Random") - var/typechoice = input("Select Guardian Type", "Type") as null|anything in possible_guardians - if(isnull(typechoice)) - return - if(typechoice != "Random") - possible_guardians -= "Random" - scarab.possible_guardians = list() - scarab.possible_guardians += typechoice - scarab.attack_self(H) - spawn(700) - qdel(scarab) - logmsg = "scarab guardian." - if("Sentient Pet") - var/pets = subtypesof(/mob/living/simple_animal) - var/petchoice = input("Select pet type", "Pets") as null|anything in pets - if(isnull(petchoice)) - return - var/list/mob/dead/observer/candidates = pollCandidates("Play as the special event pet [H]?", poll_time = 200, min_hours = 10) - var/mob/dead/observer/theghost = null - if(candidates.len) - var/mob/living/simple_animal/pet/P = new petchoice(H.loc) - theghost = pick(candidates) - P.key = theghost.key - P.master_commander = H - P.universal_speak = 1 - P.universal_understand = 1 - P.can_collar = 1 - P.faction = list("neutral") - var/obj/item/clothing/accessory/petcollar/C = new - P.add_collar(C) - var/obj/item/card/id/I = H.wear_id - if(I) - var/obj/item/card/id/D = new /obj/item/card/id(C) - D.access = I.access - D.registered_name = P.name - D.assignment = "Pet" - C.access_id = D - spawn(30) - var/newname = sanitize(copytext(input(P, "You are [P], special event pet of [H]. Change your name to something else?", "Name change", P.name) as null|text,1,MAX_NAME_LEN)) - if(newname && newname != P.name) - P.name = newname - if(P.mind) - P.mind.name = newname - logmsg = "pet ([P])." - else - to_chat(usr, "WARNING: Nobody volunteered to play the special event pet.") - logmsg = "pet (no volunteers)." - if("Human Protector") - usr.client.create_eventmob_for(H, 0) - logmsg = "syndie protector." - if("All Access") - var/obj/item/card/id/I = H.wear_id - if(I) - var/list/access_to_give = get_all_accesses() - for(var/this_access in access_to_give) - if(!(this_access in I.access)) - // don't have it - add it - I.access |= this_access - else - to_chat(usr, "ERROR: [H] is not wearing an ID card.") - logmsg = "all access." - if(logmsg) - log_admin("[key_name(owner)] answered [key_name(M)]'s prayer with a blessing: [logmsg]") - message_admins("[key_name_admin(owner)] answered [key_name_admin(M)]'s prayer with a blessing: [logmsg]") - else if(href_list["Smite"]) - if(!check_rights(R_EVENT)) - return - var/mob/living/M = locateUID(href_list["Smite"]) - var/mob/living/carbon/human/H - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - var/ptypes = list("Lightning bolt", "Fire Death", "Gib") - if(ishuman(M)) - H = M - ptypes += "Brain Damage" - ptypes += "Honk Tumor" - ptypes += "Hallucinate" - ptypes += "Cold" - ptypes += "Hunger" - ptypes += "Cluwne" - ptypes += "Mutagen Cookie" - ptypes += "Hellwater Cookie" - ptypes += "Hunter" - ptypes += "Crew Traitor" - ptypes += "Floor Cluwne" - ptypes += "Shamebrero" - var/punishment = input(owner, "How would you like to smite [M]?", "Its good to be baaaad...", "") as null|anything in ptypes - if(!(punishment in ptypes)) - return - var/logmsg = null - switch(punishment) - if("Lightning bolt") - M.electrocute_act(5, "Lightning Bolt", safety = TRUE, override = TRUE) - playsound(get_turf(M), 'sound/magic/lightningshock.ogg', 50, 1, -1) - M.adjustFireLoss(75) - M.Weaken(5) - to_chat(M, "The gods have punished you for your sins!") - logmsg = "a lightning bolt." - if("Fire Death") - to_chat(M,"You feel hotter than usual. Maybe you should lowe-wait, is that your hand melting?") - var/turf/simulated/T = get_turf(M) - new /obj/effect/hotspot(T) - M.adjustFireLoss(150) - logmsg = "a firey death." - if("Gib") - M.gib(FALSE) - logmsg = "gibbed." - - if("Brain Damage") - H.adjustBrainLoss(75) - logmsg = "75 brain damage." - if("Honk Tumor") - if(!H.get_int_organ(/obj/item/organ/internal/honktumor)) - var/obj/item/organ/internal/organ = new /obj/item/organ/internal/honktumor - to_chat(H, "Life seems funnier, somehow.") - organ.insert(H) - logmsg = "a honk tumor." - if("Hallucinate") - H.Hallucinate(1000) - logmsg = "hallucinations." - if("Cold") - H.reagents.add_reagent("frostoil", 40) - H.reagents.add_reagent("ice", 40) - logmsg = "cold." - if("Hunger") - H.set_nutrition(NUTRITION_LEVEL_CURSED) - logmsg = "starvation." - if("Cluwne") - H.makeCluwne() - H.mutations |= NOCLONE - logmsg = "cluwned." - if("Mutagen Cookie") - var/obj/item/reagent_containers/food/snacks/cookie/evilcookie = new /obj/item/reagent_containers/food/snacks/cookie - evilcookie.reagents.add_reagent("mutagen", 10) - evilcookie.desc = "It has a faint green glow." - evilcookie.bitesize = 100 - evilcookie.flags = NODROP | DROPDEL - H.drop_l_hand() - H.equip_to_slot_or_del(evilcookie, slot_l_hand) - logmsg = "a mutagen cookie." - if("Hellwater Cookie") - var/obj/item/reagent_containers/food/snacks/cookie/evilcookie = new /obj/item/reagent_containers/food/snacks/cookie - evilcookie.reagents.add_reagent("hell_water", 25) - evilcookie.desc = "Sulphur-flavored." - evilcookie.bitesize = 100 - evilcookie.flags = NODROP | DROPDEL - H.drop_l_hand() - H.equip_to_slot_or_del(evilcookie, slot_l_hand) - logmsg = "a hellwater cookie." - if("Hunter") - H.mutations |= NOCLONE - usr.client.create_eventmob_for(H, 1) - logmsg = "hunter." - if("Crew Traitor") - if(!H.mind) - to_chat(usr, "This mob has no mind!") - return - - var/list/possible_traitors = list() - for(var/mob/living/player in GLOB.living_mob_list) - if(player.client && player.mind && player.stat != DEAD && player != H) - if(ishuman(player) && !player.mind.special_role) - if(player.client && (ROLE_TRAITOR in player.client.prefs.be_special) && !jobban_isbanned(player, ROLE_TRAITOR) && !jobban_isbanned(player, "Syndicate")) - possible_traitors += player.mind - - for(var/datum/mind/player in possible_traitors) - if(player.current) - if(ismindshielded(player.current)) - possible_traitors -= player - - if(possible_traitors.len) - var/datum/mind/newtraitormind = pick(possible_traitors) - var/datum/objective/assassinate/kill_objective = new() - kill_objective.target = H.mind - kill_objective.owner = newtraitormind - kill_objective.explanation_text = "Assassinate [H.mind], the [H.mind.assigned_role]" - H.mind.objectives += kill_objective - var/datum/antagonist/traitor/T = new() - T.give_objectives = FALSE - to_chat(newtraitormind, "ATTENTION: It is time to pay your debt to the Syndicate...") - to_chat(newtraitormind, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") - newtraitormind.add_antag_datum(T) - else - to_chat(usr, "ERROR: Failed to create a traitor.") - return - logmsg = "crew traitor." - - if("Floor Cluwne") - var/turf/T = get_turf(M) - var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T) - FC.smiting = TRUE - FC.Acquire_Victim(M) - logmsg = "floor cluwne" - if("Shamebrero") - if(H.head) - H.unEquip(H.head, TRUE) - var/obj/item/clothing/head/sombrero/shamebrero/S = new(H.loc) - H.equip_to_slot_or_del(S, slot_head) - logmsg = "shamebrero" - if(logmsg) - log_admin("[key_name(owner)] smited [key_name(M)] with: [logmsg]") - message_admins("[key_name_admin(owner)] smited [key_name_admin(M)] with: [logmsg]") - else if(href_list["cryossd"]) - if(!check_rights(R_ADMIN)) - return - var/mob/living/carbon/human/H = locateUID(href_list["cryossd"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(!href_list["cryoafk"] && !isLivingSSD(H)) - to_chat(usr, "This can only be used on living, SSD players.") - return - if(istype(H.loc, /obj/machinery/cryopod)) - var/obj/machinery/cryopod/P = H.loc - P.despawn_occupant() - log_admin("[key_name(usr)] despawned [H.job] [H] in cryo.") - message_admins("[key_name_admin(usr)] despawned [H.job] [H] in cryo.") - else if(cryo_ssd(H)) - log_admin("[key_name(usr)] sent [H.job] [H] to cryo.") - message_admins("[key_name_admin(usr)] sent [H.job] [H] to cryo.") - if(href_list["cryoafk"]) // Warn them if they are send to storage and are AFK - to_chat(H, "The admins have moved you to cryo storage for being AFK. Please eject yourself (right click, eject) out of the cryostorage if you want to avoid being despawned.") - SEND_SOUND(H, 'sound/effects/adminhelp.ogg') - if(H.client) - window_flash(H.client) - else if(href_list["FaxReplyTemplate"]) - if(!check_rights(R_ADMIN)) - return - var/mob/living/carbon/human/H = locateUID(href_list["FaxReplyTemplate"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - var/obj/item/paper/P = new /obj/item/paper(null) - var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) - P.name = "Central Command - paper" - var/stypes = list("Handle it yourselves!","Illegible fax","Fax not signed","Not Right Now","You are wasting our time", "Keep up the good work", "ERT Instructions") - var/stype = input(src.owner, "Which type of standard reply do you wish to send to [H]?","Choose your paperwork", "") as null|anything in stypes - var/tmsg = "



    Nanotrasen Science Station [using_map.station_short]


    NAS Trurl Communications Department Report


    " - if(stype == "Handle it yourselves!") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.

    This is an automatic message." - else if(stype == "Illegible fax") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.

    Please consult your nearest dictionary and/or thesaurus and try again.

    This is an automatic message." - else if(stype == "Fax not signed") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax has not been correctly signed and, as such, we cannot verify your identity.

    Please sign your faxes before sending them so that we may verify your identity.

    This is an automatic message." - else if(stype == "Not Right Now") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.

    This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.

    Please try again later.

    This is an automatic message." - else if(stype == "You are wasting our time") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.

    Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.

    This is an automatic message." - else if(stype == "Keep up the good work") - tmsg += "Greetings, esteemed crewmember. Your fax has been received successfully by NAS Trurl Fax Registration.

    We at the NAS Trurl appreciate the good work that you have done here, and sincerely recommend that you continue such a display of dedication to the company.

    This is absolutely not an automated message." - else if(stype == "ERT Instructions") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please utilize the Card Swipers if you wish to call for an ERT.

    This is an automated message." - else - return - tmsg += "
    " - P.info = tmsg - P.x = rand(-2, 0) - P.y = rand(-1, 2) - P.offset_x += P.x - P.offset_y += P.y - P.update_icon() - var/stampvalue = "cent" - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.icon_state = "paper_stamp-[stampvalue]" - stampoverlay.pixel_x = P.x - stampoverlay.pixel_y = P.y - P.stamped = list() - P.stamped += /obj/item/stamp/centcom - if(!P.ico) - P.ico = new - P.ico += "paper_stamp-[stampvalue]" - P.overlays += stampoverlay - P.stamps += "
    " - P.update_icon() - fax.receivefax(P) - if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") - to_chat(src.owner, "You sent a standard '[stype]' fax to [H]") - log_admin("[key_name(src.owner)] sent [key_name(H)] a standard '[stype]' fax") - message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a standard '[stype]' fax") - - else if(href_list["HONKReply"]) - var/mob/living/carbon/human/H = locateUID(href_list["HONKReply"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") - return - - var/input = input(src.owner, "Please enter a message to reply to [key_name(H)] via [H.p_their()] headset.","Outgoing message from HONKplanet", "") - if(!input) return - - to_chat(src.owner, "You sent [input] to [H] via a secure channel.") - log_admin("[src.owner] replied to [key_name(H)]'s HONKplanet message with the message [input].") - to_chat(H, "You hear something crackle in your headset for a moment before a voice speaks. \"Please stand by for a message from your HONKbrothers. Message as follows, HONK. [input]. Message ends, HONK.\"") - - else if(href_list["ErtReply"]) - if(!check_rights(R_ADMIN)) - return - - if(alert(src.owner, "Accept or Deny ERT request?", "CentComm Response", "Accept", "Deny") == "Deny") - var/mob/living/carbon/human/H = locateUID(href_list["ErtReply"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(H.stat != 0) - to_chat(usr, "The person you are trying to contact is not conscious.") - return - if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") - return - - var/input = input(src.owner, "Please enter a reason for denying [key_name(H)]'s ERT request.","Outgoing message from CentComm", "") - if(!input) return - ert_request_answered = TRUE - to_chat(src.owner, "You sent [input] to [H] via a secure channel.") - log_admin("[src.owner] denied [key_name(H)]'s ERT request with the message [input].") - to_chat(H, "You hear something crackle in your headset for a moment before a voice speaks. \"Your ERT request has been denied for the following reasons: [input]. Message ends.\"") - else - src.owner.response_team() - - - else if(href_list["AdminFaxView"]) - if(!check_rights(R_ADMIN)) - return - - var/obj/item/fax = locate(href_list["AdminFaxView"]) - if(istype(fax, /obj/item/paper)) - var/obj/item/paper/P = fax - P.show_content(usr,1) - else if(istype(fax, /obj/item/photo)) - var/obj/item/photo/H = fax - H.show(usr) - else if(istype(fax, /obj/item/paper_bundle)) - //having multiple people turning pages on a paper_bundle can cause issues - //open a browse window listing the contents instead - var/data = "" - var/obj/item/paper_bundle/B = fax - - for(var/page = 1, page <= B.amount + 1, page++) - var/obj/pageobj = B.contents[page] - data += "Page [page] - [pageobj.name]
    " - - usr << browse(data, "window=PaperBundle[B.UID()]") - else - to_chat(usr, "The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]") - - else if(href_list["AdminFaxViewPage"]) - if(!check_rights(R_ADMIN)) - return - - var/page = text2num(href_list["AdminFaxViewPage"]) - var/obj/item/paper_bundle/bundle = locate(href_list["paper_bundle"]) - - if(!bundle) return - - if(istype(bundle.contents[page], /obj/item/paper)) - var/obj/item/paper/P = bundle.contents[page] - P.show_content(usr, 1) - else if(istype(bundle.contents[page], /obj/item/photo)) - var/obj/item/photo/H = bundle.contents[page] - H.show(usr) - return - - else if(href_list["AdminFaxCreate"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/sender = locate(href_list["AdminFaxCreate"]) - var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) - var/faxtype = href_list["faxtype"] - var/reply_to = locate(href_list["replyto"]) - var/destination - var/notify - - var/obj/item/paper/P = new /obj/item/paper(null) //hopefully the null loc won't cause trouble for us - - if(!fax) - var/list/departmentoptions = alldepartments + hidden_departments + "All Departments" - destination = input(usr, "To which department?", "Choose a department", "") as null|anything in departmentoptions - if(!destination) - qdel(P) - return - - for(var/obj/machinery/photocopier/faxmachine/F in allfaxes) - if(destination != "All Departments" && F.department == destination) - fax = F - - - var/input = input(src.owner, "Please enter a message to send a fax via secure connection. Use
    for line breaks. Both pencode and HTML work.", "Outgoing message from Centcomm", "") as message|null - if(!input) - qdel(P) - return - input = admin_pencode_to_html(html_encode(input)) // Encode everything from pencode to html - - var/customname = clean_input("Pick a title for the fax.", "Fax Title", , owner) - if(!customname) - customname = "paper" - - var/stampname - var/stamptype - var/stampvalue - var/sendername - switch(faxtype) - if("Central Command") - stamptype = "icon" - stampvalue = "cent" - sendername = command_name() - if("Syndicate") - stamptype = "icon" - stampvalue = "syndicate" - sendername = "UNKNOWN" - if("Administrator") - stamptype = input(src.owner, "Pick a stamp type.", "Stamp Type") as null|anything in list("icon","text","none") - if(stamptype == "icon") - stampname = input(src.owner, "Pick a stamp icon.", "Stamp Icon") as null|anything in list("centcom","syndicate","granted","denied","clown") - switch(stampname) - if("centcom") - stampvalue = "cent" - if("syndicate") - stampvalue = "syndicate" - if("granted") - stampvalue = "ok" - if("denied") - stampvalue = "deny" - if("clown") - stampvalue = "clown" - else if(stamptype == "text") - stampvalue = clean_input("What should the stamp say?", "Stamp Text", , owner) - else if(stamptype == "none") - stamptype = "" - else - qdel(P) - return - - sendername = clean_input("What organization does the fax come from? This determines the prefix of the paper (i.e. Central Command- Title). This is optional.", "Organization", , owner) - - if(sender) - notify = alert(src.owner, "Would you like to inform the original sender that a fax has arrived?","Notify Sender","Yes","No") - - // Create the reply message - if(sendername) - P.name = "[sendername]- [customname]" - else - P.name = "[customname]" - P.info = input - P.update_icon() - P.x = rand(-2, 0) - P.y = rand(-1, 2) - P.offset_x += P.x - P.offset_y += P.y - if(stamptype) - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.pixel_x = P.x - stampoverlay.pixel_y = P.y - - if(!P.ico) - P.ico = new - P.ico += "paper_stamp-[stampvalue]" - stampoverlay.icon_state = "paper_stamp-[stampvalue]" - - if(stamptype == "icon") - if(!P.stamped) - P.stamped = new - P.stamped += /obj/item/stamp/centcom - P.overlays += stampoverlay - P.stamps += "
    " - - else if(stamptype == "text") - if(!P.stamped) - P.stamped = new - P.stamped += /obj/item/stamp - P.overlays += stampoverlay - P.stamps += "
    [stampvalue]" - - if(destination != "All Departments") - if(!fax.receivefax(P)) - to_chat(src.owner, "Message transmission failed.") - return - else - for(var/obj/machinery/photocopier/faxmachine/F in allfaxes) - if(is_station_level(F.z)) - spawn(0) - if(!F.receivefax(P)) - to_chat(src.owner, "Message transmission to [F.department] failed.") - - var/datum/fax/admin/A = new /datum/fax/admin() - A.name = P.name - A.from_department = faxtype - if(destination != "All Departments") - A.to_department = fax.department - else - A.to_department = "All Departments" - A.origin = "Administrator" - A.message = P - A.reply_to = reply_to - A.sent_by = usr - A.sent_at = world.time - - to_chat(src.owner, "Message transmitted successfully.") - if(notify == "Yes") - var/mob/living/carbon/human/H = sender - if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.") - if(sender) - log_admin("[key_name(src.owner)] replied to a fax message from [key_name(sender)]: [input]") - message_admins("[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(sender)] (VIEW).", 1) - else - log_admin("[key_name(src.owner)] sent a fax message to [destination]: [input]") - message_admins("[key_name_admin(src.owner)] sent a fax message to [destination] (VIEW).", 1) - return - - else if(href_list["refreshfaxpanel"]) - if(!check_rights(R_ADMIN)) - return - - fax_panel(usr) - - else if(href_list["getplaytimewindow"]) - if(!check_rights(R_ADMIN)) - return - var/mob/M = locateUID(href_list["getplaytimewindow"]) - if(!M) - to_chat(usr, "ERROR: Mob not found.") - return - cmd_mentor_show_exp_panel(M.client) - - else if(href_list["jumpto"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["jumpto"]) - usr.client.jumptomob(M) - - else if(href_list["getmob"]) - if(!check_rights(R_ADMIN)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") return - var/mob/M = locateUID(href_list["getmob"]) - usr.client.Getmob(M) - - else if(href_list["sendmob"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["sendmob"]) - usr.client.sendmob(M) - - else if(href_list["narrateto"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["narrateto"]) - usr.client.cmd_admin_direct_narrate(M) - - else if(href_list["subtlemessage"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["subtlemessage"]) - usr.client.cmd_admin_subtle_message(M) - - else if(href_list["traitor"]) - if(!check_rights(R_ADMIN|R_MOD)) return - - if(!SSticker || !SSticker.mode) - alert("The game hasn't started yet!") - return - - var/mob/M = locateUID(href_list["traitor"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") - return - show_traitor_panel(M) - - else if(href_list["create_object"]) - if(!check_rights(R_SPAWN)) return - return create_object(usr) - - else if(href_list["quick_create_object"]) - if(!check_rights(R_SPAWN)) return - return quick_create_object(usr) - - else if(href_list["create_turf"]) - if(!check_rights(R_SPAWN)) return - return create_turf(usr) - - else if(href_list["create_mob"]) - if(!check_rights(R_SPAWN)) return - return create_mob(usr) - - else if(href_list["object_list"]) //this is the laggiest thing ever - if(!check_rights(R_SPAWN)) return - - var/atom/loc = usr.loc - - var/dirty_paths - if(istext(href_list["object_list"])) - dirty_paths = list(href_list["object_list"]) - else if(istype(href_list["object_list"], /list)) - dirty_paths = href_list["object_list"] - - var/paths = list() - - for(var/dirty_path in dirty_paths) - var/path = text2path(dirty_path) - if(!path) - continue - else if(!ispath(path, /obj) && !ispath(path, /turf) && !ispath(path, /mob)) - continue - paths += path - - if(!paths) - alert("The path list you sent is empty") - return - if(length(paths) > 5) - alert("Select fewer object types, (max 5)") - return - - var/list/offset = splittext(href_list["offset"],",") - var/number = dd_range(1, 100, text2num(href_list["object_count"])) - var/X = offset.len > 0 ? text2num(offset[1]) : 0 - var/Y = offset.len > 1 ? text2num(offset[2]) : 0 - var/Z = offset.len > 2 ? text2num(offset[3]) : 0 - var/tmp_dir = href_list["object_dir"] - var/obj_dir = tmp_dir ? text2num(tmp_dir) : 2 - if(!obj_dir || !(obj_dir in list(1,2,4,8,5,6,9,10))) - obj_dir = 2 - var/obj_name = sanitize(href_list["object_name"]) - - - var/atom/target //Where the object will be spawned - var/where = href_list["object_where"] - if(!( where in list("onfloor","inhand","inmarked") )) - where = "onfloor" - - - switch(where) - if("inhand") - if(!iscarbon(usr) && !isrobot(usr)) - to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") - where = "onfloor" - target = usr - - if("onfloor") - switch(href_list["offset_type"]) - if("absolute") - target = locate(0 + X,0 + Y,0 + Z) - if("relative") - target = locate(loc.x + X,loc.y + Y,loc.z + Z) - if("inmarked") - if(!marked_datum) - to_chat(usr, "You don't have any object marked. Abandoning spawn.") - return - else if(!istype(marked_datum,/atom)) - to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") - return - else - target = marked_datum - - if(target) - for(var/path in paths) - for(var/i = 0; i < number; i++) - if(path in typesof(/turf)) - var/turf/O = target - var/turf/N = O.ChangeTurf(path) - if(N && obj_name) - N.name = obj_name - else - var/atom/O = new path(target) - if(O) - O.admin_spawned = TRUE - O.dir = obj_dir - if(obj_name) - O.name = obj_name - if(istype(O,/mob)) - var/mob/M = O - M.real_name = obj_name - if(where == "inhand" && isliving(usr) && istype(O, /obj/item)) - var/mob/living/L = usr - var/obj/item/I = O - L.put_in_hands(I) - if(isrobot(L)) - var/mob/living/silicon/robot/R = L - if(R.module) - R.module.modules += I - I.loc = R.module - R.module.rebuild() - R.activate_module(I) - R.module.fix_modules() - - if(number == 1) - log_admin("[key_name(usr)] created a [english_list(paths)]") - 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)]") - for(var/path in paths) - if(ispath(path, /mob)) - message_admins("[key_name_admin(usr)] created [number]ea [english_list(paths)]") - break - return - - else if(href_list["kick_all_from_lobby"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) - var/afkonly = text2num(href_list["afkonly"]) - if(alert("Are you sure you want to kick all [afkonly ? "AFK" : ""] clients from the lobby?","Confirmation","Yes","Cancel") != "Yes") - return - var/list/listkicked = kick_clients_in_lobby("You were kicked from the lobby by an Administrator.", afkonly) - - var/strkicked = "" - for(var/name in listkicked) - strkicked += "[name], " - message_admins("[key_name_admin(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") - log_admin("[key_name(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") - else - to_chat(usr, "You may only use this when the game is running.") - - else if(href_list["memoeditlist"]) - if(!check_rights(R_SERVER)) return - var/sql_key = sanitizeSQL("[href_list["memoeditlist"]]") - var/DBQuery/query_memoedits = dbcon.NewQuery("SELECT edits FROM [format_table_name("memo")] WHERE (ckey = '[sql_key]')") - if(!query_memoedits.Execute()) - var/err = query_memoedits.ErrorMsg() - log_game("SQL ERROR obtaining edits from memo table. Error : \[[err]\]\n") - return - if(query_memoedits.NextRow()) - var/edit_log = query_memoedits.item[1] - usr << browse(edit_log,"window=memoeditlist") - - else if(href_list["secretsfun"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/ok = 0 - switch(href_list["secretsfun"]) - if("sec_clothes") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SC") - for(var/obj/item/clothing/under/O in world) - qdel(O) - ok = 1 - if("sec_all_clothes") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SAC") - for(var/obj/item/clothing/O in world) - qdel(O) - ok = 1 - if("sec_classic1") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SC1") - for(var/obj/item/clothing/suit/fire/O in world) - qdel(O) - for(var/obj/structure/grille/O in world) - qdel(O) - if("monkey") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","M") - for(var/mob/living/carbon/human/H in GLOB.mob_list) - spawn(0) - H.monkeyize() - ok = 1 - if("corgi") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","M") - for(var/mob/living/carbon/human/H in GLOB.mob_list) - spawn(0) - H.corgize() - ok = 1 - if("honksquad") - if(usr.client.honksquad()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","HONK") - if("striketeam") - if(usr.client.strike_team()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","Strike") - if("striketeam_syndicate") - if(usr.client.syndicate_strike_team()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","Strike") - if("infiltrators_syndicate") - if(usr.client.syndicate_infiltration_team()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SyndieInfiltrationTeam") - if("gimmickteam") - if(usr.client.gimmick_team()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","GimmickTeam") - if("tripleAI") - usr.client.triple_ai() - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","TriAI") - if("gravity") - if(!(SSticker && SSticker.mode)) - to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.") - return - gravity_is_on = !gravity_is_on - for(var/area/A in world) - A.gravitychange(gravity_is_on,A) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","Grav") - if(gravity_is_on) - log_admin("[key_name(usr)] toggled gravity on.", 1) - message_admins("[key_name_admin(usr)] toggled gravity on.", 1) - event_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.") - else - log_admin("[key_name(usr)] toggled gravity off.", 1) - message_admins("[key_name_admin(usr)] toggled gravity off.", 1) - event_announcement.Announce("Feedback surge detected in mass-distributions systems. Artifical gravity has been disabled whilst the system reinitializes. Further failures may result in a gravitational collapse and formation of blackholes. Have a nice day.") - - if("power") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","P") - log_admin("[key_name(usr)] made all areas powered", 1) - message_admins("[key_name_admin(usr)] made all areas powered", 1) - power_restore() - if("unpower") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","UP") - log_admin("[key_name(usr)] made all areas unpowered", 1) - message_admins("[key_name_admin(usr)] made all areas unpowered", 1) - power_failure() - if("quickpower") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","QP") - log_admin("[key_name(usr)] made all SMESs powered", 1) - message_admins("[key_name_admin(usr)] made all SMESs powered", 1) - power_restore_quick() - if("prisonwarp") - if(!SSticker) - alert("The game hasn't started yet!", null, null, null, null, null) - return - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","PW") - message_admins("[key_name_admin(usr)] teleported all players to the prison station.", 1) - for(var/mob/living/carbon/human/H in GLOB.mob_list) - var/turf/loc = find_loc(H) - var/security = 0 - if(!is_station_level(loc.z) || prisonwarped.Find(H)) - -//don't warp them if they aren't ready or are already there - continue - H.Paralyse(5) - if(H.wear_id) - var/obj/item/card/id/id = H.get_idcard() - for(var/A in id.access) - if(A == access_security) - security++ - if(!security) - //strip their stuff before they teleport into a cell :downs: - for(var/obj/item/W in H) - if(istype(W, /obj/item/organ/external)) - continue - //don't strip organs - H.unEquip(W) - if(H.client) - H.client.screen -= W - if(W) - W.loc = H.loc - W.dropped(H) - W.layer = initial(W.layer) - W.plane = initial(W.plane) - //teleport person to cell - H.loc = pick(prisonwarp) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(H), slot_shoes) - else - //teleport security person - H.loc = pick(prisonsecuritywarp) - prisonwarped += H - if("traitor_all") - if(!SSticker) - alert("The game hasn't started yet!") - return - var/objective = sanitize(copytext(input("Enter an objective"),1,MAX_MESSAGE_LEN)) - if(!objective) - return - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","TA([objective])") - - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.stat == 2 || !H.client || !H.mind) continue - if(is_special_character(H)) continue - //traitorize(H, objective, 0) - H.mind.add_antag_datum(/datum/antagonist/traitor) - - for(var/mob/living/silicon/A in GLOB.player_list) - A.mind.add_antag_datum(/datum/antagonist/traitor) - - message_admins("[key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]", 1) - log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]") - - if("togglebombcap") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","BC") - - var/newBombCap = input(usr,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be between 4 and 128)", "New Bomb Cap", MAX_EX_LIGHT_RANGE) as num|null - if(newBombCap < 4) - return - if(newBombCap > 128) - newBombCap = 128 - - MAX_EX_DEVASTATION_RANGE = round(newBombCap/4) - MAX_EX_HEAVY_RANGE = round(newBombCap/2) - MAX_EX_LIGHT_RANGE = newBombCap - //I don't know why these are their own variables, but fuck it, they are. - MAX_EX_FLASH_RANGE = newBombCap - MAX_EX_FLAME_RANGE = newBombCap - - message_admins("[key_name_admin(usr)] changed the bomb cap to [MAX_EX_DEVASTATION_RANGE], [MAX_EX_HEAVY_RANGE], [MAX_EX_LIGHT_RANGE]") - log_admin("[key_name(usr)] changed the bomb cap to [MAX_EX_DEVASTATION_RANGE], [MAX_EX_HEAVY_RANGE], [MAX_EX_LIGHT_RANGE]") - - if("flicklights") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","FL") - while(!usr.stat) -//knock yourself out to stop the ghosts - for(var/mob/M in GLOB.player_list) - if(M.stat != 2 && prob(25)) - var/area/AffectedArea = get_area(M) - if(AffectedArea.name != "Space" && AffectedArea.name != "Engine Walls" && AffectedArea.name != "Chemical Lab Test Chamber" && AffectedArea.name != "Escape Shuttle" && AffectedArea.name != "Arrival Area" && AffectedArea.name != "Arrival Shuttle" && AffectedArea.name != "start area" && AffectedArea.name != "Engine Combustion Chamber") - AffectedArea.power_light = 0 - AffectedArea.power_change() - spawn(rand(55,185)) - AffectedArea.power_light = 1 - AffectedArea.power_change() - var/Message = rand(1,4) - switch(Message) - if(1) - M.show_message(text("You shudder as if cold..."), 1) - if(2) - M.show_message(text("You feel something gliding across your back..."), 1) - if(3) - M.show_message(text("Your eyes twitch, you feel like something you can't see is here..."), 1) - if(4) - M.show_message(text("You notice something moving out of the corner of your eye, but nothing is there..."), 1) - for(var/obj/W in orange(5,M)) - if(prob(25) && !W.anchored) - step_rand(W) - sleep(rand(100,1000)) - for(var/mob/M in GLOB.player_list) - if(M.stat != 2) - M.show_message(text("The chilling wind suddenly stops..."), 1) - if("lightout") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","LO") - message_admins("[key_name_admin(usr)] has broke a lot of lights", 1) - var/datum/event/electrical_storm/E = new /datum/event/electrical_storm - E.lightsoutAmount = 2 - if("blackout") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","BO") - message_admins("[key_name_admin(usr)] broke all lights", 1) - for(var/obj/machinery/light/L in GLOB.machines) - L.break_light_tube() - if("whiteout") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","WO") - message_admins("[key_name_admin(usr)] fixed all lights", 1) - for(var/obj/machinery/light/L in GLOB.machines) - L.fix() - if("floorlava") - feedback_inc("admin_secrets_fun_used", 1) - feedback_add_details("admin_secrets_fun_used", "LF") - var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") - if(sure == "No") - return - SSweather.run_weather(/datum/weather/floor_is_lava) - message_admins("[key_name_admin(usr)] made the floor lava") - if("fakelava") - feedback_inc("admin_secrets_fun_used", 1) - feedback_add_details("admin_secrets_fun_used", "LZ") - var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") - if(sure == "No") - return - SSweather.run_weather(/datum/weather/floor_is_lava/fake) - message_admins("[key_name_admin(usr)] made aesthetic lava on the floor") - if("weatherashstorm") - feedback_inc("admin_secrets_fun_used", 1) - feedback_add_details("admin_secrets_fun_used", "WA") - var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") - if(sure == "No") - return - SSweather.run_weather(/datum/weather/ash_storm) - message_admins("[key_name_admin(usr)] spawned an ash storm on the mining level") - if("retardify") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","RET") - for(var/mob/living/carbon/human/H in GLOB.player_list) - to_chat(H, "You suddenly feel stupid.") - H.setBrainLoss(60) - message_admins("[key_name_admin(usr)] made everybody retarded") - if("fakeguns") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","FG") - for(var/obj/item/W in world) - if(istype(W, /obj/item/clothing) || istype(W, /obj/item/card/id) || istype(W, /obj/item/disk) || istype(W, /obj/item/tank)) - continue - W.icon = 'icons/obj/guns/projectile.dmi' - W.icon_state = "revolver" - W.item_state = "gun" - message_admins("[key_name_admin(usr)] made every item look like a gun") - if("schoolgirl") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SG") - for(var/obj/item/clothing/under/W in world) - W.icon_state = "schoolgirl" - W.item_state = "w_suit" - W.item_color = "schoolgirl" - message_admins("[key_name_admin(usr)] activated Japanese Animes mode") - world << sound('sound/AI/animes.ogg') - if("eagles")//SCRAW - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","EgL") - for(var/obj/machinery/door/airlock/W in GLOB.airlocks) - if(is_station_level(W.z) && !istype(get_area(W), /area/bridge) && !istype(get_area(W), /area/crew_quarters) && !istype(get_area(W), /area/security/prison)) - W.req_access = list() - message_admins("[key_name_admin(usr)] activated Egalitarian Station mode") - event_announcement.Announce("Centcomm airlock control override activated. Please take this time to get acquainted with your coworkers.", new_sound = 'sound/AI/commandreport.ogg') - if("onlyone") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","OO") - usr.client.only_one() -// message_admins("[key_name_admin(usr)] has triggered HIGHLANDER") - if("onlyme") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","OM") - usr.client.only_me() - if("onlyoneteam") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","OOT") - usr.client.only_one_team() -// message_admins("[key_name_admin(usr)] has triggered ") - if("rolldice") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ROL") - usr.client.roll_dices() - if("guns") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SG") - var/survivor_probability = 0 - switch(alert("Do you want this to create survivors antagonists?", , "No Antags", "Some Antags", "All Antags!")) - if("Some Antags") - survivor_probability = 25 - if("All Antags!") - survivor_probability = 100 - - rightandwrong(SUMMON_GUNS, usr, survivor_probability) - if("magic") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SM") - var/survivor_probability = 0 - switch(alert("Do you want this to create survivors antagonists?", , "No Antags", "Some Antags", "All Antags!")) - if("Some Antags") - survivor_probability = 25 - if("All Antags!") - survivor_probability = 100 - - rightandwrong(SUMMON_MAGIC, usr, survivor_probability) - if("tdomereset") - var/delete_mobs = alert("Clear all mobs?","Confirm","Yes","No","Cancel") - if(delete_mobs == "Cancel") - return - - var/area/thunderdome = locate(/area/tdome/arena) - if(delete_mobs == "Yes") - for(var/mob/living/mob in thunderdome) - qdel(mob) //Clear mobs - for(var/obj/obj in thunderdome) - if(!istype(obj,/obj/machinery/camera)) - qdel(obj) //Clear objects - - var/area/template = locate(/area/tdome/arena_source) - template.copy_contents_to(thunderdome) - - log_admin("[key_name(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].", 1) - message_admins("[key_name_admin(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].") - - if("tdomestart") - var/confirmation = alert("Start a Thunderdome match?","Confirm","Yes","No") - if(confirmation == "No") - return - if(makeThunderdomeTeams()) - log_admin("[key_name(usr)] started a Thunderdome match!", 1) - message_admins("[key_name_admin(usr)] has started a Thunderdome match!") - else - log_admin("[key_name(usr)] attempted to start a Thunderdome match, but no ghosts signed up.", 1) - message_admins("[key_name_admin(usr)] tried starting a Thunderdome match, but no ghosts signed up.") - if("securitylevel0") - set_security_level(0) - message_admins("[key_name_admin(usr)] change security level to Green.", 1) - if("securitylevel1") - set_security_level(1) - message_admins("[key_name_admin(usr)] change security level to Blue.", 1) - if("securitylevel2") - set_security_level(2) - message_admins("[key_name_admin(usr)] change security level to Red.", 1) - if("securitylevel3") - set_security_level(3) - message_admins("[key_name_admin(usr)] change security level to Gamma.", 1) - if("securitylevel4") - set_security_level(4) - message_admins("[key_name_admin(usr)] change security level to Epsilon.", 1) - if("securitylevel5") - set_security_level(5) - message_admins("[key_name_admin(usr)] change security level to Delta.", 1) - if("moveminingshuttle") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ShM") - if(!SSshuttle.toggleShuttle("mining","mining_home","mining_away")) - message_admins("[key_name_admin(usr)] moved mining shuttle") - log_admin("[key_name(usr)] moved the mining shuttle") - - if("movelaborshuttle") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ShL") - if(!SSshuttle.toggleShuttle("laborcamp","laborcamp_home","laborcamp_away")) - message_admins("[key_name_admin(usr)] moved labor shuttle") - log_admin("[key_name(usr)] moved the labor shuttle") - - if("moveferry") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ShF") - if(!SSshuttle.toggleShuttle("ferry","ferry_home","ferry_away")) - message_admins("[key_name_admin(usr)] moved the centcom ferry") - log_admin("[key_name(usr)] moved the centcom ferry") - - if(usr) - log_admin("[key_name(usr)] used secret [href_list["secretsfun"]]") - if(ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) - - else if(href_list["secretsadmin"]) - if(!check_rights(R_ADMIN)) return - - var/ok = 0 - switch(href_list["secretsadmin"]) - if("list_signalers") - var/dat = "Showing last [length(lastsignalers)] signalers.
    " - for(var/sig in lastsignalers) - dat += "[sig]
    " - usr << browse(dat, "window=lastsignalers;size=800x500") - if("list_lawchanges") - var/dat = "Showing last [length(lawchanges)] law changes.
    " - for(var/sig in lawchanges) - dat += "[sig]
    " - usr << browse(dat, "window=lawchanges;size=800x500") - if("list_job_debug") - var/dat = "Job Debug info.
    " - if(SSjobs) - for(var/line in SSjobs.job_debug) - dat += "[line]
    " - dat+= "*******

    " - for(var/datum/job/job in SSjobs.occupations) - if(!job) continue - dat += "job: [job.title], current_positions: [job.current_positions], total_positions: [job.total_positions]
    " - usr << browse(dat, "window=jobdebug;size=600x500") - if("showailaws") - output_ai_laws() - if("showgm") - if(!SSticker) - alert("The game hasn't started yet!") - else if(SSticker.mode) - alert("The game mode is [SSticker.mode.name]") - else alert("For some reason there's a ticker, but not a game mode") - if("manifest") - var/dat = "Showing Crew Manifest.
    " - dat += "" - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(H.ckey) - dat += text("", H.name, H.get_assignment()) - dat += "
    NamePosition
    [][]
    " - usr << browse(dat, "window=manifest;size=440x410") - if("check_antagonist") - check_antagonists() - if("DNA") - var/dat = "Showing DNA from blood.
    " - dat += "" - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(H.dna && H.ckey) - dat += "" - dat += "
    NameDNABlood Type
    [H][H.dna.unique_enzymes][H.dna.blood_type]
    " - usr << browse(dat, "window=DNA;size=440x410") - if("fingerprints") - var/dat = "Showing Fingerprints.
    " - dat += "" - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(H.ckey) - if(H.dna && H.dna.uni_identity) - dat += "" - else if(H.dna && !H.dna.uni_identity) - dat += "" - else if(!H.dna) - dat += "" - dat += "
    NameFingerprints
    [H][md5(H.dna.uni_identity)]
    [H]H.dna.uni_identity = null
    [H]H.dna = null
    " - usr << browse(dat, "window=fingerprints;size=440x410") - if("night_shift_set") - var/val = alert(usr, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "Night Shift", "On", "Off", "Automatic") - switch(val) - if("Automatic") - if(config.enable_night_shifts) - SSnightshift.can_fire = TRUE - SSnightshift.fire() - else - SSnightshift.update_nightshift(FALSE, TRUE) - to_chat(usr, "Night shift set to automatic.") - if("On") - SSnightshift.can_fire = FALSE - SSnightshift.update_nightshift(TRUE, FALSE) - to_chat(usr, "Night shift forced on.") - if("Off") - SSnightshift.can_fire = FALSE - SSnightshift.update_nightshift(FALSE, FALSE) - to_chat(usr, "Night shift forced off.") - else - if(usr) - log_admin("[key_name(usr)] used secret [href_list["secretsadmin"]]") - if(ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) - - else if(href_list["secretscoder"]) - if(!check_rights(R_DEBUG)) return - - switch(href_list["secretscoder"]) - if("spawn_objects") - var/dat = "Admin Log
    " - for(var/l in admin_log) - dat += "
  • [l]
  • " - if(!admin_log.len) - dat += "No-one has done anything this round!" - usr << browse(dat, "window=admin_log") - if("maint_access_brig") - for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) - if(access_maint_tunnels in M.req_access) - M.req_access = list(access_brig) - message_admins("[key_name_admin(usr)] made all maint doors brig access-only.") - if("maint_access_engiebrig") - for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) - if(access_maint_tunnels in M.req_access) - M.req_access = list() - M.req_one_access = list(access_brig,access_engine) - message_admins("[key_name_admin(usr)] made all maint doors engineering and brig access-only.") - if("infinite_sec") - var/datum/job/J = SSjobs.GetJob("Security Officer") - if(!J) return - J.total_positions = -1 - J.spawn_positions = -1 - message_admins("[key_name_admin(usr)] has removed the cap on security officers.") - - else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here - src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster. - src.access_news_network() - - else if(href_list["ac_set_channel_name"]) - src.admincaster_feed_channel.channel_name = strip_html_simple(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", "")) - while(findtext(src.admincaster_feed_channel.channel_name," ") == 1) - src.admincaster_feed_channel.channel_name = copytext(src.admincaster_feed_channel.channel_name,2,length(src.admincaster_feed_channel.channel_name)+1) - src.access_news_network() - - else if(href_list["ac_set_channel_lock"]) - src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked - src.access_news_network() - - else if(href_list["ac_submit_new_channel"]) - var/check = 0 - for(var/datum/feed_channel/FC in news_network.network_channels) - if(FC.channel_name == src.admincaster_feed_channel.channel_name) - check = 1 - break - if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check ) - src.admincaster_screen=7 - else - var/choice = alert("Please confirm Feed channel creation","Network Channel Handler","Confirm","Cancel") - if(choice=="Confirm") - var/datum/feed_channel/newChannel = new /datum/feed_channel - newChannel.channel_name = src.admincaster_feed_channel.channel_name - newChannel.author = src.admincaster_signature - newChannel.locked = src.admincaster_feed_channel.locked - newChannel.is_admin_channel = 1 - feedback_inc("newscaster_channels",1) - news_network.network_channels += newChannel //Adding channel to the global network - log_admin("[key_name_admin(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!") - src.admincaster_screen=5 - src.access_news_network() - - else if(href_list["ac_set_channel_receiving"]) - var/list/available_channels = list() - for(var/datum/feed_channel/F in news_network.network_channels) - available_channels += F.channel_name - src.admincaster_feed_channel.channel_name = adminscrub(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels ) - src.access_news_network() - - else if(href_list["ac_set_new_message"]) - src.admincaster_feed_message.body = adminscrub(input(usr, "Write your Feed story", "Network Channel Handler", "")) - while(findtext(src.admincaster_feed_message.body," ") == 1) - src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) - src.access_news_network() - - else if(href_list["ac_submit_new_message"]) - if(src.admincaster_feed_message.body =="" || src.admincaster_feed_message.body =="\[REDACTED\]" || src.admincaster_feed_channel.channel_name == "" ) - src.admincaster_screen = 6 - else - var/datum/feed_message/newMsg = new /datum/feed_message - newMsg.author = src.admincaster_signature - newMsg.body = src.admincaster_feed_message.body - newMsg.is_admin_message = 1 - feedback_inc("newscaster_stories",1) - for(var/datum/feed_channel/FC in news_network.network_channels) - if(FC.channel_name == src.admincaster_feed_channel.channel_name) - FC.messages += newMsg //Adding message to the network's appropriate feed_channel - break - src.admincaster_screen=4 - - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) - NEWSCASTER.newsAlert(src.admincaster_feed_channel.channel_name) - - log_admin("[key_name_admin(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!") - src.access_news_network() - - else if(href_list["ac_create_channel"]) - src.admincaster_screen=2 - src.access_news_network() - - else if(href_list["ac_create_feed_story"]) - src.admincaster_screen=3 - src.access_news_network() - - else if(href_list["ac_menu_censor_story"]) - src.admincaster_screen=10 - src.access_news_network() - - else if(href_list["ac_menu_censor_channel"]) - src.admincaster_screen=11 - src.access_news_network() - - else if(href_list["ac_menu_wanted"]) - var/already_wanted = 0 - if(news_network.wanted_issue) - already_wanted = 1 - - if(already_wanted) - src.admincaster_feed_message.author = news_network.wanted_issue.author - src.admincaster_feed_message.body = news_network.wanted_issue.body - src.admincaster_screen = 14 - src.access_news_network() - - else if(href_list["ac_set_wanted_name"]) - src.admincaster_feed_message.author = adminscrub(input(usr, "Provide the name of the Wanted person", "Network Security Handler", "")) - while(findtext(src.admincaster_feed_message.author," ") == 1) - src.admincaster_feed_message.author = copytext(admincaster_feed_message.author,2,length(admincaster_feed_message.author)+1) - src.access_news_network() - - else if(href_list["ac_set_wanted_desc"]) - src.admincaster_feed_message.body = adminscrub(input(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", "")) - while(findtext(src.admincaster_feed_message.body," ") == 1) - src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) - src.access_news_network() - - else if(href_list["ac_submit_wanted"]) - var/input_param = text2num(href_list["ac_submit_wanted"]) - if(src.admincaster_feed_message.author == "" || src.admincaster_feed_message.body == "") - src.admincaster_screen = 16 - else - var/choice = alert("Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler","Confirm","Cancel") - if(choice=="Confirm") - if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below - var/datum/feed_message/WANTED = new /datum/feed_message - WANTED.author = src.admincaster_feed_message.author //Wanted name - WANTED.body = src.admincaster_feed_message.body //Wanted desc - WANTED.backup_author = src.admincaster_signature //Submitted by - WANTED.is_admin_message = 1 - news_network.wanted_issue = WANTED - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) - NEWSCASTER.newsAlert() - NEWSCASTER.update_icon() - src.admincaster_screen = 15 - else - news_network.wanted_issue.author = src.admincaster_feed_message.author - news_network.wanted_issue.body = src.admincaster_feed_message.body - news_network.wanted_issue.backup_author = src.admincaster_feed_message.backup_author - src.admincaster_screen = 19 - log_admin("[key_name_admin(usr)] issued a Station-wide Wanted Notification for [src.admincaster_feed_message.author]!") - src.access_news_network() - - else if(href_list["ac_cancel_wanted"]) - var/choice = alert("Please confirm Wanted Issue removal","Network Security Handler","Confirm","Cancel") - if(choice=="Confirm") - news_network.wanted_issue = null - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) - NEWSCASTER.update_icon() - src.admincaster_screen=17 - src.access_news_network() - - else if(href_list["ac_censor_channel_author"]) - var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) - if(FC.author != "\[REDACTED\]") - FC.backup_author = FC.author - FC.author = "\[REDACTED\]" - else - FC.author = FC.backup_author - src.access_news_network() - - else if(href_list["ac_censor_channel_story_author"]) - var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) - if(MSG.author != "\[REDACTED\]") - MSG.backup_author = MSG.author - MSG.author = "\[REDACTED\]" - else - MSG.author = MSG.backup_author - src.access_news_network() - - else if(href_list["ac_censor_channel_story_body"]) - var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) - if(MSG.body != "\[REDACTED\]") - MSG.backup_body = MSG.body - MSG.body = "\[REDACTED\]" - else - MSG.body = MSG.backup_body - src.access_news_network() - - else if(href_list["ac_pick_d_notice"]) - var/datum/feed_channel/FC = locate(href_list["ac_pick_d_notice"]) - src.admincaster_feed_channel = FC - src.admincaster_screen=13 - src.access_news_network() - - else if(href_list["ac_toggle_d_notice"]) - var/datum/feed_channel/FC = locate(href_list["ac_toggle_d_notice"]) - FC.censored = !FC.censored - src.access_news_network() - - else if(href_list["ac_view"]) - src.admincaster_screen=1 - src.access_news_network() - - else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~ - src.admincaster_screen = text2num(href_list["ac_setScreen"]) - if(src.admincaster_screen == 0) - if(src.admincaster_feed_channel) - src.admincaster_feed_channel = new /datum/feed_channel - if(src.admincaster_feed_message) - src.admincaster_feed_message = new /datum/feed_message - src.access_news_network() - - else if(href_list["ac_show_channel"]) - var/datum/feed_channel/FC = locate(href_list["ac_show_channel"]) - src.admincaster_feed_channel = FC - src.admincaster_screen = 9 - src.access_news_network() - - else if(href_list["ac_pick_censor_channel"]) - var/datum/feed_channel/FC = locate(href_list["ac_pick_censor_channel"]) - src.admincaster_feed_channel = FC - src.admincaster_screen = 12 - src.access_news_network() - - else if(href_list["ac_refresh"]) - src.access_news_network() - - else if(href_list["ac_set_signature"]) - src.admincaster_signature = adminscrub(input(usr, "Provide your desired signature", "Network Identity Handler", "")) - src.access_news_network() - - if(href_list["secretsmenu"]) - switch(href_list["secretsmenu"]) - if("tab") - current_tab = text2num(href_list["tab"]) - Secrets(usr) - return 1 - - else if(href_list["viewruntime"]) - var/datum/ErrorViewer/error_viewer = locateUID(href_list["viewruntime"]) - if(!istype(error_viewer)) - to_chat(usr, "That runtime viewer no longer exists.") - return - if(href_list["viewruntime_backto"]) - error_viewer.showTo(usr, locateUID(href_list["viewruntime_backto"]), href_list["viewruntime_linear"]) - else - error_viewer.showTo(usr, null, href_list["viewruntime_linear"]) - - else if(href_list["add_station_goal"]) - if(!check_rights(R_EVENT)) - return - var/list/type_choices = typesof(/datum/station_goal) - var/picked = input("Choose goal type") in type_choices|null - if(!picked) - return - var/datum/station_goal/G = new picked() - if(picked == /datum/station_goal) - var/newname = clean_input("Enter goal name:") - if(!newname) - return - G.name = newname - var/description = input("Enter [command_name()] message contents:") as message|null - if(!description) - return - G.report_message = description - message_admins("[key_name_admin(usr)] created \"[G.name]\" station goal.") - SSticker.mode.station_goals += G - modify_goals() - - else if(href_list["showdetails"]) - if(!check_rights(R_ADMIN)) - return - var/text = html_decode(href_list["showdetails"]) - usr << browse("Details[replacetext(text, "\n", "
    ")]
    ", - "window=show_details;size=500x200") - - // Library stuff - else if(href_list["library_book_id"]) - var/isbn = sanitizeSQL(href_list["library_book_id"]) - - if(href_list["view_library_book"]) - var/DBQuery/query_view_book = dbcon.NewQuery("SELECT content, title FROM [format_table_name("library")] WHERE id=[isbn]") - if(!query_view_book.Execute()) - var/err = query_view_book.ErrorMsg() - log_game("SQL ERROR viewing book. Error : \[[err]\]\n") - return - - var/content = "" - var/title = "" - while(query_view_book.NextRow()) - content = query_view_book.item[1] - title = html_encode(query_view_book.item[2]) - - var/dat = "
    "
    -			dat += "[html_encode(html_to_pencode(content))]"
    -			dat += "
    " - - var/datum/browser/popup = new(usr, "admin_view_book", "[title]", 700, 400) - popup.set_content(dat) - popup.open(0) - - log_admin("[key_name(usr)] has viewed the book [isbn].") - message_admins("[key_name_admin(usr)] has viewed the book [isbn].") - return - - else if(href_list["unflag_library_book"]) - var/DBQuery/query_unflag_book = dbcon.NewQuery("UPDATE [format_table_name("library")] SET flagged = 0 WHERE id=[isbn]") - if(!query_unflag_book.Execute()) - var/err = query_unflag_book.ErrorMsg() - log_game("SQL ERROR unflagging book. Error : \[[err]\]\n") - return - - log_admin("[key_name(usr)] has unflagged the book [isbn].") - message_admins("[key_name_admin(usr)] has unflagged the book [isbn].") - - else if(href_list["delete_library_book"]) - var/DBQuery/query_delbook = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[isbn]") - if(!query_delbook.Execute()) - var/err = query_delbook.ErrorMsg() - log_game("SQL ERROR deleting book. Error : \[[err]\]\n") - return - - log_admin("[key_name(usr)] has deleted the book [isbn].") - message_admins("[key_name_admin(usr)] has deleted the book [isbn].") - - // Refresh the page - src.view_flagged_books() - - // Force unlink a discord key - else if(href_list["force_discord_unlink"]) - if(!check_rights(R_ADMIN)) - return - var/target_ckey = href_list["force_discord_unlink"] - var/DBQuery/admin_unlink_discord_id = dbcon.NewQuery("DELETE FROM [format_table_name("discord")] WHERE ckey = '[target_ckey]'") - if(!admin_unlink_discord_id.Execute()) - var/err = admin_unlink_discord_id.ErrorMsg() - log_game("SQL ERROR while admin-unlinking discord account. Error : \[[err]\]\n") - return - to_chat(src, "Successfully forcefully unlinked discord account from [target_ckey]") - message_admins("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") - log_admin("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") - - else if(href_list["create_outfit_finalize"]) - if(!check_rights(R_EVENT)) - return - create_outfit_finalize(usr,href_list) - else if(href_list["load_outfit"]) - if(!check_rights(R_EVENT)) - return - load_outfit(usr) - else if(href_list["create_outfit_menu"]) - if(!check_rights(R_EVENT)) - return - create_outfit(usr) - else if(href_list["delete_outfit"]) - if(!check_rights(R_EVENT)) - return - var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits - delete_outfit(usr,O) - else if(href_list["save_outfit"]) - if(!check_rights(R_EVENT)) - return - var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits - save_outfit(usr,O) - -/client/proc/create_eventmob_for(var/mob/living/carbon/human/H, var/killthem = 0) - if(!check_rights(R_EVENT)) - return - var/admin_outfits = subtypesof(/datum/outfit/admin) - var/hunter_outfits = list() - for(var/type in admin_outfits) - var/datum/outfit/admin/O = type - hunter_outfits[initial(O.name)] = type - var/dresscode = input("Select type", "Contracted Agents") as null|anything in hunter_outfits - if(isnull(dresscode)) - return - var/datum/outfit/O = hunter_outfits[dresscode] - message_admins("[key_name_admin(mob)] is sending a ([dresscode]) to [killthem ? "assassinate" : "protect"] [key_name_admin(H)]...") - var/list/candidates = pollCandidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, 1) - if(!candidates.len) - to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.") - return - var/mob/C = pick(candidates) - var/key_of_hunter = C.key - if(!key_of_hunter) - to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.") - return - var/datum/mind/hunter_mind = new /datum/mind(key_of_hunter) - hunter_mind.active = 1 - var/mob/living/carbon/human/hunter_mob = new /mob/living/carbon/human(pick(latejoin)) - hunter_mind.transfer_to(hunter_mob) - hunter_mob.equipOutfit(O, FALSE) - var/obj/item/pinpointer/advpinpointer/N = new /obj/item/pinpointer/advpinpointer(hunter_mob) - hunter_mob.equip_to_slot_or_del(N, slot_in_backpack) - N.active = 1 - N.mode = 2 - N.target = H - N.point_at(N.target) - if(!locate(/obj/item/implant/dust, hunter_mob)) - var/obj/item/implant/dust/D = new /obj/item/implant/dust(hunter_mob) - D.implant(hunter_mob) - if(killthem) - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = hunter_mind - kill_objective.target = H.mind - kill_objective.explanation_text = "Kill [H.real_name], the [H.mind.assigned_role]." - hunter_mind.objectives += kill_objective - else - var/datum/objective/protect/protect_objective = new - protect_objective.owner = hunter_mind - protect_objective.target = H.mind - protect_objective.explanation_text = "Protect [H.real_name], the [H.mind.assigned_role]." - hunter_mind.objectives += protect_objective - SSticker.mode.traitors |= hunter_mob.mind - to_chat(hunter_mob, "ATTENTION: You are now on a mission!") - to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]. "); - if(killthem) - to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived."); - hunter_mob.mind.special_role = SPECIAL_ROLE_TRAITOR - var/datum/atom_hud/antag/tatorhud = huds[ANTAG_HUD_TRAITOR] - tatorhud.join_hud(hunter_mob) - set_antag_hud(hunter_mob, "hudsyndicate") - -/proc/admin_jump_link(var/atom/target) - if(!target) return - // The way admin jump links handle their src is weirdly inconsistent... - - . = ADMIN_FLW(target,"FLW") - if(isAI(target)) // AI core/eye follow links - var/mob/living/silicon/ai/A = target - if(A.client && A.eyeobj) // No point following clientless AI eyes - . += "|[ADMIN_FLW(A.eyeobj,"EYE")]" - else if(istype(target, /mob/dead/observer)) - var/mob/dead/observer/O = target - if(O.mind && O.mind.current) - . += "|[ADMIN_FLW(O.mind.current,"BDY")]" +/datum/admins/Topic(href, href_list) + ..() + + if(usr.client != src.owner || !check_rights(0)) + log_admin("[key_name(usr)] tried to use the admin panel without authorization.") + message_admins("[key_name_admin(usr)] has attempted to override the admin panel!") + return + + if(SSticker.mode && SSticker.mode.check_antagonists_topic(href, href_list)) + check_antagonists() + return + + if(href_list["rejectadminhelp"]) + if(!check_rights(R_ADMIN|R_MOD)) + return + var/client/C = locateUID(href_list["rejectadminhelp"]) + if(!C) + return + + C << 'sound/effects/adminhelp.ogg' + + to_chat(C, "- AdminHelp Rejected! -") + to_chat(C, "Your admin help was rejected.") + to_chat(C, "Please try to be calm, clear, and descriptive in admin helps, do not assume the admin has seen any related events, and clearly state the names of anybody you are reporting. If you asked a question, please ensure it was clear what you were asking.") + + message_admins("[key_name_admin(usr)] rejected [key_name_admin(C.mob)]'s admin help") + log_admin("[key_name(usr)] rejected [key_name(C.mob)]'s admin help") + + if(href_list["openadminticket"]) + if(!check_rights(R_ADMIN)) + return + var/ticketID = text2num(href_list["openadminticket"]) + SStickets.showDetailUI(usr, ticketID) + + if(href_list["openmentorticket"]) + if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) + return + var/ticketID = text2num(href_list["openmentorticket"]) + SSmentor_tickets.showDetailUI(usr, ticketID) + + if(href_list["stickyban"]) + stickyban(href_list["stickyban"],href_list) + + if(href_list["makeAntag"]) + switch(href_list["makeAntag"]) + if("1") + log_admin("[key_name(usr)] has spawned a traitor.") + if(!makeTraitors()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("2") + log_admin("[key_name(usr)] has spawned a changeling.") + if(!makeChangelings()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("3") + log_admin("[key_name(usr)] has spawned revolutionaries.") + if(!makeRevs()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("4") + log_admin("[key_name(usr)] has spawned a cultists.") + if(!makeCult()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("5") + log_admin("[key_name(usr)] has spawned a wizard.") + if(!makeWizard()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("6") + log_admin("[key_name(usr)] has spawned vampires.") + if(!makeVampires()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("7") + log_admin("[key_name(usr)] has spawned vox raiders.") + if(!makeVoxRaiders()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("8") + log_admin("[key_name(usr)] has spawned an abductor team.") + if(!makeAbductorTeam()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + + else if(href_list["dbsearchckey"] || href_list["dbsearchadmin"] || href_list["dbsearchip"] || href_list["dbsearchcid"] || href_list["dbsearchbantype"]) + var/adminckey = href_list["dbsearchadmin"] + var/playerckey = href_list["dbsearchckey"] + var/playerip = href_list["dbsearchip"] + var/playercid = href_list["dbsearchcid"] + var/dbbantype = text2num(href_list["dbsearchbantype"]) + var/match = 0 + + if("dbmatch" in href_list) + match = 1 + + DB_ban_panel(playerckey, adminckey, playerip, playercid, dbbantype, match) + return + + else if(href_list["dbbanedit"]) + var/banedit = href_list["dbbanedit"] + var/banid = text2num(href_list["dbbanid"]) + if(!banedit || !banid) + return + + DB_ban_edit(banid, banedit) + return + + else if(href_list["dbbanaddtype"]) + + var/bantype = text2num(href_list["dbbanaddtype"]) + var/banckey = href_list["dbbanaddckey"] + var/banip = href_list["dbbanaddip"] + var/bancid = href_list["dbbanaddcid"] + var/banduration = text2num(href_list["dbbaddduration"]) + var/banjob = href_list["dbbanaddjob"] + var/banreason = href_list["dbbanreason"] + + banckey = ckey(banckey) + + switch(bantype) + if(BANTYPE_PERMA) + if(!banckey || !banreason) + to_chat(usr, "Not enough parameters (Requires ckey and reason)") + return + banduration = null + banjob = null + if(BANTYPE_TEMP) + if(!banckey || !banreason || !banduration) + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") + return + banjob = null + if(BANTYPE_JOB_PERMA) + if(!banckey || !banreason || !banjob) + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + return + banduration = null + if(BANTYPE_JOB_TEMP) + if(!banckey || !banreason || !banjob || !banduration) + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + return + if(BANTYPE_APPEARANCE) + if(!banckey || !banreason) + to_chat(usr, "Not enough parameters (Requires ckey and reason)") + return + banduration = null + banjob = null + if(BANTYPE_ADMIN_PERMA) + if(!banckey || !banreason) + to_chat(usr, "Not enough parameters (Requires ckey and reason)") + return + banduration = null + banjob = null + if(BANTYPE_ADMIN_TEMP) + if(!banckey || !banreason || !banduration) + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") + return + banjob = null + + var/mob/playermob + + for(var/mob/M in GLOB.player_list) + if(M.ckey == banckey) + playermob = M + break + + + banreason = "(MANUAL BAN) "+banreason + + if(!playermob) + if(banip) + banreason = "[banreason] (CUSTOM IP)" + if(bancid) + banreason = "[banreason] (CUSTOM CID)" + else + message_admins("Ban process: A mob matching [playermob.ckey] was found at location [playermob.x], [playermob.y], [playermob.z]. Custom IP and computer id fields replaced with the IP and computer id from the located mob") + + DB_ban_record(bantype, playermob, banduration, banreason, banjob, null, banckey, banip, bancid ) + + + else if(href_list["editrights"]) + if(!check_rights(R_PERMISSIONS)) + message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.") + log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.") + return + + var/adm_ckey + + var/task = href_list["editrights"] + if(task == "add") + var/new_ckey = ckey(clean_input("New admin's ckey","Admin ckey", null)) + if(!new_ckey) return + if(new_ckey in GLOB.admin_datums) + to_chat(usr, "Error: Topic 'editrights': [new_ckey] is already an admin") + return + adm_ckey = new_ckey + task = "rank" + else if(task != "show") + adm_ckey = ckey(href_list["ckey"]) + if(!adm_ckey) + to_chat(usr, "Error: Topic 'editrights': No valid ckey") + return + + var/datum/admins/D = GLOB.admin_datums[adm_ckey] + + if(task == "remove") + if(alert("Are you sure you want to remove [adm_ckey]?","Message","Yes","Cancel") == "Yes") + if(!D) return + GLOB.admin_datums -= adm_ckey + D.disassociate() + + updateranktodb(adm_ckey, "player") + message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list") + log_admin("[key_name(usr)] removed [adm_ckey] from the admins list") + log_admin_rank_modification(adm_ckey, "Removed") + + else if(task == "rank") + var/new_rank + if(GLOB.admin_ranks.len) + new_rank = input("Please select a rank", "New rank", null, null) as null|anything in (GLOB.admin_ranks|"*New Rank*") + else + new_rank = input("Please select a rank", "New rank", null, null) as null|anything in list("Mentor", "Trial Admin", "Game Admin", "*New Rank*") + + var/rights = 0 + if(D) + rights = D.rights + switch(new_rank) + if(null,"") return + if("*New Rank*") + new_rank = input("Please input a new rank", "New custom rank", null, null) as null|text + if(config.admin_legacy_system) + new_rank = ckeyEx(new_rank) + if(!new_rank) + to_chat(usr, "Error: Topic 'editrights': Invalid rank") + return + if(config.admin_legacy_system) + if(GLOB.admin_ranks.len) + if(new_rank in GLOB.admin_ranks) + rights = GLOB.admin_ranks[new_rank] //we typed a rank which already exists, use its rights + else + GLOB.admin_ranks[new_rank] = 0 //add the new rank to admin_ranks + else + if(config.admin_legacy_system) + new_rank = ckeyEx(new_rank) + rights = GLOB.admin_ranks[new_rank] //we input an existing rank, use its rights + + if(D) + D.disassociate() //remove adminverbs and unlink from client + D.rank = new_rank //update the rank + D.rights = rights //update the rights based on admin_ranks (default: 0) + else + D = new /datum/admins(new_rank, rights, adm_ckey) + + var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in) + D.associate(C) //link up with the client and add verbs + + updateranktodb(adm_ckey, new_rank) + message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]") + log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]") + log_admin_rank_modification(adm_ckey, new_rank) + + else if(task == "permissions") + if(!D) return + while(TRUE) + var/list/permissionlist = list() + for(var/i=1, i<=R_MAXPERMISSION, i<<=1) //that <<= is shorthand for i = i << 1. Which is a left bitshift + permissionlist[rights2text(i)] = i + var/new_permission = input("Select a permission to turn on/off", adm_ckey + "'s Permissions", null, null) as null|anything in permissionlist + if(!new_permission) + return + var/oldrights = D.rights + var/toggleresult = "ON" + D.rights ^= permissionlist[new_permission] + if(oldrights > D.rights) + toggleresult = "OFF" + + message_admins("[key_name_admin(usr)] toggled the [new_permission] permission of [adm_ckey] to [toggleresult]") + log_admin("[key_name(usr)] toggled the [new_permission] permission of [adm_ckey] to [toggleresult]") + log_admin_permission_modification(adm_ckey, permissionlist[new_permission]) + + + edit_admin_permissions() + + else if(href_list["call_shuttle"]) + if(!check_rights(R_ADMIN)) return + + + switch(href_list["call_shuttle"]) + if("1") + if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) + return + SSshuttle.emergency.request() + log_admin("[key_name(usr)] called the Emergency Shuttle") + message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station") + + if("2") + if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) + return + switch(SSshuttle.emergency.mode) + if(SHUTTLE_CALL) + SSshuttle.emergency.cancel() + log_admin("[key_name(usr)] sent the Emergency Shuttle back") + message_admins("[key_name_admin(usr)] sent the Emergency Shuttle back") + else + SSshuttle.emergency.cancel() + log_admin("[key_name(usr)] called the Emergency Shuttle") + message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station") + + + href_list["secrets"] = "check_antagonist" + + else if(href_list["edit_shuttle_time"]) + if(!check_rights(R_SERVER)) return + + var/timer = input("Enter new shuttle duration (seconds):","Edit Shuttle Timeleft", SSshuttle.emergency.timeLeft() ) as num + SSshuttle.emergency.setTimer(timer*10) + log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds") + GLOB.minor_announcement.Announce("The emergency shuttle will reach its destination in [round(SSshuttle.emergency.timeLeft(600))] minutes.") + message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds") + href_list["secrets"] = "check_antagonist" + + else if(href_list["delay_round_end"]) + if(!check_rights(R_SERVER)) return + + SSticker.delay_end = !SSticker.delay_end + log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].") + message_admins("[key_name_admin(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) + href_list["secretsadmin"] = "check_antagonist" + + else if(href_list["simplemake"]) + if(!check_rights(R_SPAWN)) return + + var/mob/M = locateUID(href_list["mob"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + var/delmob = 0 + switch(alert("Delete old mob?","Message","Yes","No","Cancel")) + if("Cancel") return + if("Yes") delmob = 1 + + switch(href_list["simplemake"]) + if("observer") M.change_mob_type( /mob/dead/observer , null, null, delmob, 1 ) + if("drone") M.change_mob_type( /mob/living/carbon/alien/humanoid/drone , null, null, delmob, 1 ) + if("hunter") M.change_mob_type( /mob/living/carbon/alien/humanoid/hunter , null, null, delmob, 1 ) + if("queen") M.change_mob_type( /mob/living/carbon/alien/humanoid/queen/large , null, null, delmob, 1 ) + if("sentinel") M.change_mob_type( /mob/living/carbon/alien/humanoid/sentinel , null, null, delmob, 1 ) + if("larva") M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob, 1 ) + if("human") + var/posttransformoutfit = usr.client.robust_dress_shop() + var/mob/living/carbon/human/newmob = M.change_mob_type(/mob/living/carbon/human, null, null, delmob, 1) + if(posttransformoutfit && istype(newmob)) + newmob.equipOutfit(posttransformoutfit) + if("slime") M.change_mob_type( /mob/living/simple_animal/slime , null, null, delmob, 1 ) + if("monkey") M.change_mob_type( /mob/living/carbon/human/monkey , null, null, delmob, 1 ) + if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob, 1 ) + if("cat") M.change_mob_type( /mob/living/simple_animal/pet/cat , null, null, delmob, 1 ) + if("runtime") M.change_mob_type( /mob/living/simple_animal/pet/cat/Runtime , null, null, delmob, 1 ) + if("corgi") M.change_mob_type( /mob/living/simple_animal/pet/dog/corgi , null, null, delmob, 1 ) + if("crab") M.change_mob_type( /mob/living/simple_animal/crab , null, null, delmob, 1 ) + if("coffee") M.change_mob_type( /mob/living/simple_animal/crab/Coffee , null, null, delmob, 1 ) + if("parrot") M.change_mob_type( /mob/living/simple_animal/parrot , null, null, delmob, 1 ) + if("polyparrot") M.change_mob_type( /mob/living/simple_animal/parrot/Poly , null, null, delmob, 1 ) + if("constructarmoured") M.change_mob_type( /mob/living/simple_animal/hostile/construct/armoured , null, null, delmob, 1 ) + if("constructbuilder") M.change_mob_type( /mob/living/simple_animal/hostile/construct/builder , null, null, delmob, 1 ) + if("constructwraith") M.change_mob_type( /mob/living/simple_animal/hostile/construct/wraith , null, null, delmob, 1 ) + if("shade") M.change_mob_type( /mob/living/simple_animal/shade , null, null, delmob, 1 ) + + log_admin("[key_name(usr)] has used rudimentary transformation on [key_name(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]") + message_admins("[key_name_admin(usr)] has used rudimentary transformation on [key_name_admin(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]", 1) + + + /////////////////////////////////////new ban stuff + else if(href_list["unbanf"]) + if(!check_rights(R_BAN)) return + + var/banfolder = href_list["unbanf"] + GLOB.banlist_savefile.cd = "/base/[banfolder]" + var/key = GLOB.banlist_savefile["key"] + if(alert(usr, "Are you sure you want to unban [key]?", "Confirmation", "Yes", "No") == "Yes") + if(RemoveBan(banfolder)) + unbanpanel() + else + alert(usr, "This ban has already been lifted / does not exist.", "Error", "Ok") + unbanpanel() + + else if(href_list["warn"]) + usr.client.warn(href_list["warn"]) + + else if(href_list["unbane"]) + if(!check_rights(R_BAN)) return + + UpdateTime() + var/reason + + var/banfolder = href_list["unbane"] + GLOB.banlist_savefile.cd = "/base/[banfolder]" + var/reason2 = GLOB.banlist_savefile["reason"] + var/temp = GLOB.banlist_savefile["temp"] + + var/minutes = GLOB.banlist_savefile["minutes"] + + var/banned_key = GLOB.banlist_savefile["key"] + GLOB.banlist_savefile.cd = "/base" + + var/duration + + switch(alert("Temporary Ban?",,"Yes","No")) + if("Yes") + temp = 1 + var/mins = 0 + if(minutes > GLOB.CMinutes) + mins = minutes - GLOB.CMinutes + mins = input(usr,"How long (in minutes)? (Default: 1440)","Ban time",mins ? mins : 1440) as num|null + if(!mins) return + mins = min(525599,mins) + minutes = GLOB.CMinutes + mins + duration = GetExp(minutes) + reason = input(usr,"Please state the reason","Reason",reason2) as message|null + if(!reason) return + if("No") + temp = 0 + duration = "Perma" + reason = input(usr,"Please state the reason","Reason",reason2) as message|null + if(!reason) return + + log_admin("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") + ban_unban_log_save("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") + message_admins("[key_name_admin(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]", 1) + GLOB.banlist_savefile.cd = "/base/[banfolder]" + to_chat(GLOB.banlist_savefile["reason"], reason) + to_chat(GLOB.banlist_savefile["temp"], temp) + to_chat(GLOB.banlist_savefile["minutes"], minutes) + to_chat(GLOB.banlist_savefile["bannedby"], usr.ckey) + GLOB.banlist_savefile.cd = "/base" + feedback_inc("ban_edit",1) + unbanpanel() + + /////////////////////////////////////new ban stuff + + else if(href_list["appearanceban"]) + if(!check_rights(R_BAN)) + return + var/mob/M = locateUID(href_list["appearanceban"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(!M.ckey) //sanity + to_chat(usr, "This mob has no ckey") + return + var/ban_ckey_param = href_list["dbbanaddckey"] + + var/banreason = appearance_isbanned(M) + if(banreason) + /* if(!config.ban_legacy_system) + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") + DB_ban_panel(M.ckey) + return */ + switch(alert("Reason: '[banreason]' Remove appearance ban?","Please Confirm","Yes","No")) + if("Yes") + ban_unban_log_save("[key_name(usr)] removed [key_name(M)]'s appearance ban") + log_admin("[key_name(usr)] removed [key_name(M)]'s appearance ban") + feedback_inc("ban_appearance_unban", 1) + DB_ban_unban(M.ckey, BANTYPE_APPEARANCE) + appearance_unban(M) + message_admins("[key_name_admin(usr)] removed [key_name_admin(M)]'s appearance ban", 1) + to_chat(M, "[usr.client.ckey] has removed your appearance ban.") + + else switch(alert("Appearance ban [M.ckey]?",,"Yes","No", "Cancel")) + if("Yes") + var/reason = input(usr,"Please state the reason","Reason") as message|null + if(!reason) + return + M = admin_ban_mobsearch(M, ban_ckey_param, usr) + ban_unban_log_save("[key_name(usr)] appearance banned [key_name(M)]. reason: [reason]") + log_admin("[key_name(usr)] appearance banned [key_name(M)]. \nReason: [reason]") + feedback_inc("ban_appearance",1) + DB_ban_record(BANTYPE_APPEARANCE, M, -1, reason) + appearance_fullban(M, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") + add_note(M.ckey, "Appearance banned - [reason]", null, usr.ckey, 0) + message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)]", 1) + to_chat(M, "You have been appearance banned by [usr.client.ckey].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "Appearance ban can be lifted only upon request.") + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + if("No") + return + + else if(href_list["jobban2"]) +// if(!check_rights(R_BAN)) return + + var/mob/M = locateUID(href_list["jobban2"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(!M.ckey) //sanity + to_chat(usr, "This mob has no ckey") + return + if(!SSjobs) + to_chat(usr, "SSjobs has not been setup!") + return + + var/dat = "" + var/header = "Job-Ban Panel: [M.name]" + var/body + var/jobs = "" + + /***********************************WARNING!************************************ + The jobban stuff looks mangled and disgusting + But it looks beautiful in-game + -Nodrak + ************************************WARNING!***********************************/ + var/counter = 0 +//Regular jobs + //Command (Blue) + jobs += "" + jobs += "" + for(var/jobPos in GLOB.command_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 6) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Command Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Security (Red) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.security_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Security Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Engineering (Yellow) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.engineering_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Engineering Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Medical (White) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.medical_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Medical Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Science (Purple) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.science_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Science Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Support (Grey) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.support_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Support Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Non-Human (Green) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.nonhuman_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + //Drone + if(jobban_isbanned(M, "Drone")) + jobs += "" + else + jobs += "" + + //pAI + if(jobban_isbanned(M, "pAI")) + jobs += "" + else + jobs += "" + + jobs += "
    Non-human Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    DroneDronepAIpAI
    " + + //Antagonist (Orange) + var/isbanned_dept = jobban_isbanned(M, "Syndicate") + jobs += "" + jobs += "" + + counter = 0 + for(var/role in GLOB.antag_roles) + if(jobban_isbanned(M, role) || isbanned_dept) + jobs += "" + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Antagonist Positions
    [replacetext(role, " ", " ")][replacetext(role, " ", " ")]
    " + + //Other races (BLUE, because I have no idea what other color to make this) + jobs += "" + jobs += "" + + counter = 0 + for(var/role in GLOB.other_roles) + if(jobban_isbanned(M, role) || isbanned_dept) + jobs += "" + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Other
    [replacetext(role, " ", " ")][replacetext(role, " ", " ")]
    " + + //Whitelisted positions + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.whitelisted_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Whitelisted Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + body = "[jobs]" + dat = "[header][body]" + usr << browse(dat, "window=jobban2;size=800x490") + return + + //JOBBAN'S INNARDS + else if(href_list["jobban3"]) + if(!check_rights(R_BAN)) return + + var/mob/M = locateUID(href_list["jobban4"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(M != usr) //we can jobban ourselves + if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) //they can ban too. So we can't ban them + alert("You cannot perform this action. You must be of a higher administrative rank!") + return + + var/ban_ckey_param = href_list["dbbanaddckey"] + + if(!SSjobs) + to_chat(usr, "SSjobs has not been setup!") + return + + //get jobs for department if specified, otherwise just returnt he one job in a list. + var/list/joblist = list() + switch(href_list["jobban3"]) + if("commanddept") + for(var/jobPos in GLOB.command_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("securitydept") + for(var/jobPos in GLOB.security_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("engineeringdept") + for(var/jobPos in GLOB.engineering_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("medicaldept") + for(var/jobPos in GLOB.medical_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("sciencedept") + for(var/jobPos in GLOB.science_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("supportdept") + for(var/jobPos in GLOB.support_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("nonhumandept") + joblist += "pAI" + for(var/jobPos in GLOB.nonhuman_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("whitelistdept") + for(var/jobPos in GLOB.whitelisted_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + else + joblist += href_list["jobban3"] + + //Create a list of unbanned jobs within joblist + var/list/notbannedlist = list() + for(var/job in joblist) + if(!jobban_isbanned(M, job)) + notbannedlist += job + + //Banning comes first + if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. + switch(alert("Temporary Ban of [M.ckey]?",,"Yes","No", "Cancel")) + if("Yes") + if(config.ban_legacy_system) + to_chat(usr, "Your server is using the legacy banning system, which does not support temporary job bans. Consider upgrading. Aborting ban.") + return + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null + if(!mins) + return + var/reason = input(usr,"Please state the reason","Reason","") as message|null + if(!reason) + return + + var/msg + M = admin_ban_mobsearch(M, ban_ckey_param, usr) + for(var/job in notbannedlist) + ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]") + log_admin("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes") + feedback_inc("ban_job_tmp",1) + DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job) + feedback_add_details("ban_job_tmp","- [job]") + jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") //Legacy banning does not support temporary jobbans. + if(!msg) + msg = job + else + msg += ", [job]" + add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) + message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1) + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "This jobban will be lifted in [mins] minutes.") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + if("No") + var/reason = input(usr,"Please state the reason","Reason","") as message|null + if(reason) + var/msg + M = admin_ban_mobsearch(M, ban_ckey_param, usr) + for(var/job in notbannedlist) + ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]") + log_admin("[key_name(usr)] perma-banned [key_name(M)] from [job]") + feedback_inc("ban_job",1) + DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, job) + feedback_add_details("ban_job","- [job]") + jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") + if(!msg) msg = job + else msg += ", [job]" + add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) + message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1) + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "Jobban can be lifted only upon request.") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + if("Cancel") + return + + //Unbanning joblist + //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) + if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. + if(!config.ban_legacy_system) + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") + DB_ban_panel(M.ckey) + return + var/msg + for(var/job in joblist) + var/reason = jobban_isbanned(M, job) + if(!reason) continue //skip if it isn't jobbanned anyway + switch(alert("Job: '[job]' Reason: '[reason]' Un-jobban?","Please Confirm","Yes","No")) + if("Yes") + ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") + log_admin("[key_name(usr)] unbanned [key_name(M)] from [job]") + DB_ban_unban(M.ckey, BANTYPE_JOB_PERMA, job) + feedback_inc("ban_job_unban",1) + feedback_add_details("ban_job_unban","- [job]") + jobban_unban(M, job) + if(!msg) msg = job + else msg += ", [job]" + else + continue + if(msg) + message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]", 1) + to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + return 0 //we didn't do anything! + + else if(href_list["boot2"]) + var/mob/M = locateUID(href_list["boot2"]) + if(ismob(M)) + if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) + to_chat(usr, "[key_name_admin(M)] cannot be kicked from the server.") + return + to_chat(M, "You have been kicked from the server") + log_admin("[key_name(usr)] booted [key_name(M)].") + message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1) + //M.client = null + del(M.client) + + //Player Notes + else if(href_list["addnote"]) + var/target_ckey = href_list["addnote"] + add_note(target_ckey) + + else if(href_list["addnoteempty"]) + add_note() + + else if(href_list["removenote"]) + var/note_id = href_list["removenote"] + remove_note(note_id) + + else if(href_list["editnote"]) + var/note_id = href_list["editnote"] + edit_note(note_id) + + else if(href_list["shownote"]) + var/target = href_list["shownote"] + show_note(index = target) + + else if(href_list["nonalpha"]) + var/target = href_list["nonalpha"] + target = text2num(target) + show_note(index = target) + + else if(href_list["webtools"]) + var/target_ckey = href_list["webtools"] + if(config.forum_playerinfo_url) + var/url_to_open = config.forum_playerinfo_url + target_ckey + if(alert("Open [url_to_open]",,"Yes","No")=="Yes") + usr.client << link(url_to_open) + + else if(href_list["shownoteckey"]) + var/target_ckey = href_list["shownoteckey"] + show_note(target_ckey) + + else if(href_list["notessearch"]) + var/target = href_list["notessearch"] + show_note(index = target) + + else if(href_list["noteedits"]) + var/note_id = sanitizeSQL("[href_list["noteedits"]]") + var/DBQuery/query_noteedits = GLOB.dbcon.NewQuery("SELECT edits FROM [format_table_name("notes")] WHERE id = '[note_id]'") + if(!query_noteedits.Execute()) + var/err = query_noteedits.ErrorMsg() + log_game("SQL ERROR obtaining edits from notes table. Error : \[[err]\]\n") + return + if(query_noteedits.NextRow()) + var/edit_log = query_noteedits.item[1] + usr << browse(edit_log,"window=noteedits") + + else if(href_list["removejobban"]) + if(!check_rights(R_BAN)) return + + var/t = href_list["removejobban"] + if(t) + if((alert("Do you want to unjobban [t]?","Unjobban confirmation", "Yes", "No") == "Yes") && t) //No more misclicks! Unless you do it twice. + log_admin("[key_name(usr)] removed [t]") + message_admins("[key_name_admin(usr)] removed [t]", 1) + jobban_remove(t) + href_list["ban"] = 1 // lets it fall through and refresh + var/t_split = splittext(t, " - ") + var/key = t_split[1] + var/job = t_split[2] + DB_ban_unban(ckey(key), BANTYPE_JOB_PERMA, job) + + else if(href_list["newban"]) + if(!check_rights(R_BAN)) return + + var/mob/M = locateUID(href_list["newban"]) + if(!ismob(M)) + return + var/ban_ckey_param = href_list["dbbanaddckey"] + + switch(alert("Temporary Ban of [M.ckey] / [ban_ckey_param]?",,"Yes","No", "Cancel")) + if("Yes") + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null + if(!mins) + return + if(mins >= 525600) mins = 525599 + var/reason = input(usr,"Please state the reason","Reason") as message|null + if(!reason) + return + M = admin_ban_mobsearch(M, ban_ckey_param, usr) + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) + ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") + feedback_inc("ban_tmp",1) + DB_ban_record(BANTYPE_TEMP, M, mins, reason) + feedback_inc("ban_tmp_mins",mins) + if(M.client) + M.client.link_forum_account(TRUE) + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + + del(M.client) + //qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends. + if("No") + var/reason = input(usr,"Please state the reason","Reason") as message|null + if(!reason) + return + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This ban does not expire automatically and must be appealed.") + if(M.client) + M.client.link_forum_account(TRUE) + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + ban_unban_log_save("[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This ban does not expire automatically and must be appealed.") + log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis ban does not expire automatically and must be appealed.") + message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis ban does not expire automatically and must be appealed.") + feedback_inc("ban_perma",1) + DB_ban_record(BANTYPE_PERMA, M, -1, reason) + + del(M.client) + //qdel(M) + if("Cancel") + return + + + //Watchlist + else if(href_list["watchadd"]) + var/target_ckey = href_list["watchadd"] + usr.client.watchlist_add(target_ckey) + + else if(href_list["watchremove"]) + var/target_ckey = href_list["watchremove"] + var/confirm = alert("Are you sure you want to remove [target_ckey] from the watchlist?", "Confirm Watchlist Removal", "Yes", "No") + if(confirm == "Yes") + usr.client.watchlist_remove(target_ckey) + + else if(href_list["watchedit"]) + var/target_ckey = href_list["watchedit"] + usr.client.watchlist_edit(target_ckey) + + else if(href_list["watchaddbrowse"]) + usr.client.watchlist_add(null, 1) + + else if(href_list["watchremovebrowse"]) + var/target_ckey = href_list["watchremovebrowse"] + usr.client.watchlist_remove(target_ckey, 1) + + else if(href_list["watcheditbrowse"]) + var/target_ckey = href_list["watcheditbrowse"] + usr.client.watchlist_edit(target_ckey, 1) + + else if(href_list["watchsearch"]) + var/target_ckey = href_list["watchsearch"] + usr.client.watchlist_show(target_ckey) + + else if(href_list["watchshow"]) + usr.client.watchlist_show() + + else if(href_list["watcheditlog"]) + var/target_ckey = sanitizeSQL("[href_list["watcheditlog"]]") + var/DBQuery/query_watchedits = GLOB.dbcon.NewQuery("SELECT edits FROM [format_table_name("watch")] WHERE ckey = '[target_ckey]'") + if(!query_watchedits.Execute()) + var/err = query_watchedits.ErrorMsg() + log_game("SQL ERROR obtaining edits from watch table. Error : \[[err]\]\n") + return + if(query_watchedits.NextRow()) + var/edit_log = query_watchedits.item[1] + usr << browse(edit_log,"window=watchedits") + + else if(href_list["mute"]) + if(!check_rights(R_ADMIN|R_MOD)) + return + + var/mob/M = locateUID(href_list["mute"]) + if(!ismob(M)) return + if(!M.client) return + + var/mute_type = href_list["mute_type"] + if(istext(mute_type)) mute_type = text2num(mute_type) + if(!isnum(mute_type)) return + + cmd_admin_mute(M, mute_type) + + else if(href_list["c_mode"]) + if(!check_rights(R_ADMIN)) return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + var/dat = {"What mode do you wish to play?
    "} + for(var/mode in config.modes) + dat += {"[config.mode_names[mode]]
    "} + dat += {"Secret
    "} + dat += {"Random
    "} + dat += {"Now: [GLOB.master_mode]"} + usr << browse(dat, "window=c_mode") + + else if(href_list["f_secret"]) + 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 != "secret") + return alert(usr, "The game mode has to be secret!", null, null, null, null) + var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
    "} + for(var/mode in config.modes) + dat += {"[config.mode_names[mode]]
    "} + dat += {"Random (default)
    "} + dat += {"Now: [GLOB.secret_force_mode]"} + usr << browse(dat, "window=f_secret") + + else if(href_list["c_mode2"]) + if(!check_rights(R_ADMIN|R_SERVER)) return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + GLOB.master_mode = href_list["c_mode2"] + 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].", 1) + to_chat(world, "The mode is now: [GLOB.master_mode]") + Game() // updates the main game menu + world.save_mode(GLOB.master_mode) + .(href, list("c_mode"=1)) + + else if(href_list["f_secret2"]) + if(!check_rights(R_ADMIN|R_SERVER)) return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "secret") + return alert(usr, "The game mode has to be secret!", null, null, null, null) + GLOB.secret_force_mode = href_list["f_secret2"] + log_admin("[key_name(usr)] set the forced secret mode as [GLOB.secret_force_mode].") + message_admins("[key_name_admin(usr)] set the forced secret mode as [GLOB.secret_force_mode].", 1) + Game() // updates the main game menu + .(href, list("f_secret"=1)) + + else if(href_list["monkeyone"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["monkeyone"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make monkey?",, "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)]") + message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(H)]", 1) + H.monkeyize() + + + else if(href_list["corgione"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["corgione"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + if(alert(usr, "Confirm make corgi?",, "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] attempting to corgize [key_name(H)]") + message_admins("[key_name_admin(usr)] attempting to corgize [key_name_admin(H)]", 1) + H.corgize() + + else if(href_list["makePAI"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makePAI"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make pai?",, "Yes", "No") != "Yes") + return + + var/painame = "Default" + var/name = "" + if(alert(usr, "Do you want to set their name or let them choose their own name?", "Name Choice", "Set Name", "Let them choose") == "Set Name") + name = sanitize(copytext(input(usr, "Enter a name for the new pAI. Default name is [painame].", "pAI Name", painame),1,MAX_NAME_LEN)) + else + name = sanitize(copytext(input(H, "An admin wants to make you into a pAI. Choose a name. Default is [painame].", "pAI Name", painame),1,MAX_NAME_LEN)) + + if(!name) + name = painame + + log_admin("[key_name(usr)] attempting to pAIze [key_name(H)]") + message_admins("[key_name_admin(usr)] attempting to pAIze [key_name_admin(H)]", 1) + H.paize(name) + + else if(href_list["forcespeech"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/M = locateUID(href_list["forcespeech"]) + if(!ismob(M)) + to_chat(usr, "this can only be used on instances of type /mob") + + var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. + if(!speech) return + M.say(speech) + speech = sanitize(speech) // Nah, we don't trust them + log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]") + message_admins("[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]") + + else if(href_list["sendtoprison"]) + if(!check_rights(R_ADMIN)) return + + if(alert(usr, "Send to admin prison for the round?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["sendtoprison"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + var/turf/prison_cell = pick(GLOB.prisonwarp) + if(!prison_cell) return + + var/obj/structure/closet/secure_closet/brig/locker = new /obj/structure/closet/secure_closet/brig(prison_cell) + locker.opened = 0 + locker.locked = 1 + + //strip their stuff and stick it in the crate + for(var/obj/item/I in M) + if(M.unEquip(I)) + I.loc = locker + I.layer = initial(I.layer) + I.plane = initial(I.plane) + I.dropped(M) + M.update_icons() + + //so they black out before warping + M.Paralyse(5) + sleep(5) + if(!M) return + + M.loc = prison_cell + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/prisoner = M + prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) + prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) + + to_chat(M, "You have been sent to the prison station!") + log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") + message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) + + else if(href_list["sendbacktolobby"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["sendbacktolobby"]) + + if(!isobserver(M)) + to_chat(usr, "You can only send ghost players back to the Lobby.") + return + + if(!M.client) + to_chat(usr, "[M] doesn't seem to have an active client.") + return + + if(alert(usr, "Send [key_name(M)] back to Lobby?", "Message", "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] back to the Lobby.") + + var/mob/new_player/NP = new() + GLOB.non_respawnable_keys -= M.ckey + NP.ckey = M.ckey + qdel(M) + + else if(href_list["tdome1"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["tdome1"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + for(var/obj/item/I in M) + M.unEquip(I) + if(I) + I.loc = M.loc + I.layer = initial(I.layer) + I.plane = initial(I.plane) + I.dropped(M) + + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.tdome1) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 1)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 1)", 1) + + else if(href_list["tdome2"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["tdome2"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + for(var/obj/item/I in M) + M.unEquip(I) + if(I) + I.loc = M.loc + I.layer = initial(I.layer) + I.plane = initial(I.plane) + I.dropped(M) + + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.tdome2) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 2)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 2)", 1) + + else if(href_list["tdomeadmin"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["tdomeadmin"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.tdomeadmin) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Admin.)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Admin.)", 1) + + else if(href_list["tdomeobserve"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["tdomeobserve"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + for(var/obj/item/I in M) + M.unEquip(I) + if(I) + I.loc = M.loc + I.layer = initial(I.layer) + I.plane = initial(I.plane) + I.dropped(M) + + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/observer = M + observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket(observer), slot_w_uniform) + observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(observer), slot_shoes) + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.tdomeobserve) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Observer.)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Observer.)", 1) + + else if(href_list["aroomwarp"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["aroomwarp"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.aroomwarp) + spawn(50) + to_chat(M, "You have been sent to the Admin Room!.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the Admin Room") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the Admin Room", 1) + + + else if(href_list["revive"]) + if(!check_rights(R_REJUVINATE)) return + + var/mob/living/L = locateUID(href_list["revive"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + + L.revive() + message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(L)]!", 1) + log_admin("[key_name(usr)] healed / revived [key_name(L)]") + + else if(href_list["makeai"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + if(alert(usr, "Confirm make ai?",, "Yes", "No") != "Yes") + return + + message_admins("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!", 1) + log_admin("[key_name(usr)] AIized [key_name(H)]") + var/mob/living/silicon/ai/ai_character = H.AIize() + ai_character.moveToAILandmark() + + else if(href_list["makealien"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make alien?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_alienize(H) + + else if(href_list["makeslime"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make slime?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_slimeize(H) + + else if(href_list["makesuper"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + if(alert(usr, "Confirm make superhero?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_super(H) + + else if(href_list["makerobot"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make robot?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_robotize(H) + + else if(href_list["makeanimal"]) + if(!check_rights(R_SPAWN)) return + + var/mob/M = locateUID(href_list["makeanimal"]) + if(istype(M, /mob/new_player)) + to_chat(usr, "This cannot be used on instances of type /mob/new_player") + return + if(alert(usr, "Confirm make animal?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_animalize(M) + + else if(href_list["incarn_ghost"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/dead/observer/G = locateUID(href_list["incarn_ghost"]) + if(!istype(G)) + to_chat(usr, "This will only work on /mob/dead/observer") + + var/posttransformoutfit = usr.client.robust_dress_shop() + + var/mob/living/carbon/human/H = G.incarnate_ghost() + + if(posttransformoutfit && istype(H)) + H.equipOutfit(posttransformoutfit) + + log_admin("[key_name(G)] was incarnated by [key_name(owner)]") + message_admins("[key_name_admin(G)] was incarnated by [key_name_admin(owner)]") + + else if(href_list["togmutate"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["togmutate"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + var/block=text2num(href_list["block"]) + //testing("togmutate([href_list["block"]] -> [block])") + usr.client.cmd_admin_toggle_block(H,block) + show_player_panel(H) + //H.regenerate_icons() + + else if(href_list["adminplayeropts"]) + var/mob/M = locateUID(href_list["adminplayeropts"]) + show_player_panel(M) + + else if(href_list["adminplayerobservefollow"]) + var/client/C = usr.client + if(!isobserver(usr)) + if(!check_rights(R_ADMIN|R_MOD)) // Need to be mod or admin to aghost + return + C.admin_ghost() + var/mob/M = locateUID(href_list["adminplayerobservefollow"]) + var/mob/dead/observer/A = C.mob + sleep(2) + A.ManualFollow(M) + + else if(href_list["check_antagonist"]) + check_antagonists() + + else if(href_list["take_question"]) + var/index = text2num(href_list["take_question"]) + + if(href_list["is_mhelp"]) + SSmentor_tickets.takeTicket(index) + else //Ahelp + SStickets.takeTicket(index) + + else if(href_list["resolve"]) + var/index = text2num(href_list["resolve"]) + if(href_list["is_mhelp"]) + SSmentor_tickets.resolveTicket(index) + else //Ahelp + SStickets.resolveTicket(index) + + else if(href_list["autorespond"]) + var/index = text2num(href_list["autorespond"]) + if(!check_rights(R_ADMIN|R_MOD)) + return + SStickets.autoRespond(index) + + else if(href_list["cult_nextobj"]) + if(alert(usr, "Validate the current Cult objective and unlock the next one?", "Cult Cheat Code", "Yes", "No") != "Yes") + return + + if(!GAMEMODE_IS_CULT) + alert("Couldn't locate cult mode datum! This shouldn't ever happen, tell a coder!") + return + + var/datum/game_mode/cult/cult_round = SSticker.mode + cult_round.bypass_phase() + message_admins("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.") + log_admin("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.") + + else if(href_list["cult_mindspeak"]) + var/input = stripped_input(usr, "Communicate to all the cultists with the voice of [SSticker.cultdat.entity_name]", "Voice of [SSticker.cultdat.entity_name]", "") + if(!input) + return + + for(var/datum/mind/H in SSticker.mode.cult) + if (H.current) + to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]
    ") + + for(var/mob/dead/observer/O in GLOB.player_list) + to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]
    ") + + message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [SSticker.cultdat.entity_name].") + log_admin("[key_name(usr)] Voice of [SSticker.cultdat.entity_name]: [input]") + + else if(href_list["adminplayerobservecoodjump"]) + if(!check_rights(R_ADMIN)) return + + var/x = text2num(href_list["X"]) + var/y = text2num(href_list["Y"]) + var/z = text2num(href_list["Z"]) + + var/client/C = usr.client + if(!isobserver(usr)) C.admin_ghost() + sleep(2) + C.jumptocoord(x,y,z) + + else if(href_list["adminchecklaws"]) + output_ai_laws() + + else if(href_list["adminmoreinfo"]) + var/mob/M = locateUID(href_list["adminmoreinfo"]) + admin_mob_info(M) + + else if(href_list["adminspawncookie"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + var/mob/living/carbon/human/H = locateUID(href_list["adminspawncookie"]) + if(!ishuman(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_l_hand ) + if(!(istype(H.l_hand,/obj/item/reagent_containers/food/snacks/cookie))) + H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_r_hand ) + if(!(istype(H.r_hand,/obj/item/reagent_containers/food/snacks/cookie))) + log_admin("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") + message_admins("[key_name_admin(H)] has [H.p_their()] hands full, so [H.p_they()] did not receive [H.p_their()] cookie, spawned by [key_name_admin(src.owner)].") + return + else + H.update_inv_r_hand()//To ensure the icon appears in the HUD + else + H.update_inv_l_hand() + log_admin("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") + message_admins("[key_name_admin(H)] got [H.p_their()] cookie, spawned by [key_name_admin(src.owner)]") + feedback_inc("admin_cookies_spawned",1) + to_chat(H, "Your prayers have been answered!! You received the best cookie!") + + else if(href_list["BlueSpaceArtillery"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + var/mob/living/M = locateUID(href_list["BlueSpaceArtillery"]) + if(!isliving(M)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + + if(alert(owner, "Are you sure you wish to hit [key_name(M)] with Bluespace Artillery?", "Confirm Firing?" , "Yes" , "No") != "Yes") + return + + if(GLOB.BSACooldown) + to_chat(owner, "Standby. Reload cycle in progress. Gunnery crews ready in five seconds!") + return + + GLOB.BSACooldown = 1 + spawn(50) + GLOB.BSACooldown = 0 + + to_chat(M, "You've been hit by bluespace artillery!") + log_admin("[key_name(M)] has been hit by Bluespace Artillery fired by [key_name(owner)]") + message_admins("[key_name_admin(M)] has been hit by Bluespace Artillery fired by [key_name_admin(owner)]") + + var/turf/simulated/floor/T = get_turf(M) + if(istype(T)) + if(prob(80)) + T.break_tile_to_plating() + else + T.break_tile() + + if(M.health <= 1) + M.gib() + else + M.adjustBruteLoss(min(99,(M.health - 1))) + M.Stun(20) + M.Weaken(20) + M.Stuttering(20) + + else if(href_list["CentcommReply"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["CentcommReply"]) + usr.client.admin_headset_message(M, "Centcomm") + + else if(href_list["SyndicateReply"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["SyndicateReply"]) + usr.client.admin_headset_message(M, "Syndicate") + + else if(href_list["HeadsetMessage"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["HeadsetMessage"]) + usr.client.admin_headset_message(M) + + else if(href_list["EvilFax"]) + if(!check_rights(R_ADMIN)) + return + var/mob/living/carbon/human/H = locateUID(href_list["EvilFax"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + var/etypes = list("Borgification", "Corgification", "Death By Fire", "Total Brain Death", "Honk Tumor", "Cluwne", "Demote", "Demote with Bot", "Revoke Fax Access", "Angry Fax Machine") + var/eviltype = input(src.owner, "Which type of evil fax do you wish to send [H]?","Its good to be baaaad...", "") as null|anything in etypes + if(!(eviltype in etypes)) + return + var/customname = clean_input("Pick a title for the evil fax.", "Fax Title", , owner) + if(!customname) + customname = "paper" + var/obj/item/paper/evilfax/P = new /obj/item/paper/evilfax(null) + var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) + + P.name = "Central Command - [customname]" + P.info = "You really should've known better." + P.myeffect = eviltype + P.mytarget = H + if(alert("Do you want the Evil Fax to activate automatically if [H] tries to ignore it?",,"Yes", "No") == "Yes") + P.activate_on_timeout = 1 + P.x = rand(-2, 0) + P.y = rand(-1, 2) + P.offset_x += P.x + P.offset_y += P.y + P.update_icon() + var/stampvalue = "cent" + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.icon_state = "paper_stamp-[stampvalue]" + stampoverlay.pixel_x = P.x + stampoverlay.pixel_y = P.y + P.stamped = list() + P.stamped += /obj/item/stamp/centcom + if(!P.ico) + P.ico = new + P.ico += "paper_stamp-[stampvalue]" + P.overlays += stampoverlay + P.stamps += "
    " + P.update_icon() + P.faxmachineid = fax.UID() + P.loc = fax.loc // Do not use fax.receivefax(P) here, as it won't preserve the type. Physically teleporting the fax paper is required. + if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) + to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(src.owner, "You sent a [eviltype] fax to [H]") + log_admin("[key_name(src.owner)] sent [key_name(H)] a [eviltype] fax") + message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a [eviltype] fax") + else if(href_list["Bless"]) + if(!check_rights(R_EVENT)) + return + var/mob/living/M = locateUID(href_list["Bless"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + var/btypes = list("To Arrivals", "Moderate Heal") + var/mob/living/carbon/human/H + if(ishuman(M)) + H = M + btypes += "Heal Over Time" + btypes += "Permanent Regeneration" + btypes += "Super Powers" + btypes += "Scarab Guardian" + btypes += "Human Protector" + btypes += "Sentient Pet" + btypes += "All Access" + var/blessing = input(owner, "How would you like to bless [M]?", "Its good to be good...", "") as null|anything in btypes + if(!(blessing in btypes)) + return + var/logmsg = null + switch(blessing) + if("To Arrivals") + M.forceMove(pick(GLOB.latejoin)) + to_chat(M, "You are abruptly pulled through space!") + logmsg = "a teleport to arrivals." + if("Moderate Heal") + M.adjustBruteLoss(-25) + M.adjustFireLoss(-25) + M.adjustToxLoss(-25) + M.adjustOxyLoss(-25) + to_chat(M,"You feel invigorated!") + logmsg = "a moderate heal." + if("Heal Over Time") + H.reagents.add_reagent("salglu_solution", 30) + H.reagents.add_reagent("salbutamol", 20) + H.reagents.add_reagent("spaceacillin", 20) + logmsg = "a heal over time." + if("Permanent Regeneration") + H.dna.SetSEState(GLOB.regenerateblock, 1) + genemutcheck(H, GLOB.regenerateblock, null, MUTCHK_FORCED) + H.update_mutations() + H.gene_stability = 100 + logmsg = "permanent regeneration." + if("Super Powers") + var/list/default_genes = list(GLOB.regenerateblock, GLOB.breathlessblock, GLOB.coldblock) + for(var/gene in default_genes) + H.dna.SetSEState(gene, 1) + genemutcheck(H, gene, null, MUTCHK_FORCED) + H.update_mutations() + H.gene_stability = 100 + logmsg = "superpowers." + if("Scarab Guardian") + var/obj/item/guardiancreator/biological/scarab = new /obj/item/guardiancreator/biological(H) + var/list/possible_guardians = list("Chaos", "Standard", "Ranged", "Support", "Explosive", "Random") + var/typechoice = input("Select Guardian Type", "Type") as null|anything in possible_guardians + if(isnull(typechoice)) + return + if(typechoice != "Random") + possible_guardians -= "Random" + scarab.possible_guardians = list() + scarab.possible_guardians += typechoice + scarab.attack_self(H) + spawn(700) + qdel(scarab) + logmsg = "scarab guardian." + if("Sentient Pet") + var/pets = subtypesof(/mob/living/simple_animal) + var/petchoice = input("Select pet type", "Pets") as null|anything in pets + if(isnull(petchoice)) + return + var/list/mob/dead/observer/candidates = pollCandidates("Play as the special event pet [H]?", poll_time = 200, min_hours = 10) + var/mob/dead/observer/theghost = null + if(candidates.len) + var/mob/living/simple_animal/pet/P = new petchoice(H.loc) + theghost = pick(candidates) + P.key = theghost.key + P.master_commander = H + P.universal_speak = 1 + P.universal_understand = 1 + P.can_collar = 1 + P.faction = list("neutral") + var/obj/item/clothing/accessory/petcollar/C = new + P.add_collar(C) + var/obj/item/card/id/I = H.wear_id + if(I) + var/obj/item/card/id/D = new /obj/item/card/id(C) + D.access = I.access + D.registered_name = P.name + D.assignment = "Pet" + C.access_id = D + spawn(30) + var/newname = sanitize(copytext(input(P, "You are [P], special event pet of [H]. Change your name to something else?", "Name change", P.name) as null|text,1,MAX_NAME_LEN)) + if(newname && newname != P.name) + P.name = newname + if(P.mind) + P.mind.name = newname + logmsg = "pet ([P])." + else + to_chat(usr, "WARNING: Nobody volunteered to play the special event pet.") + logmsg = "pet (no volunteers)." + if("Human Protector") + usr.client.create_eventmob_for(H, 0) + logmsg = "syndie protector." + if("All Access") + var/obj/item/card/id/I = H.wear_id + if(I) + var/list/access_to_give = get_all_accesses() + for(var/this_access in access_to_give) + if(!(this_access in I.access)) + // don't have it - add it + I.access |= this_access + else + to_chat(usr, "ERROR: [H] is not wearing an ID card.") + logmsg = "all access." + if(logmsg) + log_admin("[key_name(owner)] answered [key_name(M)]'s prayer with a blessing: [logmsg]") + message_admins("[key_name_admin(owner)] answered [key_name_admin(M)]'s prayer with a blessing: [logmsg]") + else if(href_list["Smite"]) + if(!check_rights(R_EVENT)) + return + var/mob/living/M = locateUID(href_list["Smite"]) + var/mob/living/carbon/human/H + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + var/ptypes = list("Lightning bolt", "Fire Death", "Gib") + if(ishuman(M)) + H = M + ptypes += "Brain Damage" + ptypes += "Honk Tumor" + ptypes += "Hallucinate" + ptypes += "Cold" + ptypes += "Hunger" + ptypes += "Cluwne" + ptypes += "Mutagen Cookie" + ptypes += "Hellwater Cookie" + ptypes += "Hunter" + ptypes += "Crew Traitor" + ptypes += "Floor Cluwne" + ptypes += "Shamebrero" + ptypes += "Dust" + var/punishment = input(owner, "How would you like to smite [M]?", "Its good to be baaaad...", "") as null|anything in ptypes + if(!(punishment in ptypes)) + return + var/logmsg = null + switch(punishment) + // These smiting types are valid for all living mobs + if("Lightning bolt") + M.electrocute_act(5, "Lightning Bolt", safety = TRUE, override = TRUE) + playsound(get_turf(M), 'sound/magic/lightningshock.ogg', 50, 1, -1) + M.adjustFireLoss(75) + M.Weaken(5) + to_chat(M, "The gods have punished you for your sins!") + logmsg = "a lightning bolt." + if("Fire Death") + to_chat(M,"You feel hotter than usual. Maybe you should lowe-wait, is that your hand melting?") + var/turf/simulated/T = get_turf(M) + new /obj/effect/hotspot(T) + M.adjustFireLoss(150) + logmsg = "a firey death." + if("Gib") + M.gib(FALSE) + logmsg = "gibbed." + + // These smiting types are only valid for ishuman() mobs + if("Brain Damage") + H.adjustBrainLoss(75) + logmsg = "75 brain damage." + if("Honk Tumor") + if(!H.get_int_organ(/obj/item/organ/internal/honktumor)) + var/obj/item/organ/internal/organ = new /obj/item/organ/internal/honktumor + to_chat(H, "Life seems funnier, somehow.") + organ.insert(H) + logmsg = "a honk tumor." + if("Hallucinate") + H.Hallucinate(1000) + logmsg = "hallucinations." + if("Cold") + H.reagents.add_reagent("frostoil", 40) + H.reagents.add_reagent("ice", 40) + logmsg = "cold." + if("Hunger") + H.set_nutrition(NUTRITION_LEVEL_CURSED) + logmsg = "starvation." + if("Cluwne") + H.makeCluwne() + H.mutations |= NOCLONE + logmsg = "cluwned." + if("Mutagen Cookie") + var/obj/item/reagent_containers/food/snacks/cookie/evilcookie = new /obj/item/reagent_containers/food/snacks/cookie + evilcookie.reagents.add_reagent("mutagen", 10) + evilcookie.desc = "It has a faint green glow." + evilcookie.bitesize = 100 + evilcookie.flags = NODROP | DROPDEL + H.drop_l_hand() + H.equip_to_slot_or_del(evilcookie, slot_l_hand) + logmsg = "a mutagen cookie." + if("Hellwater Cookie") + var/obj/item/reagent_containers/food/snacks/cookie/evilcookie = new /obj/item/reagent_containers/food/snacks/cookie + evilcookie.reagents.add_reagent("hell_water", 25) + evilcookie.desc = "Sulphur-flavored." + evilcookie.bitesize = 100 + evilcookie.flags = NODROP | DROPDEL + H.drop_l_hand() + H.equip_to_slot_or_del(evilcookie, slot_l_hand) + logmsg = "a hellwater cookie." + if("Hunter") + H.mutations |= NOCLONE + usr.client.create_eventmob_for(H, 1) + logmsg = "hunter." + if("Crew Traitor") + if(!H.mind) + to_chat(usr, "ERROR: This mob ([H]) has no mind!") + return + var/list/possible_traitors = list() + for(var/mob/living/player in GLOB.living_mob_list) + if(player.client && player.mind && player.stat != DEAD && player != H) + if(ishuman(player) && !player.mind.special_role) + if(player.client && (ROLE_TRAITOR in player.client.prefs.be_special) && !jobban_isbanned(player, ROLE_TRAITOR) && !jobban_isbanned(player, "Syndicate")) + possible_traitors += player.mind + for(var/datum/mind/player in possible_traitors) + if(player.current) + if(ismindshielded(player.current)) + possible_traitors -= player + if(possible_traitors.len) + var/datum/mind/newtraitormind = pick(possible_traitors) + var/datum/objective/assassinate/kill_objective = new() + kill_objective.target = H.mind + kill_objective.owner = newtraitormind + kill_objective.explanation_text = "Assassinate [H.mind.name], the [H.mind.assigned_role]" + newtraitormind.objectives += kill_objective + var/datum/antagonist/traitor/T = new() + T.give_objectives = FALSE + to_chat(newtraitormind.current, "ATTENTION: It is time to pay your debt to the Syndicate...") + to_chat(newtraitormind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") + newtraitormind.add_antag_datum(T) + else + to_chat(usr, "ERROR: Unable to find any valid candidate to send after [H].") + return + logmsg = "crew traitor." + if("Floor Cluwne") + var/turf/T = get_turf(M) + var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T) + FC.smiting = TRUE + FC.Acquire_Victim(M) + logmsg = "floor cluwne" + if("Shamebrero") + if(H.head) + H.unEquip(H.head, TRUE) + var/obj/item/clothing/head/sombrero/shamebrero/S = new(H.loc) + H.equip_to_slot_or_del(S, slot_head) + logmsg = "shamebrero" + if("Dust") + H.dust() + logmsg = "dust" + if(logmsg) + log_admin("[key_name(owner)] smited [key_name(M)] with: [logmsg]") + message_admins("[key_name_admin(owner)] smited [key_name_admin(M)] with: [logmsg]") + else if(href_list["cryossd"]) + if(!check_rights(R_ADMIN)) + return + var/mob/living/carbon/human/H = locateUID(href_list["cryossd"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(!href_list["cryoafk"] && !isLivingSSD(H)) + to_chat(usr, "This can only be used on living, SSD players.") + return + if(istype(H.loc, /obj/machinery/cryopod)) + var/obj/machinery/cryopod/P = H.loc + P.despawn_occupant() + log_admin("[key_name(usr)] despawned [H.job] [H] in cryo.") + message_admins("[key_name_admin(usr)] despawned [H.job] [H] in cryo.") + else if(cryo_ssd(H)) + log_admin("[key_name(usr)] sent [H.job] [H] to cryo.") + message_admins("[key_name_admin(usr)] sent [H.job] [H] to cryo.") + if(href_list["cryoafk"]) // Warn them if they are send to storage and are AFK + to_chat(H, "The admins have moved you to cryo storage for being AFK. Please eject yourself (right click, eject) out of the cryostorage if you want to avoid being despawned.") + SEND_SOUND(H, 'sound/effects/adminhelp.ogg') + if(H.client) + window_flash(H.client) + else if(href_list["FaxReplyTemplate"]) + if(!check_rights(R_ADMIN)) + return + var/mob/living/carbon/human/H = locateUID(href_list["FaxReplyTemplate"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + var/obj/item/paper/P = new /obj/item/paper(null) + var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) + P.name = "Central Command - paper" + var/stypes = list("Handle it yourselves!","Illegible fax","Fax not signed","Not Right Now","You are wasting our time", "Keep up the good work", "ERT Instructions") + var/stype = input(src.owner, "Which type of standard reply do you wish to send to [H]?","Choose your paperwork", "") as null|anything in stypes + var/tmsg = "



    Nanotrasen Science Station [GLOB.using_map.station_short]


    NAS Trurl Communications Department Report


    " + if(stype == "Handle it yourselves!") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.

    This is an automatic message." + else if(stype == "Illegible fax") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.

    Please consult your nearest dictionary and/or thesaurus and try again.

    This is an automatic message." + else if(stype == "Fax not signed") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax has not been correctly signed and, as such, we cannot verify your identity.

    Please sign your faxes before sending them so that we may verify your identity.

    This is an automatic message." + else if(stype == "Not Right Now") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.

    This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.

    Please try again later.

    This is an automatic message." + else if(stype == "You are wasting our time") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.

    Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.

    This is an automatic message." + else if(stype == "Keep up the good work") + tmsg += "Greetings, esteemed crewmember. Your fax has been received successfully by NAS Trurl Fax Registration.

    We at the NAS Trurl appreciate the good work that you have done here, and sincerely recommend that you continue such a display of dedication to the company.

    This is absolutely not an automated message." + else if(stype == "ERT Instructions") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please utilize the Card Swipers if you wish to call for an ERT.

    This is an automated message." + else + return + tmsg += "
    " + P.info = tmsg + P.x = rand(-2, 0) + P.y = rand(-1, 2) + P.offset_x += P.x + P.offset_y += P.y + P.update_icon() + var/stampvalue = "cent" + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.icon_state = "paper_stamp-[stampvalue]" + stampoverlay.pixel_x = P.x + stampoverlay.pixel_y = P.y + P.stamped = list() + P.stamped += /obj/item/stamp/centcom + if(!P.ico) + P.ico = new + P.ico += "paper_stamp-[stampvalue]" + P.overlays += stampoverlay + P.stamps += "
    " + P.update_icon() + fax.receivefax(P) + if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) + to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(src.owner, "You sent a standard '[stype]' fax to [H]") + log_admin("[key_name(src.owner)] sent [key_name(H)] a standard '[stype]' fax") + message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a standard '[stype]' fax") + + else if(href_list["HONKReply"]) + var/mob/living/carbon/human/H = locateUID(href_list["HONKReply"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) + to_chat(usr, "The person you are trying to contact is not wearing a headset") + return + + var/input = input(src.owner, "Please enter a message to reply to [key_name(H)] via [H.p_their()] headset.","Outgoing message from HONKplanet", "") + if(!input) return + + to_chat(src.owner, "You sent [input] to [H] via a secure channel.") + log_admin("[src.owner] replied to [key_name(H)]'s HONKplanet message with the message [input].") + to_chat(H, "You hear something crackle in your headset for a moment before a voice speaks. \"Please stand by for a message from your HONKbrothers. Message as follows, HONK. [input]. Message ends, HONK.\"") + + else if(href_list["ErtReply"]) + if(!check_rights(R_ADMIN)) + return + + if(alert(src.owner, "Accept or Deny ERT request?", "CentComm Response", "Accept", "Deny") == "Deny") + var/mob/living/carbon/human/H = locateUID(href_list["ErtReply"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(H.stat != 0) + to_chat(usr, "The person you are trying to contact is not conscious.") + return + if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) + to_chat(usr, "The person you are trying to contact is not wearing a headset") + return + + var/input = input(src.owner, "Please enter a reason for denying [key_name(H)]'s ERT request.","Outgoing message from CentComm", "") + if(!input) return + GLOB.ert_request_answered = TRUE + to_chat(src.owner, "You sent [input] to [H] via a secure channel.") + log_admin("[src.owner] denied [key_name(H)]'s ERT request with the message [input].") + to_chat(H, "Incoming priority transmission from Central Command. Message as follows, Your ERT request has been denied for the following reasons: [input].") + else + src.owner.response_team() + + + else if(href_list["AdminFaxView"]) + if(!check_rights(R_ADMIN)) + return + + var/obj/item/fax = locate(href_list["AdminFaxView"]) + if(istype(fax, /obj/item/paper)) + var/obj/item/paper/P = fax + P.show_content(usr,1) + else if(istype(fax, /obj/item/photo)) + var/obj/item/photo/H = fax + H.show(usr) + else if(istype(fax, /obj/item/paper_bundle)) + //having multiple people turning pages on a paper_bundle can cause issues + //open a browse window listing the contents instead + var/data = "" + var/obj/item/paper_bundle/B = fax + + for(var/page = 1, page <= B.amount + 1, page++) + var/obj/pageobj = B.contents[page] + data += "Page [page] - [pageobj.name]
    " + + usr << browse(data, "window=PaperBundle[B.UID()]") + else + to_chat(usr, "The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]") + + else if(href_list["AdminFaxViewPage"]) + if(!check_rights(R_ADMIN)) + return + + var/page = text2num(href_list["AdminFaxViewPage"]) + var/obj/item/paper_bundle/bundle = locate(href_list["paper_bundle"]) + + if(!bundle) return + + if(istype(bundle.contents[page], /obj/item/paper)) + var/obj/item/paper/P = bundle.contents[page] + P.show_content(usr, 1) + else if(istype(bundle.contents[page], /obj/item/photo)) + var/obj/item/photo/H = bundle.contents[page] + H.show(usr) + return + + else if(href_list["AdminFaxCreate"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/sender = locate(href_list["AdminFaxCreate"]) + var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) + var/faxtype = href_list["faxtype"] + var/reply_to = locate(href_list["replyto"]) + var/destination + var/notify + var/obj/item/paper/P + var/use_letterheard = alert("Use letterhead? If so, do not add your own header or a footer. Type and format only your actual message.",,"Nanotrasen","Syndicate", "No") + switch(use_letterheard) + if("Nanotrasen") + P = new /obj/item/paper/central_command(null) + if("Syndicate") + P = new /obj/item/paper/syndicate(null) + if("No") + P = new /obj/item/paper(null) + if(!fax) + var/list/departmentoptions = GLOB.alldepartments + GLOB.hidden_departments + "All Departments" + destination = input(usr, "To which department?", "Choose a department", "") as null|anything in departmentoptions + if(!destination) + qdel(P) + return + + for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) + if(destination != "All Departments" && F.department == destination) + fax = F + + + var/input = input(src.owner, "Please enter a message to send a fax via secure connection. Use
    for line breaks. Both pencode and HTML work.", "Outgoing message from Centcomm", "") as message|null + if(!input) + qdel(P) + return + input = admin_pencode_to_html(html_encode(input)) // Encode everything from pencode to html + + var/customname = clean_input("Pick a title for the fax.", "Fax Title", , owner) + if(!customname) + customname = "paper" + + var/stampname + var/stamptype + var/stampvalue + var/sendername + switch(faxtype) + if("Central Command") + stamptype = "icon" + stampvalue = "cent" + sendername = command_name() + if("Syndicate") + stamptype = "icon" + stampvalue = "syndicate" + sendername = "UNKNOWN" + if("Administrator") + stamptype = input(src.owner, "Pick a stamp type.", "Stamp Type") as null|anything in list("icon","text","none") + if(stamptype == "icon") + stampname = input(src.owner, "Pick a stamp icon.", "Stamp Icon") as null|anything in list("centcom","syndicate","granted","denied","clown") + switch(stampname) + if("centcom") + stampvalue = "cent" + if("syndicate") + stampvalue = "syndicate" + if("granted") + stampvalue = "ok" + if("denied") + stampvalue = "deny" + if("clown") + stampvalue = "clown" + else if(stamptype == "text") + stampvalue = clean_input("What should the stamp say?", "Stamp Text", , owner) + else if(stamptype == "none") + stamptype = "" + else + qdel(P) + return + + sendername = clean_input("What organization does the fax come from? This determines the prefix of the paper (i.e. Central Command- Title). This is optional.", "Organization", , owner) + + if(sender) + notify = alert(src.owner, "Would you like to inform the original sender that a fax has arrived?","Notify Sender","Yes","No") + + // Create the reply message + if(sendername) + P.name = "[sendername]- [customname]" + else + P.name = "[customname]" + P.info = input + P.update_icon() + P.x = rand(-2, 0) + P.y = rand(-1, 2) + P.offset_x += P.x + P.offset_y += P.y + if(stamptype) + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.pixel_x = P.x + stampoverlay.pixel_y = P.y + + if(!P.ico) + P.ico = new + P.ico += "paper_stamp-[stampvalue]" + stampoverlay.icon_state = "paper_stamp-[stampvalue]" + + if(stamptype == "icon") + if(!P.stamped) + P.stamped = new + P.stamped += /obj/item/stamp/centcom + P.overlays += stampoverlay + P.stamps += "
    " + + else if(stamptype == "text") + if(!P.stamped) + P.stamped = new + P.stamped += /obj/item/stamp + P.overlays += stampoverlay + P.stamps += "
    [stampvalue]" + + if(destination != "All Departments") + if(!fax.receivefax(P)) + to_chat(src.owner, "Message transmission failed.") + return + else + for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) + if(is_station_level(F.z)) + spawn(0) + if(!F.receivefax(P)) + to_chat(src.owner, "Message transmission to [F.department] failed.") + + var/datum/fax/admin/A = new /datum/fax/admin() + A.name = P.name + A.from_department = faxtype + if(destination != "All Departments") + A.to_department = fax.department + else + A.to_department = "All Departments" + A.origin = "Administrator" + A.message = P + A.reply_to = reply_to + A.sent_by = usr + A.sent_at = world.time + + to_chat(src.owner, "Message transmitted successfully.") + if(notify == "Yes") + var/mob/living/carbon/human/H = sender + if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) + to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.") + if(sender) + log_admin("[key_name(src.owner)] replied to a fax message from [key_name(sender)]: [input]") + message_admins("[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(sender)] (VIEW).", 1) + else + log_admin("[key_name(src.owner)] sent a fax message to [destination]: [input]") + message_admins("[key_name_admin(src.owner)] sent a fax message to [destination] (VIEW).", 1) + return + + else if(href_list["refreshfaxpanel"]) + if(!check_rights(R_ADMIN)) + return + + fax_panel(usr) + + else if(href_list["getplaytimewindow"]) + if(!check_rights(R_ADMIN)) + return + var/mob/M = locateUID(href_list["getplaytimewindow"]) + if(!M) + to_chat(usr, "ERROR: Mob not found.") + return + cmd_mentor_show_exp_panel(M.client) + + else if(href_list["jumpto"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["jumpto"]) + usr.client.jumptomob(M) + + else if(href_list["getmob"]) + if(!check_rights(R_ADMIN)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") return + var/mob/M = locateUID(href_list["getmob"]) + usr.client.Getmob(M) + + else if(href_list["sendmob"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["sendmob"]) + usr.client.sendmob(M) + + else if(href_list["narrateto"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["narrateto"]) + usr.client.cmd_admin_direct_narrate(M) + + else if(href_list["subtlemessage"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["subtlemessage"]) + usr.client.cmd_admin_subtle_message(M) + + else if(href_list["traitor"]) + if(!check_rights(R_ADMIN|R_MOD)) return + + if(!SSticker || !SSticker.mode) + alert("The game hasn't started yet!") + return + + var/mob/M = locateUID(href_list["traitor"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + show_traitor_panel(M) + + else if(href_list["create_object"]) + if(!check_rights(R_SPAWN)) return + return create_object(usr) + + else if(href_list["quick_create_object"]) + if(!check_rights(R_SPAWN)) return + return quick_create_object(usr) + + else if(href_list["create_turf"]) + if(!check_rights(R_SPAWN)) return + return create_turf(usr) + + else if(href_list["create_mob"]) + if(!check_rights(R_SPAWN)) return + return create_mob(usr) + + else if(href_list["object_list"]) //this is the laggiest thing ever + if(!check_rights(R_SPAWN)) return + + var/atom/loc = usr.loc + + var/dirty_paths + if(istext(href_list["object_list"])) + dirty_paths = list(href_list["object_list"]) + else if(istype(href_list["object_list"], /list)) + dirty_paths = href_list["object_list"] + + var/paths = list() + + for(var/dirty_path in dirty_paths) + var/path = text2path(dirty_path) + if(!path) + continue + else if(!ispath(path, /obj) && !ispath(path, /turf) && !ispath(path, /mob)) + continue + paths += path + + if(!paths) + alert("The path list you sent is empty") + return + if(length(paths) > 5) + alert("Select fewer object types, (max 5)") + return + + var/list/offset = splittext(href_list["offset"],",") + var/number = dd_range(1, 100, text2num(href_list["object_count"])) + var/X = offset.len > 0 ? text2num(offset[1]) : 0 + var/Y = offset.len > 1 ? text2num(offset[2]) : 0 + var/Z = offset.len > 2 ? text2num(offset[3]) : 0 + var/tmp_dir = href_list["object_dir"] + var/obj_dir = tmp_dir ? text2num(tmp_dir) : 2 + if(!obj_dir || !(obj_dir in list(1,2,4,8,5,6,9,10))) + obj_dir = 2 + var/obj_name = sanitize(href_list["object_name"]) + + + var/atom/target //Where the object will be spawned + var/where = href_list["object_where"] + if(!( where in list("onfloor","inhand","inmarked") )) + where = "onfloor" + + + switch(where) + if("inhand") + if(!iscarbon(usr) && !isrobot(usr)) + to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") + where = "onfloor" + target = usr + + if("onfloor") + switch(href_list["offset_type"]) + if("absolute") + target = locate(0 + X,0 + Y,0 + Z) + if("relative") + target = locate(loc.x + X,loc.y + Y,loc.z + Z) + if("inmarked") + if(!marked_datum) + to_chat(usr, "You don't have any object marked. Abandoning spawn.") + return + else if(!istype(marked_datum,/atom)) + to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") + return + else + target = marked_datum + + if(target) + for(var/path in paths) + for(var/i = 0; i < number; i++) + if(path in typesof(/turf)) + var/turf/O = target + var/turf/N = O.ChangeTurf(path) + if(N && obj_name) + N.name = obj_name + else + var/atom/O = new path(target) + if(O) + O.admin_spawned = TRUE + O.dir = obj_dir + if(obj_name) + O.name = obj_name + if(istype(O,/mob)) + var/mob/M = O + M.real_name = obj_name + if(where == "inhand" && isliving(usr) && istype(O, /obj/item)) + var/mob/living/L = usr + var/obj/item/I = O + L.put_in_hands(I) + if(isrobot(L)) + var/mob/living/silicon/robot/R = L + if(R.module) + R.module.modules += I + I.loc = R.module + R.module.rebuild() + R.activate_module(I) + R.module.fix_modules() + + if(number == 1) + log_admin("[key_name(usr)] created a [english_list(paths)]") + 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)]") + for(var/path in paths) + if(ispath(path, /mob)) + message_admins("[key_name_admin(usr)] created [number]ea [english_list(paths)]") + break + return + + else if(href_list["kick_all_from_lobby"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) + var/afkonly = text2num(href_list["afkonly"]) + if(alert("Are you sure you want to kick all [afkonly ? "AFK" : ""] clients from the lobby?","Confirmation","Yes","Cancel") != "Yes") + return + var/list/listkicked = kick_clients_in_lobby("You were kicked from the lobby by an Administrator.", afkonly) + + var/strkicked = "" + for(var/name in listkicked) + strkicked += "[name], " + message_admins("[key_name_admin(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") + log_admin("[key_name(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") + else + to_chat(usr, "You may only use this when the game is running.") + + else if(href_list["memoeditlist"]) + if(!check_rights(R_SERVER)) return + var/sql_key = sanitizeSQL("[href_list["memoeditlist"]]") + var/DBQuery/query_memoedits = GLOB.dbcon.NewQuery("SELECT edits FROM [format_table_name("memo")] WHERE (ckey = '[sql_key]')") + if(!query_memoedits.Execute()) + var/err = query_memoedits.ErrorMsg() + log_game("SQL ERROR obtaining edits from memo table. Error : \[[err]\]\n") + return + if(query_memoedits.NextRow()) + var/edit_log = query_memoedits.item[1] + usr << browse(edit_log,"window=memoeditlist") + + else if(href_list["secretsfun"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/ok = 0 + switch(href_list["secretsfun"]) + if("sec_clothes") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SC") + for(var/obj/item/clothing/under/O in world) + qdel(O) + ok = 1 + if("sec_all_clothes") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SAC") + for(var/obj/item/clothing/O in world) + qdel(O) + ok = 1 + if("sec_classic1") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SC1") + for(var/obj/item/clothing/suit/fire/O in world) + qdel(O) + for(var/obj/structure/grille/O in world) + qdel(O) + if("monkey") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","M") + for(var/mob/living/carbon/human/H in GLOB.mob_list) + spawn(0) + H.monkeyize() + ok = 1 + if("corgi") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","M") + for(var/mob/living/carbon/human/H in GLOB.mob_list) + spawn(0) + H.corgize() + ok = 1 + if("honksquad") + if(usr.client.honksquad()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","HONK") + if("striketeam") + if(usr.client.strike_team()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","Strike") + if("striketeam_syndicate") + if(usr.client.syndicate_strike_team()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","Strike") + if("infiltrators_syndicate") + if(usr.client.syndicate_infiltration_team()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SyndieInfiltrationTeam") + if("gimmickteam") + if(usr.client.gimmick_team()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","GimmickTeam") + if("tripleAI") + usr.client.triple_ai() + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","TriAI") + if("gravity") + if(!(SSticker && SSticker.mode)) + to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.") + return + GLOB.gravity_is_on = !GLOB.gravity_is_on + for(var/area/A in world) + A.gravitychange(GLOB.gravity_is_on,A) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","Grav") + if(GLOB.gravity_is_on) + log_admin("[key_name(usr)] toggled gravity on.", 1) + message_admins("[key_name_admin(usr)] toggled gravity on.", 1) + GLOB.event_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.") + else + log_admin("[key_name(usr)] toggled gravity off.", 1) + message_admins("[key_name_admin(usr)] toggled gravity off.", 1) + GLOB.event_announcement.Announce("Feedback surge detected in mass-distributions systems. Artifical gravity has been disabled whilst the system reinitializes. Further failures may result in a gravitational collapse and formation of blackholes. Have a nice day.") + + if("power") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","P") + log_admin("[key_name(usr)] made all areas powered", 1) + message_admins("[key_name_admin(usr)] made all areas powered", 1) + power_restore() + if("unpower") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","UP") + log_admin("[key_name(usr)] made all areas unpowered", 1) + message_admins("[key_name_admin(usr)] made all areas unpowered", 1) + power_failure() + if("quickpower") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","QP") + log_admin("[key_name(usr)] made all SMESs powered", 1) + message_admins("[key_name_admin(usr)] made all SMESs powered", 1) + power_restore_quick() + if("prisonwarp") + if(!SSticker) + alert("The game hasn't started yet!", null, null, null, null, null) + return + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","PW") + message_admins("[key_name_admin(usr)] teleported all players to the prison station.", 1) + for(var/mob/living/carbon/human/H in GLOB.mob_list) + var/turf/loc = find_loc(H) + var/security = 0 + if(!is_station_level(loc.z) || GLOB.prisonwarped.Find(H)) + +//don't warp them if they aren't ready or are already there + continue + H.Paralyse(5) + if(H.wear_id) + var/obj/item/card/id/id = H.get_idcard() + for(var/A in id.access) + if(A == ACCESS_SECURITY) + security++ + if(!security) + //strip their stuff before they teleport into a cell :downs: + for(var/obj/item/W in H) + if(istype(W, /obj/item/organ/external)) + continue + //don't strip organs + H.unEquip(W) + if(H.client) + H.client.screen -= W + if(W) + W.loc = H.loc + W.dropped(H) + W.layer = initial(W.layer) + W.plane = initial(W.plane) + //teleport person to cell + H.loc = pick(GLOB.prisonwarp) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(H), slot_shoes) + else + //teleport security person + H.loc = pick(GLOB.prisonsecuritywarp) + GLOB.prisonwarped += H + if("traitor_all") + if(!SSticker) + alert("The game hasn't started yet!") + return + var/objective = sanitize(copytext(input("Enter an objective"),1,MAX_MESSAGE_LEN)) + if(!objective) + return + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","TA([objective])") + + for(var/mob/living/carbon/human/H in GLOB.player_list) + if(H.stat == 2 || !H.client || !H.mind) continue + if(is_special_character(H)) continue + //traitorize(H, objective, 0) + H.mind.add_antag_datum(/datum/antagonist/traitor) + + for(var/mob/living/silicon/A in GLOB.player_list) + A.mind.add_antag_datum(/datum/antagonist/traitor) + + message_admins("[key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]", 1) + log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]") + + if("togglebombcap") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","BC") + + var/newBombCap = input(usr,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be between 4 and 128)", "New Bomb Cap", GLOB.max_ex_light_range) as num|null + if(newBombCap < 4) + return + if(newBombCap > 128) + newBombCap = 128 + + GLOB.max_ex_devastation_range = round(newBombCap/4) + GLOB.max_ex_heavy_range = round(newBombCap/2) + GLOB.max_ex_light_range = newBombCap + //I don't know why these are their own variables, but fuck it, they are. + GLOB.max_ex_flash_range = newBombCap + GLOB.max_ex_flame_range = newBombCap + + message_admins("[key_name_admin(usr)] changed the bomb cap to [GLOB.max_ex_devastation_range], [GLOB.max_ex_heavy_range], [GLOB.max_ex_light_range]") + log_admin("[key_name(usr)] changed the bomb cap to [GLOB.max_ex_devastation_range], [GLOB.max_ex_heavy_range], [GLOB.max_ex_light_range]") + + if("flicklights") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","FL") + while(!usr.stat) +//knock yourself out to stop the ghosts + for(var/mob/M in GLOB.player_list) + if(M.stat != 2 && prob(25)) + var/area/AffectedArea = get_area(M) + if(AffectedArea.name != "Space" && AffectedArea.name != "Engine Walls" && AffectedArea.name != "Chemical Lab Test Chamber" && AffectedArea.name != "Escape Shuttle" && AffectedArea.name != "Arrival Area" && AffectedArea.name != "Arrival Shuttle" && AffectedArea.name != "start area" && AffectedArea.name != "Engine Combustion Chamber") + AffectedArea.power_light = 0 + AffectedArea.power_change() + spawn(rand(55,185)) + AffectedArea.power_light = 1 + AffectedArea.power_change() + var/Message = rand(1,4) + switch(Message) + if(1) + M.show_message(text("You shudder as if cold..."), 1) + if(2) + M.show_message(text("You feel something gliding across your back..."), 1) + if(3) + M.show_message(text("Your eyes twitch, you feel like something you can't see is here..."), 1) + if(4) + M.show_message(text("You notice something moving out of the corner of your eye, but nothing is there..."), 1) + for(var/obj/W in orange(5,M)) + if(prob(25) && !W.anchored) + step_rand(W) + sleep(rand(100,1000)) + for(var/mob/M in GLOB.player_list) + if(M.stat != 2) + M.show_message(text("The chilling wind suddenly stops..."), 1) + if("lightout") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","LO") + message_admins("[key_name_admin(usr)] has broke a lot of lights", 1) + var/datum/event/electrical_storm/E = new /datum/event/electrical_storm + E.lightsoutAmount = 2 + if("blackout") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","BO") + message_admins("[key_name_admin(usr)] broke all lights", 1) + for(var/obj/machinery/light/L in GLOB.machines) + L.break_light_tube() + if("whiteout") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","WO") + message_admins("[key_name_admin(usr)] fixed all lights", 1) + for(var/obj/machinery/light/L in GLOB.machines) + L.fix() + if("floorlava") + feedback_inc("admin_secrets_fun_used", 1) + feedback_add_details("admin_secrets_fun_used", "LF") + var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") + if(sure == "No") + return + SSweather.run_weather(/datum/weather/floor_is_lava) + message_admins("[key_name_admin(usr)] made the floor lava") + if("fakelava") + feedback_inc("admin_secrets_fun_used", 1) + feedback_add_details("admin_secrets_fun_used", "LZ") + var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") + if(sure == "No") + return + SSweather.run_weather(/datum/weather/floor_is_lava/fake) + message_admins("[key_name_admin(usr)] made aesthetic lava on the floor") + if("weatherashstorm") + feedback_inc("admin_secrets_fun_used", 1) + feedback_add_details("admin_secrets_fun_used", "WA") + var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") + if(sure == "No") + return + SSweather.run_weather(/datum/weather/ash_storm) + message_admins("[key_name_admin(usr)] spawned an ash storm on the mining level") + if("stupify") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","RET") + for(var/mob/living/carbon/human/H in GLOB.player_list) + to_chat(H, "You suddenly feel stupid.") + H.setBrainLoss(60) + message_admins("[key_name_admin(usr)] made everybody stupid") + if("fakeguns") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","FG") + for(var/obj/item/W in world) + if(istype(W, /obj/item/clothing) || istype(W, /obj/item/card/id) || istype(W, /obj/item/disk) || istype(W, /obj/item/tank)) + continue + W.icon = 'icons/obj/guns/projectile.dmi' + W.icon_state = "revolver" + W.item_state = "gun" + message_admins("[key_name_admin(usr)] made every item look like a gun") + if("schoolgirl") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SG") + for(var/obj/item/clothing/under/W in world) + W.icon_state = "schoolgirl" + W.item_state = "w_suit" + W.item_color = "schoolgirl" + message_admins("[key_name_admin(usr)] activated Japanese Animes mode") + world << sound('sound/AI/animes.ogg') + if("eagles")//SCRAW + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","EgL") + for(var/obj/machinery/door/airlock/W in GLOB.airlocks) + if(is_station_level(W.z) && !istype(get_area(W), /area/bridge) && !istype(get_area(W), /area/crew_quarters) && !istype(get_area(W), /area/security/prison)) + W.req_access = list() + message_admins("[key_name_admin(usr)] activated Egalitarian Station mode") + GLOB.event_announcement.Announce("Centcomm airlock control override activated. Please take this time to get acquainted with your coworkers.", new_sound = 'sound/AI/commandreport.ogg') + if("onlyone") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","OO") + usr.client.only_one() +// message_admins("[key_name_admin(usr)] has triggered HIGHLANDER") + if("onlyme") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","OM") + usr.client.only_me() + if("onlyoneteam") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","OOT") + usr.client.only_one_team() +// message_admins("[key_name_admin(usr)] has triggered ") + if("rolldice") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","ROL") + usr.client.roll_dices() + if("guns") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SG") + var/survivor_probability = 0 + switch(alert("Do you want this to create survivors antagonists?", , "No Antags", "Some Antags", "All Antags!")) + if("Some Antags") + survivor_probability = 25 + if("All Antags!") + survivor_probability = 100 + + rightandwrong(SUMMON_GUNS, usr, survivor_probability) + if("magic") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SM") + var/survivor_probability = 0 + switch(alert("Do you want this to create survivors antagonists?", , "No Antags", "Some Antags", "All Antags!")) + if("Some Antags") + survivor_probability = 25 + if("All Antags!") + survivor_probability = 100 + + rightandwrong(SUMMON_MAGIC, usr, survivor_probability) + if("tdomereset") + var/delete_mobs = alert("Clear all mobs?","Confirm","Yes","No","Cancel") + if(delete_mobs == "Cancel") + return + + var/area/thunderdome = locate(/area/tdome/arena) + if(delete_mobs == "Yes") + for(var/mob/living/mob in thunderdome) + qdel(mob) //Clear mobs + for(var/obj/obj in thunderdome) + if(!istype(obj,/obj/machinery/camera)) + qdel(obj) //Clear objects + + var/area/template = locate(/area/tdome/arena_source) + template.copy_contents_to(thunderdome) + + log_admin("[key_name(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].", 1) + message_admins("[key_name_admin(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].") + + if("tdomestart") + var/confirmation = alert("Start a Thunderdome match?","Confirm","Yes","No") + if(confirmation == "No") + return + if(makeThunderdomeTeams()) + log_admin("[key_name(usr)] started a Thunderdome match!", 1) + message_admins("[key_name_admin(usr)] has started a Thunderdome match!") + else + log_admin("[key_name(usr)] attempted to start a Thunderdome match, but no ghosts signed up.", 1) + message_admins("[key_name_admin(usr)] tried starting a Thunderdome match, but no ghosts signed up.") + if("securitylevel0") + set_security_level(0) + message_admins("[key_name_admin(usr)] change security level to Green.", 1) + if("securitylevel1") + set_security_level(1) + message_admins("[key_name_admin(usr)] change security level to Blue.", 1) + if("securitylevel2") + set_security_level(2) + message_admins("[key_name_admin(usr)] change security level to Red.", 1) + if("securitylevel3") + set_security_level(3) + message_admins("[key_name_admin(usr)] change security level to Gamma.", 1) + if("securitylevel4") + set_security_level(4) + message_admins("[key_name_admin(usr)] change security level to Epsilon.", 1) + if("securitylevel5") + set_security_level(5) + message_admins("[key_name_admin(usr)] change security level to Delta.", 1) + if("moveminingshuttle") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","ShM") + if(!SSshuttle.toggleShuttle("mining","mining_home","mining_away")) + message_admins("[key_name_admin(usr)] moved mining shuttle") + log_admin("[key_name(usr)] moved the mining shuttle") + + if("movelaborshuttle") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","ShL") + if(!SSshuttle.toggleShuttle("laborcamp","laborcamp_home","laborcamp_away")) + message_admins("[key_name_admin(usr)] moved labor shuttle") + log_admin("[key_name(usr)] moved the labor shuttle") + + if("moveferry") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","ShF") + if(!SSshuttle.toggleShuttle("ferry","ferry_home","ferry_away")) + message_admins("[key_name_admin(usr)] moved the centcom ferry") + log_admin("[key_name(usr)] moved the centcom ferry") + + if(usr) + log_admin("[key_name(usr)] used secret [href_list["secretsfun"]]") + if(ok) + to_chat(world, text("A secret has been activated by []!", usr.key)) + + else if(href_list["secretsadmin"]) + if(!check_rights(R_ADMIN)) return + + var/ok = 0 + switch(href_list["secretsadmin"]) + if("list_signalers") + var/dat = "Showing last [length(GLOB.lastsignalers)] signalers.
    " + for(var/sig in GLOB.lastsignalers) + dat += "[sig]
    " + usr << browse(dat, "window=lastsignalers;size=800x500") + if("list_lawchanges") + var/dat = "Showing last [length(GLOB.lawchanges)] law changes.
    " + for(var/sig in GLOB.lawchanges) + dat += "[sig]
    " + usr << browse(dat, "window=lawchanges;size=800x500") + if("list_job_debug") + var/dat = "Job Debug info.
    " + if(SSjobs) + for(var/line in SSjobs.job_debug) + dat += "[line]
    " + dat+= "*******

    " + for(var/datum/job/job in SSjobs.occupations) + if(!job) continue + dat += "job: [job.title], current_positions: [job.current_positions], total_positions: [job.total_positions]
    " + usr << browse(dat, "window=jobdebug;size=600x500") + if("showailaws") + output_ai_laws() + if("showgm") + if(!SSticker) + alert("The game hasn't started yet!") + else if(SSticker.mode) + alert("The game mode is [SSticker.mode.name]") + else alert("For some reason there's a ticker, but not a game mode") + if("manifest") + var/dat = "Showing Crew Manifest.
    " + dat += "" + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(H.ckey) + dat += text("", H.name, H.get_assignment()) + dat += "
    NamePosition
    [][]
    " + usr << browse(dat, "window=manifest;size=440x410") + if("check_antagonist") + check_antagonists() + if("DNA") + var/dat = "Showing DNA from blood.
    " + dat += "" + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(H.dna && H.ckey) + dat += "" + dat += "
    NameDNABlood Type
    [H][H.dna.unique_enzymes][H.dna.blood_type]
    " + usr << browse(dat, "window=DNA;size=440x410") + if("fingerprints") + var/dat = "Showing Fingerprints.
    " + dat += "" + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(H.ckey) + if(H.dna && H.dna.uni_identity) + dat += "" + else if(H.dna && !H.dna.uni_identity) + dat += "" + else if(!H.dna) + dat += "" + dat += "
    NameFingerprints
    [H][md5(H.dna.uni_identity)]
    [H]H.dna.uni_identity = null
    [H]H.dna = null
    " + usr << browse(dat, "window=fingerprints;size=440x410") + if("night_shift_set") + var/val = alert(usr, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "Night Shift", "On", "Off", "Automatic") + switch(val) + if("Automatic") + if(config.enable_night_shifts) + SSnightshift.can_fire = TRUE + SSnightshift.fire() + else + SSnightshift.update_nightshift(FALSE, TRUE) + to_chat(usr, "Night shift set to automatic.") + if("On") + SSnightshift.can_fire = FALSE + SSnightshift.update_nightshift(TRUE, FALSE) + to_chat(usr, "Night shift forced on.") + if("Off") + SSnightshift.can_fire = FALSE + SSnightshift.update_nightshift(FALSE, FALSE) + to_chat(usr, "Night shift forced off.") + else + if(usr) + log_admin("[key_name(usr)] used secret [href_list["secretsadmin"]]") + if(ok) + to_chat(world, text("A secret has been activated by []!", usr.key)) + + else if(href_list["secretscoder"]) + if(!check_rights(R_DEBUG)) return + + switch(href_list["secretscoder"]) + if("spawn_objects") + var/dat = "Admin Log
    " + for(var/l in GLOB.admin_log) + dat += "
  • [l]
  • " + if(!GLOB.admin_log.len) + dat += "No-one has done anything this round!" + usr << browse(dat, "window=admin_log") + if("maint_ACCESS_BRIG") + for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) + if(ACCESS_MAINT_TUNNELS in M.req_access) + M.req_access = list(ACCESS_BRIG) + message_admins("[key_name_admin(usr)] made all maint doors brig access-only.") + if("maint_access_engiebrig") + for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) + if(ACCESS_MAINT_TUNNELS in M.req_access) + M.req_access = list() + M.req_one_access = list(ACCESS_BRIG,ACCESS_ENGINE) + message_admins("[key_name_admin(usr)] made all maint doors engineering and brig access-only.") + if("infinite_sec") + var/datum/job/J = SSjobs.GetJob("Security Officer") + if(!J) return + J.total_positions = -1 + J.spawn_positions = -1 + message_admins("[key_name_admin(usr)] has removed the cap on security officers.") + + else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here + src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster. + src.access_news_network() + + else if(href_list["ac_set_channel_name"]) + src.admincaster_feed_channel.channel_name = strip_html_simple(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", "")) + while(findtext(src.admincaster_feed_channel.channel_name," ") == 1) + src.admincaster_feed_channel.channel_name = copytext(src.admincaster_feed_channel.channel_name,2,length(src.admincaster_feed_channel.channel_name)+1) + src.access_news_network() + + else if(href_list["ac_set_channel_lock"]) + src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked + src.access_news_network() + + else if(href_list["ac_submit_new_channel"]) + var/check = 0 + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + check = 1 + break + if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check ) + src.admincaster_screen=7 + else + var/choice = alert("Please confirm Feed channel creation","Network Channel Handler","Confirm","Cancel") + if(choice=="Confirm") + var/datum/feed_channel/newChannel = new /datum/feed_channel + newChannel.channel_name = src.admincaster_feed_channel.channel_name + newChannel.author = src.admincaster_signature + newChannel.locked = src.admincaster_feed_channel.locked + newChannel.is_admin_channel = 1 + feedback_inc("newscaster_channels",1) + GLOB.news_network.network_channels += newChannel //Adding channel to the global network + log_admin("[key_name_admin(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!") + src.admincaster_screen=5 + src.access_news_network() + + else if(href_list["ac_set_channel_receiving"]) + var/list/available_channels = list() + for(var/datum/feed_channel/F in GLOB.news_network.network_channels) + available_channels += F.channel_name + src.admincaster_feed_channel.channel_name = adminscrub(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels ) + src.access_news_network() + + else if(href_list["ac_set_new_message"]) + src.admincaster_feed_message.body = adminscrub(input(usr, "Write your Feed story", "Network Channel Handler", "")) + while(findtext(src.admincaster_feed_message.body," ") == 1) + src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) + src.access_news_network() + + else if(href_list["ac_submit_new_message"]) + if(src.admincaster_feed_message.body =="" || src.admincaster_feed_message.body =="\[REDACTED\]" || src.admincaster_feed_channel.channel_name == "" ) + src.admincaster_screen = 6 + else + var/datum/feed_message/newMsg = new /datum/feed_message + newMsg.author = src.admincaster_signature + newMsg.body = src.admincaster_feed_message.body + newMsg.is_admin_message = 1 + feedback_inc("newscaster_stories",1) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + FC.messages += newMsg //Adding message to the network's appropriate feed_channel + break + src.admincaster_screen=4 + + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) + NEWSCASTER.newsAlert(src.admincaster_feed_channel.channel_name) + + log_admin("[key_name_admin(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!") + src.access_news_network() + + else if(href_list["ac_create_channel"]) + src.admincaster_screen=2 + src.access_news_network() + + else if(href_list["ac_create_feed_story"]) + src.admincaster_screen=3 + src.access_news_network() + + else if(href_list["ac_menu_censor_story"]) + src.admincaster_screen=10 + src.access_news_network() + + else if(href_list["ac_menu_censor_channel"]) + src.admincaster_screen=11 + src.access_news_network() + + else if(href_list["ac_menu_wanted"]) + var/already_wanted = 0 + if(GLOB.news_network.wanted_issue) + already_wanted = 1 + + if(already_wanted) + src.admincaster_feed_message.author = GLOB.news_network.wanted_issue.author + src.admincaster_feed_message.body = GLOB.news_network.wanted_issue.body + src.admincaster_screen = 14 + src.access_news_network() + + else if(href_list["ac_set_wanted_name"]) + src.admincaster_feed_message.author = adminscrub(input(usr, "Provide the name of the Wanted person", "Network Security Handler", "")) + while(findtext(src.admincaster_feed_message.author," ") == 1) + src.admincaster_feed_message.author = copytext(admincaster_feed_message.author,2,length(admincaster_feed_message.author)+1) + src.access_news_network() + + else if(href_list["ac_set_wanted_desc"]) + src.admincaster_feed_message.body = adminscrub(input(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", "")) + while(findtext(src.admincaster_feed_message.body," ") == 1) + src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) + src.access_news_network() + + else if(href_list["ac_submit_wanted"]) + var/input_param = text2num(href_list["ac_submit_wanted"]) + if(src.admincaster_feed_message.author == "" || src.admincaster_feed_message.body == "") + src.admincaster_screen = 16 + else + var/choice = alert("Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler","Confirm","Cancel") + if(choice=="Confirm") + if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below + var/datum/feed_message/WANTED = new /datum/feed_message + WANTED.author = src.admincaster_feed_message.author //Wanted name + WANTED.body = src.admincaster_feed_message.body //Wanted desc + WANTED.backup_author = src.admincaster_signature //Submitted by + WANTED.is_admin_message = 1 + GLOB.news_network.wanted_issue = WANTED + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) + NEWSCASTER.newsAlert() + NEWSCASTER.update_icon() + src.admincaster_screen = 15 + else + GLOB.news_network.wanted_issue.author = src.admincaster_feed_message.author + GLOB.news_network.wanted_issue.body = src.admincaster_feed_message.body + GLOB.news_network.wanted_issue.backup_author = src.admincaster_feed_message.backup_author + src.admincaster_screen = 19 + log_admin("[key_name_admin(usr)] issued a Station-wide Wanted Notification for [src.admincaster_feed_message.author]!") + src.access_news_network() + + else if(href_list["ac_cancel_wanted"]) + var/choice = alert("Please confirm Wanted Issue removal","Network Security Handler","Confirm","Cancel") + if(choice=="Confirm") + GLOB.news_network.wanted_issue = null + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) + NEWSCASTER.update_icon() + src.admincaster_screen=17 + src.access_news_network() + + else if(href_list["ac_censor_channel_author"]) + var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) + if(FC.author != "\[REDACTED\]") + FC.backup_author = FC.author + FC.author = "\[REDACTED\]" + else + FC.author = FC.backup_author + src.access_news_network() + + else if(href_list["ac_censor_channel_story_author"]) + var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) + if(MSG.author != "\[REDACTED\]") + MSG.backup_author = MSG.author + MSG.author = "\[REDACTED\]" + else + MSG.author = MSG.backup_author + src.access_news_network() + + else if(href_list["ac_censor_channel_story_body"]) + var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) + if(MSG.body != "\[REDACTED\]") + MSG.backup_body = MSG.body + MSG.body = "\[REDACTED\]" + else + MSG.body = MSG.backup_body + src.access_news_network() + + else if(href_list["ac_pick_d_notice"]) + var/datum/feed_channel/FC = locate(href_list["ac_pick_d_notice"]) + src.admincaster_feed_channel = FC + src.admincaster_screen=13 + src.access_news_network() + + else if(href_list["ac_toggle_d_notice"]) + var/datum/feed_channel/FC = locate(href_list["ac_toggle_d_notice"]) + FC.censored = !FC.censored + src.access_news_network() + + else if(href_list["ac_view"]) + src.admincaster_screen=1 + src.access_news_network() + + else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~ + src.admincaster_screen = text2num(href_list["ac_setScreen"]) + if(src.admincaster_screen == 0) + if(src.admincaster_feed_channel) + src.admincaster_feed_channel = new /datum/feed_channel + if(src.admincaster_feed_message) + src.admincaster_feed_message = new /datum/feed_message + src.access_news_network() + + else if(href_list["ac_show_channel"]) + var/datum/feed_channel/FC = locate(href_list["ac_show_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 9 + src.access_news_network() + + else if(href_list["ac_pick_censor_channel"]) + var/datum/feed_channel/FC = locate(href_list["ac_pick_censor_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 12 + src.access_news_network() + + else if(href_list["ac_refresh"]) + src.access_news_network() + + else if(href_list["ac_set_signature"]) + src.admincaster_signature = adminscrub(input(usr, "Provide your desired signature", "Network Identity Handler", "")) + src.access_news_network() + + if(href_list["secretsmenu"]) + switch(href_list["secretsmenu"]) + if("tab") + current_tab = text2num(href_list["tab"]) + Secrets(usr) + return 1 + + else if(href_list["viewruntime"]) + var/datum/ErrorViewer/error_viewer = locateUID(href_list["viewruntime"]) + if(!istype(error_viewer)) + to_chat(usr, "That runtime viewer no longer exists.") + return + if(href_list["viewruntime_backto"]) + error_viewer.showTo(usr, locateUID(href_list["viewruntime_backto"]), href_list["viewruntime_linear"]) + else + error_viewer.showTo(usr, null, href_list["viewruntime_linear"]) + + else if(href_list["add_station_goal"]) + if(!check_rights(R_EVENT)) + return + var/list/type_choices = typesof(/datum/station_goal) + var/picked = input("Choose goal type") in type_choices|null + if(!picked) + return + var/datum/station_goal/G = new picked() + if(picked == /datum/station_goal) + var/newname = clean_input("Enter goal name:") + if(!newname) + return + G.name = newname + var/description = input("Enter [command_name()] message contents:") as message|null + if(!description) + return + G.report_message = description + message_admins("[key_name_admin(usr)] created \"[G.name]\" station goal.") + SSticker.mode.station_goals += G + modify_goals() + + else if(href_list["showdetails"]) + if(!check_rights(R_ADMIN)) + return + var/text = html_decode(href_list["showdetails"]) + usr << browse("Details[replacetext(text, "\n", "
    ")]
    ", + "window=show_details;size=500x200") + + // Library stuff + else if(href_list["library_book_id"]) + var/isbn = sanitizeSQL(href_list["library_book_id"]) + + if(href_list["view_library_book"]) + var/DBQuery/query_view_book = GLOB.dbcon.NewQuery("SELECT content, title FROM [format_table_name("library")] WHERE id=[isbn]") + if(!query_view_book.Execute()) + var/err = query_view_book.ErrorMsg() + log_game("SQL ERROR viewing book. Error : \[[err]\]\n") + return + + var/content = "" + var/title = "" + while(query_view_book.NextRow()) + content = query_view_book.item[1] + title = html_encode(query_view_book.item[2]) + + var/dat = "
    "
    +			dat += "[html_encode(html_to_pencode(content))]"
    +			dat += "
    " + + var/datum/browser/popup = new(usr, "admin_view_book", "[title]", 700, 400) + popup.set_content(dat) + popup.open(0) + + log_admin("[key_name(usr)] has viewed the book [isbn].") + message_admins("[key_name_admin(usr)] has viewed the book [isbn].") + return + + else if(href_list["unflag_library_book"]) + var/DBQuery/query_unflag_book = GLOB.dbcon.NewQuery("UPDATE [format_table_name("library")] SET flagged = 0 WHERE id=[isbn]") + if(!query_unflag_book.Execute()) + var/err = query_unflag_book.ErrorMsg() + log_game("SQL ERROR unflagging book. Error : \[[err]\]\n") + return + + log_admin("[key_name(usr)] has unflagged the book [isbn].") + message_admins("[key_name_admin(usr)] has unflagged the book [isbn].") + + else if(href_list["delete_library_book"]) + var/DBQuery/query_delbook = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[isbn]") + if(!query_delbook.Execute()) + var/err = query_delbook.ErrorMsg() + log_game("SQL ERROR deleting book. Error : \[[err]\]\n") + return + + log_admin("[key_name(usr)] has deleted the book [isbn].") + message_admins("[key_name_admin(usr)] has deleted the book [isbn].") + + // Refresh the page + src.view_flagged_books() + + // Force unlink a discord key + // TODO: Delete this + else if(href_list["force_discord_unlink"]) + if(!check_rights(R_ADMIN)) + return + var/target_ckey = href_list["force_discord_unlink"] + var/DBQuery/admin_unlink_discord_id = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("discord")] WHERE ckey = '[target_ckey]'") + if(!admin_unlink_discord_id.Execute()) + var/err = admin_unlink_discord_id.ErrorMsg() + log_game("SQL ERROR while admin-unlinking discord account. Error : \[[err]\]\n") + return + to_chat(src, "Successfully forcefully unlinked discord account from [target_ckey]") + message_admins("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") + log_admin("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") + + else if(href_list["create_outfit_finalize"]) + if(!check_rights(R_EVENT)) + return + create_outfit_finalize(usr,href_list) + else if(href_list["load_outfit"]) + if(!check_rights(R_EVENT)) + return + load_outfit(usr) + else if(href_list["create_outfit_menu"]) + if(!check_rights(R_EVENT)) + return + create_outfit(usr) + else if(href_list["delete_outfit"]) + if(!check_rights(R_EVENT)) + return + var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits + delete_outfit(usr,O) + else if(href_list["save_outfit"]) + if(!check_rights(R_EVENT)) + return + var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits + save_outfit(usr,O) + +/client/proc/create_eventmob_for(var/mob/living/carbon/human/H, var/killthem = 0) + if(!check_rights(R_EVENT)) + return + var/admin_outfits = subtypesof(/datum/outfit/admin) + var/hunter_outfits = list() + for(var/type in admin_outfits) + var/datum/outfit/admin/O = type + hunter_outfits[initial(O.name)] = type + var/dresscode = input("Select type", "Contracted Agents") as null|anything in hunter_outfits + if(isnull(dresscode)) + return + var/datum/outfit/O = hunter_outfits[dresscode] + message_admins("[key_name_admin(mob)] is sending a ([dresscode]) to [killthem ? "assassinate" : "protect"] [key_name_admin(H)]...") + var/list/candidates = pollCandidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, 1) + if(!candidates.len) + to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.") + return + var/mob/C = pick(candidates) + var/key_of_hunter = C.key + if(!key_of_hunter) + to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.") + return + var/datum/mind/hunter_mind = new /datum/mind(key_of_hunter) + hunter_mind.active = 1 + var/mob/living/carbon/human/hunter_mob = new /mob/living/carbon/human(pick(GLOB.latejoin)) + hunter_mind.transfer_to(hunter_mob) + hunter_mob.equipOutfit(O, FALSE) + var/obj/item/pinpointer/advpinpointer/N = new /obj/item/pinpointer/advpinpointer(hunter_mob) + hunter_mob.equip_to_slot_or_del(N, slot_in_backpack) + N.active = 1 + N.mode = 2 + N.target = H + N.point_at(N.target) + N.modelocked = TRUE + if(!locate(/obj/item/implant/dust, hunter_mob)) + var/obj/item/implant/dust/D = new /obj/item/implant/dust(hunter_mob) + D.implant(hunter_mob) + if(killthem) + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = hunter_mind + kill_objective.target = H.mind + kill_objective.explanation_text = "Kill [H.real_name], the [H.mind.assigned_role]." + hunter_mind.objectives += kill_objective + else + var/datum/objective/protect/protect_objective = new + protect_objective.owner = hunter_mind + protect_objective.target = H.mind + protect_objective.explanation_text = "Protect [H.real_name], the [H.mind.assigned_role]." + hunter_mind.objectives += protect_objective + SSticker.mode.traitors |= hunter_mob.mind + to_chat(hunter_mob, "ATTENTION: You are now on a mission!") + to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]. "); + if(killthem) + to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived."); + hunter_mob.mind.special_role = SPECIAL_ROLE_TRAITOR + var/datum/atom_hud/antag/tatorhud = GLOB.huds[ANTAG_HUD_TRAITOR] + tatorhud.join_hud(hunter_mob) + set_antag_hud(hunter_mob, "hudsyndicate") + +/proc/admin_jump_link(var/atom/target) + if(!target) return + // The way admin jump links handle their src is weirdly inconsistent... + + . = ADMIN_FLW(target,"FLW") + if(isAI(target)) // AI core/eye follow links + var/mob/living/silicon/ai/A = target + if(A.client && A.eyeobj) // No point following clientless AI eyes + . += "|[ADMIN_FLW(A.eyeobj,"EYE")]" + else if(istype(target, /mob/dead/observer)) + var/mob/dead/observer/O = target + if(O.mind && O.mind.current) + . += "|[ADMIN_FLW(O.mind.current,"BDY")]" diff --git a/code/modules/admin/verbs/BrokenInhands.dm b/code/modules/admin/verbs/BrokenInhands.dm index e0fb1a46913..75e051b1ff0 100644 --- a/code/modules/admin/verbs/BrokenInhands.dm +++ b/code/modules/admin/verbs/BrokenInhands.dm @@ -1,36 +1,36 @@ -/proc/getbrokeninhands() - set name = "Broken Sprite List" - set category = "Debug" - - var/text - for(var/A in typesof(/obj/item)) - var/obj/item/O = new A( locate(1,1,1) ) - if(!O) continue - var/icon/IL = new(O.lefthand_file) - var/list/Lstates = IL.IconStates() - var/icon/IR = new(O.righthand_file) - var/list/Rstates = IR.IconStates() - var/icon/J = new(O.icon) - var/list/istates = J.IconStates() - if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" - if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" - - - if(O.icon_state) - if(!istates.Find(O.icon_state)) - text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" - //if(O.item_state) - // if(!istates.Find(O.item_state)) - // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" - //text+="\n" - qdel(O) - if(text) - var/F = file("broken_hand_icons.txt") - fdel(F) - to_chat(F, text) - to_chat(world, "Completed and written to [F]") - +/proc/getbrokeninhands() + set name = "Broken Sprite List" + set category = "Debug" + + var/text + for(var/A in typesof(/obj/item)) + var/obj/item/O = new A( locate(1,1,1) ) + if(!O) continue + var/icon/IL = new(O.lefthand_file) + var/list/Lstates = IL.IconStates() + var/icon/IR = new(O.righthand_file) + var/list/Rstates = IR.IconStates() + var/icon/J = new(O.icon) + var/list/istates = J.IconStates() + if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" + if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" + + + if(O.icon_state) + if(!istates.Find(O.icon_state)) + text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" + //if(O.item_state) + // if(!istates.Find(O.item_state)) + // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" + //text+="\n" + qdel(O) + if(text) + var/F = file("broken_hand_icons.txt") + fdel(F) + to_chat(F, text) + to_chat(world, "Completed and written to [F]") + diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 76e22259893..8ac9c4fd2b0 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -1,187 +1,187 @@ - - -//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") - -/client/verb/adminhelp() - set category = "Admin" - set name = "Adminhelp" - - //handle muting and automuting - if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).") - return - - adminhelped = 1 //Determines if they get the message to reply by clicking the name. - - var/msg - var/list/type = list("Mentorhelp","Adminhelp") - var/selected_type = input("Pick a category.", "Admin Help", null, null) as null|anything in type - if(selected_type) - msg = clean_input("Please enter your message.", "Admin Help", null) - - //clean the input msg - if(!msg) - return - - if(handle_spam_prevention(msg, MUTE_ADMINHELP, OOC_COOLDOWN)) - return - - msg = sanitize_simple(copytext(msg,1,MAX_MESSAGE_LEN)) - if(!msg) return - var/original_msg = msg - - //explode the input msg into a list - var/list/msglist = splittext(msg, " ") - - //generate keywords lookup - var/list/surnames = list() - var/list/forenames = list() - var/list/ckeys = list() - for(var/mob/M in GLOB.mob_list) - var/list/indexing = list(M.real_name, M.name) - if(M.mind) indexing += M.mind.name - - for(var/string in indexing) - var/list/L = splittext(string, " ") - var/surname_found = 0 - //surnames - for(var/i=L.len, i>=1, i--) - var/word = ckey(L[i]) - if(word) - surnames[word] = M - surname_found = i - break - //forenames - for(var/i=1, iTICKET) [ai_found ? "(CL)" : ""] (TAKE) (RESOLVE) [isMhelp ? "" : "(AUTO)"] :
    [span][msg]
    " - if(isMhelp) - //Open a new adminticket and inform the user. - SSmentor_tickets.newTicket(src, prunedmsg, msg) - for(var/client/X in mentorholders + modholders + adminholders) - if(X.prefs.sound & SOUND_MENTORHELP) - X << 'sound/effects/adminhelp.ogg' - to_chat(X, msg) - else //Ahelp - //Open a new adminticket and inform the user. - SStickets.newTicket(src, prunedmsg, msg) - for(var/client/X in modholders + adminholders) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - window_flash(X) - to_chat(X, msg) - - - - //show it to the person adminhelping too - to_chat(src, "[selected_type]
    : [original_msg]") - - var/admin_number_present = adminholders.len - admin_number_afk - log_admin("[selected_type]: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.") - if(admin_number_present <= 0) - if(!admin_number_afk) - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!No admins online!!") - else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!All admins AFK ([admin_number_afk])!!") - else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg]") - feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN) - var/admin_number_total = 0 //Total number of admins - var/admin_number_afk = 0 //Holds the number of admins who are afk - var/admin_number_ignored = 0 //Holds the number of admins without +BAN (so admins who are not really admins) - var/admin_number_decrease = 0 //Holds the number of admins with are afk, ignored or both - for(var/client/X in GLOB.admins) - admin_number_total++; - var/invalid = 0 - if(requiredflags != 0 && !check_rights_for(X, requiredflags)) - admin_number_ignored++ - invalid = 1 - if(X.is_afk()) - admin_number_afk++ - invalid = 1 - if(X.holder.fakekey) - admin_number_ignored++ - invalid = 1 - if(invalid) - admin_number_decrease++ - var/admin_number_present = admin_number_total - admin_number_decrease //Number of admins who are neither afk nor invalid - if(admin_number_present <= 0) - if(!admin_number_afk && !admin_number_ignored) - send2irc(source, "[msg] - No admins online") - else - send2irc(source, "[msg] - All admins AFK ([admin_number_afk]/[admin_number_total]) or skipped ([admin_number_ignored]/[admin_number_total])") - return admin_number_present + + +//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE! +GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","monkey","alien","as")) + +/client/verb/adminhelp() + set category = "Admin" + set name = "Adminhelp" + + //handle muting and automuting + if(prefs.muted & MUTE_ADMINHELP) + to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).") + return + + adminhelped = 1 //Determines if they get the message to reply by clicking the name. + + var/msg + var/list/type = list("Mentorhelp","Adminhelp") + var/selected_type = input("Pick a category.", "Admin Help", null, null) as null|anything in type + if(selected_type) + msg = clean_input("Please enter your message.", "Admin Help", null) + + //clean the input msg + if(!msg) + return + + if(handle_spam_prevention(msg, MUTE_ADMINHELP, OOC_COOLDOWN)) + return + + msg = sanitize_simple(copytext(msg,1,MAX_MESSAGE_LEN)) + if(!msg) return + var/original_msg = msg + + //explode the input msg into a list + var/list/msglist = splittext(msg, " ") + + //generate keywords lookup + var/list/surnames = list() + var/list/forenames = list() + var/list/ckeys = list() + for(var/mob/M in GLOB.mob_list) + var/list/indexing = list(M.real_name, M.name) + if(M.mind) indexing += M.mind.name + + for(var/string in indexing) + var/list/L = splittext(string, " ") + var/surname_found = 0 + //surnames + for(var/i=L.len, i>=1, i--) + var/word = ckey(L[i]) + if(word) + surnames[word] = M + surname_found = i + break + //forenames + for(var/i=1, iTICKET) [ai_found ? "(CL)" : ""] (TAKE) (RESOLVE) [isMhelp ? "" : "(AUTO)"] : [span][msg]" + if(isMhelp) + //Open a new adminticket and inform the user. + SSmentor_tickets.newTicket(src, prunedmsg, msg) + for(var/client/X in mentorholders + modholders + adminholders) + if(X.prefs.sound & SOUND_MENTORHELP) + X << 'sound/effects/adminhelp.ogg' + to_chat(X, msg) + else //Ahelp + //Open a new adminticket and inform the user. + SStickets.newTicket(src, prunedmsg, msg) + for(var/client/X in modholders + adminholders) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + window_flash(X) + to_chat(X, msg) + + + + //show it to the person adminhelping too + to_chat(src, "[selected_type]
    : [original_msg]") + + var/admin_number_present = adminholders.len - admin_number_afk + log_admin("[selected_type]: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.") + if(admin_number_present <= 0) + if(!admin_number_afk) + send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!No admins online!!") + else + send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!All admins AFK ([admin_number_afk])!!") + else + send2adminirc("[selected_type] from [key_name(src)]: [original_msg]") + feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN) + var/admin_number_total = 0 //Total number of admins + var/admin_number_afk = 0 //Holds the number of admins who are afk + var/admin_number_ignored = 0 //Holds the number of admins without +BAN (so admins who are not really admins) + var/admin_number_decrease = 0 //Holds the number of admins with are afk, ignored or both + for(var/client/X in GLOB.admins) + admin_number_total++; + var/invalid = 0 + if(requiredflags != 0 && !check_rights_for(X, requiredflags)) + admin_number_ignored++ + invalid = 1 + if(X.is_afk()) + admin_number_afk++ + invalid = 1 + if(X.holder.fakekey) + admin_number_ignored++ + invalid = 1 + if(invalid) + admin_number_decrease++ + var/admin_number_present = admin_number_total - admin_number_decrease //Number of admins who are neither afk nor invalid + if(admin_number_present <= 0) + if(!admin_number_afk && !admin_number_ignored) + send2irc(source, "[msg] - No admins online") + else + send2irc(source, "[msg] - All admins AFK ([admin_number_afk]/[admin_number_total]) or skipped ([admin_number_ignored]/[admin_number_total])") + return admin_number_present diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index 16a1c14694f..0c1a8c5f14f 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -1,161 +1,161 @@ -/client/proc/Jump(area/A in return_sorted_areas()) - set name = "Jump to Area" - set desc = "Area to jump to" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - if(!A) - return - - var/list/turfs = list() - for(var/turf/T in A) - if(T.density) - continue - if(locate(/obj/structure/grille, T)) // Quick check to not spawn in windows - continue - turfs.Add(T) - - var/turf/T = pick_n_take(turfs) - if(!T) - to_chat(src, "Nowhere to jump to!") - return - - admin_forcemove(usr, T) - log_admin("[key_name(usr)] jumped to [A]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [A]") - feedback_add_details("admin_verb","JA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/jumptoturf(var/turf/T in world) - set name = "Jump to Turf" - set category = null - - if(!check_rights(R_ADMIN)) - return - - log_admin("[key_name(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]", 1) - admin_forcemove(usr, T) - feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/jumptomob(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Jump to Mob" - - if(!check_rights(R_ADMIN)) - return - - log_admin("[key_name(usr)] jumped to [key_name(M)]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) - if(src.mob) - var/mob/A = src.mob - var/turf/T = get_turf(M) - if(T && isturf(T)) - feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - admin_forcemove(A, M.loc) - else - to_chat(A, "This mob is not located in the game world.") - -/client/proc/jumptocoord(tx as num, ty as num, tz as num) - set category = "Admin" - set name = "Jump to Coordinate" - - if(!check_rights(R_ADMIN)) - return - - var/turf/T = locate(tx, ty, tz) - if(T) - admin_forcemove(usr, T) - if(isobserver(usr)) - var/mob/dead/observer/O = usr - O.ManualFollow(T) - feedback_add_details("admin_verb","JC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]") - -/client/proc/jumptokey() - set category = "Admin" - set name = "Jump to Key" - - if(!check_rights(R_ADMIN)) - return - - var/list/keys = list() - for(var/mob/M in GLOB.player_list) - keys += M.client - var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) - if(!selection) - to_chat(src, "No keys found.") - return - var/mob/M = selection:mob - log_admin("[key_name(usr)] jumped to [key_name(M)]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) - - admin_forcemove(usr, M.loc) - - feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/Getmob(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Get Mob" - set desc = "Mob to teleport" - - if(!check_rights(R_ADMIN)) - return - - log_admin("[key_name(usr)] teleported [key_name(M)]") - message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1) - admin_forcemove(M, get_turf(usr)) - feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/Getkey() - set category = "Admin" - set name = "Get Key" - set desc = "Key to teleport" - - if(!check_rights(R_ADMIN)) - return - - var/list/keys = list() - for(var/mob/M in GLOB.player_list) - keys += M.client - var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) - if(!selection) - return - var/mob/M = selection:mob - - if(!M) - return - log_admin("[key_name(usr)] teleported [key_name(M)]") - message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1) - if(M) - admin_forcemove(M, get_turf(usr)) - admin_forcemove(usr, M.loc) - feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/sendmob(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Send Mob" - - if(!check_rights(R_ADMIN)) - return - - var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas() - if(A) - admin_forcemove(M, pick(get_area_turfs(A))) - feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] teleported [key_name(M)] to [A]") - message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)] to [A]", 1) - -/proc/admin_forcemove(mob/mover, atom/newloc) - mover.forceMove(newloc) - mover.on_forcemove(newloc) - -/mob/proc/on_forcemove(atom/newloc) - return +/client/proc/Jump(area/A in return_sorted_areas()) + set name = "Jump to Area" + set desc = "Area to jump to" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + if(!A) + return + + var/list/turfs = list() + for(var/turf/T in A) + if(T.density) + continue + if(locate(/obj/structure/grille, T)) // Quick check to not spawn in windows + continue + turfs.Add(T) + + var/turf/T = pick_n_take(turfs) + if(!T) + to_chat(src, "Nowhere to jump to!") + return + + admin_forcemove(usr, T) + log_admin("[key_name(usr)] jumped to [A]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [A]") + feedback_add_details("admin_verb","JA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/jumptoturf(var/turf/T in world) + set name = "Jump to Turf" + set category = null + + if(!check_rights(R_ADMIN)) + return + + log_admin("[key_name(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]", 1) + admin_forcemove(usr, T) + feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/jumptomob(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Jump to Mob" + + if(!check_rights(R_ADMIN)) + return + + log_admin("[key_name(usr)] jumped to [key_name(M)]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) + if(src.mob) + var/mob/A = src.mob + var/turf/T = get_turf(M) + if(T && isturf(T)) + feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + admin_forcemove(A, M.loc) + else + to_chat(A, "This mob is not located in the game world.") + +/client/proc/jumptocoord(tx as num, ty as num, tz as num) + set category = "Admin" + set name = "Jump to Coordinate" + + if(!check_rights(R_ADMIN)) + return + + var/turf/T = locate(tx, ty, tz) + if(T) + admin_forcemove(usr, T) + if(isobserver(usr)) + var/mob/dead/observer/O = usr + O.ManualFollow(T) + feedback_add_details("admin_verb","JC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]") + +/client/proc/jumptokey() + set category = "Admin" + set name = "Jump to Key" + + if(!check_rights(R_ADMIN)) + return + + var/list/keys = list() + for(var/mob/M in GLOB.player_list) + keys += M.client + var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) + if(!selection) + to_chat(src, "No keys found.") + return + var/mob/M = selection:mob + log_admin("[key_name(usr)] jumped to [key_name(M)]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) + + admin_forcemove(usr, M.loc) + + feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/Getmob(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Get Mob" + set desc = "Mob to teleport" + + if(!check_rights(R_ADMIN)) + return + + log_admin("[key_name(usr)] teleported [key_name(M)]") + message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1) + admin_forcemove(M, get_turf(usr)) + feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/Getkey() + set category = "Admin" + set name = "Get Key" + set desc = "Key to teleport" + + if(!check_rights(R_ADMIN)) + return + + var/list/keys = list() + for(var/mob/M in GLOB.player_list) + keys += M.client + var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) + if(!selection) + return + var/mob/M = selection:mob + + if(!M) + return + log_admin("[key_name(usr)] teleported [key_name(M)]") + message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1) + if(M) + admin_forcemove(M, get_turf(usr)) + admin_forcemove(usr, M.loc) + feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/sendmob(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Send Mob" + + if(!check_rights(R_ADMIN)) + return + + var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas() + if(A) + admin_forcemove(M, pick(get_area_turfs(A))) + feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] teleported [key_name(M)] to [A]") + message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)] to [A]", 1) + +/proc/admin_forcemove(mob/mover, atom/newloc) + mover.forceMove(newloc) + mover.on_forcemove(newloc) + +/mob/proc/on_forcemove(atom/newloc) + return diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 32355cd17fd..164d499e04a 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -261,7 +261,7 @@ /datum/pm_tracker var/current_title = "" var/open = FALSE - var/list/pms = list() + var/list/datum/pm_convo/pms = list() var/show_archived = FALSE var/window_id = "pms_window" diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 6f6b3484924..a0e8b4b9088 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -1,82 +1,82 @@ -/client/proc/cmd_admin_say(msg as text) - set category = "Admin" - set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite - set hidden = 1 - if(!check_rights(R_ADMIN)) return - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - if(!msg) return - - log_adminsay(msg, src) - - if(check_rights(R_ADMIN,0)) - for(var/client/C in GLOB.admins) - if(R_ADMIN & C.holder.rights) - msg = "[msg]" - to_chat(C, "ADMIN: [key_name(usr, 1)] ([admin_jump_link(mob)]): [msg]") - - feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_admin_say() - var/msg = input(src, null, "asay \"text\"") as text|null - cmd_admin_say(msg) - -/client/proc/cmd_mentor_say(msg as text) - set category = "Admin" - set name = "Msay" - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD|R_MENTOR)) - return - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - log_mentorsay(msg, src) - - if(!msg) - return - - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, C.mob)) - var/display_name = key - if(holder.fakekey) - if(C.holder && C.holder.rights & R_ADMIN) - display_name = "[holder.fakekey]/([key])" - else - display_name = holder.fakekey - msg = "[msg]" - to_chat(C, "MENTOR: [display_name] ([admin_jump_link(mob)]): [msg]") - - feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_mentor_chat() - set category = "Server" - set name = "Toggle Mentor Chat" - set desc = "Toggle whether mentors have access to the msay command" - - if(!check_rights(R_ADMIN)) - return - - var/enabling - var/msay = /client/proc/cmd_mentor_say - - if(msay in admin_verbs_mentor) - enabling = FALSE - admin_verbs_mentor -= msay - else - enabling = TRUE - admin_verbs_mentor += msay - - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN|R_MOD, 0, C.mob)) - continue - if(!check_rights(R_MENTOR, 0, C.mob)) - continue - if(enabling) - C.verbs += msay - to_chat(C, "Mentor chat has been enabled. Use 'msay' to speak in it.") - else - C.verbs -= msay - to_chat(C, "Mentor chat has been disabled.") - - admin_log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].") - feedback_add_details("admin_verb", "TMC") +/client/proc/cmd_admin_say(msg as text) + set category = "Admin" + set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite + set hidden = 1 + if(!check_rights(R_ADMIN)) return + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + if(!msg) return + + log_adminsay(msg, src) + + if(check_rights(R_ADMIN,0)) + for(var/client/C in GLOB.admins) + if(R_ADMIN & C.holder.rights) + msg = "[msg]" + to_chat(C, "ADMIN: [key_name(usr, 1)] ([admin_jump_link(mob)]): [msg]") + + feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_admin_say() + var/msg = input(src, null, "asay \"text\"") as text|null + cmd_admin_say(msg) + +/client/proc/cmd_mentor_say(msg as text) + set category = "Admin" + set name = "Msay" + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD|R_MENTOR)) + return + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + log_mentorsay(msg, src) + + if(!msg) + return + + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, C.mob)) + var/display_name = key + if(holder.fakekey) + if(C.holder && C.holder.rights & R_ADMIN) + display_name = "[holder.fakekey]/([key])" + else + display_name = holder.fakekey + msg = "[msg]" + to_chat(C, "MENTOR: [display_name] ([admin_jump_link(mob)]): [msg]") + + feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_mentor_chat() + set category = "Server" + set name = "Toggle Mentor Chat" + set desc = "Toggle whether mentors have access to the msay command" + + if(!check_rights(R_ADMIN)) + return + + var/enabling + var/msay = /client/proc/cmd_mentor_say + + if(msay in GLOB.admin_verbs_mentor) + enabling = FALSE + GLOB.admin_verbs_mentor -= msay + else + enabling = TRUE + GLOB.admin_verbs_mentor += msay + + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD, 0, C.mob)) + continue + if(!check_rights(R_MENTOR, 0, C.mob)) + continue + if(enabling) + C.verbs += msay + to_chat(C, "Mentor chat has been enabled. Use 'msay' to speak in it.") + else + C.verbs -= msay + to_chat(C, "Mentor chat has been disabled.") + + admin_log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].") + feedback_add_details("admin_verb", "TMC") diff --git a/code/modules/admin/verbs/alt_check.dm b/code/modules/admin/verbs/alt_check.dm index d5118d4eb8f..c8060f22036 100644 --- a/code/modules/admin/verbs/alt_check.dm +++ b/code/modules/admin/verbs/alt_check.dm @@ -6,7 +6,7 @@
    Additionally make an attempt to introduce new players to the server
    "} - if(dbcon.IsConnected()) + if(GLOB.dbcon.IsConnected()) for(var/client/C in GLOB.clients) dat += "

    [C.ckey] (Player Age: [C.player_age]) - [C.computer_id] / [C.address]
    " if(C.related_accounts_cid.len) diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm index de281d34e68..087a90eebe6 100644 --- a/code/modules/admin/verbs/atmosdebug.dm +++ b/code/modules/admin/verbs/atmosdebug.dm @@ -1,63 +1,63 @@ -/client/proc/atmosscan() - set category = "Mapping" - set name = "Check Piping" - set background = 1 - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - if(alert("WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", "No", "Yes") == "No") - return - - to_chat(usr, "Checking for disconnected pipes...") - //all plumbing - yes, some things might get stated twice, doesn't matter. - for(var/obj/machinery/atmospherics/plumbing in world) - if(plumbing.nodealert) - to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") - - //Manifolds - for(var/obj/machinery/atmospherics/pipe/manifold/pipe in world) - if(!pipe.node1 || !pipe.node2 || !pipe.node3) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - //Pipes - for(var/obj/machinery/atmospherics/pipe/simple/pipe in world) - if(!pipe.node1 || !pipe.node2) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - to_chat(usr, "Checking for overlapping pipes...") - for(var/turf/T in world) - for(var/dir in cardinal) - var/list/check = list(0, 0, 0) - var/done = 0 - for(var/obj/machinery/atmospherics/pipe in T) - if(dir & pipe.initialize_directions) - for(var/ct in pipe.connect_types) - check[ct]++ - if(check[ct] > 1) - to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") - done = 1 - break - if(done) - break - to_chat(usr, "Done") - -/client/proc/powerdebug() - set category = "Mapping" - set name = "Check Power" - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - for(var/datum/powernet/PN in SSmachines.powernets) - if(!PN.nodes || !PN.nodes.len) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") - - if(!PN.cables || (PN.cables.len < 10)) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") +/client/proc/atmosscan() + set category = "Mapping" + set name = "Check Piping" + set background = 1 + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + if(alert("WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", "No", "Yes") == "No") + return + + to_chat(usr, "Checking for disconnected pipes...") + //all plumbing - yes, some things might get stated twice, doesn't matter. + for(var/obj/machinery/atmospherics/plumbing in world) + if(plumbing.nodealert) + to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") + + //Manifolds + for(var/obj/machinery/atmospherics/pipe/manifold/pipe in world) + if(!pipe.node1 || !pipe.node2 || !pipe.node3) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + //Pipes + for(var/obj/machinery/atmospherics/pipe/simple/pipe in world) + if(!pipe.node1 || !pipe.node2) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + to_chat(usr, "Checking for overlapping pipes...") + for(var/turf/T in world) + for(var/dir in GLOB.cardinal) + var/list/check = list(0, 0, 0) + var/done = 0 + for(var/obj/machinery/atmospherics/pipe in T) + if(dir & pipe.initialize_directions) + for(var/ct in pipe.connect_types) + check[ct]++ + if(check[ct] > 1) + to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") + done = 1 + break + if(done) + break + to_chat(usr, "Done") + +/client/proc/powerdebug() + set category = "Mapping" + set name = "Check Power" + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + for(var/datum/powernet/PN in SSmachines.powernets) + if(!PN.nodes || !PN.nodes.len) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] + to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") + + if(!PN.cables || (PN.cables.len < 10)) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] + to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") diff --git a/code/modules/admin/verbs/cinematic.dm b/code/modules/admin/verbs/cinematic.dm index 63ebd52d09b..5bfd7c65312 100644 --- a/code/modules/admin/verbs/cinematic.dm +++ b/code/modules/admin/verbs/cinematic.dm @@ -14,4 +14,4 @@ override = input(src, "mode = ?","Enter Parameter", null) as anything in list("nuclear emergency", "fake", "no override") if(0) override = input(src, "mode = ?","Enter Parameter", null) as anything in list("blob", "nuclear emergency", "AI malfunction", "no override") - SSticker.station_explosion_cinematic(parameter, override) \ No newline at end of file + SSticker.station_explosion_cinematic(parameter, override) diff --git a/code/modules/admin/verbs/custom_event.dm b/code/modules/admin/verbs/custom_event.dm index 68d3301b11e..b3d1543aedc 100644 --- a/code/modules/admin/verbs/custom_event.dm +++ b/code/modules/admin/verbs/custom_event.dm @@ -7,9 +7,9 @@ to_chat(src, "Only administrators may use this command.") return - var/input = input(usr, "Enter the description of the custom event. Be descriptive. To cancel the event, make this blank or hit cancel.", "Custom Event", custom_event_msg) as message|null + var/input = input(usr, "Enter the description of the custom event. Be descriptive. To cancel the event, make this blank or hit cancel.", "Custom Event", GLOB.custom_event_msg) as message|null if(!input || input == "") - custom_event_msg = null + GLOB.custom_event_msg = null log_admin("[key_name(usr)] has cleared the custom event text.") message_admins("[key_name_admin(usr)] has cleared the custom event text.") return @@ -17,11 +17,11 @@ log_admin("[key_name(usr)] has changed the custom event text.") message_admins("[key_name_admin(usr)] has changed the custom event text.") - custom_event_msg = input + GLOB.custom_event_msg = input to_chat(world, "

    Custom Event

    ") to_chat(world, "

    A custom event is starting. OOC Info:

    ") - to_chat(world, "[html_encode(custom_event_msg)]") + to_chat(world, "[html_encode(GLOB.custom_event_msg)]") to_chat(world, "
    ") // normal verb for players to view info @@ -29,12 +29,12 @@ set category = "OOC" set name = "Custom Event Info" - if(!custom_event_msg || custom_event_msg == "") + if(!GLOB.custom_event_msg || GLOB.custom_event_msg == "") to_chat(src, "There currently is no known custom event taking place.") to_chat(src, "Keep in mind: it is possible that an admin has not properly set this.") return to_chat(src, "

    Custom Event

    ") to_chat(src, "

    A custom event is taking place. OOC Info:

    ") - to_chat(src, "[html_encode(custom_event_msg)]") + to_chat(src, "[html_encode(GLOB.custom_event_msg)]") to_chat(src, "
    ") diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index e1bbc885c75..aae13ad882d 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -1,49 +1,49 @@ -/client/proc/dsay(msg as text) - set category = "Admin" - set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(!src.mob) - return - - if(prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot send DSAY messages (muted).") - return - - if(!(prefs.toggles & CHAT_DEAD)) - to_chat(src, "You have deadchat muted.") - return - - if(handle_spam_prevention(msg,MUTE_DEADCHAT)) - return - - var/stafftype = null - - if(check_rights(R_MENTOR, 0)) - stafftype = "MENTOR" - - if(check_rights(R_MOD, 0)) - stafftype = "MOD" - - if(check_rights(R_ADMIN, 0)) - stafftype = "ADMIN" - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - log_admin("[key_name(src)] : [msg]") - - if(!msg) - return - - var/prefix = "[stafftype] ([src.key])" - if(holder.fakekey) - prefix = "Administrator" - say_dead_direct("[prefix] says, \"[msg]\"") - - feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_dead_say() - var/msg = input(src, null, "dsay \"text\"") as text - dsay(msg) \ No newline at end of file +/client/proc/dsay(msg as text) + set category = "Admin" + set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD)) + return + + if(!src.mob) + return + + if(prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot send DSAY messages (muted).") + return + + if(!(prefs.toggles & CHAT_DEAD)) + to_chat(src, "You have deadchat muted.") + return + + if(handle_spam_prevention(msg,MUTE_DEADCHAT)) + return + + var/stafftype = null + + if(check_rights(R_MENTOR, 0)) + stafftype = "MENTOR" + + if(check_rights(R_MOD, 0)) + stafftype = "MOD" + + if(check_rights(R_ADMIN, 0)) + stafftype = "ADMIN" + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + log_admin("[key_name(src)] : [msg]") + + if(!msg) + return + + var/prefix = "[stafftype] ([src.key])" + if(holder.fakekey) + prefix = "Administrator" + say_dead_direct("[prefix] says, \"[msg]\"") + + feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_dead_say() + var/msg = input(src, null, "dsay \"text\"") as text + dsay(msg) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 57f88d20351..32dcf3daa7b 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -1,892 +1,892 @@ -/client/proc/Debug2() - set category = "Debug" - set name = "Debug-Game" - - if(!check_rights(R_DEBUG)) - return - - if(Debug2) - Debug2 = 0 - message_admins("[key_name_admin(src)] toggled debugging off.") - log_admin("[key_name(src)] toggled debugging off.") - else - Debug2 = 1 - message_admins("[key_name_admin(src)] toggled debugging on.") - log_admin("[key_name(src)] toggled debugging on.") - - feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/* 21st Sept 2010 -Updated by Skie -- Still not perfect but better! -Stuff you can't do: -Call proc /mob/proc/Dizzy() for some player -Because if you select a player mob as owner it tries to do the proc for -/mob/living/carbon/human/ instead. And that gives a run-time error. -But you can call procs that are of type /mob/living/carbon/human/proc/ for that player. -*/ - -/client/proc/callproc() - set category = "Debug" - set name = "Advanced ProcCall" - - if(!check_rights(R_PROCCALL)) - return - - spawn(0) - var/target = null - var/targetselected = 0 - var/returnval = null - var/class = null - - switch(alert("Proc owned by something?",,"Yes","No")) - if("Yes") - targetselected = 1 - if(src.holder && src.holder.marked_datum) - class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client","Marked datum ([holder.marked_datum.type])") - if(class == "Marked datum ([holder.marked_datum.type])") - class = "Marked datum" - else - class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client") - switch(class) - if("Obj") - target = input("Enter target:","Target",usr) as obj in world - if("Mob") - target = input("Enter target:","Target",usr) as mob in world - if("Area or Turf") - target = input("Enter target:","Target",usr.loc) as area|turf in world - if("Client") - var/list/keys = list() - for(var/client/C) - keys += C - target = input("Please, select a player!", "Selection", null, null) as null|anything in keys - if("Marked datum") - target = holder.marked_datum - else - return - if("No") - target = null - targetselected = 0 - - var/procname = clean_input("Proc path, eg: /proc/fake_blood","Path:", null) - if(!procname) return - - if(targetselected && !hascall(target,procname)) - to_chat(usr, "Error: callproc(): target has no such call [procname].") - return - - var/list/lst = get_callproc_args() - if(!lst) - return - - if(targetselected) - if(!target) - to_chat(usr, "Error: callproc(): owner of proc no longer exists.") - return - message_admins("[key_name_admin(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - log_admin("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - returnval = call(target,procname)(arglist(lst)) // Pass the lst as an argument list to the proc - else - //this currently has no hascall protection. wasn't able to get it working. - message_admins("[key_name_admin(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - returnval = call(procname)(arglist(lst)) // Pass the lst as an argument list to the proc - - to_chat(usr, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") - feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -GLOBAL_VAR(AdminProcCaller) -GLOBAL_PROTECT(AdminProcCaller) - -/proc/IsAdminAdvancedProcCall() -#ifdef TESTING - return FALSE -#else - return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey -#endif - -/client/proc/callproc_datum(var/A as null|area|mob|obj|turf) - set category = "Debug" - set name = "Atom ProcCall" - - if(!check_rights(R_PROCCALL)) - return - - var/procname = clean_input("Proc name, eg: fake_blood","Proc:", null) - if(!procname) - return - - if(!hascall(A,procname)) - to_chat(usr, "Error: callproc_datum(): target has no such call [procname].") - return - - var/list/lst = get_callproc_args() - if(!lst) - return - - if(!A || !IsValidSrc(A)) - to_chat(src, "Error: callproc_datum(): owner of proc no longer exists.") - return - message_admins("[key_name_admin(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - - spawn() - var/returnval = call(A,procname)(arglist(lst)) // Pass the lst as an argument list to the proc - to_chat(src, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") - - feedback_add_details("admin_verb","DPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_callproc_args() - var/argnum = input("Number of arguments","Number:",0) as num|null - if(!argnum && (argnum!=0)) return - - var/list/lst = list() - //TODO: make a list to store whether each argument was initialised as null. - //Reason: So we can abort the proccall if say, one of our arguments was a mob which no longer exists - //this will protect us from a fair few errors ~Carn - - while(argnum--) - var/class = null - // Make a list with each index containing one variable, to be given to the proc - if(src.holder && src.holder.marked_datum) - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","Marked datum ([holder.marked_datum.type])","CANCEL") - if(holder.marked_datum && class == "Marked datum ([holder.marked_datum.type])") - class = "Marked datum" - else - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","CANCEL") - switch(class) - if("CANCEL") - return null - - if("text") - lst += clean_input("Enter new text:","Text",null) - - if("num") - lst += input("Enter new number:","Num",0) as num - - if("type") - lst += input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) - - if("reference") - lst += input("Select reference:","Reference",src) as mob|obj|turf|area in world - - if("mob reference") - lst += input("Select reference:","Reference",usr) as mob in world - - if("file") - lst += input("Pick file:","File") as file - - if("icon") - lst += input("Pick icon:","Icon") as icon - - if("client") - var/list/keys = list() - for(var/mob/M in world) - keys += M.client - lst += input("Please, select a player!", "Selection", null, null) as null|anything in keys - - if("mob's area") - var/mob/temp = input("Select mob", "Selection", usr) as mob in world - lst += temp.loc - - if("Marked datum") - lst += holder.marked_datum - return lst - -/client/proc/Cell() - set category = "Debug" - set name = "Air Status in Location" - - if(!check_rights(R_DEBUG)) - return - - if(!mob) - return - var/turf/T = mob.loc - - if(!( istype(T, /turf) )) - return - - var/datum/gas_mixture/env = T.return_air() - - var/t = "" - t+= "Nitrogen : [env.nitrogen]\n" - t+= "Oxygen : [env.oxygen]\n" - t+= "Plasma : [env.toxins]\n" - t+= "CO2: [env.carbon_dioxide]\n" - - usr.show_message(t, 1) - feedback_add_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_robotize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Robot" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon/human)) - log_admin("[key_name(src)] has robotized [M.key].") - spawn(10) - M:Robotize() - - else - alert("Invalid mob") - -/client/proc/cmd_admin_animalize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Simple Animal" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - - if(!M) - alert("That mob doesn't seem to exist, close the panel and try again.") - return - - if(istype(M, /mob/new_player)) - alert("The mob must not be a new_player.") - return - - log_admin("[key_name(src)] has animalized [M.key].") - spawn(10) - M.Animalize() - - -/client/proc/makepAI(var/turf/T in GLOB.mob_list) - set category = "Event" - set name = "Make pAI" - set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI" - - if(!check_rights(R_SPAWN)) - return - - var/list/available = list() - for(var/mob/C in GLOB.mob_list) - if(C.key) - available.Add(C) - var/mob/choice = input("Choose a player to play the pAI", "Spawn pAI") in available - if(!choice) - return 0 - if(!istype(choice, /mob/dead/observer)) - var/confirm = input("[choice.key] isn't ghosting right now. Are you sure you want to yank [choice.p_them()] out of [choice.p_their()] body and place [choice.p_them()] in this pAI?", "Spawn pAI Confirmation", "No") in list("Yes", "No") - if(confirm != "Yes") - return 0 - var/obj/item/paicard/card = new(T) - var/mob/living/silicon/pai/pai = new(card) - var/raw_name = clean_input("Enter your pAI name:", "pAI Name", "Personal AI", choice) - var/new_name = reject_bad_name(raw_name, 1) - if(new_name) - pai.name = new_name - pai.real_name = new_name - else - to_chat(usr, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - pai.real_name = pai.name - pai.key = choice.key - card.setPersonality(pai) - for(var/datum/paiCandidate/candidate in paiController.pai_candidates) - if(candidate.key == choice.key) - paiController.pai_candidates.Remove(candidate) - feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_alienize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Alien" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(ishuman(M)) - log_admin("[key_name(src)] has alienized [M.key].") - spawn(10) - M:Alienize() - feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] made [key_name(M)] into an alien.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into an alien.", 1) - else - alert("Invalid mob") - -/client/proc/cmd_admin_slimeize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make slime" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(ishuman(M)) - log_admin("[key_name(src)] has slimeized [M.key].") - spawn(10) - M:slimeize() - feedback_add_details("admin_verb","MKMET") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] made [key_name(M)] into a slime.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into a slime.", 1) - else - alert("Invalid mob") - -/client/proc/cmd_admin_super(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Superhero" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(ishuman(M)) - var/type = input("Pick the Superhero","Superhero") as null|anything in GLOB.all_superheroes - var/datum/superheroes/S = GLOB.all_superheroes[type] - if(S) - S.create(M) - log_admin("[key_name(src)] has turned [M.key] into a Superhero.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into a Superhero.", 1) - else - alert("Invalid mob") - -//TODO: merge the vievars version into this or something maybe mayhaps -/client/proc/cmd_debug_del_all() - set category = "Debug" - set name = "Del-All" - - if(!check_rights(R_DEBUG)) - return - - // to prevent REALLY stupid deletions - var/blocked = list(/mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/dead, /mob/dead/observer, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai) - var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in subtypesof(/obj) + subtypesof(/mob) - blocked - if(hsbitem) - for(var/atom/O in world) - if(istype(O, hsbitem)) - qdel(O) - log_admin("[key_name(src)] has deleted all instances of [hsbitem].") - message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0) - feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_debug_del_sing() - set category = "Debug" - set name = "Del Singulo / Tesla" - - if(!check_rights(R_DEBUG)) - return - - //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with del-all - var/confirm = alert("This will delete ALL Singularities and Tesla orbs except for any that are on away mission z-levels or the centcomm z-level. Are you sure you want to delete them?", "Confirm Panic Button", "Yes", "No") - if(confirm != "Yes") - return - - for(var/I in GLOB.singularities) - var/obj/singularity/S = I - if(!is_level_reachable(S.z)) - continue - qdel(S) - log_admin("[key_name(src)] has deleted all Singularities and Tesla orbs.") - message_admins("[key_name_admin(src)] has deleted all Singularities and Tesla orbs.", 0) - feedback_add_details("admin_verb","DELS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_debug_make_powernets() - set category = "Debug" - set name = "Make Powernets" - - if(!check_rights(R_DEBUG)) - return - - SSmachines.makepowernets() - log_admin("[key_name(src)] has remade the powernet. makepowernets() called.") - message_admins("[key_name_admin(src)] has remade the powernets. makepowernets() called.", 0) - feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_grantfullaccess(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Grant Full Access" - - if(!check_rights(R_EVENT)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.wear_id) - var/obj/item/card/id/id = H.wear_id - if(istype(H.wear_id, /obj/item/pda)) - var/obj/item/pda/pda = H.wear_id - id = pda.id - id.icon_state = "gold" - id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - else - var/obj/item/card/id/id = new/obj/item/card/id(M); - id.icon_state = "gold" - id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - id.registered_name = H.real_name - id.assignment = "Captain" - id.name = "[id.registered_name]'s ID Card ([id.assignment])" - H.equip_to_slot_or_del(id, slot_wear_id) - H.update_inv_wear_id() - else - alert("Invalid mob") - feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(src)] has granted [M.key] full access.") - message_admins("[key_name_admin(usr)] has granted [M.key] full access.", 1) - -/client/proc/cmd_assume_direct_control(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Assume direct control" - set desc = "Direct intervention" - - if(!check_rights(R_DEBUG|R_ADMIN)) - return - - if(M.ckey) - if(alert("This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.",,"Yes","No") != "Yes") - return - else - var/mob/dead/observer/ghost = new/mob/dead/observer(M,1) - ghost.ckey = M.ckey - message_admins("[key_name_admin(usr)] assumed direct control of [M].", 1) - log_admin("[key_name(usr)] assumed direct control of [M].") - var/mob/adminmob = src.mob - M.ckey = src.ckey - if( isobserver(adminmob) ) - qdel(adminmob) - feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/cmd_admin_areatest() - set category = "Mapping" - set name = "Test areas" - - if(!check_rights(R_DEBUG)) - return - - var/list/areas_all = list() - var/list/areas_with_APC = list() - var/list/areas_with_air_alarm = list() - var/list/areas_with_RC = list() - var/list/areas_with_light = list() - var/list/areas_with_LS = list() - var/list/areas_with_intercom = list() - var/list/areas_with_camera = list() - - var/list/areas_with_multiple_APCs = list() - var/list/areas_with_multiple_air_alarms = list() - - for(var/area/A in world) - areas_all |= A.type - - for(var/obj/machinery/power/apc/APC in world) - var/area/A = get_area(APC) - if(!A) - continue - if(!(A.type in areas_with_APC)) - areas_with_APC |= A.type - else - areas_with_multiple_APCs |= A.type - - for(var/obj/machinery/alarm/alarm in world) - var/area/A = get_area(alarm) - if(!A) - continue - if(!(A.type in areas_with_air_alarm)) - areas_with_air_alarm |= A.type - else - areas_with_multiple_air_alarms |= A.type - - for(var/obj/machinery/requests_console/RC in world) - var/area/A = get_area(RC) - if(!A) - continue - areas_with_RC |= A.type - - for(var/obj/machinery/light/L in world) - var/area/A = get_area(L) - if(!A) - continue - areas_with_light |= A.type - - for(var/obj/machinery/light_switch/LS in world) - var/area/A = get_area(LS) - if(!A) - continue - areas_with_LS |= A.type - - for(var/obj/item/radio/intercom/I in world) - var/area/A = get_area(I) - if(!A) - continue - areas_with_intercom |= A.type - - for(var/obj/machinery/camera/C in world) - var/area/A = get_area(C) - if(!A) - continue - areas_with_camera |= A.type - - var/list/areas_without_APC = areas_all - areas_with_APC - var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm - var/list/areas_without_RC = areas_all - areas_with_RC - var/list/areas_without_light = areas_all - areas_with_light - var/list/areas_without_LS = areas_all - areas_with_LS - var/list/areas_without_intercom = areas_all - areas_with_intercom - var/list/areas_without_camera = areas_all - areas_with_camera - - to_chat(world, "AREAS WITHOUT AN APC:") - for(var/areatype in areas_without_APC) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT AN AIR ALARM:") - for(var/areatype in areas_without_air_alarm) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITH TOO MANY APCS:") - for(var/areatype in areas_with_multiple_APCs) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITH TOO MANY AIR ALARMS:") - for(var/areatype in areas_with_multiple_air_alarms) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT A REQUEST CONSOLE:") - for(var/areatype in areas_without_RC) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT ANY LIGHTS:") - for(var/areatype in areas_without_light) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT A LIGHT SWITCH:") - for(var/areatype in areas_without_LS) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT ANY INTERCOMS:") - for(var/areatype in areas_without_intercom) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT ANY CAMERAS:") - for(var/areatype in areas_without_camera) - to_chat(world, "* [areatype]") - -/client/proc/cmd_admin_dress(var/mob/living/carbon/human/M in GLOB.mob_list) - set category = "Event" - set name = "Select equipment" - - if(!check_rights(R_EVENT)) - return - - if(!ishuman(M) && !isobserver(M)) - alert("Invalid mob") - return - - var/dresscode = robust_dress_shop() - - if(!dresscode) - return - - var/delete_pocket - var/mob/living/carbon/human/H - if(isobserver(M)) - H = M.change_mob_type(/mob/living/carbon/human, null, null, TRUE) - else - H = M - if(H.l_store || H.r_store || H.s_store) //saves a lot of time for admins and coders alike - if(alert("Should the items in their pockets be dropped? Selecting \"No\" will delete them.", "Robust quick dress shop", "Yes", "No") == "No") - delete_pocket = TRUE - - for (var/obj/item/I in H.get_equipped_items(delete_pocket)) - qdel(I) - if(dresscode != "Naked") - H.equipOutfit(dresscode) - - H.regenerate_icons() - - feedback_add_details("admin_verb", "SE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] changed the equipment of [key_name(M)] to [dresscode].") - message_admins("[key_name_admin(usr)] changed the equipment of [key_name_admin(M)] to [dresscode].", 1) - -/client/proc/robust_dress_shop() - var/list/outfits = list( - "Naked", - "As Job...", - "Custom..." - ) - - var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job) - for(var/path in paths) - var/datum/outfit/O = path //not much to initalize here but whatever - if(initial(O.can_be_admin_equipped)) - outfits[initial(O.name)] = path - - var/dresscode = input("Select outfit", "Robust quick dress shop") as null|anything in outfits - if(isnull(dresscode)) - return - - if(outfits[dresscode]) - dresscode = outfits[dresscode] - - if(dresscode == "As Job...") - var/list/job_paths = subtypesof(/datum/outfit/job) - var/list/job_outfits = list() - for(var/path in job_paths) - var/datum/outfit/O = path - if(initial(O.can_be_admin_equipped)) - job_outfits[initial(O.name)] = path - - dresscode = input("Select job equipment", "Robust quick dress shop") as null|anything in job_outfits - dresscode = job_outfits[dresscode] - if(isnull(dresscode)) - return - - if(dresscode == "Custom...") - var/list/custom_names = list() - for(var/datum/outfit/D in GLOB.custom_outfits) - custom_names[D.name] = D - var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in custom_names - dresscode = custom_names[selected_name] - if(isnull(dresscode)) - return - - return dresscode - -/client/proc/startSinglo() - set category = "Debug" - set name = "Start Singularity" - set desc = "Sets up the singularity and all machines to get power flowing through the station" - - if(!check_rights(R_DEBUG)) - return - - if(alert("Are you sure? This will start up the engine. Should only be used during debug!",,"Yes","No") != "Yes") - return - - for(var/obj/machinery/power/emitter/E in GLOB.machines) - if(E.anchored) - E.active = 1 - - for(var/obj/machinery/field/generator/F in GLOB.machines) - if(F.active == 0) - F.active = 1 - F.state = 2 - F.power = 250 - F.anchored = 1 - F.warming_up = 3 - F.start_fields() - F.update_icon() - - spawn(30) - for(var/obj/machinery/the_singularitygen/G in GLOB.machines) - if(G.anchored) - var/obj/singularity/S = new /obj/singularity(get_turf(G)) - S.energy = 800 - break - - for(var/obj/machinery/power/rad_collector/Rad in GLOB.machines) - if(Rad.anchored) - if(!Rad.P) - var/obj/item/tank/plasma/Plasma = new/obj/item/tank/plasma(Rad) - Plasma.air_contents.toxins = 70 - Rad.drainratio = 0 - Rad.P = Plasma - Plasma.loc = Rad - - if(!Rad.active) - Rad.toggle_power() - - for(var/obj/machinery/power/smes/SMES in GLOB.machines) - if(SMES.anchored) - SMES.input_attempt = 1 - -/client/proc/cmd_debug_mob_lists() - set category = "Debug" - set name = "Debug Mob Lists" - set desc = "For when you just gotta know" - - if(!check_rights(R_DEBUG)) - return - - switch(input("Which list?") in list("Players","Admins","Mobs","Living Mobs","Dead Mobs","Silicons","Clients","Respawnable Mobs")) - if("Players") - to_chat(usr, jointext(GLOB.player_list,",")) - if("Admins") - to_chat(usr, jointext(GLOB.admins,",")) - if("Mobs") - to_chat(usr, jointext(GLOB.mob_list,",")) - if("Living Mobs") - to_chat(usr, jointext(GLOB.living_mob_list,",")) - if("Dead Mobs") - to_chat(usr, jointext(GLOB.dead_mob_list,",")) - if("Silicons") - to_chat(usr, jointext(GLOB.silicon_mob_list,",")) - if("Clients") - to_chat(usr, jointext(GLOB.clients,",")) - if("Respawnable Mobs") - to_chat(usr, jointext(GLOB.respawnable_list,",")) - -/client/proc/cmd_display_del_log() - set category = "Debug" - set name = "Display del() Log" - set desc = "Display del's log of everything that's passed through it." - - if(!check_rights(R_DEBUG)) - return - - var/list/dellog = list("List of things that have gone through qdel this round

      ") - sortTim(SSgarbage.items, cmp=/proc/cmp_qdel_item_time, associative = TRUE) - for(var/path in SSgarbage.items) - var/datum/qdel_item/I = SSgarbage.items[path] - dellog += "
    1. [path]
        " - if(I.failures) - dellog += "
      • Failures: [I.failures]
      • " - dellog += "
      • qdel() Count: [I.qdels]
      • " - dellog += "
      • Destroy() Cost: [I.destroy_time]ms
      • " - if(I.hard_deletes) - dellog += "
      • Total Hard Deletes [I.hard_deletes]
      • " - dellog += "
      • Time Spent Hard Deleting: [I.hard_delete_time]ms
      • " - if(I.slept_destroy) - dellog += "
      • Sleeps: [I.slept_destroy]
      • " - if(I.no_respect_force) - dellog += "
      • Ignored force: [I.no_respect_force]
      • " - if(I.no_hint) - dellog += "
      • No hint: [I.no_hint]
      • " - dellog += "
    2. " - - dellog += "
    " - - usr << browse(dellog.Join(), "window=dellog") - -/client/proc/cmd_display_del_log_simple() - set category = "Debug" - set name = "Display Simple del() Log" - set desc = "Display a compacted del's log." - - if(!check_rights(R_DEBUG)) - return - - var/dat = "List of things that failed to GC this round

    " - for(var/path in SSgarbage.items) - var/datum/qdel_item/I = SSgarbage.items[path] - if(I.failures) - dat += "[I] - [I.failures] times
    " - - dat += "List of paths that did not return a qdel hint in Destroy()

    " - for(var/path in SSgarbage.items) - var/datum/qdel_item/I = SSgarbage.items[path] - if(I.no_hint) - dat += "[I]
    " - - dat += "List of paths that slept in Destroy()

    " - for(var/path in SSgarbage.items) - var/datum/qdel_item/I = SSgarbage.items[path] - if(I.slept_destroy) - dat += "[I]
    " - - usr << browse(dat, "window=simpledellog") - -/client/proc/cmd_admin_toggle_block(var/mob/M,var/block) - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon)) - M.dna.SetSEState(block,!M.dna.GetSEState(block)) - genemutcheck(M,block,null,MUTCHK_FORCED) - M.update_mutations() - var/state="[M.dna.GetSEState(block)?"on":"off"]" - var/blockname=assigned_blocks[block] - message_admins("[key_name_admin(src)] has toggled [M.key]'s [blockname] block [state]!") - log_admin("[key_name(src)] has toggled [M.key]'s [blockname] block [state]!") - else - alert("Invalid mob") - -/client/proc/reload_nanoui_resources() - set category = "Debug" - set name = "Reload NanoUI Resources" - set desc = "Force the client to redownload NanoUI Resources" - - // Close open NanoUIs. - SSnanoui.close_user_uis(usr) - - // Re-load the assets. - var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui) - assets.register() - - // Clear the user's cache so they get resent. - usr.client.cache = list() - -/client/proc/view_runtimes() - set category = "Debug" - set name = "View Runtimes" - set desc = "Open the Runtime Viewer" - - if(!check_rights(R_DEBUG)) - return - - error_cache.showTo(usr) - -/client/proc/jump_to_ruin() - set category = "Debug" - set name = "Jump to Ruin" - set desc = "Displays a list of all placed ruins to teleport to." - - if(!check_rights(R_DEBUG)) - return - - var/list/names = list() - for(var/i in GLOB.ruin_landmarks) - var/obj/effect/landmark/ruin/ruin_landmark = i - var/datum/map_template/ruin/template = ruin_landmark.ruin_template - - var/count = 1 - var/name = template.name - var/original_name = name - - while(name in names) - count++ - name = "[original_name] ([count])" - - names[name] = ruin_landmark - - var/ruinname = input("Select ruin", "Jump to Ruin") as null|anything in names - - var/obj/effect/landmark/ruin/landmark = names[ruinname] - - if(istype(landmark)) - var/datum/map_template/ruin/template = landmark.ruin_template - admin_forcemove(usr, get_turf(landmark)) - - to_chat(usr, "[template.name]") - to_chat(usr, "[template.description]") - - log_admin("[key_name(usr)] jumped to ruin [ruinname]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to ruin [ruinname]", 1) - - feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_medal_disable() - set category = "Debug" - set name = "Toggle Medal Disable" - set desc = "Toggles the safety lock on trying to contact the medal hub." - - if(!check_rights(R_DEBUG)) - return - - SSmedals.hub_enabled = !SSmedals.hub_enabled - - message_admins("[key_name_admin(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") - feedback_add_details("admin_verb","TMH") // If... - log_admin("[key_name(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") +/client/proc/Debug2() + set category = "Debug" + set name = "Debug-Game" + + if(!check_rights(R_DEBUG)) + return + + if(GLOB.debug2) + GLOB.debug2 = 0 + message_admins("[key_name_admin(src)] toggled debugging off.") + log_admin("[key_name(src)] toggled debugging off.") + else + GLOB.debug2 = 1 + message_admins("[key_name_admin(src)] toggled debugging on.") + log_admin("[key_name(src)] toggled debugging on.") + + feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/* 21st Sept 2010 +Updated by Skie -- Still not perfect but better! +Stuff you can't do: +Call proc /mob/proc/Dizzy() for some player +Because if you select a player mob as owner it tries to do the proc for +/mob/living/carbon/human/ instead. And that gives a run-time error. +But you can call procs that are of type /mob/living/carbon/human/proc/ for that player. +*/ + +/client/proc/callproc() + set category = "Debug" + set name = "Advanced ProcCall" + + if(!check_rights(R_PROCCALL)) + return + + spawn(0) + var/target = null + var/targetselected = 0 + var/returnval = null + var/class = null + + switch(alert("Proc owned by something?",,"Yes","No")) + if("Yes") + targetselected = 1 + if(src.holder && src.holder.marked_datum) + class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client","Marked datum ([holder.marked_datum.type])") + if(class == "Marked datum ([holder.marked_datum.type])") + class = "Marked datum" + else + class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client") + switch(class) + if("Obj") + target = input("Enter target:","Target",usr) as obj in world + if("Mob") + target = input("Enter target:","Target",usr) as mob in world + if("Area or Turf") + target = input("Enter target:","Target",usr.loc) as area|turf in world + if("Client") + var/list/keys = list() + for(var/client/C) + keys += C + target = input("Please, select a player!", "Selection", null, null) as null|anything in keys + if("Marked datum") + target = holder.marked_datum + else + return + if("No") + target = null + targetselected = 0 + + var/procname = clean_input("Proc path, eg: /proc/fake_blood","Path:", null) + if(!procname) return + + if(targetselected && !hascall(target,procname)) + to_chat(usr, "Error: callproc(): target has no such call [procname].") + return + + var/list/lst = get_callproc_args() + if(!lst) + return + + if(targetselected) + if(!target) + to_chat(usr, "Error: callproc(): owner of proc no longer exists.") + return + message_admins("[key_name_admin(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + log_admin("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + returnval = call(target,procname)(arglist(lst)) // Pass the lst as an argument list to the proc + else + //this currently has no hascall protection. wasn't able to get it working. + message_admins("[key_name_admin(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") + log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") + returnval = call(procname)(arglist(lst)) // Pass the lst as an argument list to the proc + + to_chat(usr, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") + feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +GLOBAL_VAR(AdminProcCaller) +GLOBAL_PROTECT(AdminProcCaller) + +/proc/IsAdminAdvancedProcCall() +#ifdef TESTING + return FALSE +#else + return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey +#endif + +/client/proc/callproc_datum(var/A as null|area|mob|obj|turf) + set category = "Debug" + set name = "Atom ProcCall" + + if(!check_rights(R_PROCCALL)) + return + + var/procname = clean_input("Proc name, eg: fake_blood","Proc:", null) + if(!procname) + return + + if(!hascall(A,procname)) + to_chat(usr, "Error: callproc_datum(): target has no such call [procname].") + return + + var/list/lst = get_callproc_args() + if(!lst) + return + + if(!A || !IsValidSrc(A)) + to_chat(src, "Error: callproc_datum(): owner of proc no longer exists.") + return + message_admins("[key_name_admin(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") + log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") + + spawn() + var/returnval = call(A,procname)(arglist(lst)) // Pass the lst as an argument list to the proc + to_chat(src, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") + + feedback_add_details("admin_verb","DPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_callproc_args() + var/argnum = input("Number of arguments","Number:",0) as num|null + if(!argnum && (argnum!=0)) return + + var/list/lst = list() + //TODO: make a list to store whether each argument was initialised as null. + //Reason: So we can abort the proccall if say, one of our arguments was a mob which no longer exists + //this will protect us from a fair few errors ~Carn + + while(argnum--) + var/class = null + // Make a list with each index containing one variable, to be given to the proc + if(src.holder && src.holder.marked_datum) + class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","Marked datum ([holder.marked_datum.type])","CANCEL") + if(holder.marked_datum && class == "Marked datum ([holder.marked_datum.type])") + class = "Marked datum" + else + class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","CANCEL") + switch(class) + if("CANCEL") + return null + + if("text") + lst += clean_input("Enter new text:","Text",null) + + if("num") + lst += input("Enter new number:","Num",0) as num + + if("type") + lst += input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) + + if("reference") + lst += input("Select reference:","Reference",src) as mob|obj|turf|area in world + + if("mob reference") + lst += input("Select reference:","Reference",usr) as mob in world + + if("file") + lst += input("Pick file:","File") as file + + if("icon") + lst += input("Pick icon:","Icon") as icon + + if("client") + var/list/keys = list() + for(var/mob/M in world) + keys += M.client + lst += input("Please, select a player!", "Selection", null, null) as null|anything in keys + + if("mob's area") + var/mob/temp = input("Select mob", "Selection", usr) as mob in world + lst += temp.loc + + if("Marked datum") + lst += holder.marked_datum + return lst + +/client/proc/Cell() + set category = "Debug" + set name = "Air Status in Location" + + if(!check_rights(R_DEBUG)) + return + + if(!mob) + return + var/turf/T = mob.loc + + if(!( istype(T, /turf) )) + return + + var/datum/gas_mixture/env = T.return_air() + + var/t = "" + t+= "Nitrogen : [env.nitrogen]\n" + t+= "Oxygen : [env.oxygen]\n" + t+= "Plasma : [env.toxins]\n" + t+= "CO2: [env.carbon_dioxide]\n" + + usr.show_message(t, 1) + feedback_add_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_robotize(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make Robot" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(istype(M, /mob/living/carbon/human)) + log_admin("[key_name(src)] has robotized [M.key].") + spawn(10) + M:Robotize() + + else + alert("Invalid mob") + +/client/proc/cmd_admin_animalize(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make Simple Animal" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + + if(!M) + alert("That mob doesn't seem to exist, close the panel and try again.") + return + + if(istype(M, /mob/new_player)) + alert("The mob must not be a new_player.") + return + + log_admin("[key_name(src)] has animalized [M.key].") + spawn(10) + M.Animalize() + + +/client/proc/makepAI(var/turf/T in GLOB.mob_list) + set category = "Event" + set name = "Make pAI" + set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI" + + if(!check_rights(R_SPAWN)) + return + + var/list/available = list() + for(var/mob/C in GLOB.mob_list) + if(C.key) + available.Add(C) + var/mob/choice = input("Choose a player to play the pAI", "Spawn pAI") in available + if(!choice) + return 0 + if(!istype(choice, /mob/dead/observer)) + var/confirm = input("[choice.key] isn't ghosting right now. Are you sure you want to yank [choice.p_them()] out of [choice.p_their()] body and place [choice.p_them()] in this pAI?", "Spawn pAI Confirmation", "No") in list("Yes", "No") + if(confirm != "Yes") + return 0 + var/obj/item/paicard/card = new(T) + var/mob/living/silicon/pai/pai = new(card) + var/raw_name = clean_input("Enter your pAI name:", "pAI Name", "Personal AI", choice) + var/new_name = reject_bad_name(raw_name, 1) + if(new_name) + pai.name = new_name + pai.real_name = new_name + else + to_chat(usr, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + pai.real_name = pai.name + pai.key = choice.key + card.setPersonality(pai) + for(var/datum/paiCandidate/candidate in GLOB.paiController.pai_candidates) + if(candidate.key == choice.key) + GLOB.paiController.pai_candidates.Remove(candidate) + feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_alienize(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make Alien" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(ishuman(M)) + log_admin("[key_name(src)] has alienized [M.key].") + spawn(10) + M:Alienize() + feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] made [key_name(M)] into an alien.") + message_admins("[key_name_admin(usr)] made [key_name(M)] into an alien.", 1) + else + alert("Invalid mob") + +/client/proc/cmd_admin_slimeize(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make slime" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(ishuman(M)) + log_admin("[key_name(src)] has slimeized [M.key].") + spawn(10) + M:slimeize() + feedback_add_details("admin_verb","MKMET") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] made [key_name(M)] into a slime.") + message_admins("[key_name_admin(usr)] made [key_name(M)] into a slime.", 1) + else + alert("Invalid mob") + +/client/proc/cmd_admin_super(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make Superhero" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(ishuman(M)) + var/type = input("Pick the Superhero","Superhero") as null|anything in GLOB.all_superheroes + var/datum/superheroes/S = GLOB.all_superheroes[type] + if(S) + S.create(M) + log_admin("[key_name(src)] has turned [M.key] into a Superhero.") + message_admins("[key_name_admin(usr)] made [key_name(M)] into a Superhero.", 1) + else + alert("Invalid mob") + +//TODO: merge the vievars version into this or something maybe mayhaps +/client/proc/cmd_debug_del_all() + set category = "Debug" + set name = "Del-All" + + if(!check_rights(R_DEBUG)) + return + + // to prevent REALLY stupid deletions + var/blocked = list(/mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/dead, /mob/dead/observer, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai) + var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in subtypesof(/obj) + subtypesof(/mob) - blocked + if(hsbitem) + for(var/atom/O in world) + if(istype(O, hsbitem)) + qdel(O) + log_admin("[key_name(src)] has deleted all instances of [hsbitem].") + message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0) + feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_debug_del_sing() + set category = "Debug" + set name = "Del Singulo / Tesla" + + if(!check_rights(R_DEBUG)) + return + + //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with del-all + var/confirm = alert("This will delete ALL Singularities and Tesla orbs except for any that are on away mission z-levels or the centcomm z-level. Are you sure you want to delete them?", "Confirm Panic Button", "Yes", "No") + if(confirm != "Yes") + return + + for(var/I in GLOB.singularities) + var/obj/singularity/S = I + if(!is_level_reachable(S.z)) + continue + qdel(S) + log_admin("[key_name(src)] has deleted all Singularities and Tesla orbs.") + message_admins("[key_name_admin(src)] has deleted all Singularities and Tesla orbs.", 0) + feedback_add_details("admin_verb","DELS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_debug_make_powernets() + set category = "Debug" + set name = "Make Powernets" + + if(!check_rights(R_DEBUG)) + return + + SSmachines.makepowernets() + log_admin("[key_name(src)] has remade the powernet. makepowernets() called.") + message_admins("[key_name_admin(src)] has remade the powernets. makepowernets() called.", 0) + feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_grantfullaccess(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Grant Full Access" + + if(!check_rights(R_EVENT)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(H.wear_id) + var/obj/item/card/id/id = H.wear_id + if(istype(H.wear_id, /obj/item/pda)) + var/obj/item/pda/pda = H.wear_id + id = pda.id + id.icon_state = "gold" + id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() + else + var/obj/item/card/id/id = new/obj/item/card/id(M); + id.icon_state = "gold" + id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() + id.registered_name = H.real_name + id.assignment = "Captain" + id.name = "[id.registered_name]'s ID Card ([id.assignment])" + H.equip_to_slot_or_del(id, slot_wear_id) + H.update_inv_wear_id() + else + alert("Invalid mob") + feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(src)] has granted [M.key] full access.") + message_admins("[key_name_admin(usr)] has granted [M.key] full access.", 1) + +/client/proc/cmd_assume_direct_control(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Assume direct control" + set desc = "Direct intervention" + + if(!check_rights(R_DEBUG|R_ADMIN)) + return + + if(M.ckey) + if(alert("This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.",,"Yes","No") != "Yes") + return + else + var/mob/dead/observer/ghost = new/mob/dead/observer(M,1) + ghost.ckey = M.ckey + message_admins("[key_name_admin(usr)] assumed direct control of [M].", 1) + log_admin("[key_name(usr)] assumed direct control of [M].") + var/mob/adminmob = src.mob + M.ckey = src.ckey + if( isobserver(adminmob) ) + qdel(adminmob) + feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/cmd_admin_areatest() + set category = "Mapping" + set name = "Test areas" + + if(!check_rights(R_DEBUG)) + return + + var/list/areas_all = list() + var/list/areas_with_APC = list() + var/list/areas_with_air_alarm = list() + var/list/areas_with_RC = list() + var/list/areas_with_light = list() + var/list/areas_with_LS = list() + var/list/areas_with_intercom = list() + var/list/areas_with_camera = list() + + var/list/areas_with_multiple_APCs = list() + var/list/areas_with_multiple_air_alarms = list() + + for(var/area/A in world) + areas_all |= A.type + + for(var/obj/machinery/power/apc/APC in world) + var/area/A = get_area(APC) + if(!A) + continue + if(!(A.type in areas_with_APC)) + areas_with_APC |= A.type + else + areas_with_multiple_APCs |= A.type + + for(var/obj/machinery/alarm/alarm in world) + var/area/A = get_area(alarm) + if(!A) + continue + if(!(A.type in areas_with_air_alarm)) + areas_with_air_alarm |= A.type + else + areas_with_multiple_air_alarms |= A.type + + for(var/obj/machinery/requests_console/RC in world) + var/area/A = get_area(RC) + if(!A) + continue + areas_with_RC |= A.type + + for(var/obj/machinery/light/L in world) + var/area/A = get_area(L) + if(!A) + continue + areas_with_light |= A.type + + for(var/obj/machinery/light_switch/LS in world) + var/area/A = get_area(LS) + if(!A) + continue + areas_with_LS |= A.type + + for(var/obj/item/radio/intercom/I in world) + var/area/A = get_area(I) + if(!A) + continue + areas_with_intercom |= A.type + + for(var/obj/machinery/camera/C in world) + var/area/A = get_area(C) + if(!A) + continue + areas_with_camera |= A.type + + var/list/areas_without_APC = areas_all - areas_with_APC + var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm + var/list/areas_without_RC = areas_all - areas_with_RC + var/list/areas_without_light = areas_all - areas_with_light + var/list/areas_without_LS = areas_all - areas_with_LS + var/list/areas_without_intercom = areas_all - areas_with_intercom + var/list/areas_without_camera = areas_all - areas_with_camera + + to_chat(world, "AREAS WITHOUT AN APC:") + for(var/areatype in areas_without_APC) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT AN AIR ALARM:") + for(var/areatype in areas_without_air_alarm) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITH TOO MANY APCS:") + for(var/areatype in areas_with_multiple_APCs) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITH TOO MANY AIR ALARMS:") + for(var/areatype in areas_with_multiple_air_alarms) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT A REQUEST CONSOLE:") + for(var/areatype in areas_without_RC) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT ANY LIGHTS:") + for(var/areatype in areas_without_light) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT A LIGHT SWITCH:") + for(var/areatype in areas_without_LS) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT ANY INTERCOMS:") + for(var/areatype in areas_without_intercom) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT ANY CAMERAS:") + for(var/areatype in areas_without_camera) + to_chat(world, "* [areatype]") + +/client/proc/cmd_admin_dress(var/mob/living/carbon/human/M in GLOB.mob_list) + set category = "Event" + set name = "Select equipment" + + if(!check_rights(R_EVENT)) + return + + if(!ishuman(M) && !isobserver(M)) + alert("Invalid mob") + return + + var/dresscode = robust_dress_shop() + + if(!dresscode) + return + + var/delete_pocket + var/mob/living/carbon/human/H + if(isobserver(M)) + H = M.change_mob_type(/mob/living/carbon/human, null, null, TRUE) + else + H = M + if(H.l_store || H.r_store || H.s_store) //saves a lot of time for admins and coders alike + if(alert("Should the items in their pockets be dropped? Selecting \"No\" will delete them.", "Robust quick dress shop", "Yes", "No") == "No") + delete_pocket = TRUE + + for (var/obj/item/I in H.get_equipped_items(delete_pocket)) + qdel(I) + if(dresscode != "Naked") + H.equipOutfit(dresscode) + + H.regenerate_icons() + + feedback_add_details("admin_verb", "SE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] changed the equipment of [key_name(M)] to [dresscode].") + message_admins("[key_name_admin(usr)] changed the equipment of [key_name_admin(M)] to [dresscode].", 1) + +/client/proc/robust_dress_shop() + var/list/outfits = list( + "Naked", + "As Job...", + "Custom..." + ) + + var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job) + for(var/path in paths) + var/datum/outfit/O = path //not much to initalize here but whatever + if(initial(O.can_be_admin_equipped)) + outfits[initial(O.name)] = path + + var/dresscode = input("Select outfit", "Robust quick dress shop") as null|anything in outfits + if(isnull(dresscode)) + return + + if(outfits[dresscode]) + dresscode = outfits[dresscode] + + if(dresscode == "As Job...") + var/list/job_paths = subtypesof(/datum/outfit/job) + var/list/job_outfits = list() + for(var/path in job_paths) + var/datum/outfit/O = path + if(initial(O.can_be_admin_equipped)) + job_outfits[initial(O.name)] = path + + dresscode = input("Select job equipment", "Robust quick dress shop") as null|anything in job_outfits + dresscode = job_outfits[dresscode] + if(isnull(dresscode)) + return + + if(dresscode == "Custom...") + var/list/custom_names = list() + for(var/datum/outfit/D in GLOB.custom_outfits) + custom_names[D.name] = D + var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in custom_names + dresscode = custom_names[selected_name] + if(isnull(dresscode)) + return + + return dresscode + +/client/proc/startSinglo() + set category = "Debug" + set name = "Start Singularity" + set desc = "Sets up the singularity and all machines to get power flowing through the station" + + if(!check_rights(R_DEBUG)) + return + + if(alert("Are you sure? This will start up the engine. Should only be used during debug!",,"Yes","No") != "Yes") + return + + for(var/obj/machinery/power/emitter/E in GLOB.machines) + if(E.anchored) + E.active = 1 + + for(var/obj/machinery/field/generator/F in GLOB.machines) + if(F.active == 0) + F.active = 1 + F.state = 2 + F.power = 250 + F.anchored = 1 + F.warming_up = 3 + F.start_fields() + F.update_icon() + + spawn(30) + for(var/obj/machinery/the_singularitygen/G in GLOB.machines) + if(G.anchored) + var/obj/singularity/S = new /obj/singularity(get_turf(G)) + S.energy = 800 + break + + for(var/obj/machinery/power/rad_collector/Rad in GLOB.machines) + if(Rad.anchored) + if(!Rad.P) + var/obj/item/tank/plasma/Plasma = new/obj/item/tank/plasma(Rad) + Plasma.air_contents.toxins = 70 + Rad.drainratio = 0 + Rad.P = Plasma + Plasma.loc = Rad + + if(!Rad.active) + Rad.toggle_power() + + for(var/obj/machinery/power/smes/SMES in GLOB.machines) + if(SMES.anchored) + SMES.input_attempt = 1 + +/client/proc/cmd_debug_mob_lists() + set category = "Debug" + set name = "Debug Mob Lists" + set desc = "For when you just gotta know" + + if(!check_rights(R_DEBUG)) + return + + switch(input("Which list?") in list("Players","Admins","Mobs","Living Mobs","Dead Mobs","Silicons","Clients","Respawnable Mobs")) + if("Players") + to_chat(usr, jointext(GLOB.player_list,",")) + if("Admins") + to_chat(usr, jointext(GLOB.admins,",")) + if("Mobs") + to_chat(usr, jointext(GLOB.mob_list,",")) + if("Living Mobs") + to_chat(usr, jointext(GLOB.living_mob_list,",")) + if("Dead Mobs") + to_chat(usr, jointext(GLOB.dead_mob_list,",")) + if("Silicons") + to_chat(usr, jointext(GLOB.silicon_mob_list,",")) + if("Clients") + to_chat(usr, jointext(GLOB.clients,",")) + if("Respawnable Mobs") + to_chat(usr, jointext(GLOB.respawnable_list,",")) + +/client/proc/cmd_display_del_log() + set category = "Debug" + set name = "Display del() Log" + set desc = "Display del's log of everything that's passed through it." + + if(!check_rights(R_DEBUG)) + return + + var/list/dellog = list("List of things that have gone through qdel this round

      ") + sortTim(SSgarbage.items, cmp=/proc/cmp_qdel_item_time, associative = TRUE) + for(var/path in SSgarbage.items) + var/datum/qdel_item/I = SSgarbage.items[path] + dellog += "
    1. [path]
        " + if(I.failures) + dellog += "
      • Failures: [I.failures]
      • " + dellog += "
      • qdel() Count: [I.qdels]
      • " + dellog += "
      • Destroy() Cost: [I.destroy_time]ms
      • " + if(I.hard_deletes) + dellog += "
      • Total Hard Deletes [I.hard_deletes]
      • " + dellog += "
      • Time Spent Hard Deleting: [I.hard_delete_time]ms
      • " + if(I.slept_destroy) + dellog += "
      • Sleeps: [I.slept_destroy]
      • " + if(I.no_respect_force) + dellog += "
      • Ignored force: [I.no_respect_force]
      • " + if(I.no_hint) + dellog += "
      • No hint: [I.no_hint]
      • " + dellog += "
    2. " + + dellog += "
    " + + usr << browse(dellog.Join(), "window=dellog") + +/client/proc/cmd_display_del_log_simple() + set category = "Debug" + set name = "Display Simple del() Log" + set desc = "Display a compacted del's log." + + if(!check_rights(R_DEBUG)) + return + + var/dat = "List of things that failed to GC this round

    " + for(var/path in SSgarbage.items) + var/datum/qdel_item/I = SSgarbage.items[path] + if(I.failures) + dat += "[I] - [I.failures] times
    " + + dat += "List of paths that did not return a qdel hint in Destroy()

    " + for(var/path in SSgarbage.items) + var/datum/qdel_item/I = SSgarbage.items[path] + if(I.no_hint) + dat += "[I]
    " + + dat += "List of paths that slept in Destroy()

    " + for(var/path in SSgarbage.items) + var/datum/qdel_item/I = SSgarbage.items[path] + if(I.slept_destroy) + dat += "[I]
    " + + usr << browse(dat, "window=simpledellog") + +/client/proc/cmd_admin_toggle_block(var/mob/M,var/block) + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(istype(M, /mob/living/carbon)) + M.dna.SetSEState(block,!M.dna.GetSEState(block)) + genemutcheck(M,block,null,MUTCHK_FORCED) + M.update_mutations() + var/state="[M.dna.GetSEState(block)?"on":"off"]" + var/blockname=GLOB.assigned_blocks[block] + message_admins("[key_name_admin(src)] has toggled [M.key]'s [blockname] block [state]!") + log_admin("[key_name(src)] has toggled [M.key]'s [blockname] block [state]!") + else + alert("Invalid mob") + +/client/proc/reload_nanoui_resources() + set category = "Debug" + set name = "Reload NanoUI Resources" + set desc = "Force the client to redownload NanoUI Resources" + + // Close open NanoUIs. + SSnanoui.close_user_uis(usr) + + // Re-load the assets. + var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui) + assets.register() + + // Clear the user's cache so they get resent. + usr.client.cache = list() + +/client/proc/view_runtimes() + set category = "Debug" + set name = "View Runtimes" + set desc = "Open the Runtime Viewer" + + if(!check_rights(R_DEBUG|R_VIEWRUNTIMES)) + return + + GLOB.error_cache.showTo(usr) + +/client/proc/jump_to_ruin() + set category = "Debug" + set name = "Jump to Ruin" + set desc = "Displays a list of all placed ruins to teleport to." + + if(!check_rights(R_DEBUG)) + return + + var/list/names = list() + for(var/i in GLOB.ruin_landmarks) + var/obj/effect/landmark/ruin/ruin_landmark = i + var/datum/map_template/ruin/template = ruin_landmark.ruin_template + + var/count = 1 + var/name = template.name + var/original_name = name + + while(name in names) + count++ + name = "[original_name] ([count])" + + names[name] = ruin_landmark + + var/ruinname = input("Select ruin", "Jump to Ruin") as null|anything in names + + var/obj/effect/landmark/ruin/landmark = names[ruinname] + + if(istype(landmark)) + var/datum/map_template/ruin/template = landmark.ruin_template + admin_forcemove(usr, get_turf(landmark)) + + to_chat(usr, "[template.name]") + to_chat(usr, "[template.description]") + + log_admin("[key_name(usr)] jumped to ruin [ruinname]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to ruin [ruinname]", 1) + + feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_medal_disable() + set category = "Debug" + set name = "Toggle Medal Disable" + set desc = "Toggles the safety lock on trying to contact the medal hub." + + if(!check_rights(R_DEBUG)) + return + + SSmedals.hub_enabled = !SSmedals.hub_enabled + + message_admins("[key_name_admin(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") + feedback_add_details("admin_verb","TMH") // If... + log_admin("[key_name(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 23d171e69a0..ce10383ca4e 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -1,178 +1,178 @@ -/client/proc/air_status(turf/target as turf) - set category = "Debug" - set name = "Display Air Status" - - if(!check_rights(R_DEBUG)) - return - - if(!isturf(target)) - return - - var/datum/gas_mixture/GM = target.return_air() - var/burning = 0 - if(istype(target, /turf/simulated)) - var/turf/simulated/T = target - if(T.active_hotspot) - burning = 1 - - to_chat(usr, "@[target.x],[target.y]: O:[GM.oxygen] T:[GM.toxins] N:[GM.nitrogen] C:[GM.carbon_dioxide] w [GM.temperature] Kelvin, [GM.return_pressure()] kPa [(burning)?("BURNING"):(null)]") - for(var/datum/gas/trace_gas in GM.trace_gases) - to_chat(usr, "[trace_gas.type]: [trace_gas.moles]") - - message_admins("[key_name_admin(usr)] has checked the air status of [T]") - log_admin("[key_name(usr)] has checked the air status of [T]") - - feedback_add_details("admin_verb","DAST") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/fix_next_move() - set category = "Debug" - set name = "Unfreeze Everyone" - - if(!check_rights(R_DEBUG)) - return - - message_admins("[key_name_admin(usr)] has unfrozen everyone") - log_admin("[key_name(usr)] has unfrozen everyone") - - var/largest_move_time = 0 - 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 - if(M.next_move >= largest_move_time) - largest_move_mob = M - if(M.next_move > world.time) - largest_move_time = M.next_move - world.time - else - largest_move_time = 1 - if(M.next_click >= largest_click_time) - largest_click_mob = M - if(M.next_click > world.time) - largest_click_time = M.next_click - world.time - else - largest_click_time = 0 - log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] next_click = [M.next_click] world.time = [world.time]") - M.next_move = 1 - M.next_click = 0 - - message_admins("[key_name_admin(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [largest_move_time/10] seconds!", 1) - message_admins("[key_name_admin(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [largest_click_time/10] seconds!", 1) - message_admins("world.time = [world.time]", 1) - - feedback_add_details("admin_verb","UFE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/radio_report() - set category = "Debug" - set name = "Radio report" - - if(!check_rights(R_DEBUG)) - return - - var/filters = list( - "1" = "RADIO_TO_AIRALARM", - "2" = "RADIO_FROM_AIRALARM", - "3" = "RADIO_CHAT", - "4" = "RADIO_ATMOSIA", - "5" = "RADIO_NAVBEACONS", - "6" = "RADIO_AIRLOCK", - "7" = "RADIO_SECBOT", - "8" = "RADIO_MULEBOT", - "_default" = "NO_FILTER" - ) - var/output = "Radio Report
    " - for(var/fq in SSradio.frequencies) - output += "Freq: [fq]
    " - var/list/datum/radio_frequency/fqs = SSradio.frequencies[fq] - if(!fqs) - output += "  ERROR
    " - continue - for(var/filter in fqs.devices) - var/list/f = fqs.devices[filter] - if(!f) - output += "  [filters[filter]]: ERROR
    " - continue - output += "  [filters[filter]]: [f.len]
    " - for(var/device in f) - if(isobj(device)) - output += "    [device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)])
    " - else - output += "    [device]
    " - - usr << browse(output,"window=radioreport") - - message_admins("[key_name_admin(usr)] has generated a radio report") - log_admin("[key_name(usr)] has generated a radio report") - - feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/reload_admins() - set name = "Reload Admins" - set category = "Debug" - - if(!check_rights(R_SERVER)) - return - - message_admins("[key_name_admin(usr)] has manually reloaded admins") - log_admin("[key_name(usr)] has manually reloaded admins") - - load_admins() - feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/print_jobban_old() - set name = "Print Jobban Log" - set desc = "This spams all the active jobban entries for the current round to standard output." - set category = "Debug" - - if(!check_rights(R_DEBUG)) - return - - to_chat(usr, "Jobbans active in this round.") - for(var/t in jobban_keylist) - to_chat(usr, "[t]") - - message_admins("[key_name_admin(usr)] has printed the jobban log") - log_admin("[key_name(usr)] has printed the jobban log") - -/client/proc/print_jobban_old_filter() - set name = "Search Jobban Log" - set desc = "This searches all the active jobban entries for the current round and outputs the results to standard output." - set category = "Debug" - - if(!check_rights(R_DEBUG)) - return - - var/filter = clean_input("Contains what?","Filter") - if(!filter) - return - - to_chat(usr, "Jobbans active in this round.") - for(var/t in jobban_keylist) - if(findtext(t, filter)) - to_chat(usr, "[t]") - - message_admins("[key_name_admin(usr)] has searched the jobban log for [filter]") - log_admin("[key_name(usr)] has searched the jobban log for [filter]") - -/client/proc/vv_by_ref() - set name = "VV by Ref" - set desc = "Give this a ref string, and you will see its corresponding VV panel if it exists" - set category = "Debug" - - // It's gated by "Debug Verbs", so might as well gate it to the debug permission - if(!check_rights(R_DEBUG)) - return - - var/refstring = clean_input("Which reference?","Ref") - if(!refstring) - return - - var/datum/D = locate(refstring) - if(!D) - to_chat(usr, "That ref string does not correspond to any datum.") - return - - debug_variables(D) +/client/proc/air_status(turf/target as turf) + set category = "Debug" + set name = "Display Air Status" + + if(!check_rights(R_DEBUG)) + return + + if(!isturf(target)) + return + + var/datum/gas_mixture/GM = target.return_air() + var/burning = 0 + if(istype(target, /turf/simulated)) + var/turf/simulated/T = target + if(T.active_hotspot) + burning = 1 + + to_chat(usr, "@[target.x],[target.y]: O:[GM.oxygen] T:[GM.toxins] N:[GM.nitrogen] C:[GM.carbon_dioxide] w [GM.temperature] Kelvin, [GM.return_pressure()] kPa [(burning)?("BURNING"):(null)]") + for(var/datum/gas/trace_gas in GM.trace_gases) + to_chat(usr, "[trace_gas.type]: [trace_gas.moles]") + + message_admins("[key_name_admin(usr)] has checked the air status of [target]") + log_admin("[key_name(usr)] has checked the air status of [target]") + + feedback_add_details("admin_verb","DAST") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/fix_next_move() + set category = "Debug" + set name = "Unfreeze Everyone" + + if(!check_rights(R_DEBUG)) + return + + message_admins("[key_name_admin(usr)] has unfrozen everyone") + log_admin("[key_name(usr)] has unfrozen everyone") + + var/largest_move_time = 0 + 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 + if(M.next_move >= largest_move_time) + largest_move_mob = M + if(M.next_move > world.time) + largest_move_time = M.next_move - world.time + else + largest_move_time = 1 + if(M.next_click >= largest_click_time) + largest_click_mob = M + if(M.next_click > world.time) + largest_click_time = M.next_click - world.time + else + largest_click_time = 0 + log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] next_click = [M.next_click] world.time = [world.time]") + M.next_move = 1 + M.next_click = 0 + + message_admins("[key_name_admin(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [largest_move_time/10] seconds!", 1) + message_admins("[key_name_admin(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [largest_click_time/10] seconds!", 1) + message_admins("world.time = [world.time]", 1) + + feedback_add_details("admin_verb","UFE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/radio_report() + set category = "Debug" + set name = "Radio report" + + if(!check_rights(R_DEBUG)) + return + + var/filters = list( + "1" = "RADIO_TO_AIRALARM", + "2" = "RADIO_FROM_AIRALARM", + "3" = "RADIO_CHAT", + "4" = "RADIO_ATMOSIA", + "5" = "RADIO_NAVBEACONS", + "6" = "RADIO_AIRLOCK", + "7" = "RADIO_SECBOT", + "8" = "RADIO_MULEBOT", + "_default" = "NO_FILTER" + ) + var/output = "Radio Report
    " + for(var/fq in SSradio.frequencies) + output += "Freq: [fq]
    " + var/datum/radio_frequency/fqs = SSradio.frequencies[fq] + if(!fqs) + output += "  ERROR
    " + continue + for(var/filter in fqs.devices) + var/list/f = fqs.devices[filter] + if(!f) + output += "  [filters[filter]]: ERROR
    " + continue + output += "  [filters[filter]]: [f.len]
    " + for(var/device in f) + if(isobj(device)) + output += "    [device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)])
    " + else + output += "    [device]
    " + + usr << browse(output,"window=radioreport") + + message_admins("[key_name_admin(usr)] has generated a radio report") + log_admin("[key_name(usr)] has generated a radio report") + + feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/reload_admins() + set name = "Reload Admins" + set category = "Debug" + + if(!check_rights(R_SERVER)) + return + + message_admins("[key_name_admin(usr)] has manually reloaded admins") + log_admin("[key_name(usr)] has manually reloaded admins") + + load_admins() + feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/print_jobban_old() + set name = "Print Jobban Log" + set desc = "This spams all the active jobban entries for the current round to standard output." + set category = "Debug" + + if(!check_rights(R_DEBUG)) + return + + to_chat(usr, "Jobbans active in this round.") + for(var/t in GLOB.jobban_keylist) + to_chat(usr, "[t]") + + message_admins("[key_name_admin(usr)] has printed the jobban log") + log_admin("[key_name(usr)] has printed the jobban log") + +/client/proc/print_jobban_old_filter() + set name = "Search Jobban Log" + set desc = "This searches all the active jobban entries for the current round and outputs the results to standard output." + set category = "Debug" + + if(!check_rights(R_DEBUG)) + return + + var/filter = clean_input("Contains what?","Filter") + if(!filter) + return + + to_chat(usr, "Jobbans active in this round.") + for(var/t in GLOB.jobban_keylist) + if(findtext(t, filter)) + to_chat(usr, "[t]") + + message_admins("[key_name_admin(usr)] has searched the jobban log for [filter]") + log_admin("[key_name(usr)] has searched the jobban log for [filter]") + +/client/proc/vv_by_ref() + set name = "VV by Ref" + set desc = "Give this a ref string, and you will see its corresponding VV panel if it exists" + set category = "Debug" + + // It's gated by "Debug Verbs", so might as well gate it to the debug permission + if(!check_rights(R_DEBUG)) + return + + var/refstring = clean_input("Which reference?","Ref") + if(!refstring) + return + + var/datum/D = locate(refstring) + if(!D) + to_chat(usr, "That ref string does not correspond to any datum.") + return + + debug_variables(D) diff --git a/code/modules/admin/verbs/dice.dm b/code/modules/admin/verbs/dice.dm index 4df6e3107a0..58bac2e113a 100644 --- a/code/modules/admin/verbs/dice.dm +++ b/code/modules/admin/verbs/dice.dm @@ -19,4 +19,4 @@ if(alert("Do you want to inform the world about the result?",,"Yes", "No") == "Yes") to_chat(world, "

    Gods rolled [dice], result is [result]

    ") - message_admins("[key_name_admin(src)] rolled dice [dice], result is [result]", 1) \ No newline at end of file + message_admins("[key_name_admin(src)] rolled dice [dice], result is [result]", 1) diff --git a/code/modules/admin/verbs/freeze.dm b/code/modules/admin/verbs/freeze.dm index 9b01e94846c..0f52680acea 100644 --- a/code/modules/admin/verbs/freeze.dm +++ b/code/modules/admin/verbs/freeze.dm @@ -5,121 +5,99 @@ //////Allows admin's to right click on any mob/mech and freeze them in place./// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -var/global/list/frozen_mob_list = list() -/client/proc/freeze(var/mob/living/M as mob in GLOB.mob_list) + +GLOBAL_LIST_EMPTY(frozen_atom_list) // A list of admin-frozen atoms. + +/client/proc/freeze(atom/movable/M) set name = "Freeze" set category = null if(!check_rights(R_ADMIN)) return - if(!istype(M)) - return + M.admin_Freeze(src) - if(M in frozen_mob_list) - M.admin_unFreeze(src) - else - M.admin_Freeze(src) +/// Created here as a base proc. Override as needed for any type of object or mob you want able to be frozen. +/atom/movable/proc/admin_Freeze(client/admin) + to_chat(admin, "Freeze is not able to be called on this type of object.You have been frozen by [admin]") - message_admins("[key_name_admin(admin)] froze [key_name_admin(src)]") - log_admin("[key_name(admin)] froze [key_name(src)]") +/mob/living/admin_Freeze(client/admin, skip_overlays = FALSE, mech = null) + if(!istype(admin)) + return - var/obj/effect/overlay/adminoverlay/AO = new - if(skip_overlays) - overlays += AO + if(!(src in GLOB.frozen_atom_list)) + GLOB.frozen_atom_list += src - anchored = TRUE - canmove = FALSE - admin_prev_sleeping = sleeping - AdjustSleeping(20000) - frozen = AO - if(!(src in frozen_mob_list)) - frozen_mob_list += src + var/obj/effect/overlay/adminoverlay/AO = new + if(skip_overlays) + overlays += AO -/mob/living/proc/admin_unFreeze(client/admin, skip_overlays = FALSE) - if(istype(admin)) - to_chat(src, "You have been unfrozen by [admin]") - message_admins("[key_name_admin(admin)] unfroze [key_name_admin(src)]") - log_admin("[key_name(admin)] unfroze [key_name(src)]") + anchored = TRUE + canmove = FALSE + admin_prev_sleeping = sleeping + AdjustSleeping(20000) + frozen = AO - if(skip_overlays) - overlays -= frozen + else + GLOB.frozen_atom_list -= src - anchored = FALSE - canmove = TRUE - frozen = null - SetSleeping(admin_prev_sleeping) - admin_prev_sleeping = null - if(src in frozen_mob_list) - frozen_mob_list -= src + if(skip_overlays) + overlays -= frozen + anchored = FALSE + canmove = TRUE + frozen = null + SetSleeping(admin_prev_sleeping) + admin_prev_sleeping = null + + to_chat(src, "You have been [frozen ? "frozen" : "unfrozen"] by [admin]") + message_admins("[key_name_admin(admin)] [frozen ? "froze" : "unfroze"] [key_name_admin(src)] [mech ? "in a [mech]" : ""]") + log_admin("[key_name(admin)] [frozen ? "froze" : "unfroze"] [key_name(src)] [mech ? "in a [mech]" : ""]") update_icons() + return frozen + /mob/living/simple_animal/slime/admin_Freeze(admin) - ..(admin) - adjustHealth(1000) //arbitrary large value - -/mob/living/simple_animal/slime/admin_unFreeze(admin) - ..(admin) - revive() - + if(..()) // The result of the parent call here will be the value of the mob's `frozen` variable after they get (un)frozen. + adjustHealth(1000) //arbitrary large value + else + revive() /mob/living/simple_animal/var/admin_prev_health = null /mob/living/simple_animal/admin_Freeze(admin) - ..(admin) - admin_prev_health = health - health = 0 - -/mob/living/simple_animal/admin_unFreeze(admin) - ..(admin) - revive() - overlays.Cut() + if(..()) // The result of the parent call here will be the value of the mob's `frozen` variable after they get (un)frozen. + admin_prev_health = health + health = 0 + else + revive() + overlays.Cut() //////////////////////////Freeze Mech -/client/proc/freezemecha(var/obj/mecha/O as obj in GLOB.mechas_list) - set name = "Freeze Mech" - set category = null - - if(!check_rights(R_ADMIN)) - return - - var/obj/mecha/M = O - if(!istype(M,/obj/mecha)) - to_chat(src, "This can only be used on mechs!") - return +/obj/mecha/admin_Freeze(client/admin) + var/obj/effect/overlay/adminoverlay/freeze_overlay = new + if(!frozen) + GLOB.frozen_atom_list += src + frozen = TRUE + overlays += freeze_overlay else - if(usr) - if(usr.client) - if(usr.client.holder) - var/adminomaly = new/obj/effect/overlay/adminoverlay - if(M.can_move == 1) - M.can_move = 0 - M.overlays += adminomaly - if(M.occupant) - to_chat(M.occupant, "You have been frozen by [key]") - message_admins("[key_name_admin(usr)] froze [key_name(M.occupant)] in a [M.name]") - log_admin("[key_name(usr)] froze [key_name(M.occupant)] in a [M.name]") - else - message_admins("[key_name_admin(usr)] froze an empty [M.name]") - log_admin("[key_name(usr)] froze an empty [M.name]") - else if(M.can_move == 0) - M.can_move = 1 - M.overlays -= adminomaly - if(M.occupant) - to_chat(M.occupant, "You have been unfrozen by [key]") - message_admins("[key_name_admin(usr)] unfroze [key_name(M.occupant)] in a [M.name]") - log_admin("[key_name(usr)] unfroze [key_name(M.occupant)] in a [M.name]") - else - message_admins("[key_name_admin(usr)] unfroze an empty [M.name]") - log_admin("[key_name(usr)] unfroze an empty [M.name]") + GLOB.frozen_atom_list -= src + frozen = FALSE + overlays -= freeze_overlay + + if(occupant) + occupant.admin_Freeze(admin, mech = name) // We also want to freeze the driver of the mech. + else + message_admins("[key_name_admin(admin)] [frozen ? "froze" : "unfroze"] an empty [name]") + log_admin("[key_name(admin)] [frozen ? "froze" : "unfroze"] an empty [name]") diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm index 22c6d778733..73f993c61de 100644 --- a/code/modules/admin/verbs/getlogs.dm +++ b/code/modules/admin/verbs/getlogs.dm @@ -38,4 +38,4 @@ else return to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.") - return \ No newline at end of file + return diff --git a/code/modules/admin/verbs/gimmick_team.dm b/code/modules/admin/verbs/gimmick_team.dm index cad93e8b880..37afe35ef6a 100644 --- a/code/modules/admin/verbs/gimmick_team.dm +++ b/code/modules/admin/verbs/gimmick_team.dm @@ -27,7 +27,7 @@ if(!themission) alert("No mission specified. Aborting.") return - var/admin_outfits = subtypesof(/datum/outfit/admin) + var/admin_outfits = subtypesof(/datum/outfit/admin) + list(/datum/outfit/naked) var/outfit_list = list() for(var/type in admin_outfits) var/datum/outfit/admin/O = type diff --git a/code/modules/admin/verbs/honksquad.dm b/code/modules/admin/verbs/honksquad.dm index f4013edbe1e..8c8d46ec97b 100644 --- a/code/modules/admin/verbs/honksquad.dm +++ b/code/modules/admin/verbs/honksquad.dm @@ -1,7 +1,7 @@ //HONKsquad -var/const/honksquad_possible = 6 //if more Commandos are needed in the future -var/global/sent_honksquad = 0 +#define HONKSQUAD_POSSIBLE 6 //if more Commandos are needed in the future +GLOBAL_VAR_INIT(sent_honksquad, 0) /client/proc/honksquad() if(!SSticker) @@ -10,7 +10,7 @@ var/global/sent_honksquad = 0 if(world.time < 6000) to_chat(usr, "There are [(6000-world.time)/10] seconds remaining before it may be called.") return - if(sent_honksquad == 1) + if(GLOB.sent_honksquad == 1) to_chat(usr, "Clown Planet has already dispatched a HONKsquad.") return if(alert("Do you want to send in the HONKsquad? Once enabled, this is irreversible.",,"Yes","No")!="Yes") @@ -24,14 +24,14 @@ var/global/sent_honksquad = 0 if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") return - if(sent_honksquad) + if(GLOB.sent_honksquad) to_chat(usr, "Looks like someone beat you to it. HONK.") return - sent_honksquad = 1 + GLOB.sent_honksquad = 1 - var/honksquad_number = honksquad_possible //for selecting a leader + var/honksquad_number = HONKSQUAD_POSSIBLE //for selecting a leader var/honk_leader_selected = 0 //when the leader is chosen. The last person spawned. @@ -42,7 +42,7 @@ var/global/sent_honksquad = 0 if(!G.client.holder && !G.client.is_afk()) //Whoever called/has the proc won't be added to the list. if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) candidates += G.key - for(var/i=honksquad_possible,(i>0&&candidates.len),i--)//Decrease with every commando selected. + for(var/i=HONKSQUAD_POSSIBLE,(i>0&&candidates.len),i--)//Decrease with every commando selected. var/candidate = input("Pick characters to spawn as the HONKsquad. This will go on until there either no more ghosts to pick from or the slots are full.", "Active Players") as null|anything in candidates //It will auto-pick a person when there is only one candidate. candidates -= candidate //Subtract from candidates. commandos += candidate//Add their ghost to commandos. @@ -132,7 +132,7 @@ var/global/sent_honksquad = 0 var/obj/item/card/id/W = new(src) W.name = "[real_name]'s ID Card" W.icon_state = "centcom_old" - W.access = list(access_clown)//They get full station access. + W.access = list(ACCESS_CLOWN)//They get full station access. W.assignment = "HONKsquad" W.registered_name = real_name equip_to_slot_or_del(W, slot_wear_id) diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm index a7da1553ec0..459f2b8fb4b 100644 --- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm +++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm @@ -1,7 +1,7 @@ // Syndicate Infiltration Team (SIT) // A little like Syndicate Strike Team (SST) but geared towards stealthy team missions rather than murderbone. -var/global/sent_syndicate_infiltration_team = 0 +GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0) /client/proc/syndicate_infiltration_team() set category = "Event" @@ -35,7 +35,7 @@ var/global/sent_syndicate_infiltration_team = 0 var/tctext = input(src, "How much TC do you want to give each team member? Suggested: 20-30. They cannot trade TC.") as num var/tcamount = text2num(tctext) tcamount = between(0, tcamount, 1000) - if(sent_syndicate_infiltration_team == 1) + if(GLOB.sent_syndicate_infiltration_team == 1) if(alert("A Syndicate Infiltration Team has already been sent. Sure you want to send another?",,"Yes","No")=="No") return @@ -61,7 +61,7 @@ var/global/sent_syndicate_infiltration_team = 0 to_chat(src, "Nobody volunteered.") return 0 - sent_syndicate_infiltration_team = 1 + GLOB.sent_syndicate_infiltration_team = 1 var/list/sit_spawns = list() var/list/sit_spawns_leader = list() @@ -90,7 +90,7 @@ var/global/sent_syndicate_infiltration_team = 0 to_chat(new_syndicate_infiltrator, "You are a [!syndicate_leader_selected?"Infiltrator":"Lead Infiltrator"] in the service of the Syndicate. \nYour current mission is: [input]") to_chat(new_syndicate_infiltrator, "You are equipped with an uplink implant to help you achieve your objectives. ((activate it via button in top left of screen))") new_syndicate_infiltrator.faction += "syndicate" - data_core.manifest_inject(new_syndicate_infiltrator) + GLOB.data_core.manifest_inject(new_syndicate_infiltrator) if(syndicate_leader_selected) var/obj/effect/landmark/warpto = pick(sit_spawns_leader) new_syndicate_infiltrator.loc = warpto.loc @@ -104,7 +104,7 @@ var/global/sent_syndicate_infiltration_team = 0 new_syndicate_infiltrator.mind.store_memory("Mission: [input] ") new_syndicate_infiltrator.mind.store_memory("Team Leader: [team_leader] ") new_syndicate_infiltrator.mind.store_memory("Starting Equipment:
    - Syndicate Headset ((.h for your radio))
    - Chameleon Jumpsuit ((right click to Change Color))
    - Agent ID card ((disguise as another job))
    - Uplink Implant ((top left of screen))
    - Dust Implant ((destroys your body on death))
    - Combat Gloves ((insulated, disguised as black gloves))
    - Anything bought with your uplink implant") - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] opshud.join_hud(new_syndicate_infiltrator.mind.current) set_antag_hud(new_syndicate_infiltrator.mind.current, "hudoperative") new_syndicate_infiltrator.regenerate_icons() @@ -177,10 +177,10 @@ var/global/sent_syndicate_infiltration_team = 0 W.icon_state = "commander" else W.icon_state = "id" - W.access = list(access_maint_tunnels,access_external_airlocks) + W.access = list(ACCESS_MAINT_TUNNELS,ACCESS_EXTERNAL_AIRLOCKS) W.assignment = "Civilian" W.access += get_access("Civilian") - W.access += list(access_medical, access_engine, access_cargo, access_research) + W.access += list(ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CARGO, ACCESS_RESEARCH) if(flag_mgmt) W.assignment = "Syndicate Management Consultant" W.access += get_syndicate_access("Syndicate Commando") diff --git a/code/modules/admin/verbs/logging_view.dm b/code/modules/admin/verbs/logging_view.dm new file mode 100644 index 00000000000..903926f4923 --- /dev/null +++ b/code/modules/admin/verbs/logging_view.dm @@ -0,0 +1,11 @@ +GLOBAL_LIST_INIT(open_logging_views, list()) + +/client/proc/cmd_admin_open_logging_view() + set category = "Admin" + set name = "Open Logging View" + set desc = "Opens the detailed logging viewer" + + if(!GLOB.open_logging_views[usr.client.ckey]) + GLOB.open_logging_views[usr.client.ckey] = new /datum/log_viewer() + var/datum/log_viewer/LV = GLOB.open_logging_views[usr.client.ckey] + LV.show_ui(usr) diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index f447810d860..edbca5a842f 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -6,10 +6,10 @@ return var/datum/map_template/template - var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in map_templates + var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in GLOB.map_templates if(!map) return - template = map_templates[map] + template = GLOB.map_templates[map] var/turf/T = get_turf(mob) if(!T) @@ -48,7 +48,7 @@ var/datum/map_template/M = new(map=map, rename="[map]") if(M.preload_size(map)) to_chat(usr, "Map template '[map]' ready to place ([M.width]x[M.height])") - map_templates[M.name] = M + GLOB.map_templates[M.name] = M message_admins("[key_name_admin(usr)] has uploaded a map template ([map]). Took [stop_watch(timer)]s.") else to_chat(usr, "Map template '[map]' failed to load properly") diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 80a171494ea..10ad31ef410 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -1,185 +1,185 @@ -//- Are all the floors with or without air, as they should be? (regular or airless) -//- Does the area have an APC? -//- Does the area have an Air Alarm? -//- Does the area have a Request Console? -//- Does the area have lights? -//- Does the area have a light switch? -//- Does the area have enough intercoms? -//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug) -//- Is the area connected to the scrubbers air loop? -//- Is the area connected to the vent air loop? (vent pumps) -//- Is everything wired properly? -//- Does the area have a fire alarm and firedoors? -//- Do all pod doors work properly? -//- Are accesses set properly on doors, pod buttons, etc. -//- Are all items placed properly? (not below vents, scrubbers, tables) -//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room? -//- Check for any misplaced or stacked piece of pipe (air and disposal) -//- Check for any misplaced or stacked piece of wire -//- Identify how hard it is to break into the area and where the weak points are -//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels. - -var/camera_range_display_status = 0 -var/intercom_range_display_status = 0 - -/obj/effect/debugging/camera_range - icon = 'icons/480x480.dmi' - icon_state = "25percent" - - New() - src.pixel_x = -224 - src.pixel_y = -224 - -/obj/effect/debugging/mapfix_marker - name = "map fix marker" - icon = 'icons/mob/screen_gen.dmi' - icon_state = "mapfixmarker" - desc = "I am a mappers mistake." - -/obj/effect/debugging/marker - icon = 'icons/turf/areas.dmi' - icon_state = "yellow" - -/obj/effect/debugging/marker/Move() - return 0 - -/client/proc/camera_view() - set category = "Mapping" - set name = "Camera Range Display" - - if(!check_rights(R_DEBUG)) - return - - if(camera_range_display_status) - camera_range_display_status = 0 - else - camera_range_display_status = 1 - - for(var/obj/effect/debugging/camera_range/C in world) - qdel(C) - - if(camera_range_display_status) - for(var/obj/machinery/camera/C in cameranet.cameras) - new/obj/effect/debugging/camera_range(C.loc) - feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/sec_camera_report() - set category = "Mapping" - set name = "Camera Report" - - if(!check_rights(R_DEBUG)) - return - - var/list/obj/machinery/camera/CL = list() - - for(var/obj/machinery/camera/C in cameranet.cameras) - CL += C - - var/output = {"CAMERA ANOMALIES REPORT
    -The following anomalies have been detected. The ones in red need immediate attention: Some of those in black may be intentional.
      "} - - for(var/obj/machinery/camera/C1 in CL) - for(var/obj/machinery/camera/C2 in CL) - if(C1 != C2) - if(C1.c_tag == C2.c_tag) - output += "
    • c_tag match for sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) and \[[C2.x], [C2.y], [C2.z]\] ([C2.loc.loc]) - c_tag is [C1.c_tag]
    • " - if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y) - output += "
    • FULLY overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
    • " - if(C1.loc == C2.loc) - output += "
    • overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
    • " - var/turf/T = get_step(C1,turn(C1.dir,180)) - if(!T || !isturf(T) || !T.density ) - if(!(locate(/obj/structure/grille,T))) - var/window_check = 0 - for(var/obj/structure/window/W in T) - if(W.dir == turn(C1.dir,180) || W.fulltile) - window_check = 1 - break - if(!window_check) - output += "
    • Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]
    • " - - output += "
    " - usr << browse(output,"window=airreport;size=1000x500") - feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/intercom_view() - set category = "Mapping" - set name = "Intercom Range Display" - - if(!check_rights(R_DEBUG)) - return - - if(intercom_range_display_status) - intercom_range_display_status = 0 - else - intercom_range_display_status = 1 - - for(var/obj/effect/debugging/marker/M in world) - qdel(M) - - if(intercom_range_display_status) - for(var/obj/item/radio/intercom/I in world) - for(var/turf/T in orange(7,I)) - var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T) - if(!(F in view(7,I.loc))) - qdel(F) - feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/count_objects_on_z_level() - set category = "Mapping" - set name = "Count Objects On Level" - - if(!check_rights(R_DEBUG)) - return - - var/level = clean_input("Which z-level?","Level?") - if(!level) return - var/num_level = text2num(level) - if(!num_level) return - if(!isnum(num_level)) return - - var/type_text = clean_input("Which type path?","Path?") - if(!type_text) return - var/type_path = text2path(type_text) - if(!type_path) return - - var/count = 0 - - 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))) - if(B && B.loc) - B = B.loc - else - break - if(B) - if(B.z == num_level) - count++ - atom_list += A - - to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level].") - feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/count_objects_all() - set category = "Mapping" - set name = "Count Objects All" - - if(!check_rights(R_DEBUG)) - return - - var/type_text = clean_input("Which type path?","") - if(!type_text) return - var/type_path = text2path(type_text) - if(!type_path) return - - var/count = 0 - - for(var/atom/A in world) - if(istype(A,type_path)) - count++ - - to_chat(world, "There are [count] objects of type [type_path] in the game world.") - feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +//- Are all the floors with or without air, as they should be? (regular or airless) +//- Does the area have an APC? +//- Does the area have an Air Alarm? +//- Does the area have a Request Console? +//- Does the area have lights? +//- Does the area have a light switch? +//- Does the area have enough intercoms? +//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug) +//- Is the area connected to the scrubbers air loop? +//- Is the area connected to the vent air loop? (vent pumps) +//- Is everything wired properly? +//- Does the area have a fire alarm and firedoors? +//- Do all pod doors work properly? +//- Are accesses set properly on doors, pod buttons, etc. +//- Are all items placed properly? (not below vents, scrubbers, tables) +//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room? +//- Check for any misplaced or stacked piece of pipe (air and disposal) +//- Check for any misplaced or stacked piece of wire +//- Identify how hard it is to break into the area and where the weak points are +//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels. + +GLOBAL_VAR_INIT(camera_range_display_status, 0) +GLOBAL_VAR_INIT(intercom_range_display_status, 0) + +/obj/effect/debugging/camera_range + icon = 'icons/480x480.dmi' + icon_state = "25percent" + + New() + src.pixel_x = -224 + src.pixel_y = -224 + +/obj/effect/debugging/mapfix_marker + name = "map fix marker" + icon = 'icons/mob/screen_gen.dmi' + icon_state = "mapfixmarker" + desc = "I am a mappers mistake." + +/obj/effect/debugging/marker + icon = 'icons/turf/areas.dmi' + icon_state = "yellow" + +/obj/effect/debugging/marker/Move() + return 0 + +/client/proc/camera_view() + set category = "Mapping" + set name = "Camera Range Display" + + if(!check_rights(R_DEBUG)) + return + + if(GLOB.camera_range_display_status) + GLOB.camera_range_display_status = 0 + else + GLOB.camera_range_display_status = 1 + + for(var/obj/effect/debugging/camera_range/C in world) + qdel(C) + + if(GLOB.camera_range_display_status) + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + new/obj/effect/debugging/camera_range(C.loc) + feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/sec_camera_report() + set category = "Mapping" + set name = "Camera Report" + + if(!check_rights(R_DEBUG)) + return + + var/list/obj/machinery/camera/CL = list() + + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + CL += C + + var/output = {"CAMERA ANOMALIES REPORT
    +The following anomalies have been detected. The ones in red need immediate attention: Some of those in black may be intentional.
      "} + + for(var/obj/machinery/camera/C1 in CL) + for(var/obj/machinery/camera/C2 in CL) + if(C1 != C2) + if(C1.c_tag == C2.c_tag) + output += "
    • c_tag match for sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) and \[[C2.x], [C2.y], [C2.z]\] ([C2.loc.loc]) - c_tag is [C1.c_tag]
    • " + if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y) + output += "
    • FULLY overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
    • " + if(C1.loc == C2.loc) + output += "
    • overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
    • " + var/turf/T = get_step(C1,turn(C1.dir,180)) + if(!T || !isturf(T) || !T.density ) + if(!(locate(/obj/structure/grille,T))) + var/window_check = 0 + for(var/obj/structure/window/W in T) + if(W.dir == turn(C1.dir,180) || W.fulltile) + window_check = 1 + break + if(!window_check) + output += "
    • Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]
    • " + + output += "
    " + usr << browse(output,"window=airreport;size=1000x500") + feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/intercom_view() + set category = "Mapping" + set name = "Intercom Range Display" + + if(!check_rights(R_DEBUG)) + return + + if(GLOB.intercom_range_display_status) + GLOB.intercom_range_display_status = 0 + else + GLOB.intercom_range_display_status = 1 + + for(var/obj/effect/debugging/marker/M in world) + qdel(M) + + if(GLOB.intercom_range_display_status) + for(var/obj/item/radio/intercom/I in world) + for(var/turf/T in orange(7,I)) + var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T) + if(!(F in view(7,I.loc))) + qdel(F) + feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/count_objects_on_z_level() + set category = "Mapping" + set name = "Count Objects On Level" + + if(!check_rights(R_DEBUG)) + return + + var/level = clean_input("Which z-level?","Level?") + if(!level) return + var/num_level = text2num(level) + if(!num_level) return + if(!isnum(num_level)) return + + var/type_text = clean_input("Which type path?","Path?") + if(!type_text) return + var/type_path = text2path(type_text) + if(!type_path) return + + var/count = 0 + + 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))) + if(B && B.loc) + B = B.loc + else + break + if(B) + if(B.z == num_level) + count++ + atom_list += A + + to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level].") + feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/count_objects_all() + set category = "Mapping" + set name = "Count Objects All" + + if(!check_rights(R_DEBUG)) + return + + var/type_text = clean_input("Which type path?","") + if(!type_text) return + var/type_path = text2path(type_text) + if(!type_path) return + + var/count = 0 + + for(var/atom/A in world) + if(istype(A,type_path)) + count++ + + to_chat(world, "There are [count] objects of type [type_path] in the game world.") + feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/massmodvar.dm b/code/modules/admin/verbs/massmodvar.dm index bafa7d629d3..50629d5a1cc 100644 --- a/code/modules/admin/verbs/massmodvar.dm +++ b/code/modules/admin/verbs/massmodvar.dm @@ -1,272 +1,272 @@ -/client/proc/cmd_mass_modify_object_variables(atom/A, var/var_name) - set category = "Debug" - set name = "Mass Edit Variables" - set desc="(target) Edit all instances of a target item's variables" - - var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL itms) - - if(!check_rights(R_VAREDIT)) return - - if(A && A.type) - if(typesof(A.type)) - switch(input("Strict object type detection?") as null|anything in list("Strictly this type","This type and subtypes", "Cancel")) - if("Strictly this type") - method = 0 - if("This type and subtypes") - method = 1 - if("Cancel") - return - if(null) - return - - src.massmodify_variables(A, var_name, method) - feedback_add_details("admin_verb","MEV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/massmodify_variables(datum/O, var_name = "", method = 0) - if(!check_rights(R_VAREDIT)) - return - if(!istype(O)) - return - - var/variable = "" - if(!var_name) - var/list/names = list() - for(var/V in O.vars) - names += V - - names = sortList(names) - - variable = input("Which var?", "Var") as null|anything in names - else - variable = var_name - - if(!variable || !O.can_vv_get(variable)) - return - var/default - var/var_value = O.vars[variable] - - if(variable in VVckey_edit) - to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.") - return - if(variable in VVlocked) - if(!check_rights(R_DEBUG)) - return - if(variable in VVicon_edit_lock) - if(!check_rights(R_EVENT | R_DEBUG)) - return - if(variable in VVpixelmovement) - if(!check_rights(R_DEBUG)) - return - var/prompt = alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") - if(prompt != "Continue") - return - - default = vv_get_class(var_value) - - if(isnull(default)) - to_chat(src, "Unable to determine variable type.") - else - to_chat(src, "Variable appears to be [uppertext(default)].") - - to_chat(src, "Variable contains: [var_value]") - - if(default == VV_NUM) - var/dir_text = "" - if(dir < 0 && dir < 16) - if(dir & 1) - dir_text += "NORTH" - if(dir & 2) - dir_text += "SOUTH" - if(dir & 4) - dir_text += "EAST" - if(dir & 8) - dir_text += "WEST" - - if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") - - var/value = vv_get_value(default_class = default) - var/new_value = value["value"] - var/class = value["class"] - - if(!class || !new_value == null && class != VV_NULL) - return - - if(class == VV_MESSAGE) - class = VV_TEXT - - if(value["type"]) - class = VV_NEW_TYPE - - var/original_name = "[O]" - - var/rejected = 0 - var/accepted = 0 - - switch(class) - if(VV_RESTORE_DEFAULT) - to_chat(src, "Finding items...") - var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") - for(var/thing in items) - if(!thing) - continue - var/datum/D = thing - if(D.vv_edit_var(variable, initial(D.vars[variable])) != FALSE) - accepted++ - else - rejected++ - CHECK_TICK - - if(VV_TEXT) - var/list/varsvars = vv_parse_text(O, new_value) - var/pre_processing = new_value - var/unique - if(varsvars && varsvars.len) - unique = alert(usr, "Process vars unique to each instance, or same for all?", "Variable Association", "Unique", "Same") - if(unique == "Unique") - unique = TRUE - else - unique = FALSE - for(var/V in varsvars) - new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]") - - to_chat(src, "Finding items...") - var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") - for(var/thing in items) - if(!thing) - continue - var/datum/D = thing - if(unique) - new_value = pre_processing - for(var/V in varsvars) - new_value = replacetext(new_value,"\[[V]]","[D.vars[V]]") - - if(D.vv_edit_var(variable, new_value) != FALSE) - accepted++ - else - rejected++ - CHECK_TICK - - if(VV_NEW_TYPE) - var/many = alert(src, "Create only one [value["type"]] and assign each or a new one for each thing", "How Many", "One", "Many", "Cancel") - if(many == "Cancel") - return - if(many == "Many") - many = TRUE - else - many = FALSE - - var/type = value["type"] - to_chat(src, "Finding items...") - var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") - for(var/thing in items) - if(!thing) - continue - var/datum/D = thing - if(many && !new_value) - new_value = new type() - - if(D.vv_edit_var(variable, new_value) != FALSE) - accepted++ - else - rejected++ - new_value = null - CHECK_TICK - - else - to_chat(src, "Finding items...") - var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") - for(var/thing in items) - if(!thing) - continue - var/datum/D = thing - if(D.vv_edit_var(variable, new_value) != FALSE) - accepted++ - else - rejected++ - CHECK_TICK - - - var/count = rejected+accepted - if(!count) - to_chat(src, "No objects found") - return - if(!accepted) - to_chat(src, "Every object rejected your edit") - return - if(rejected) - to_chat(src, "[rejected] out of [count] objects rejected your edit") - - log_world("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])") - log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") - message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") - -/proc/get_all_of_type(var/T, subtypes = TRUE) - var/list/typecache = list() - typecache[T] = 1 - if(subtypes) - typecache = typecacheof(typecache) - . = list() - if(ispath(T, /mob)) - for(var/mob/thing in GLOB.mob_list) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /obj/machinery/door)) - for(var/obj/machinery/door/thing in GLOB.airlocks) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /obj/machinery)) - for(var/obj/machinery/thing in GLOB.machines) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /obj)) - for(var/obj/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /atom/movable)) - for(var/atom/movable/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /turf)) - for(var/turf/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /atom)) - for(var/atom/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /client)) - for(var/client/thing in GLOB.clients) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /datum)) - for(var/datum/thing) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else - for(var/datum/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK \ No newline at end of file +/client/proc/cmd_mass_modify_object_variables(atom/A, var/var_name) + set category = "Debug" + set name = "Mass Edit Variables" + set desc="(target) Edit all instances of a target item's variables" + + var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL itms) + + if(!check_rights(R_VAREDIT)) return + + if(A && A.type) + if(typesof(A.type)) + switch(input("Strict object type detection?") as null|anything in list("Strictly this type","This type and subtypes", "Cancel")) + if("Strictly this type") + method = 0 + if("This type and subtypes") + method = 1 + if("Cancel") + return + if(null) + return + + src.massmodify_variables(A, var_name, method) + feedback_add_details("admin_verb","MEV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/massmodify_variables(datum/O, var_name = "", method = 0) + if(!check_rights(R_VAREDIT)) + return + if(!istype(O)) + return + + var/variable = "" + if(!var_name) + var/list/names = list() + for(var/V in O.vars) + names += V + + names = sortList(names) + + variable = input("Which var?", "Var") as null|anything in names + else + variable = var_name + + if(!variable || !O.can_vv_get(variable)) + return + var/default + var/var_value = O.vars[variable] + + if(variable in GLOB.VVckey_edit) + to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.") + return + if(variable in GLOB.VVlocked) + if(!check_rights(R_DEBUG)) + return + if(variable in GLOB.VVicon_edit_lock) + if(!check_rights(R_EVENT | R_DEBUG)) + return + if(variable in GLOB.VVpixelmovement) + if(!check_rights(R_DEBUG)) + return + var/prompt = alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") + if(prompt != "Continue") + return + + default = vv_get_class(var_value) + + if(isnull(default)) + to_chat(src, "Unable to determine variable type.") + else + to_chat(src, "Variable appears to be [uppertext(default)].") + + to_chat(src, "Variable contains: [var_value]") + + if(default == VV_NUM) + var/dir_text = "" + if(dir < 0 && dir < 16) + if(dir & 1) + dir_text += "NORTH" + if(dir & 2) + dir_text += "SOUTH" + if(dir & 4) + dir_text += "EAST" + if(dir & 8) + dir_text += "WEST" + + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + + var/value = vv_get_value(default_class = default) + var/new_value = value["value"] + var/class = value["class"] + + if(!class || !new_value == null && class != VV_NULL) + return + + if(class == VV_MESSAGE) + class = VV_TEXT + + if(value["type"]) + class = VV_NEW_TYPE + + var/original_name = "[O]" + + var/rejected = 0 + var/accepted = 0 + + switch(class) + if(VV_RESTORE_DEFAULT) + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(D.vv_edit_var(variable, initial(D.vars[variable])) != FALSE) + accepted++ + else + rejected++ + CHECK_TICK + + if(VV_TEXT) + var/list/varsvars = vv_parse_text(O, new_value) + var/pre_processing = new_value + var/unique + if(varsvars && varsvars.len) + unique = alert(usr, "Process vars unique to each instance, or same for all?", "Variable Association", "Unique", "Same") + if(unique == "Unique") + unique = TRUE + else + unique = FALSE + for(var/V in varsvars) + new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]") + + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(unique) + new_value = pre_processing + for(var/V in varsvars) + new_value = replacetext(new_value,"\[[V]]","[D.vars[V]]") + + if(D.vv_edit_var(variable, new_value) != FALSE) + accepted++ + else + rejected++ + CHECK_TICK + + if(VV_NEW_TYPE) + var/many = alert(src, "Create only one [value["type"]] and assign each or a new one for each thing", "How Many", "One", "Many", "Cancel") + if(many == "Cancel") + return + if(many == "Many") + many = TRUE + else + many = FALSE + + var/type = value["type"] + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(many && !new_value) + new_value = new type() + + if(D.vv_edit_var(variable, new_value) != FALSE) + accepted++ + else + rejected++ + new_value = null + CHECK_TICK + + else + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(D.vv_edit_var(variable, new_value) != FALSE) + accepted++ + else + rejected++ + CHECK_TICK + + + var/count = rejected+accepted + if(!count) + to_chat(src, "No objects found") + return + if(!accepted) + to_chat(src, "Every object rejected your edit") + return + if(rejected) + to_chat(src, "[rejected] out of [count] objects rejected your edit") + + log_world("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])") + log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") + message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") + +/proc/get_all_of_type(var/T, subtypes = TRUE) + var/list/typecache = list() + typecache[T] = 1 + if(subtypes) + typecache = typecacheof(typecache) + . = list() + if(ispath(T, /mob)) + for(var/mob/thing in GLOB.mob_list) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /obj/machinery/door)) + for(var/obj/machinery/door/thing in GLOB.airlocks) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /obj/machinery)) + for(var/obj/machinery/thing in GLOB.machines) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /obj)) + for(var/obj/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /atom/movable)) + for(var/atom/movable/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /turf)) + for(var/turf/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /atom)) + for(var/atom/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /client)) + for(var/client/thing in GLOB.clients) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /datum)) + for(var/datum/thing) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else + for(var/datum/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index c5348fbeabe..0474629dea0 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -1,633 +1,633 @@ -var/list/VVlocked = list("vars", "var_edited", "client", "firemut", "ishulk", "telekinesis", "xray", "ka", "virus", "viruses", "cuffed", "last_eaten", "unlock_content") // R_DEBUG -var/list/VVicon_edit_lock = list("icon", "icon_state", "overlays", "underlays", "resize") // R_EVENT | R_DEBUG -var/list/VVckey_edit = list("key", "ckey") // R_EVENT | R_DEBUG -var/list/VVpixelmovement = list("step_x", "step_y", "step_size", "bound_height", "bound_width", "bound_x", "bound_y") // R_DEBUG + warning -/client/proc/vv_get_class(var/var_value) - if(isnull(var_value)) - . = VV_NULL - - else if(isnum(var_value)) - . = VV_NUM - - else if(istext(var_value)) - if(findtext(var_value, "\n")) - . = VV_MESSAGE - else - . = VV_TEXT - - else if(isicon(var_value)) - . = VV_ICON - - else if(ismob(var_value)) - . = VV_MOB_REFERENCE - - else if(isloc(var_value)) - . = VV_ATOM_REFERENCE - - else if(istype(var_value, /matrix)) - . = VV_MATRIX - - else if(istype(var_value,/client)) - . = VV_CLIENT - - else if(istype(var_value, /datum)) - . = VV_DATUM_REFERENCE - - else if(ispath(var_value)) - if(ispath(var_value, /atom)) - . = VV_ATOM_TYPE - else if(ispath(var_value, /datum)) - . = VV_DATUM_TYPE - else - . = VV_TYPE - - else if(islist(var_value)) - . = VV_LIST - - else if(isfile(var_value)) - . = VV_FILE - else if(istype(var_value, /regex)) - . = VV_REGEX - else - . = VV_NULL - -/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes) - . = list("class" = class, "value" = null) - if(!class) - if(!classes) - classes = list( - VV_NUM, - VV_TEXT, - VV_MESSAGE, - VV_ICON, - VV_ATOM_REFERENCE, - VV_DATUM_REFERENCE, - VV_MOB_REFERENCE, - VV_CLIENT, - VV_ATOM_TYPE, - VV_DATUM_TYPE, - VV_TYPE, - VV_MATRIX, - VV_REGEX, - VV_FILE, - VV_NEW_ATOM, - VV_NEW_DATUM, - VV_NEW_TYPE, - VV_NEW_LIST, - VV_NULL, - VV_RESTORE_DEFAULT - ) - - if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes)) - classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])" - if(restricted_classes) - classes -= restricted_classes - - if(extra_classes) - classes += extra_classes - - .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes - if(holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])") - .["class"] = VV_MARKED_DATUM - - - switch(.["class"]) - if(VV_TEXT) - .["value"] = input("Enter new text:", "Text", current_value) as null|text - if(.["value"] == null) - .["class"] = null - return - if(VV_MESSAGE) - .["value"] = input("Enter new text:", "Text", current_value) as null|message - if(.["value"] == null) - .["class"] = null - return - - - if(VV_NUM) - .["value"] = input("Enter new number:", "Num", current_value) as null|num - if(.["value"] == null) - .["class"] = null - return - - - if(VV_ATOM_TYPE) - .["value"] = pick_closest_path(FALSE) - if(.["value"] == null) - .["class"] = null - return - - if(VV_DATUM_TYPE) - .["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - if(.["value"] == null) - .["class"] = null - return - - if(VV_TYPE) - var/type = current_value - var/error = "" - do - type = input("Enter type:[error]", "Type", type) as null|text - if(!type) - break - type = text2path(type) - error = "\nType not found, Please try again" - while(!type) - if(!type) - .["class"] = null - return - .["value"] = type - - if(VV_MATRIX) - .["value"] = text2matrix(input("Enter a, b, c, d, e, and f, seperated by a space.", "Matrix", "1 0 0 0 1 0") as null|text) - if(.["value"] == null) - .["class"] = null - return - - if(VV_REGEX) - var/reg = input("Enter regex", "Regex", "") as null|text - if(!reg) - return - .["value"] = regex(reg) - if(.["value"] == null) - .["class"] = null - - - if(VV_ATOM_REFERENCE) - var/type = pick_closest_path(FALSE) - var/subtypes = vv_subtype_prompt(type) - if(subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if(!value) - .["class"] = null - return - .["value"] = things[value] - - if(VV_DATUM_REFERENCE) - var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - var/subtypes = vv_subtype_prompt(type) - if(subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if(!value) - .["class"] = null - return - .["value"] = things[value] - - if(VV_MOB_REFERENCE) - var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob))) - var/subtypes = vv_subtype_prompt(type) - if(subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if(!value) - .["class"] = null - return - .["value"] = things[value] - - - - if(VV_CLIENT) - .["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients - if(.["value"] == null) - .["class"] = null - return - - - if(VV_FILE) - .["value"] = input("Pick file:", "File") as null|file - if(.["value"] == null) - .["class"] = null - return - - - if(VV_ICON) - .["value"] = input("Pick icon:", "Icon") as null|icon - if(.["value"] == null) - .["class"] = null - return - - - if(VV_MARKED_DATUM) - .["value"] = holder.marked_datum - if(.["value"] == null) - .["class"] = null - return - - if(VV_NEW_ATOM) - var/type = pick_closest_path(FALSE) - if(!type) - .["class"] = null - return - .["type"] = type - .["value"] = new type() - - if(VV_NEW_DATUM) - var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - if(!type) - .["class"] = null - return - .["type"] = type - .["value"] = new type() - - if(VV_NEW_TYPE) - var/type = current_value - var/error = "" - do - type = input("Enter type:[error]", "Type", type) as null|text - if(!type) - break - type = text2path(type) - error = "\nType not found, Please try again" - while(!type) - if(!type) - .["class"] = null - return - .["type"] = type - .["value"] = new type() - - - if(VV_NEW_LIST) - .["value"] = list() - .["type"] = /list - -/client/proc/vv_parse_text(O, new_var) - if(O && findtext(new_var, "\[")) - var/process_vars = alert(usr, "\[] detected in string, process as variables?", "Process Variables?", "Yes", "No") - if(process_vars == "Yes") - . = string2listofvars(new_var, O) - -//do they want you to include subtypes? -//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes) -//TRUE = Yes subtypes -//NULL = User cancelled at the prompt or invalid type given -/client/proc/vv_subtype_prompt(var/type) - if(!ispath(type)) - return - var/list/subtypes = subtypesof(type) - if(!subtypes || !subtypes.len) - return FALSE - if(subtypes && subtypes.len) - switch(alert("Strict object type detection?", "Type detection", "Strictly this type","This type and subtypes", "Cancel")) - if("Strictly this type") - return FALSE - if("This type and subtypes") - return TRUE - else - return - -/client/proc/vv_reference_list(type, subtypes) - . = list() - var/list/types = list(type) - if(subtypes) - types = typesof(type) - - var/list/fancytypes = make_types_fancy(types) - - for(var/fancytype in fancytypes) //swap the assoication - types[fancytypes[fancytype]] = fancytype - - var/things = get_all_of_type(type, subtypes) - - var/i = 0 - for(var/thing in things) - var/datum/D = thing - i++ - //try one of 3 methods to shorten the type text: - // fancy type, - // fancy type with the base type removed from the begaining, - // the type with the base type removed from the begaining - var/fancytype = types[D.type] - if(findtext(fancytype, types[type])) - fancytype = copytext(fancytype, length(types[type])+1) - var/shorttype = copytext("[D.type]", length("[type]")+1) - if(length(shorttype) > length(fancytype)) - shorttype = fancytype - if(!length(shorttype)) - shorttype = "/" - - .["[D]([shorttype])\ref[D]#[i]"] = D - -/client/proc/mod_list_add_ass(atom/O) //haha - var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - var/class = L["class"] - if(!class) - return - var/var_value = L["value"] - - if(class == VV_TEXT || class == VV_MESSAGE) - var/list/varsvars = vv_parse_text(O, var_value) - for(var/V in varsvars) - var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") - - return var_value - -/client/proc/mod_list_add(list/L, atom/O, original_name, objectvar) - var/list/LL = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - var/class = LL["class"] - if(!class) - return - var/var_value = LL["value"] - - if(class == VV_TEXT || class == VV_MESSAGE) - var/list/varsvars = vv_parse_text(O, var_value) - for(var/V in varsvars) - var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") - - if(O) - L = L.Copy() - - L += var_value - - switch(alert("Would you like to associate a value with the list entry?",,"Yes","No")) - if("Yes") - L[var_value] = mod_list_add_ass(O) //hehe - if(O) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") - message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") - -/client/proc/mod_list(list/L, atom/O, original_name, objectvar, index, autodetect_class = FALSE) - if(!check_rights(R_VAREDIT)) - return - if(!istype(L, /list)) - to_chat(src, "Not a List.") - return - - if(L.len > 1000) - var/confirm = alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", "Continue", "Abort") - if(confirm != "Continue") - return - - - - var/list/names = list() - for(var/i in 1 to L.len) - var/key = L[i] - var/value - if(IS_NORMAL_LIST(L) && !isnum(key)) - value = L[key] - if(value == null) - value = "null" - names["#[i] [key] = [value]"] = i - if(!index) - var/variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)" - - if(variable == null) - return - - if(variable == "(ADD VAR)") - mod_list_add(L, O, original_name, objectvar) - return - - if(variable == "(CLEAR NULLS)") - L = L.Copy() - listclearnulls(L) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS") - message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR NULLS") - return - - if(variable == "(CLEAR DUPES)") - L = uniqueList(L) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES") - message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR DUPES") - return - - if(variable == "(SHUFFLE)") - L = shuffle(L) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE") - message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: SHUFFLE") - return - - index = names[variable] - - - var/assoc_key - if(index == null) - return - var/assoc = 0 - var/prompt = alert(src, "Do you want to edit the key or it's assigned value?", "Associated List", "Key", "Assigned Value", "Cancel") - if(prompt == "Cancel") - return - if(prompt == "Assigned Value") - assoc = 1 - assoc_key = L[index] - var/default - var/variable - if(assoc) - variable = L[assoc_key] - else - variable = L[index] - - default = vv_get_class(variable) - - to_chat(src, "Variable appears to be [uppertext(default)].") - - to_chat(src, "Variable contains: [L[index]]") - - if(default == VV_NUM) - var/dir_text = "" - if(dir < 0 && dir < 16) - if(dir & 1) - dir_text += "NORTH" - if(dir & 2) - dir_text += "SOUTH" - if(dir & 4) - dir_text += "EAST" - if(dir & 8) - dir_text += "WEST" - - if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") - - var/original_var - if(assoc) - original_var = L[assoc_key] - else - original_var = L[index] - if(O) - L = L.Copy() - var/class - if(autodetect_class) - if(default == VV_TEXT) - default = VV_MESSAGE - class = default - var/list/LL = vv_get_value(default_class = default, current_value = original_var, restricted_classes = list(VV_RESTORE_DEFAULT), extra_classes = list(VV_LIST, "DELETE FROM LIST")) - class = LL["class"] - if(!class) - return - var/new_var = LL["value"] - - if(class == VV_MESSAGE) - class = VV_TEXT - - switch(class) //Spits a runtime error if you try to modify an entry in the contents list. Dunno how to fix it, yet. - if(VV_LIST) - mod_list(variable, O, original_name, objectvar) - - if("DELETE FROM LIST") - L.Cut(index, index+1) - if(O) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") - message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") - return - - if(VV_TEXT) - var/list/varsvars = vv_parse_text(O, new_var) - for(var/V in varsvars) - new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]") - - - if(assoc) - L[assoc_key] = new_var - else - L[index] = new_var - if(O) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]") - message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]") - -/proc/vv_varname_lockcheck(param_var_name) - if(param_var_name in VVlocked) - if(!check_rights(R_DEBUG)) - return FALSE - if(param_var_name in VVckey_edit) - if(!check_rights(R_EVENT | R_DEBUG)) - return FALSE - if(param_var_name in VVicon_edit_lock) - if(!check_rights(R_EVENT | R_DEBUG)) - return FALSE - if(param_var_name in VVpixelmovement) - if(!check_rights(R_DEBUG)) - return FALSE - var/prompt = alert(usr, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") - if(prompt != "Continue") - return FALSE - return TRUE - -/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0) - if(!check_rights(R_VAREDIT)) - return - - var/class - var/variable - var/var_value - - if(param_var_name) - if(!param_var_name in O.vars) - to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])") - return - variable = param_var_name - - else - var/list/names = list() - for(var/V in O.vars) - names += V - - names = sortList(names) - - variable = input("Which var?","Var") as null|anything in names - if(!variable) - return - - if(!O.can_vv_get(variable)) - return - - if(!vv_varname_lockcheck(variable)) - return - - var_value = O.vars[variable] - - var/default = vv_get_class(var_value) - - if(isnull(default)) - to_chat(src, "Unable to determine variable type.") - else - to_chat(src, "Variable appears to be [uppertext(default)].") - - to_chat(src, "Variable contains: [var_value]") - - if(default == VV_NUM) - var/dir_text = "" - if(dir < 0 && dir < 16) - if(dir & 1) - dir_text += "NORTH" - if(dir & 2) - dir_text += "SOUTH" - if(dir & 4) - dir_text += "EAST" - if(dir & 8) - dir_text += "WEST" - - if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") - - if(autodetect_class && default != VV_NULL) - if(default == VV_TEXT) - default = VV_MESSAGE - class = default - - var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST)) - class = value["class"] - - if(!class) - return - var/var_new = value["value"] - - if(class == VV_MESSAGE) - class = VV_TEXT - - var/original_name = "[O]" - - switch(class) - if(VV_LIST) - if(!islist(var_value)) - mod_list(list(), O, original_name, variable) - - mod_list(var_value, O, original_name, variable) - return - - if(VV_RESTORE_DEFAULT) - var_new = initial(O.vars[variable]) - - if(VV_TEXT) - var/list/varsvars = vv_parse_text(O, var_new) - for(var/V in varsvars) - var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]") - - if(!O.vv_edit_var(variable, var_new)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### VarEdit by [src]: [O.type] [variable]=[html_encode("[var_new]")]") - log_admin("[key_name(src)] modified [original_name]'s [variable] to [var_new]") - var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] to [var_new]" - message_admins(msg) +GLOBAL_LIST_INIT(VVlocked, list("vars", "var_edited", "client", "firemut", "ishulk", "telekinesis", "xray", "ka", "virus", "viruses", "cuffed", "last_eaten", "unlock_content")) // R_DEBUG +GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays", "resize")) // R_EVENT | R_DEBUG +GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) // R_EVENT | R_DEBUG +GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_height", "bound_width", "bound_x", "bound_y")) // R_DEBUG + warning +/client/proc/vv_get_class(var/var_value) + if(isnull(var_value)) + . = VV_NULL + + else if(isnum(var_value)) + . = VV_NUM + + else if(istext(var_value)) + if(findtext(var_value, "\n")) + . = VV_MESSAGE + else + . = VV_TEXT + + else if(isicon(var_value)) + . = VV_ICON + + else if(ismob(var_value)) + . = VV_MOB_REFERENCE + + else if(isloc(var_value)) + . = VV_ATOM_REFERENCE + + else if(istype(var_value, /matrix)) + . = VV_MATRIX + + else if(istype(var_value,/client)) + . = VV_CLIENT + + else if(istype(var_value, /datum)) + . = VV_DATUM_REFERENCE + + else if(ispath(var_value)) + if(ispath(var_value, /atom)) + . = VV_ATOM_TYPE + else if(ispath(var_value, /datum)) + . = VV_DATUM_TYPE + else + . = VV_TYPE + + else if(islist(var_value)) + . = VV_LIST + + else if(isfile(var_value)) + . = VV_FILE + else if(istype(var_value, /regex)) + . = VV_REGEX + else + . = VV_NULL + +/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes) + . = list("class" = class, "value" = null) + if(!class) + if(!classes) + classes = list( + VV_NUM, + VV_TEXT, + VV_MESSAGE, + VV_ICON, + VV_ATOM_REFERENCE, + VV_DATUM_REFERENCE, + VV_MOB_REFERENCE, + VV_CLIENT, + VV_ATOM_TYPE, + VV_DATUM_TYPE, + VV_TYPE, + VV_MATRIX, + VV_REGEX, + VV_FILE, + VV_NEW_ATOM, + VV_NEW_DATUM, + VV_NEW_TYPE, + VV_NEW_LIST, + VV_NULL, + VV_RESTORE_DEFAULT + ) + + if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes)) + classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])" + if(restricted_classes) + classes -= restricted_classes + + if(extra_classes) + classes += extra_classes + + .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes + if(holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])") + .["class"] = VV_MARKED_DATUM + + + switch(.["class"]) + if(VV_TEXT) + .["value"] = input("Enter new text:", "Text", current_value) as null|text + if(.["value"] == null) + .["class"] = null + return + if(VV_MESSAGE) + .["value"] = input("Enter new text:", "Text", current_value) as null|message + if(.["value"] == null) + .["class"] = null + return + + + if(VV_NUM) + .["value"] = input("Enter new number:", "Num", current_value) as null|num + if(.["value"] == null) + .["class"] = null + return + + + if(VV_ATOM_TYPE) + .["value"] = pick_closest_path(FALSE) + if(.["value"] == null) + .["class"] = null + return + + if(VV_DATUM_TYPE) + .["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if(.["value"] == null) + .["class"] = null + return + + if(VV_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if(!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if(!type) + .["class"] = null + return + .["value"] = type + + if(VV_MATRIX) + .["value"] = text2matrix(input("Enter a, b, c, d, e, and f, seperated by a space.", "Matrix", "1 0 0 0 1 0") as null|text) + if(.["value"] == null) + .["class"] = null + return + + if(VV_REGEX) + var/reg = input("Enter regex", "Regex", "") as null|text + if(!reg) + return + .["value"] = regex(reg) + if(.["value"] == null) + .["class"] = null + + + if(VV_ATOM_REFERENCE) + var/type = pick_closest_path(FALSE) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + if(VV_DATUM_REFERENCE) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + if(VV_MOB_REFERENCE) + var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob))) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + + + if(VV_CLIENT) + .["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients + if(.["value"] == null) + .["class"] = null + return + + + if(VV_FILE) + .["value"] = input("Pick file:", "File") as null|file + if(.["value"] == null) + .["class"] = null + return + + + if(VV_ICON) + .["value"] = input("Pick icon:", "Icon") as null|icon + if(.["value"] == null) + .["class"] = null + return + + + if(VV_MARKED_DATUM) + .["value"] = holder.marked_datum + if(.["value"] == null) + .["class"] = null + return + + if(VV_NEW_ATOM) + var/type = pick_closest_path(FALSE) + if(!type) + .["class"] = null + return + .["type"] = type + .["value"] = new type() + + if(VV_NEW_DATUM) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if(!type) + .["class"] = null + return + .["type"] = type + .["value"] = new type() + + if(VV_NEW_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if(!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if(!type) + .["class"] = null + return + .["type"] = type + .["value"] = new type() + + + if(VV_NEW_LIST) + .["value"] = list() + .["type"] = /list + +/client/proc/vv_parse_text(O, new_var) + if(O && findtext(new_var, "\[")) + var/process_vars = alert(usr, "\[] detected in string, process as variables?", "Process Variables?", "Yes", "No") + if(process_vars == "Yes") + . = string2listofvars(new_var, O) + +//do they want you to include subtypes? +//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes) +//TRUE = Yes subtypes +//NULL = User cancelled at the prompt or invalid type given +/client/proc/vv_subtype_prompt(var/type) + if(!ispath(type)) + return + var/list/subtypes = subtypesof(type) + if(!subtypes || !subtypes.len) + return FALSE + if(subtypes && subtypes.len) + switch(alert("Strict object type detection?", "Type detection", "Strictly this type","This type and subtypes", "Cancel")) + if("Strictly this type") + return FALSE + if("This type and subtypes") + return TRUE + else + return + +/client/proc/vv_reference_list(type, subtypes) + . = list() + var/list/types = list(type) + if(subtypes) + types = typesof(type) + + var/list/fancytypes = make_types_fancy(types) + + for(var/fancytype in fancytypes) //swap the assoication + types[fancytypes[fancytype]] = fancytype + + var/things = get_all_of_type(type, subtypes) + + var/i = 0 + for(var/thing in things) + var/datum/D = thing + i++ + //try one of 3 methods to shorten the type text: + // fancy type, + // fancy type with the base type removed from the begaining, + // the type with the base type removed from the begaining + var/fancytype = types[D.type] + if(findtext(fancytype, types[type])) + fancytype = copytext(fancytype, length(types[type])+1) + var/shorttype = copytext("[D.type]", length("[type]")+1) + if(length(shorttype) > length(fancytype)) + shorttype = fancytype + if(!length(shorttype)) + shorttype = "/" + + .["[D]([shorttype])\ref[D]#[i]"] = D + +/client/proc/mod_list_add_ass(atom/O) //haha + var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + var/class = L["class"] + if(!class) + return + var/var_value = L["value"] + + if(class == VV_TEXT || class == VV_MESSAGE) + var/list/varsvars = vv_parse_text(O, var_value) + for(var/V in varsvars) + var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") + + return var_value + +/client/proc/mod_list_add(list/L, atom/O, original_name, objectvar) + var/list/LL = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + var/class = LL["class"] + if(!class) + return + var/var_value = LL["value"] + + if(class == VV_TEXT || class == VV_MESSAGE) + var/list/varsvars = vv_parse_text(O, var_value) + for(var/V in varsvars) + var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") + + if(O) + L = L.Copy() + + L += var_value + + switch(alert("Would you like to associate a value with the list entry?",,"Yes","No")) + if("Yes") + L[var_value] = mod_list_add_ass(O) //hehe + if(O) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") + message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") + +/client/proc/mod_list(list/L, atom/O, original_name, objectvar, index, autodetect_class = FALSE) + if(!check_rights(R_VAREDIT)) + return + if(!istype(L, /list)) + to_chat(src, "Not a List.") + return + + if(L.len > 1000) + var/confirm = alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", "Continue", "Abort") + if(confirm != "Continue") + return + + + + var/list/names = list() + for(var/i in 1 to L.len) + var/key = L[i] + var/value + if(IS_NORMAL_LIST(L) && !isnum(key)) + value = L[key] + if(value == null) + value = "null" + names["#[i] [key] = [value]"] = i + if(!index) + var/variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)" + + if(variable == null) + return + + if(variable == "(ADD VAR)") + mod_list_add(L, O, original_name, objectvar) + return + + if(variable == "(CLEAR NULLS)") + L = L.Copy() + listclearnulls(L) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS") + message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR NULLS") + return + + if(variable == "(CLEAR DUPES)") + L = uniqueList(L) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES") + message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR DUPES") + return + + if(variable == "(SHUFFLE)") + L = shuffle(L) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE") + message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: SHUFFLE") + return + + index = names[variable] + + + var/assoc_key + if(index == null) + return + var/assoc = 0 + var/prompt = alert(src, "Do you want to edit the key or it's assigned value?", "Associated List", "Key", "Assigned Value", "Cancel") + if(prompt == "Cancel") + return + if(prompt == "Assigned Value") + assoc = 1 + assoc_key = L[index] + var/default + var/variable + if(assoc) + variable = L[assoc_key] + else + variable = L[index] + + default = vv_get_class(variable) + + to_chat(src, "Variable appears to be [uppertext(default)].") + + to_chat(src, "Variable contains: [L[index]]") + + if(default == VV_NUM) + var/dir_text = "" + if(dir < 0 && dir < 16) + if(dir & 1) + dir_text += "NORTH" + if(dir & 2) + dir_text += "SOUTH" + if(dir & 4) + dir_text += "EAST" + if(dir & 8) + dir_text += "WEST" + + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + + var/original_var + if(assoc) + original_var = L[assoc_key] + else + original_var = L[index] + if(O) + L = L.Copy() + var/class + if(autodetect_class) + if(default == VV_TEXT) + default = VV_MESSAGE + class = default + var/list/LL = vv_get_value(default_class = default, current_value = original_var, restricted_classes = list(VV_RESTORE_DEFAULT), extra_classes = list(VV_LIST, "DELETE FROM LIST")) + class = LL["class"] + if(!class) + return + var/new_var = LL["value"] + + if(class == VV_MESSAGE) + class = VV_TEXT + + switch(class) //Spits a runtime error if you try to modify an entry in the contents list. Dunno how to fix it, yet. + if(VV_LIST) + mod_list(variable, O, original_name, objectvar) + + if("DELETE FROM LIST") + L.Cut(index, index+1) + if(O) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") + message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") + return + + if(VV_TEXT) + var/list/varsvars = vv_parse_text(O, new_var) + for(var/V in varsvars) + new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]") + + + if(assoc) + L[assoc_key] = new_var + else + L[index] = new_var + if(O) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]") + message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]") + +/proc/vv_varname_lockcheck(param_var_name) + if(param_var_name in GLOB.VVlocked) + if(!check_rights(R_DEBUG)) + return FALSE + if(param_var_name in GLOB.VVckey_edit) + if(!check_rights(R_EVENT | R_DEBUG)) + return FALSE + if(param_var_name in GLOB.VVicon_edit_lock) + if(!check_rights(R_EVENT | R_DEBUG)) + return FALSE + if(param_var_name in GLOB.VVpixelmovement) + if(!check_rights(R_DEBUG)) + return FALSE + var/prompt = alert(usr, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") + if(prompt != "Continue") + return FALSE + return TRUE + +/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0) + if(!check_rights(R_VAREDIT)) + return + + var/class + var/variable + var/var_value + + if(param_var_name) + if(!(param_var_name in O.vars)) + to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])") + return + variable = param_var_name + + else + var/list/names = list() + for(var/V in O.vars) + names += V + + names = sortList(names) + + variable = input("Which var?","Var") as null|anything in names + if(!variable) + return + + if(!O.can_vv_get(variable)) + return + + if(!vv_varname_lockcheck(variable)) + return + + var_value = O.vars[variable] + + var/default = vv_get_class(var_value) + + if(isnull(default)) + to_chat(src, "Unable to determine variable type.") + else + to_chat(src, "Variable appears to be [uppertext(default)].") + + to_chat(src, "Variable contains: [var_value]") + + if(default == VV_NUM) + var/dir_text = "" + if(dir < 0 && dir < 16) + if(dir & 1) + dir_text += "NORTH" + if(dir & 2) + dir_text += "SOUTH" + if(dir & 4) + dir_text += "EAST" + if(dir & 8) + dir_text += "WEST" + + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + + if(autodetect_class && default != VV_NULL) + if(default == VV_TEXT) + default = VV_MESSAGE + class = default + + var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST)) + class = value["class"] + + if(!class) + return + var/var_new = value["value"] + + if(class == VV_MESSAGE) + class = VV_TEXT + + var/original_name = "[O]" + + switch(class) + if(VV_LIST) + if(!islist(var_value)) + mod_list(list(), O, original_name, variable) + + mod_list(var_value, O, original_name, variable) + return + + if(VV_RESTORE_DEFAULT) + var_new = initial(O.vars[variable]) + + if(VV_TEXT) + var/list/varsvars = vv_parse_text(O, var_new) + for(var/V in varsvars) + var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]") + + if(!O.vv_edit_var(variable, var_new)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### VarEdit by [src]: [O.type] [variable]=[html_encode("[var_new]")]") + log_admin("[key_name(src)] modified [original_name]'s [variable] to [var_new]") + var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] to [var_new]" + message_admins(msg) diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index a2a95db957a..57f7cad31c4 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -1,632 +1,632 @@ -client/proc/one_click_antag() - set name = "Create Antagonist" - set desc = "Auto-create an antagonist of your choice" - set category = "Event" - - if(!check_rights(R_SERVER|R_EVENT)) return - - if(holder) - holder.one_click_antag() - return - - -/datum/admins/proc/one_click_antag() - - var/dat = {"One-click Antagonist
    - Make Traitors
    - Make Changelings
    - Make Revolutionaries
    - Make Cult
    - Make Wizard (Requires Ghosts)
    - Make Vampires
    - Make Vox Raiders (Requires Ghosts)
    - Make Abductor Team (Requires Ghosts)
    - "} - usr << browse(dat, "window=oneclickantag;size=400x400") - return - -/datum/admins/proc/CandCheck(var/role = null, var/mob/living/carbon/human/M, var/datum/game_mode/temp = null) - // You pass in ROLE define (optional), the applicant, and the gamemode, and it will return true / false depending on whether the applicant qualify for the candidacy in question - if(jobban_isbanned(M, "Syndicate")) - return FALSE - if(M.stat || !M.mind || M.mind.special_role || M.mind.offstation_role) - return FALSE - if(temp) - if((M.mind.assigned_role in temp.restricted_jobs) || (M.client.prefs.species in temp.protected_species)) - return FALSE - if(role) // Don't even bother evaluating if there's no role - if(player_old_enough_antag(M.client,role) && (role in M.client.prefs.be_special) && !M.client.skip_antag && (!jobban_isbanned(M, role))) - return TRUE - else - return FALSE - else - return TRUE - -/datum/admins/proc/makeTraitors() - var/datum/game_mode/traitor/temp = new - - if(config.protect_roles_from_antagonist) - temp.restricted_jobs += temp.protected_jobs - - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H = null - - var/antnum = input(owner, "How many traitors you want to create? Enter 0 to cancel","Amount:", 0) as num - if(!antnum || antnum <= 0) - return - log_admin("[key_name(owner)] tried making [antnum] traitors with One-Click-Antag") - message_admins("[key_name_admin(owner)] tried making [antnum] traitors with One-Click-Antag") - - for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(CandCheck(ROLE_TRAITOR, applicant, temp)) - candidates += applicant - - if(candidates.len) - var/numTraitors = min(candidates.len, antnum) - - for(var/i = 0, i300)//If more than 30 game seconds passed. - return - candidates += G - if("No") - return - else - return - - sleep(300) - - if(candidates.len) - var/agentcount = 0 - - for(var/i = 0, i300)//If more than 30 game seconds passed. - return - candidates += G - if("No") - return - else - return - sleep(300) - - for(var/mob/dead/observer/G in candidates) - if(!G.key) - candidates.Remove(G) - - if(candidates.len) - //Spawns commandos and equips them. - for(var/obj/effect/landmark/L in /area/syndicate_mothership/elite_squad) - if(antnum <= 0) - break - if(L.name == "Syndicate-Commando") - syndicate_leader_selected = antnum == 1?1:0 - - var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, syndicate_leader_selected) - - while((!theghost || !theghost.client) && candidates.len) - theghost = pick(candidates) - candidates.Remove(theghost) - - if(!theghost) - qdel(new_syndicate_commando) - break - - new_syndicate_commando.key = theghost.key - new_syndicate_commando.internal = new_syndicate_commando.s_store - new_syndicate_commando.update_action_buttons_icon() - - //So they don't forget their code or mission. - - - to_chat(new_syndicate_commando, "You are an Elite Syndicate. [!syndicate_leader_selected ? "commando" : "LEADER"] in the service of the Syndicate. \nYour current mission is: [input]") - - antnum-- - - for(var/obj/effect/landmark/L in /area/shuttle/syndicate_elite) - if(L.name == "Syndicate-Commando-Bomb") - new /obj/effect/spawner/newbomb/timer/syndicate(L.loc) - return 1 - - -/proc/makeBody(var/mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character - if(!G_found || !G_found.key) return - - //First we spawn a dude. - var/mob/living/carbon/human/new_character = new(pick(latejoin))//The mob being spawned. - - var/datum/preferences/A = new(G_found.client) - A.copy_to(new_character) - - new_character.dna.ready_dna(new_character) - new_character.key = G_found.key - - return new_character - -/datum/admins/proc/create_syndicate_death_commando(obj/spawn_location, syndicate_leader_selected = 0) - var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) - var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") - var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") - var/syndicate_commando_name = pick(GLOB.last_names) - - var/datum/preferences/A = new()//Randomize appearance for the commando. - if(syndicate_leader_selected) - A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" - A.age = rand(35,45) - else - A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" - A.copy_to(new_syndicate_commando) - - new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. - - //Creates mind stuff. - new_syndicate_commando.mind_initialize() - new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.offstation_role = TRUE - //Adds them to current traitor list. Which is really the extra antagonist list. - SSticker.mode.traitors += new_syndicate_commando.mind - new_syndicate_commando.equip_syndicate_commando(syndicate_leader_selected) - - return new_syndicate_commando - -/datum/admins/proc/makeVoxRaiders() - - var/list/mob/candidates = list() - var/mob/theghost = null - var/time_passed = world.time - var/input = "Disregard shinies, acquire hardware." - - var/leader_chosen = 0 //when the leader is chosen. The last person spawned. - - var/antnum = input(owner, "How many raiders you want to create? Enter 0 to cancel.","Amount:", 0) as num - if(!antnum || antnum <= 0) - return - log_admin("[key_name(owner)] tried making Vox Raiders with One-Click-Antag") - message_admins("[key_name_admin(owner)] tried making Vox Raiders with One-Click-Antag") -//Generates a list of candidates from active ghosts. - for(var/mob/G in GLOB.respawnable_list) - if(istype(G) && G.client && (ROLE_RAIDER in G.client.prefs.be_special)) - if(player_old_enough_antag(G.client,ROLE_RAIDER)) - if(!jobban_isbanned(G, "raider") && !jobban_isbanned(G, "Syndicate")) - spawn(0) - switch(alert(G,"Do you wish to be considered for a vox raiding party arriving on the station?","Please answer in 30 seconds!","Yes","No")) - if("Yes") - if((world.time-time_passed)>300)//If more than 30 game seconds passed. - return - candidates += G - if("No") - return - else - return - - sleep(300) //Debug. - - for(var/mob/dead/observer/G in candidates) - if(!G.key) - candidates.Remove(G) - - if(candidates.len) - var/raiders = min(antnum, candidates.len) - //Spawns vox raiders and equips them. - for(var/obj/effect/landmark/L in world) - if(L.name == "voxstart") - if(raiders<=0) - break - - var/mob/living/carbon/human/new_vox = create_vox_raider(L, leader_chosen) - - while((!theghost || !theghost.client) && candidates.len) - theghost = pick(candidates) - candidates.Remove(theghost) - - if(!theghost) - qdel(new_vox) - break - - new_vox.key = theghost.key - SSticker.mode.traitors += new_vox.mind - - to_chat(new_vox, "You are a Vox Primalis, fresh out of the Shoal. Your ship has arrived at the Tau Ceti system hosting the NSV Exodus... or was it the Luna? NSS? Utopia? Nobody is really sure, but everyong is raring to start pillaging! Your current goal is: [input]") - to_chat(new_vox, "Don't forget to turn on your nitrogen internals!") - - raiders-- - else - return 0 - return 1 - -/datum/admins/proc/create_vox_raider(obj/spawn_location, leader_chosen = 0) - - var/sounds = rand(2,8) - var/i = 0 - var/newname = "" - - while(i<=sounds) - i++ - newname += pick(list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) - - var/mob/living/carbon/human/new_vox = new /mob/living/carbon/human/vox(spawn_location.loc) - - new_vox.add_language("Tradeband") - new_vox.real_name = capitalize(newname) - new_vox.dna.real_name = new_vox.real_name - new_vox.name = new_vox.real_name - new_vox.age = rand(12,20) - new_vox.flavor_text = "" - new_vox.change_eye_color(rand(1, 255), rand(1, 255), rand(1, 255)) - new_vox.s_tone = rand(1, 6) - - // Do the initial caching of the player's body icons. - new_vox.force_update_limbs() - new_vox.update_dna() - new_vox.update_eyes() - - for(var/obj/item/organ/external/limb in new_vox.bodyparts) - limb.status &= ~ORGAN_ROBOT - - //Now apply cortical stack. - var/obj/item/implant/cortical/I = new(new_vox) - I.implant(new_vox) - cortical_stacks += I - - new_vox.equip_vox_raider() - new_vox.regenerate_icons() - - return new_vox - -/datum/admins/proc/makeVampires() - - var/datum/game_mode/vampire/temp = new - if(config.protect_roles_from_antagonist) - temp.restricted_jobs += temp.protected_jobs - - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H = null - - var/antnum = input(owner, "How many vampires you want to create? Enter 0 to cancel","Amount:", 0) as num - if(!antnum || antnum <= 0) - return - - log_admin("[key_name(owner)] tried making Vampires with One-Click-Antag") - message_admins("[key_name_admin(owner)] tried making Vampires with One-Click-Antag") - - for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(CandCheck(ROLE_VAMPIRE, applicant, temp)) - candidates += applicant - - if(candidates.len) - var/numVampires = min(candidates.len, antnum) - - for(var/i = 0, i300)//If more than 30 game seconds passed. - return - candidates += G - if("No") - return - else - return - - sleep(300) //Debug. - - for(var/mob/dead/observer/G in candidates) - if(!G.key) - candidates.Remove(G) - - if(candidates.len) - var/teamOneMembers = 5 - var/teamTwoMembers = 5 - var/datum/preferences/A = new() - for(var/obj/effect/landmark/L in world) - if(L.name == "tdome1") - if(teamOneMembers<=0) - break - - var/mob/living/carbon/human/newMember = new(L.loc) - - A.copy_to(newMember) - - newMember.dna.ready_dna(newMember) - - while((!theghost || !theghost.client) && candidates.len) - theghost = pick(candidates) - candidates.Remove(theghost) - - if(!theghost) - qdel(newMember) - break - - newMember.key = theghost.key - teamOneMembers-- - to_chat(newMember, "You are a member of the GREEN Thunderdome team! Gear up and help your team destroy the red team!") - - if(L.name == "tdome2") - if(teamTwoMembers<=0) - break - - var/mob/living/carbon/human/newMember = new(L.loc) - - A.copy_to(newMember) - - newMember.dna.ready_dna(newMember) - - while((!theghost || !theghost.client) && candidates.len) - theghost = pick(candidates) - candidates.Remove(theghost) - - if(!theghost) - qdel(newMember) - break - - newMember.key = theghost.key - teamTwoMembers-- - to_chat(newMember, "You are a member of the RED Thunderdome team! Gear up and help your team destroy the green team!") - else - return 0 - return 1 +client/proc/one_click_antag() + set name = "Create Antagonist" + set desc = "Auto-create an antagonist of your choice" + set category = "Event" + + if(!check_rights(R_SERVER|R_EVENT)) return + + if(holder) + holder.one_click_antag() + return + + +/datum/admins/proc/one_click_antag() + + var/dat = {"One-click Antagonist
    + Make Traitors
    + Make Changelings
    + Make Revolutionaries
    + Make Cult
    + Make Wizard (Requires Ghosts)
    + Make Vampires
    + Make Vox Raiders (Requires Ghosts)
    + Make Abductor Team (Requires Ghosts)
    + "} + usr << browse(dat, "window=oneclickantag;size=400x400") + return + +/datum/admins/proc/CandCheck(var/role = null, var/mob/living/carbon/human/M, var/datum/game_mode/temp = null) + // You pass in ROLE define (optional), the applicant, and the gamemode, and it will return true / false depending on whether the applicant qualify for the candidacy in question + if(jobban_isbanned(M, "Syndicate")) + return FALSE + if(M.stat || !M.mind || M.mind.special_role || M.mind.offstation_role) + return FALSE + if(temp) + if((M.mind.assigned_role in temp.restricted_jobs) || (M.client.prefs.species in temp.protected_species)) + return FALSE + if(role) // Don't even bother evaluating if there's no role + if(player_old_enough_antag(M.client,role) && (role in M.client.prefs.be_special) && !M.client.skip_antag && (!jobban_isbanned(M, role))) + return TRUE + else + return FALSE + else + return TRUE + +/datum/admins/proc/makeTraitors() + var/datum/game_mode/traitor/temp = new + + if(config.protect_roles_from_antagonist) + temp.restricted_jobs += temp.protected_jobs + + var/list/mob/living/carbon/human/candidates = list() + var/mob/living/carbon/human/H = null + + var/antnum = input(owner, "How many traitors you want to create? Enter 0 to cancel","Amount:", 0) as num + if(!antnum || antnum <= 0) + return + log_admin("[key_name(owner)] tried making [antnum] traitors with One-Click-Antag") + message_admins("[key_name_admin(owner)] tried making [antnum] traitors with One-Click-Antag") + + for(var/mob/living/carbon/human/applicant in GLOB.player_list) + if(CandCheck(ROLE_TRAITOR, applicant, temp)) + candidates += applicant + + if(candidates.len) + var/numTraitors = min(candidates.len, antnum) + + for(var/i = 0, i300)//If more than 30 game seconds passed. + return + candidates += G + if("No") + return + else + return + + sleep(300) + + if(candidates.len) + var/agentcount = 0 + + for(var/i = 0, i300)//If more than 30 game seconds passed. + return + candidates += G + if("No") + return + else + return + sleep(300) + + for(var/mob/dead/observer/G in candidates) + if(!G.key) + candidates.Remove(G) + + if(candidates.len) + //Spawns commandos and equips them. + for(var/obj/effect/landmark/L in /area/syndicate_mothership/elite_squad) + if(antnum <= 0) + break + if(L.name == "Syndicate-Commando") + syndicate_leader_selected = antnum == 1?1:0 + + var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, syndicate_leader_selected) + + while((!theghost || !theghost.client) && candidates.len) + theghost = pick(candidates) + candidates.Remove(theghost) + + if(!theghost) + qdel(new_syndicate_commando) + break + + new_syndicate_commando.key = theghost.key + new_syndicate_commando.internal = new_syndicate_commando.s_store + new_syndicate_commando.update_action_buttons_icon() + + //So they don't forget their code or mission. + + + to_chat(new_syndicate_commando, "You are an Elite Syndicate. [!syndicate_leader_selected ? "commando" : "LEADER"] in the service of the Syndicate. \nYour current mission is: [input]") + + antnum-- + + for(var/obj/effect/landmark/L in /area/shuttle/syndicate_elite) + if(L.name == "Syndicate-Commando-Bomb") + new /obj/effect/spawner/newbomb/timer/syndicate(L.loc) + return 1 + + +/proc/makeBody(var/mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character + if(!G_found || !G_found.key) return + + //First we spawn a dude. + var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))//The mob being spawned. + + var/datum/preferences/A = new(G_found.client) + A.copy_to(new_character) + + new_character.dna.ready_dna(new_character) + new_character.key = G_found.key + + return new_character + +/datum/admins/proc/create_syndicate_death_commando(obj/spawn_location, syndicate_leader_selected = 0) + var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) + var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") + var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") + var/syndicate_commando_name = pick(GLOB.last_names) + + var/datum/preferences/A = new()//Randomize appearance for the commando. + if(syndicate_leader_selected) + A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" + A.age = rand(35,45) + else + A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" + A.copy_to(new_syndicate_commando) + + new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. + + //Creates mind stuff. + new_syndicate_commando.mind_initialize() + new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.offstation_role = TRUE + //Adds them to current traitor list. Which is really the extra antagonist list. + SSticker.mode.traitors += new_syndicate_commando.mind + new_syndicate_commando.equip_syndicate_commando(syndicate_leader_selected) + + return new_syndicate_commando + +/datum/admins/proc/makeVoxRaiders() + + var/list/mob/candidates = list() + var/mob/theghost = null + var/time_passed = world.time + var/input = "Disregard shinies, acquire hardware." + + var/leader_chosen = 0 //when the leader is chosen. The last person spawned. + + var/antnum = input(owner, "How many raiders you want to create? Enter 0 to cancel.","Amount:", 0) as num + if(!antnum || antnum <= 0) + return + log_admin("[key_name(owner)] tried making Vox Raiders with One-Click-Antag") + message_admins("[key_name_admin(owner)] tried making Vox Raiders with One-Click-Antag") +//Generates a list of candidates from active ghosts. + for(var/mob/G in GLOB.respawnable_list) + if(istype(G) && G.client && (ROLE_RAIDER in G.client.prefs.be_special)) + if(player_old_enough_antag(G.client,ROLE_RAIDER)) + if(!jobban_isbanned(G, "raider") && !jobban_isbanned(G, "Syndicate")) + spawn(0) + switch(alert(G,"Do you wish to be considered for a vox raiding party arriving on the station?","Please answer in 30 seconds!","Yes","No")) + if("Yes") + if((world.time-time_passed)>300)//If more than 30 game seconds passed. + return + candidates += G + if("No") + return + else + return + + sleep(300) //Debug. + + for(var/mob/dead/observer/G in candidates) + if(!G.key) + candidates.Remove(G) + + if(candidates.len) + var/raiders = min(antnum, candidates.len) + //Spawns vox raiders and equips them. + for(var/obj/effect/landmark/L in world) + if(L.name == "voxstart") + if(raiders<=0) + break + + var/mob/living/carbon/human/new_vox = create_vox_raider(L, leader_chosen) + + while((!theghost || !theghost.client) && candidates.len) + theghost = pick(candidates) + candidates.Remove(theghost) + + if(!theghost) + qdel(new_vox) + break + + new_vox.key = theghost.key + SSticker.mode.traitors += new_vox.mind + + to_chat(new_vox, "You are a Vox Primalis, fresh out of the Shoal. Your ship has arrived at the Tau Ceti system hosting the NSV Exodus... or was it the Luna? NSS? Utopia? Nobody is really sure, but everyong is raring to start pillaging! Your current goal is: [input]") + to_chat(new_vox, "Don't forget to turn on your nitrogen internals!") + + raiders-- + else + return 0 + return 1 + +/datum/admins/proc/create_vox_raider(obj/spawn_location, leader_chosen = 0) + + var/sounds = rand(2,8) + var/i = 0 + var/newname = "" + + while(i<=sounds) + i++ + newname += pick(list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) + + var/mob/living/carbon/human/new_vox = new /mob/living/carbon/human/vox(spawn_location.loc) + + new_vox.add_language("Tradeband") + new_vox.real_name = capitalize(newname) + new_vox.dna.real_name = new_vox.real_name + new_vox.name = new_vox.real_name + new_vox.age = rand(12,20) + new_vox.flavor_text = "" + new_vox.change_eye_color(rand(1, 255), rand(1, 255), rand(1, 255)) + new_vox.s_tone = rand(1, 6) + + // Do the initial caching of the player's body icons. + new_vox.force_update_limbs() + new_vox.update_dna() + new_vox.update_eyes() + + for(var/obj/item/organ/external/limb in new_vox.bodyparts) + limb.status &= ~ORGAN_ROBOT + + //Now apply cortical stack. + var/obj/item/implant/cortical/I = new(new_vox) + I.implant(new_vox) + GLOB.cortical_stacks += I + + new_vox.equip_vox_raider() + new_vox.regenerate_icons() + + return new_vox + +/datum/admins/proc/makeVampires() + + var/datum/game_mode/vampire/temp = new + if(config.protect_roles_from_antagonist) + temp.restricted_jobs += temp.protected_jobs + + var/list/mob/living/carbon/human/candidates = list() + var/mob/living/carbon/human/H = null + + var/antnum = input(owner, "How many vampires you want to create? Enter 0 to cancel","Amount:", 0) as num + if(!antnum || antnum <= 0) + return + + log_admin("[key_name(owner)] tried making Vampires with One-Click-Antag") + message_admins("[key_name_admin(owner)] tried making Vampires with One-Click-Antag") + + for(var/mob/living/carbon/human/applicant in GLOB.player_list) + if(CandCheck(ROLE_VAMPIRE, applicant, temp)) + candidates += applicant + + if(candidates.len) + var/numVampires = min(candidates.len, antnum) + + for(var/i = 0, i300)//If more than 30 game seconds passed. + return + candidates += G + if("No") + return + else + return + + sleep(300) //Debug. + + for(var/mob/dead/observer/G in candidates) + if(!G.key) + candidates.Remove(G) + + if(candidates.len) + var/teamOneMembers = 5 + var/teamTwoMembers = 5 + var/datum/preferences/A = new() + for(var/obj/effect/landmark/L in world) + if(L.name == "tdome1") + if(teamOneMembers<=0) + break + + var/mob/living/carbon/human/newMember = new(L.loc) + + A.copy_to(newMember) + + newMember.dna.ready_dna(newMember) + + while((!theghost || !theghost.client) && candidates.len) + theghost = pick(candidates) + candidates.Remove(theghost) + + if(!theghost) + qdel(newMember) + break + + newMember.key = theghost.key + teamOneMembers-- + to_chat(newMember, "You are a member of the GREEN Thunderdome team! Gear up and help your team destroy the red team!") + + if(L.name == "tdome2") + if(teamTwoMembers<=0) + break + + var/mob/living/carbon/human/newMember = new(L.loc) + + A.copy_to(newMember) + + newMember.dna.ready_dna(newMember) + + while((!theghost || !theghost.client) && candidates.len) + theghost = pick(candidates) + candidates.Remove(theghost) + + if(!theghost) + qdel(newMember) + break + + newMember.key = theghost.key + teamTwoMembers-- + to_chat(newMember, "You are a member of the RED Thunderdome team! Gear up and help your team destroy the green team!") + else + return 0 + return 1 diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm index 3db8f102320..e51f6da991a 100644 --- a/code/modules/admin/verbs/onlyone.dm +++ b/code/modules/admin/verbs/onlyone.dm @@ -1,104 +1,104 @@ -/client/proc/only_one() - if(!SSticker) - alert("The game hasn't started yet!") - return - - var/list/incompatible_species = list(/datum/species/plasmaman, /datum/species/vox) - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.stat == DEAD || !(H.client)) - continue - if(is_special_character(H)) - continue - if(is_type_in_list(H.dna.species, incompatible_species)) - H.set_species(/datum/species/human) - var/datum/preferences/A = new() // Randomize appearance - A.copy_to(H) - - SSticker.mode.traitors += H.mind - H.mind.special_role = SPECIAL_ROLE_TRAITOR - - var/datum/objective/hijack/hijack_objective = new - hijack_objective.owner = H.mind - H.mind.objectives += hijack_objective - - to_chat(H, "You are a Highlander. Kill all other Highlanders. There can be only one.") - var/obj_count = 1 - for(var/datum/objective/OBJ in H.mind.objectives) - to_chat(H, "Objective #[obj_count]: [OBJ.explanation_text]") - obj_count++ - - for(var/obj/item/I in H) - if(istype(I, /obj/item/implant)) - continue - if(istype(I, /obj/item/organ)) - continue - qdel(I) - - H.equip_to_slot_or_del(new /obj/item/clothing/under/kilt(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/radio/headset/heads/captain(H), slot_l_ear) - H.equip_to_slot_or_del(new /obj/item/clothing/head/beret(H), slot_head) - H.equip_to_slot_or_del(new /obj/item/claymore/highlander(H), slot_r_hand) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/pinpointer(H.loc), slot_l_store) - - var/obj/item/card/id/W = new(H) - W.name = "[H.real_name]'s ID Card" - W.icon_state = "centcom" - W.access = get_all_accesses() - W.access += get_all_centcom_access() - W.assignment = "Highlander" - W.registered_name = H.real_name - H.equip_to_slot_or_del(W, slot_wear_id) - H.dna.species.after_equip_job(null, H) - H.regenerate_icons() - - message_admins("[key_name_admin(usr)] used THERE CAN BE ONLY ONE! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) - log_admin("[key_name(usr)] used there can be only one.") - nologevent = 1 - world << sound('sound/music/thunderdome.ogg') - -/client/proc/only_me() - if(!SSticker) - alert("The game hasn't started yet!") - return - - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.stat == 2 || !(H.client)) continue - if(is_special_character(H)) continue - - SSticker.mode.traitors += H.mind - H.mind.special_role = "[H.real_name] Prime" - - var/datum/objective/hijackclone/hijack_objective = new /datum/objective/hijackclone - hijack_objective.owner = H.mind - H.mind.objectives += hijack_objective - - to_chat(H, "You are the multiverse summoner. Activate your blade to summon copies of yourself from another universe to fight by your side.") - var/obj_count = 1 - for(var/datum/objective/OBJ in H.mind.objectives) - to_chat(H, "Objective #[obj_count]: [OBJ.explanation_text]") - obj_count++ - - var/obj/item/slot_item_ID = H.get_item_by_slot(slot_wear_id) - qdel(slot_item_ID) - var/obj/item/slot_item_hand = H.get_item_by_slot(slot_r_hand) - H.unEquip(slot_item_hand) - - var /obj/item/multisword/pure_evil/multi = new(H) - H.equip_to_slot_or_del(multi, slot_r_hand) - - var/obj/item/card/id/W = new(H) - W.icon_state = "centcom" - W.access = get_all_accesses() - W.access += get_all_centcom_access() - W.assignment = "Multiverse Summoner" - W.registered_name = H.real_name - W.update_label(H.real_name) - H.equip_to_slot_or_del(W, slot_wear_id) - - H.update_icons() - - message_admins("[key_name_admin(usr)] used THERE CAN BE ONLY ME! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) - log_admin("[key_name(usr)] used there can be only me.") - nologevent = 1 - world << sound('sound/music/thunderdome.ogg') +/client/proc/only_one() + if(!SSticker) + alert("The game hasn't started yet!") + return + + var/list/incompatible_species = list(/datum/species/plasmaman, /datum/species/vox) + for(var/mob/living/carbon/human/H in GLOB.player_list) + if(H.stat == DEAD || !(H.client)) + continue + if(is_special_character(H)) + continue + if(is_type_in_list(H.dna.species, incompatible_species)) + H.set_species(/datum/species/human) + var/datum/preferences/A = new() // Randomize appearance + A.copy_to(H) + + SSticker.mode.traitors += H.mind + H.mind.special_role = SPECIAL_ROLE_TRAITOR + + var/datum/objective/hijack/hijack_objective = new + hijack_objective.owner = H.mind + H.mind.objectives += hijack_objective + + to_chat(H, "You are a Highlander. Kill all other Highlanders. There can be only one.") + var/obj_count = 1 + for(var/datum/objective/OBJ in H.mind.objectives) + to_chat(H, "Objective #[obj_count]: [OBJ.explanation_text]") + obj_count++ + + for(var/obj/item/I in H) + if(istype(I, /obj/item/implant)) + continue + if(istype(I, /obj/item/organ)) + continue + qdel(I) + + H.equip_to_slot_or_del(new /obj/item/clothing/under/kilt(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/radio/headset/heads/captain(H), slot_l_ear) + H.equip_to_slot_or_del(new /obj/item/clothing/head/beret(H), slot_head) + H.equip_to_slot_or_del(new /obj/item/claymore/highlander(H), slot_r_hand) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/pinpointer(H.loc), slot_l_store) + + var/obj/item/card/id/W = new(H) + W.name = "[H.real_name]'s ID Card" + W.icon_state = "centcom" + W.access = get_all_accesses() + W.access += get_all_centcom_access() + W.assignment = "Highlander" + W.registered_name = H.real_name + H.equip_to_slot_or_del(W, slot_wear_id) + H.dna.species.after_equip_job(null, H) + H.regenerate_icons() + + message_admins("[key_name_admin(usr)] used THERE CAN BE ONLY ONE! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) + log_admin("[key_name(usr)] used there can be only one.") + GLOB.nologevent = 1 + world << sound('sound/music/thunderdome.ogg') + +/client/proc/only_me() + if(!SSticker) + alert("The game hasn't started yet!") + return + + for(var/mob/living/carbon/human/H in GLOB.player_list) + if(H.stat == 2 || !(H.client)) continue + if(is_special_character(H)) continue + + SSticker.mode.traitors += H.mind + H.mind.special_role = "[H.real_name] Prime" + + var/datum/objective/hijackclone/hijack_objective = new /datum/objective/hijackclone + hijack_objective.owner = H.mind + H.mind.objectives += hijack_objective + + to_chat(H, "You are the multiverse summoner. Activate your blade to summon copies of yourself from another universe to fight by your side.") + var/obj_count = 1 + for(var/datum/objective/OBJ in H.mind.objectives) + to_chat(H, "Objective #[obj_count]: [OBJ.explanation_text]") + obj_count++ + + var/obj/item/slot_item_ID = H.get_item_by_slot(slot_wear_id) + qdel(slot_item_ID) + var/obj/item/slot_item_hand = H.get_item_by_slot(slot_r_hand) + H.unEquip(slot_item_hand) + + var /obj/item/multisword/pure_evil/multi = new(H) + H.equip_to_slot_or_del(multi, slot_r_hand) + + var/obj/item/card/id/W = new(H) + W.icon_state = "centcom" + W.access = get_all_accesses() + W.access += get_all_centcom_access() + W.assignment = "Multiverse Summoner" + W.registered_name = H.real_name + W.update_label(H.real_name) + H.equip_to_slot_or_del(W, slot_wear_id) + + H.update_icons() + + message_admins("[key_name_admin(usr)] used THERE CAN BE ONLY ME! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) + log_admin("[key_name(usr)] used there can be only me.") + GLOB.nologevent = 1 + world << sound('sound/music/thunderdome.ogg') diff --git a/code/modules/admin/verbs/onlyoneteam.dm b/code/modules/admin/verbs/onlyoneteam.dm index 846a77c508a..9575cc5b340 100644 --- a/code/modules/admin/verbs/onlyoneteam.dm +++ b/code/modules/admin/verbs/onlyoneteam.dm @@ -29,7 +29,7 @@ H.equip_to_slot_or_del(new /obj/item/clothing/shoes/white(H), slot_shoes) if(!team_toggle) - team_alpha += H + GLOB.team_alpha += H H.equip_to_slot_or_del(new /obj/item/clothing/under/color/red/dodgeball(H), slot_w_uniform) var/obj/item/card/id/W = new(H) @@ -42,7 +42,7 @@ H.equip_to_slot_or_del(W, slot_wear_id) else - team_bravo += H + GLOB.team_bravo += H H.equip_to_slot_or_del(new /obj/item/clothing/under/color/blue/dodgeball(H), slot_w_uniform) var/obj/item/card/id/W = new(H) @@ -60,7 +60,7 @@ message_admins("[key_name_admin(usr)] used DODGEBAWWWWWWWL! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) log_admin("[key_name(usr)] used dodgeball.") - nologevent = 1 + GLOB.nologevent = 1 /obj/item/beach_ball/dodgeball name = "dodgeball" @@ -76,13 +76,13 @@ if(H.r_hand == src) return if(H.l_hand == src) return var/mob/A = H.LAssailant - if((H in team_alpha) && (A in team_alpha)) + if((H in GLOB.team_alpha) && (A in GLOB.team_alpha)) to_chat(A, "He's on your team!") return - else if((H in team_bravo) && (A in team_bravo)) + else if((H in GLOB.team_bravo) && (A in GLOB.team_bravo)) to_chat(A, "He's on your team!") return - else if(!A in team_alpha && !A in team_bravo) + else if(!(A in GLOB.team_alpha) && !(A in GLOB.team_bravo)) to_chat(A, "You're not part of the dodgeball game, sorry!") return else diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 995673e7d20..6ec103cccc9 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -1,153 +1,153 @@ -var/list/sounds_cache = list() - -/client/proc/stop_global_admin_sounds() - set category = "Event" - set name = "Stop Global Admin Sounds" - if(!check_rights(R_SOUNDS)) - return - - var/sound/awful_sound = sound(null, repeat = 0, wait = 0, channel = CHANNEL_ADMIN) - - log_admin("[key_name(src)] stopped admin sounds.") - message_admins("[key_name_admin(src)] stopped admin sounds.", 1) - for(var/mob/M in GLOB.player_list) - M << awful_sound - -/client/proc/play_sound(S as sound) - set category = "Event" - set name = "Play Global Sound" - if(!check_rights(R_SOUNDS)) return - - var/sound/uploaded_sound = sound(S, repeat = 0, wait = 1, channel = CHANNEL_ADMIN) - uploaded_sound.priority = 250 - - sounds_cache += S - - if(alert("Are you sure?\nSong: [S]\nNow you can also play this sound using \"Play Server Sound\".", "Confirmation request" ,"Play", "Cancel") == "Cancel") - return - - log_admin("[key_name(src)] played sound [S]") - message_admins("[key_name_admin(src)] played sound [S]", 1) - - for(var/mob/M in GLOB.player_list) - if(M.client.prefs.sound & SOUND_MIDI) - if(isnewplayer(M) && (M.client.prefs.sound & SOUND_LOBBY)) - M.stop_sound_channel(CHANNEL_LOBBYMUSIC) - SEND_SOUND(M, uploaded_sound) - - feedback_add_details("admin_verb","PGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/play_local_sound(S as sound) - set category = "Event" - set name = "Play Local Sound" - if(!check_rights(R_SOUNDS)) return - - log_admin("[key_name(src)] played a local sound [S]") - message_admins("[key_name_admin(src)] played a local sound [S]", 1) - playsound(get_turf(src.mob), S, 50, 0, 0) - feedback_add_details("admin_verb","PLS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/play_server_sound() - set category = "Event" - set name = "Play Server Sound" - if(!check_rights(R_SOUNDS)) return - - var/list/sounds = file2list("sound/serversound_list.txt"); - sounds += sounds_cache - - var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds - if(!melody) return - - play_sound(melody) - feedback_add_details("admin_verb","PSS") //If you are copy-pasting this, ensure the 2nd paramter is unique to the new proc! - -/client/proc/play_intercomm_sound() - set category = "Event" - set name = "Play Sound via Intercomms" - set desc = "Plays a sound at every intercomm on the station z level. Works best with small sounds." - if(!check_rights(R_SOUNDS)) return - - var/A = alert("This will play a sound at every intercomm, are you sure you want to continue? This works best with short sounds, beware.","Warning","Yep","Nope") - if(A != "Yep") return - - var/list/sounds = file2list("sound/serversound_list.txt"); - sounds += sounds_cache - - var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds - if(!melody) return - - var/cvol = 35 - var/inputvol = input("How loud would you like this to be? (1-70)", "Volume", "35") as num | null - if(!inputvol) return - if(inputvol && inputvol >= 1 && inputvol <= 70) - cvol = inputvol - - //Allows for override to utilize intercomms on all z-levels - var/B = alert("Do you want to play through intercomms on ALL Z-levels, or just the station?", "Override", "All", "Station") - var/ignore_z = 0 - if(B == "All") - ignore_z = 1 - - //Allows for override to utilize incomplete and unpowered intercomms - var/C = alert("Do you want to play through unpowered / incomplete intercomms, so the crew can't silence it?", "Override", "Yep", "Nope") - var/ignore_power = 0 - if(C == "Yep") - ignore_power = 1 - - for(var/O in GLOB.global_intercoms) - var/obj/item/radio/intercom/I = O - if(!is_station_level(I.z) && !ignore_z) - continue - if(!I.on && !ignore_power) - continue - playsound(I, melody, cvol) - -/* -/client/proc/cuban_pete() - set category = "Event" - set name = "Cuban Pete Time" - - message_admins("[key_name_admin(usr)] has declared Cuban Pete Time!", 1) - for(var/mob/M in world) - if(M.client) - if(M.client.midis) - M << 'cubanpetetime.ogg' - - for(var/mob/living/carbon/human/CP in world) - if(CP.real_name=="Cuban Pete" && CP.key!="Rosham") - C << "Your body can't contain the rhumba beat" - CP.gib() - - -/client/proc/bananaphone() - set category = "Event" - set name = "Banana Phone" - - message_admins("[key_name_admin(usr)] has activated Banana Phone!", 1) - for(var/mob/M in world) - if(M.client) - if(M.client.midis) - M << 'bananaphone.ogg' - - -client/proc/space_asshole() - set category = "Event" - set name = "Space Asshole" - - message_admins("[key_name_admin(usr)] has played the Space Asshole Hymn.", 1) - for(var/mob/M in world) - if(M.client) - if(M.client.midis) - M << 'sound/music/space_asshole.ogg' - - -client/proc/honk_theme() - set category = "Event" - set name = "Honk" - - message_admins("[key_name_admin(usr)] has creeped everyone out with Blackest Honks.", 1) - for(var/mob/M in world) - if(M.client) - if(M.client.midis) - M << 'honk_theme.ogg'*/ +GLOBAL_LIST_EMPTY(sounds_cache) + +/client/proc/stop_global_admin_sounds() + set category = "Event" + set name = "Stop Global Admin Sounds" + if(!check_rights(R_SOUNDS)) + return + + var/sound/awful_sound = sound(null, repeat = 0, wait = 0, channel = CHANNEL_ADMIN) + + log_admin("[key_name(src)] stopped admin sounds.") + message_admins("[key_name_admin(src)] stopped admin sounds.", 1) + for(var/mob/M in GLOB.player_list) + M << awful_sound + +/client/proc/play_sound(S as sound) + set category = "Event" + set name = "Play Global Sound" + if(!check_rights(R_SOUNDS)) return + + var/sound/uploaded_sound = sound(S, repeat = 0, wait = 1, channel = CHANNEL_ADMIN) + uploaded_sound.priority = 250 + + GLOB.sounds_cache += S + + if(alert("Are you sure?\nSong: [S]\nNow you can also play this sound using \"Play Server Sound\".", "Confirmation request" ,"Play", "Cancel") == "Cancel") + return + + log_admin("[key_name(src)] played sound [S]") + message_admins("[key_name_admin(src)] played sound [S]", 1) + + for(var/mob/M in GLOB.player_list) + if(M.client.prefs.sound & SOUND_MIDI) + if(isnewplayer(M) && (M.client.prefs.sound & SOUND_LOBBY)) + M.stop_sound_channel(CHANNEL_LOBBYMUSIC) + SEND_SOUND(M, uploaded_sound) + + feedback_add_details("admin_verb","PGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/play_local_sound(S as sound) + set category = "Event" + set name = "Play Local Sound" + if(!check_rights(R_SOUNDS)) return + + log_admin("[key_name(src)] played a local sound [S]") + message_admins("[key_name_admin(src)] played a local sound [S]", 1) + playsound(get_turf(src.mob), S, 50, 0, 0) + feedback_add_details("admin_verb","PLS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/play_server_sound() + set category = "Event" + set name = "Play Server Sound" + if(!check_rights(R_SOUNDS)) return + + var/list/sounds = file2list("sound/serversound_list.txt"); + sounds += GLOB.sounds_cache + + var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds + if(!melody) return + + play_sound(melody) + feedback_add_details("admin_verb","PSS") //If you are copy-pasting this, ensure the 2nd paramter is unique to the new proc! + +/client/proc/play_intercomm_sound() + set category = "Event" + set name = "Play Sound via Intercomms" + set desc = "Plays a sound at every intercomm on the station z level. Works best with small sounds." + if(!check_rights(R_SOUNDS)) return + + var/A = alert("This will play a sound at every intercomm, are you sure you want to continue? This works best with short sounds, beware.","Warning","Yep","Nope") + if(A != "Yep") return + + var/list/sounds = file2list("sound/serversound_list.txt"); + sounds += GLOB.sounds_cache + + var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds + if(!melody) return + + var/cvol = 35 + var/inputvol = input("How loud would you like this to be? (1-70)", "Volume", "35") as num | null + if(!inputvol) return + if(inputvol && inputvol >= 1 && inputvol <= 70) + cvol = inputvol + + //Allows for override to utilize intercomms on all z-levels + var/B = alert("Do you want to play through intercomms on ALL Z-levels, or just the station?", "Override", "All", "Station") + var/ignore_z = 0 + if(B == "All") + ignore_z = 1 + + //Allows for override to utilize incomplete and unpowered intercomms + var/C = alert("Do you want to play through unpowered / incomplete intercomms, so the crew can't silence it?", "Override", "Yep", "Nope") + var/ignore_power = 0 + if(C == "Yep") + ignore_power = 1 + + for(var/O in GLOB.global_intercoms) + var/obj/item/radio/intercom/I = O + if(!is_station_level(I.z) && !ignore_z) + continue + if(!I.on && !ignore_power) + continue + playsound(I, melody, cvol) + +/* +/client/proc/cuban_pete() + set category = "Event" + set name = "Cuban Pete Time" + + message_admins("[key_name_admin(usr)] has declared Cuban Pete Time!", 1) + for(var/mob/M in world) + if(M.client) + if(M.client.midis) + M << 'cubanpetetime.ogg' + + for(var/mob/living/carbon/human/CP in world) + if(CP.real_name=="Cuban Pete" && CP.key!="Rosham") + C << "Your body can't contain the rhumba beat" + CP.gib() + + +/client/proc/bananaphone() + set category = "Event" + set name = "Banana Phone" + + message_admins("[key_name_admin(usr)] has activated Banana Phone!", 1) + for(var/mob/M in world) + if(M.client) + if(M.client.midis) + M << 'bananaphone.ogg' + + +client/proc/space_asshole() + set category = "Event" + set name = "Space Asshole" + + message_admins("[key_name_admin(usr)] has played the Space Asshole Hymn.", 1) + for(var/mob/M in world) + if(M.client) + if(M.client.midis) + M << 'sound/music/space_asshole.ogg' + + +client/proc/honk_theme() + set category = "Event" + set name = "Honk" + + message_admins("[key_name_admin(usr)] has creeped everyone out with Blackest Honks.", 1) + for(var/mob/M in world) + if(M.client) + if(M.client.midis) + M << 'honk_theme.ogg'*/ diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 9973de89c85..95a66684d1d 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -1,89 +1,89 @@ -/mob/living/verb/pray(msg as text) - set category = "IC" - set name = "Pray" - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - if(!msg) - return - - if(usr.client) - if(usr.client.prefs.muted & MUTE_PRAY) - to_chat(usr, "You cannot pray (muted).") - return - if(client.handle_spam_prevention(msg, MUTE_PRAY, OOC_COOLDOWN)) - return - - var/image/cross = image('icons/obj/storage.dmi',"bible") - var/font_color = "purple" - var/prayer_type = "PRAYER" - var/deity - if(usr.job == "Chaplain") - if(SSticker && SSticker.Bible_deity_name) - deity = SSticker.Bible_deity_name - cross = image('icons/obj/storage.dmi',"kingyellow") - font_color = "blue" - prayer_type = "CHAPLAIN PRAYER" - else if(iscultist(usr)) - cross = image('icons/obj/storage.dmi',"tome") - font_color = "red" - prayer_type = "CULTIST PRAYER" - deity = SSticker.cultdat.entity_name - - log_say("(PRAYER) [msg]", usr) - msg = "[bicon(cross)][prayer_type][deity ? " (to [deity])" : ""][mind && mind.isholy ? " (blessings: [mind.num_blessed])" : ""]: [key_name(src, 1)] ([ADMIN_QUE(src,"?")]) ([ADMIN_PP(src,"PP")]) ([ADMIN_VV(src,"VV")]) ([ADMIN_TP(src,"TP")]) ([ADMIN_SM(src,"SM")]) ([admin_jump_link(src)]) ([ADMIN_SC(src,"SC")]) (BLESS) (SMITE): [msg]" - - for(var/client/X in GLOB.admins) - if(check_rights(R_EVENT,0,X.mob)) - to_chat(X, msg) - to_chat(usr, "Your prayers have been received by the gods.") - - feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/proc/Centcomm_announce(var/text , var/mob/Sender) - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "CENTCOMM: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_CENTCOM_REPLY(Sender,"RPLY")])): [msg]" - for(var/client/X in GLOB.admins) - if(R_EVENT & X.holder.rights) - to_chat(X, msg) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - -/proc/Syndicate_announce(var/text , var/mob/Sender) - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "SYNDICATE: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_SYNDICATE_REPLY(Sender,"RPLY")]): [msg]" - for(var/client/X in GLOB.admins) - if(check_rights(R_EVENT,0,X.mob)) - to_chat(X, msg) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - -/proc/HONK_announce(var/text , var/mob/Sender) - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "HONK: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) (RPLY): [msg]" - for(var/client/X in GLOB.admins) - if(R_EVENT & X.holder.rights) - to_chat(X, msg) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - -/proc/ERT_Announce(var/text , var/mob/Sender, var/repeat_warning) - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "ERT REQUEST: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) (RESPOND): [msg]" - if(repeat_warning) - msg += "
    WARNING: ERT request has gone 5 minutes with no reply!" - for(var/client/X in GLOB.admins) - if(check_rights(R_EVENT,0,X.mob)) - to_chat(X, msg) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - -/proc/Nuke_request(text , mob/Sender) - var/nuke_code = get_nuke_code() - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "NUKE CODE REQUEST: [key_name(Sender)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_CENTCOM_REPLY(Sender,"RPLY")]): [msg]" - for(var/client/X in GLOB.admins) - if(check_rights(R_EVENT,0,X.mob)) - to_chat(X, msg) - to_chat(X, "The nuke code is [nuke_code].") - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' +/mob/living/verb/pray(msg as text) + set category = "IC" + set name = "Pray" + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + if(!msg) + return + + if(usr.client) + if(usr.client.prefs.muted & MUTE_PRAY) + to_chat(usr, "You cannot pray (muted).") + return + if(client.handle_spam_prevention(msg, MUTE_PRAY, OOC_COOLDOWN)) + return + + var/image/cross = image('icons/obj/storage.dmi',"bible") + var/font_color = "purple" + var/prayer_type = "PRAYER" + var/deity + if(usr.job == "Chaplain") + if(SSticker && SSticker.Bible_deity_name) + deity = SSticker.Bible_deity_name + cross = image('icons/obj/storage.dmi',"kingyellow") + font_color = "blue" + prayer_type = "CHAPLAIN PRAYER" + else if(iscultist(usr)) + cross = image('icons/obj/storage.dmi',"tome") + font_color = "red" + prayer_type = "CULTIST PRAYER" + deity = SSticker.cultdat.entity_name + + log_say("(PRAYER) [msg]", usr) + msg = "[bicon(cross)][prayer_type][deity ? " (to [deity])" : ""][mind && mind.isholy ? " (blessings: [mind.num_blessed])" : ""]: [key_name(src, 1)] ([ADMIN_QUE(src,"?")]) ([ADMIN_PP(src,"PP")]) ([ADMIN_VV(src,"VV")]) ([ADMIN_TP(src,"TP")]) ([ADMIN_SM(src,"SM")]) ([admin_jump_link(src)]) ([ADMIN_SC(src,"SC")]) (BLESS) (SMITE): [msg]" + + for(var/client/X in GLOB.admins) + if(check_rights(R_EVENT,0,X.mob)) + to_chat(X, msg) + to_chat(usr, "Your prayers have been received by the gods.") + + feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/proc/Centcomm_announce(var/text , var/mob/Sender) + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "CENTCOMM: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_CENTCOM_REPLY(Sender,"RPLY")])): [msg]" + for(var/client/X in GLOB.admins) + if(R_EVENT & X.holder.rights) + to_chat(X, msg) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + +/proc/Syndicate_announce(var/text , var/mob/Sender) + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "SYNDICATE: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_SYNDICATE_REPLY(Sender,"RPLY")]): [msg]" + for(var/client/X in GLOB.admins) + if(check_rights(R_EVENT,0,X.mob)) + to_chat(X, msg) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + +/proc/HONK_announce(var/text , var/mob/Sender) + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "HONK: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) (RPLY): [msg]" + for(var/client/X in GLOB.admins) + if(R_EVENT & X.holder.rights) + to_chat(X, msg) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + +/proc/ERT_Announce(var/text , var/mob/Sender, var/repeat_warning) + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "ERT REQUEST: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) (RESPOND): [msg]" + if(repeat_warning) + msg += "
    WARNING: ERT request has gone 5 minutes with no reply!" + for(var/client/X in GLOB.admins) + if(check_rights(R_EVENT,0,X.mob)) + to_chat(X, msg) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + +/proc/Nuke_request(text , mob/Sender) + var/nuke_code = get_nuke_code() + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "NUKE CODE REQUEST: [key_name(Sender)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_CENTCOM_REPLY(Sender,"RPLY")]): [msg]" + for(var/client/X in GLOB.admins) + if(check_rights(R_EVENT,0,X.mob)) + to_chat(X, msg) + to_chat(X, "The nuke code is [nuke_code].") + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index a347f65590b..1864a677727 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1,1159 +1,1159 @@ -/client/proc/cmd_admin_drop_everything(mob/M as mob in GLOB.mob_list) - set category = null - set name = "Drop Everything" - - if(!check_rights(R_DEBUG|R_ADMIN)) - return - - var/confirm = alert(src, "Make [M] drop everything?", "Message", "Yes", "No") - if(confirm != "Yes") - return - - for(var/obj/item/W in M) - M.unEquip(W) - - log_admin("[key_name(usr)] made [key_name(M)] drop everything!") - message_admins("[key_name_admin(usr)] made [key_name_admin(M)] drop everything!", 1) - feedback_add_details("admin_verb","DEVR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_prison(mob/M as mob in GLOB.mob_list) - set category = "Admin" - set name = "Prison" - - if(!check_rights(R_ADMIN)) - return - - if(ismob(M)) - if(istype(M, /mob/living/silicon/ai)) - alert("The AI can't be sent to prison you jerk!", null, null, null, null, null) - return - //strip their stuff before they teleport into a cell :downs: - for(var/obj/item/W in M) - M.unEquip(W) - //teleport person to cell - M.Paralyse(5) - sleep(5) //so they black out before warping - M.loc = pick(prisonwarp) - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/prisoner = M - prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) - prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) - spawn(50) - to_chat(M, "You have been sent to the prison station!") - log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") - message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) - feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_subtle_message(mob/M as mob in GLOB.mob_list) - set category = "Event" - set name = "Subtle Message" - - if(!ismob(M)) - return - - if(!check_rights(R_SERVER|R_EVENT)) - return - - var/msg = clean_input("Message:", text("Subtle PM to [M.key]")) - - if(!msg) - return - - msg = admin_pencode_to_html(msg) - - if(usr) - if(usr.client) - if(usr.client.holder) - to_chat(M, "You hear a voice in your head... [msg]") - - log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") - message_admins("SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]", 1) - feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_mentor_check_new_players() //Allows mentors / admins to determine who the newer players are. - set category = "Admin" - set name = "Check new Players" - - if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) - return - - var/age = alert(src, "Age check", "Show accounts yonger then _____ days","7", "30" , "All") - - if(age == "All") - age = 9999999 - else - age = text2num(age) - - var/missing_ages = 0 - var/msg = "" - for(var/client/C in GLOB.clients) - if(C.player_age == "Requires database") - missing_ages = 1 - continue - if(C.player_age < age) - if(check_rights(R_ADMIN, 0)) - msg += "[key_name_admin(C.mob)]: [C.player_age] days old
    " - else - msg += "[key_name_mentor(C.mob)]: [C.player_age] days old
    " - - if(missing_ages) - to_chat(src, "Some accounts did not have proper ages set in their clients. This function requires database to be present") - - if(msg != "") - src << browse(msg, "window=Player_age_check") - else - to_chat(src, "No matches for that age range found.") - - -/client/proc/cmd_admin_world_narrate() // Allows administrators to fluff events a little easier -- TLE - set category = "Event" - set name = "Global Narrate" - - if(!check_rights(R_SERVER|R_EVENT)) - return - - var/msg = clean_input("Message:", text("Enter the text you wish to appear to everyone:")) - - if(!msg) - return - msg = admin_pencode_to_html(msg) - to_chat(world, "[msg]") - log_admin("GlobalNarrate: [key_name(usr)] : [msg]") - message_admins("GlobalNarrate: [key_name_admin(usr)]: [msg]
    ", 1) - feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE - set category = "Event" - set name = "Direct Narrate" - - if(!check_rights(R_SERVER|R_EVENT)) - return - - if(!M) - M = input("Direct narrate to who?", "Active Players") as null|anything in get_mob_with_client_list() - - if(!M) - return - - var/msg = clean_input("Message:", text("Enter the text you wish to appear to your target:")) - - if( !msg ) - return - msg = admin_pencode_to_html(msg) - - to_chat(M, msg) - log_admin("DirectNarrate: [key_name(usr)] to ([key_name(M)]): [msg]") - message_admins("DirectNarrate: [key_name_admin(usr)] to ([key_name_admin(M)]): [msg]
    ", 1) - feedback_add_details("admin_verb","DIRN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - - - -/client/proc/cmd_admin_headset_message(mob/M in GLOB.mob_list) - set category = "Event" - set name = "Headset Message" - - admin_headset_message(M) - -/client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null) - var/mob/living/carbon/human/H = M - - if(!check_rights(R_ADMIN)) - return - - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") - return - - if(!sender) - sender = input("Who is the message from?", "Sender") as null|anything in list("Centcomm", "Syndicate") - if(!sender) - return - - message_admins("[key_name_admin(src)] has started answering [key_name_admin(H)]'s [sender] request.") - var/input = clean_input("Please enter a message to reply to [key_name(H)] via their headset.", "Outgoing message from [sender]", "") - if(!input) - message_admins("[key_name_admin(src)] decided not to answer [key_name_admin(H)]'s [sender] request.") - return - - log_admin("[key_name(src)] replied to [key_name(H)]'s [sender] message with the message [input].") - message_admins("[key_name_admin(src)] replied to [key_name_admin(H)]'s [sender] message with: \"[input]\"") - to_chat(H, "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from [sender == "Syndicate" ? "your benefactor" : "Central Command"]. Message as follows[sender == "Syndicate" ? ", agent." : ":"] [input]. Message ends.\"") - - - - -/client/proc/cmd_admin_godmode(mob/M as mob in GLOB.mob_list) - set category = "Admin" - set name = "Godmode" - - if(!check_rights(R_ADMIN)) - return - - M.status_flags ^= GODMODE - to_chat(usr, "Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]") - - log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") - message_admins("[key_name_admin(usr)] has toggled [key_name_admin(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]", 1) - feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) - if(automute) - if(!config.automute_on) - return - else - if(!usr || !usr.client) - return - if(!check_rights(R_ADMIN|R_MOD)) - to_chat(usr, "Error: cmd_admin_mute: You don't have permission to do this.") - return - if(!M.client) - to_chat(usr, "Error: cmd_admin_mute: This mob doesn't have a client tied to it.") - if(!M.client) - return - - var/muteunmute - var/mute_string - - switch(mute_type) - if(MUTE_IC) mute_string = "IC (say and emote)" - if(MUTE_OOC) mute_string = "OOC" - if(MUTE_PRAY) mute_string = "pray" - if(MUTE_ADMINHELP) mute_string = "adminhelp, admin PM and ASAY" - if(MUTE_DEADCHAT) mute_string = "deadchat and DSAY" - if(MUTE_ALL) mute_string = "everything" - else return - - if(automute) - muteunmute = "auto-muted" - M.client.prefs.muted |= mute_type - log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]") - message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") - feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - - if(M.client.prefs.muted & mute_type) - muteunmute = "unmuted" - M.client.prefs.muted &= ~mute_type - else - muteunmute = "muted" - M.client.prefs.muted |= mute_type - - log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]") - message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - to_chat(M, "You have been [muteunmute] from [mute_string].") - feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_add_random_ai_law() - set category = "Event" - set name = "Add Random AI Law" - - if(!check_rights(R_EVENT)) - return - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm != "Yes") return - log_admin("[key_name(src)] has added a random AI law.") - message_admins("[key_name_admin(src)] has added a random AI law.") - - var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") - var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) - - new /datum/event/ion_storm(0, announce_ion_laws) - feedback_add_details("admin_verb","ION") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_antagHUD_use() - set category = "Server" - set name = "Toggle antagHUD usage" - set desc = "Toggles antagHUD usage for observers" - - if(!check_rights(R_SERVER)) - return - - var/action="" - if(config.antag_hud_allowed) - for(var/mob/dead/observer/g in get_ghosts()) - if(!g.client.holder) //Remove the verb from non-admin ghosts - g.verbs -= /mob/dead/observer/verb/toggle_antagHUD - if(g.antagHUD) - g.antagHUD = 0 // Disable it on those that have it enabled - g.has_enabled_antagHUD = 2 // We'll allow them to respawn - to_chat(g, "The Administrator has disabled AntagHUD ") - config.antag_hud_allowed = 0 - to_chat(src, "AntagHUD usage has been disabled") - action = "disabled" - else - for(var/mob/dead/observer/g in get_ghosts()) - if(!g.client.holder) // Add the verb back for all non-admin ghosts - g.verbs += /mob/dead/observer/verb/toggle_antagHUD - to_chat(g, "The Administrator has enabled AntagHUD ")// Notify all observers they can now use AntagHUD - - config.antag_hud_allowed = 1 - action = "enabled" - to_chat(src, "AntagHUD usage has been enabled") - - - log_admin("[key_name(usr)] has [action] antagHUD usage for observers") - message_admins("Admin [key_name_admin(usr)] has [action] antagHUD usage for observers", 1) - -/client/proc/toggle_antagHUD_restrictions() - set category = "Server" - set name = "Toggle antagHUD Restrictions" - set desc = "Restricts players that have used antagHUD from being able to join this round." - - if(!check_rights(R_SERVER)) - return - - var/action="" - if(config.antag_hud_restricted) - for(var/mob/dead/observer/g in get_ghosts()) - to_chat(g, "The administrator has lifted restrictions on joining the round if you use AntagHUD") - action = "lifted restrictions" - config.antag_hud_restricted = 0 - to_chat(src, "AntagHUD restrictions have been lifted") - else - for(var/mob/dead/observer/g in get_ghosts()) - to_chat(g, "The administrator has placed restrictions on joining the round if you use AntagHUD") - to_chat(g, "Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions ") - g.antagHUD = 0 - g.has_enabled_antagHUD = 0 - action = "placed restrictions" - config.antag_hud_restricted = 1 - to_chat(src, "AntagHUD restrictions have been enabled") - - log_admin("[key_name(usr)] has [action] on joining the round if they use AntagHUD") - message_admins("Admin [key_name_admin(usr)] has [action] on joining the round if they use AntagHUD", 1) - -/* -If a guy was gibbed and you want to revive him, this is a good way to do so. -Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. -Traitors and the like can also be revived with the previous role mostly intact. -/N */ -/client/proc/respawn_character() - set category = "Event" - set name = "Respawn Character" - set desc = "Respawn a person that has been gibbed/dusted/killed. They must be a ghost for this to work and preferably should not have a body to go back into." - - if(!check_rights(R_SPAWN)) - return - - var/input = ckey(input(src, "Please specify which key will be respawned.", "Key", "")) - if(!input) - return - - var/mob/dead/observer/G_found - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.ckey == input) - G_found = G - break - - if(!G_found)//If a ghost was not found. - to_chat(usr, "There is no active key like that in the game or the person is not currently a ghost.") - return - - if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something - //Check if they were an alien - if(G_found.mind.assigned_role=="Alien") - if(alert("This character appears to have been an alien. Would you like to respawn them as such?",,"Yes","No")=="Yes") - var/turf/T - if(xeno_spawn.len) T = pick(xeno_spawn) - else T = pick(latejoin) - - var/mob/living/carbon/alien/new_xeno - switch(G_found.mind.special_role)//If they have a mind, we can determine which caste they were. - if("Hunter") new_xeno = new /mob/living/carbon/alien/humanoid/hunter(T) - if("Sentinel") new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(T) - if("Drone") new_xeno = new /mob/living/carbon/alien/humanoid/drone(T) - if("Queen") new_xeno = new /mob/living/carbon/alien/humanoid/queen(T) - else//If we don't know what special role they have, for whatever reason, or they're a larva. - create_xeno(G_found.ckey) - return - - //Now to give them their mind back. - G_found.mind.transfer_to(new_xeno) //be careful when doing stuff like this! I've already checked the mind isn't in use - new_xeno.key = G_found.key - to_chat(new_xeno, "You have been fully respawned. Enjoy the game.") - message_admins("[key_name_admin(usr)] has respawned [new_xeno.key] as a filthy xeno.", 1) - return //all done. The ghost is auto-deleted - - var/mob/living/carbon/human/new_character = new(pick(latejoin))//The mob being spawned. - - var/datum/data/record/record_found //Referenced to later to either randomize or not randomize the character. - if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something - /*Try and locate a record for the person being respawned through data_core. - This isn't an exact science but it does the trick more often than not.*/ - var/id = md5("[G_found.real_name][G_found.mind.assigned_role]") - for(var/datum/data/record/t in data_core.locked) - if(t.fields["id"]==id) - record_found = t//We shall now reference the record. - break - - if(record_found)//If they have a record we can determine a few things. - new_character.real_name = record_found.fields["name"] - new_character.change_gender(record_found.fields["sex"]) - new_character.age = record_found.fields["age"] - new_character.dna.blood_type = record_found.fields["blood_type"] - else - new_character.change_gender(pick(MALE,FEMALE)) - var/datum/preferences/A = new() - A.real_name = G_found.real_name - A.copy_to(new_character) - - if(!new_character.real_name) - new_character.real_name = random_name(new_character.gender) - new_character.name = new_character.real_name - - if(G_found.mind && !G_found.mind.active) - G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use - new_character.mind.special_verbs = list() - else - new_character.mind_initialize() - if(!new_character.mind.assigned_role) new_character.mind.assigned_role = "Civilian"//If they somehow got a null assigned role. - - //DNA - if(record_found)//Pull up their name from database records if they did have a mind. - new_character.dna = new()//Let's first give them a new DNA. - new_character.dna.unique_enzymes = record_found.fields["b_dna"]//Enzymes are based on real name but we'll use the record for conformity. - - // I HATE BYOND. HATE. HATE. - N3X - var/list/newSE= record_found.fields["enzymes"] - var/list/newUI = record_found.fields["identity"] - new_character.dna.SE = newSE.Copy() //This is the default of enzymes so I think it's safe to go with. - new_character.dna.UpdateSE() - new_character.UpdateAppearance(newUI.Copy())//Now we configure their appearance based on their unique identity, same as with a DNA machine or somesuch. - else//If they have no records, we just do a random DNA for them, based on their random appearance/savefile. - new_character.dna.ready_dna(new_character) - - new_character.key = G_found.key - - /* - The code below functions with the assumption that the mob is already a traitor if they have a special role. - So all it does is re-equip the mob with powers and/or items. Or not, if they have no special role. - If they don't have a mind, they obviously don't have a special role. - */ - - //Now for special roles and equipment. - switch(new_character.mind.special_role) - if("traitor") - if(new_character.mind.has_antag_datum(/datum/antagonist/traitor)) - var/datum/antagonist/traitor/T = new_character.mind.has_antag_datum(/datum/antagonist/traitor) - T.equip_traitor(src) - else - new_character.mind.add_antag_datum(/datum/antagonist/traitor) - if("Wizard") - new_character.loc = pick(wizardstart) - //ticker.mode.learn_basic_spells(new_character) - SSticker.mode.equip_wizard(new_character) - if("Syndicate") - var/obj/effect/landmark/synd_spawn = locate("landmark*Syndicate-Spawn") - if(synd_spawn) - new_character.loc = get_turf(synd_spawn) - call(/datum/game_mode/proc/equip_syndicate)(new_character) - - if("Death Commando")//Leaves them at late-join spawn. - new_character.equip_death_commando() - new_character.internal = new_character.s_store - new_character.update_action_buttons_icon() - else//They may also be a cyborg or AI. - switch(new_character.mind.assigned_role) - if("Cyborg")//More rigging to make em' work and check if they're traitor. - new_character = new_character.Robotize() - if(new_character.mind.special_role=="traitor") - new_character.mind.add_antag_datum(/datum/antagonist/traitor) - if("AI") - new_character = new_character.AIize() - var/mob/living/silicon/ai/ai_character = new_character - ai_character.moveToAILandmark() - if(new_character.mind.special_role=="traitor") - new_character.mind.add_antag_datum(/datum/antagonist/traitor) - //Add aliens. - else - SSjobs.AssignRank(new_character, new_character.mind.assigned_role, 0) - SSjobs.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them. - - //Announces the character on all the systems, based on the record. - if(!issilicon(new_character))//If they are not a cyborg/AI. - if(!record_found && new_character.mind.assigned_role != new_character.mind.special_role)//If there are no records for them. If they have a record, this info is already in there. Offstation special characters announced anyway. - //Power to the user! - if(alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,"No","Yes")=="Yes") - data_core.manifest_inject(new_character) - - if(alert(new_character,"Would you like an active AI to announce this character?",,"No","Yes")=="Yes") - call(/mob/new_player/proc/AnnounceArrival)(new_character, new_character.mind.assigned_role) - - message_admins("[key_name_admin(usr)] has respawned [key_name_admin(G_found)] as [new_character.real_name].", 1) - - to_chat(new_character, "You have been fully respawned. Enjoy the game.") - - feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return new_character - -//I use this proc for respawn character too. /N -/proc/create_xeno(ckey) - if(!ckey) - var/list/candidates = list() - for(var/mob/M in GLOB.player_list) - if(M.stat != DEAD) continue //we are not dead! - if(!(ROLE_ALIEN in M.client.prefs.be_special)) continue //we don't want to be an alium - if(jobban_isbanned(M, "alien") || jobban_isbanned(M, "Syndicate")) continue //we are jobbanned - if(M.client.is_afk()) continue //we are afk - if(M.mind && M.mind.current && M.mind.current.stat != DEAD) continue //we have a live body we are tied to - candidates += M.ckey - if(candidates.len) - ckey = input("Pick the player you want to respawn as a xeno.", "Suitable Candidates") as null|anything in candidates - else - to_chat(usr, "Error: create_xeno(): no suitable candidates.") - if(!istext(ckey)) return 0 - - var/alien_caste = input(usr, "Please choose which caste to spawn.","Pick a caste",null) as null|anything in list("Queen","Hunter","Sentinel","Drone","Larva") - var/obj/effect/landmark/spawn_here = xeno_spawn.len ? pick(xeno_spawn) : pick(latejoin) - var/mob/living/carbon/alien/new_xeno - switch(alien_caste) - if("Queen") new_xeno = new /mob/living/carbon/alien/humanoid/queen/large(spawn_here) - if("Hunter") new_xeno = new /mob/living/carbon/alien/humanoid/hunter(spawn_here) - if("Sentinel") new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(spawn_here) - if("Drone") new_xeno = new /mob/living/carbon/alien/humanoid/drone(spawn_here) - if("Larva") new_xeno = new /mob/living/carbon/alien/larva(spawn_here) - else return 0 - - new_xeno.ckey = ckey - message_admins("[key_name_admin(usr)] has spawned [ckey] as a filthy xeno [alien_caste].", 1) - return 1 - - -/client/proc/get_ghosts(var/notify = 0,var/what = 2) - // what = 1, return ghosts ass list. - // what = 2, return mob list - - var/list/mobs = list() - var/list/ghosts = list() - var/list/sortmob = sortAtom(GLOB.mob_list) // get the mob list. - /var/any=0 - for(var/mob/dead/observer/M in sortmob) - mobs.Add(M) //filter it where it's only ghosts - any = 1 //if no ghosts show up, any will just be 0 - if(!any) - if(notify) - to_chat(src, "There doesn't appear to be any ghosts for you to select.") - return - - for(var/mob/M in mobs) - var/name = M.name - ghosts[name] = M //get the name of the mob for the popup list - if(what==1) - return ghosts - else - return mobs - -/client/proc/cmd_admin_add_freeform_ai_law() - set category = "Event" - set name = "Add Custom AI law" - - if(!check_rights(R_EVENT)) - return - - var/input = clean_input("Please enter anything you want the AI to do. Anything. Serious.", "What?", "") - if(!input) - return - - log_admin("Admin [key_name(usr)] has added a new AI law - [input]") - message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]") - - var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") - var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) - - new /datum/event/ion_storm(0, announce_ion_laws, input) - - feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in GLOB.mob_list) - set category = "Event" - set name = "Rejuvenate" - - if(!check_rights(R_REJUVINATE)) - return - - if(!mob) - return - if(!istype(M)) - alert("Cannot revive a ghost") - return - M.revive() - - log_admin("[key_name(usr)] healed / revived [key_name(M)]") - message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(M)]!", 1) - feedback_add_details("admin_verb","REJU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_create_centcom_report() - set category = "Event" - set name = "Create Communications Report" - - if(!check_rights(R_SERVER|R_EVENT)) - return - -//the stuff on the list is |"report type" = "report title"|, if that makes any sense - var/list/MsgType = list("Central Command Report" = "Nanotrasen Update", - "Syndicate Communique" = "Syndicate Message", - "Space Wizard Federation Message" = "Sorcerous Message", - "Enemy Communications" = "Unknown Message", - "Custom" = "Cryptic Message") - - var/list/MsgSound = list("Beep" = 'sound/misc/notice2.ogg', - "Enemy Communications Intercepted" = 'sound/AI/intercept2.ogg', - "New Command Report Created" = 'sound/AI/commandreport.ogg') - - var/type = input(usr, "Pick a type of report to send", "Report Type", "") as anything in MsgType - - if(type == "Custom") - type = clean_input("What would you like the report type to be?", "Report Type", "Encrypted Transmission") - - var/customname = input(usr, "Pick a title for the report.", "Title", MsgType[type]) as text|null - if(!customname) - return - var/input = input(usr, "Please enter anything you want. Anything. Serious.", "What's the message?") as message|null - if(!input) - return - - switch(alert("Should this be announced to the general population?",,"Yes","No", "Cancel")) - if("Yes") - var/beepsound = input(usr, "What sound should the announcement make?", "Announcement Sound", "") as anything in MsgSound - - command_announcement.Announce(input, customname, MsgSound[beepsound], , , type) - print_command_report(input, "[command_name()] Update") - if("No") - //same thing as the blob stuff - it's not public, so it's classified, dammit - command_announcer.autosay("A classified message has been printed out at all communication consoles."); - print_command_report(input, "Classified [command_name()] Update") - else - return - - log_admin("[key_name(src)] has created a communications report: [input]") - message_admins("[key_name_admin(src)] has created a communications report", 1) - feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in view()) - set category = "Admin" - set name = "Delete" - - if(!check_rights(R_ADMIN)) - return - - admin_delete(A) - -/client/proc/admin_delete(datum/D) - if(istype(D) && !D.can_vv_delete()) - to_chat(src, "[D] rejected your deletion") - return - var/atom/A = D - var/coords = istype(A) ? "at ([A.x], [A.y], [A.z])" : "" - if(alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", "Yes", "No") == "Yes") - log_admin("[key_name(usr)] deleted [D] [coords]") - message_admins("[key_name_admin(usr)] deleted [D] [coords]", 1) - feedback_add_details("admin_verb","DEL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - if(isturf(D)) - var/turf/T = D - T.ChangeTurf(T.baseturf) - else - qdel(D) - -/client/proc/cmd_admin_list_open_jobs() - set category = "Admin" - set name = "List free slots" - - if(!check_rights(R_ADMIN)) - return - - if(SSjobs) - var/currentpositiontally - var/totalpositiontally - to_chat(src, "Job Name: Filled job slot / Total job slots (Free job slots)") - for(var/datum/job/job in SSjobs.occupations) - to_chat(src, "[job.title]: [job.current_positions] / \ - [job.total_positions == -1 ? "UNLIMITED" : job.total_positions] \ - ([job.total_positions == -1 ? "UNLIMITED" : job.total_positions - job.current_positions])") - if(job.total_positions != -1) // Only count position that isn't unlimited - currentpositiontally += job.current_positions - totalpositiontally += job.total_positions - to_chat(src, "Currently filled job slots (Excluding unlimited): [currentpositiontally] / [totalpositiontally] ([totalpositiontally - currentpositiontally])") - feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in view()) - set category = "Event" - set name = "Explosion" - - if(!check_rights(R_DEBUG|R_EVENT)) - return - - var/devastation = input("Range of total devastation. -1 to none", text("Input")) as num|null - if(devastation == null) return - var/heavy = input("Range of heavy impact. -1 to none", text("Input")) as num|null - if(heavy == null) return - var/light = input("Range of light impact. -1 to none", text("Input")) as num|null - if(light == null) return - var/flash = input("Range of flash. -1 to none", text("Input")) as num|null - if(flash == null) return - var/flames = input("Range of flames. -1 to none", text("Input")) as num|null - if(flames == null) return - - if((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1) || (flames != -1)) - if((devastation > 20) || (heavy > 20) || (light > 20) || (flames > 20)) - if(alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", "Yes", "No") == "No") - return - - explosion(O, devastation, heavy, light, flash, null, null,flames) - log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") - feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - else - return - -/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in view()) - set category = "Event" - set name = "EM Pulse" - - if(!check_rights(R_DEBUG|R_EVENT)) - return - - var/heavy = input("Range of heavy pulse.", text("Input")) as num|null - if(heavy == null) return - var/light = input("Range of light pulse.", text("Input")) as num|null - if(light == null) return - - if(heavy || light) - - empulse(O, heavy, light) - log_admin("[key_name(usr)] created an EM pulse ([heavy], [light]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an EM pulse ([heavy], [light]) at ([O.x],[O.y],[O.z])", 1) - feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - return - else - return - -/client/proc/cmd_admin_gib(mob/M as mob in GLOB.mob_list) - set category = "Admin" - set name = "Gib" - - if(!check_rights(R_ADMIN|R_EVENT)) - return - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm != "Yes") return - //Due to the delay here its easy for something to have happened to the mob - if(!M) return - - log_admin("[key_name(usr)] has gibbed [key_name(M)]") - message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]", 1) - - if(istype(M, /mob/dead/observer)) - gibs(M.loc) - return - - M.gib() - feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_gib_self() - set name = "Gibself" - set category = "Event" - - if(!check_rights(R_ADMIN|R_EVENT)) - return - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm == "Yes") - if(istype(mob, /mob/dead/observer)) // so they don't spam gibs everywhere - return - else - mob.gib() - - log_admin("[key_name(usr)] used gibself.") - message_admins("[key_name_admin(usr)] used gibself.", 1) - feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_check_contents(mob/living/M as mob in GLOB.mob_list) - set name = "Check Contents" - set category = null - - if(!check_rights(R_ADMIN)) - return - - var/list/L = M.get_contents() - for(var/t in L) - to_chat(usr, "[t]") - feedback_add_details("admin_verb","CC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_view_range() - set category = "Admin" - set name = "Change View Range" - set desc = "switches between 1x and custom views" - - if(!check_rights(R_ADMIN)) - return - - if(view == world.view) - view = input("Select view range:", "View Range", world.view) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128) - else - view = world.view - - log_admin("[key_name(usr)] changed their view range to [view].") - //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI - - feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/admin_call_shuttle() - - set category = "Admin" - set name = "Call Shuttle" - - if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) - return - - if(!check_rights(R_ADMIN)) - return - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm != "Yes") return - - if(alert("Set Shuttle Recallable (Select Yes unless you know what this does)", "Recallable?", "Yes", "No") == "Yes") - SSshuttle.emergency.canRecall = TRUE - else - SSshuttle.emergency.canRecall = FALSE - - SSshuttle.emergency.request() - - feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-called the emergency shuttle.") - message_admins("[key_name_admin(usr)] admin-called the emergency shuttle.") - return - -/client/proc/admin_cancel_shuttle() - set category = "Admin" - set name = "Cancel Shuttle" - - if(!check_rights(R_ADMIN)) - return - if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes") return - - if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) - return - - if(SSshuttle.emergency.canRecall == FALSE) - if(alert("Shuttle is currently set to be nonrecallable. Recalling may break things. Respect Recall Status?", "Override Recall Status?", "Yes", "No") == "Yes") - return - else - var/keepStatus = alert("Maintain recall status on future shuttle calls?", "Maintain Status?", "Yes", "No") == "Yes" //Keeps or drops recallability - SSshuttle.emergency.canRecall = TRUE // must be true for cancel proc to work - SSshuttle.emergency.cancel() - if(keepStatus) - SSshuttle.emergency.canRecall = FALSE // restores original status - else - SSshuttle.emergency.cancel() - - feedback_add_details("admin_verb","CCSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") - message_admins("[key_name_admin(usr)] admin-recalled the emergency shuttle.") - return - -/client/proc/admin_deny_shuttle() - set category = "Admin" - set name = "Toggle Deny Shuttle" - - if(!SSticker) - return - - if(!check_rights(R_ADMIN)) - return - - if(SSshuttle) - SSshuttle.emergencyNoEscape = !SSshuttle.emergencyNoEscape - - log_admin("[key_name(src)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") - message_admins("[key_name_admin(usr)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") - -/client/proc/cmd_admin_attack_log(mob/M as mob in GLOB.mob_list) - set category = "Admin" - set name = "Attack Log" - - if(!check_rights(R_ADMIN)) - return - - to_chat(usr, text("Attack Log for []", mob)) - for(var/t in M.attack_log) - to_chat(usr, t) - feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/everyone_random() - set category = "Event" - set name = "Make Everyone Random" - set desc = "Make everyone have a random appearance. You can only use this before rounds!" - - if(!check_rights(R_SERVER|R_EVENT)) - return - - if(SSticker && SSticker.mode) - to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") - return - - if(SSticker.random_players) - SSticker.random_players = 0 - message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.", 1) - to_chat(usr, "Disabled.") - return - - - var/notifyplayers = alert(src, "Do you want to notify the players?", "Options", "Yes", "No", "Cancel") - if(notifyplayers == "Cancel") - return - - log_admin("Admin [key_name(src)] has forced the players to have random appearances.") - message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.", 1) - - if(notifyplayers == "Yes") - to_chat(world, "Admin [usr.key] has forced the players to have completely random identities!") - - to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") - - SSticker.random_players = 1 - feedback_add_details("admin_verb","MER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_random_events() - set category = "Event" - set name = "Toggle random events on/off" - - set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" - if(!check_rights(R_SERVER|R_EVENT)) - return - - if(!config.allow_random_events) - config.allow_random_events = 1 - to_chat(usr, "Random events enabled") - message_admins("Admin [key_name_admin(usr)] has enabled random events.", 1) - else - config.allow_random_events = 0 - to_chat(usr, "Random events disabled") - message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1) - feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/reset_all_tcs() - set category = "Admin" - set name = "Reset NTTC Configuration" - set desc = "Resets NTTC to the default configuration." - - if(!check_rights(R_ADMIN)) - return - - var/confirm = alert(src, "You sure you want to reset NTTC?", "Confirm", "Yes", "No") - if(confirm != "Yes") - return - - GLOB.nttc_config.reset() - log_admin("[key_name(usr)] reset NTTC scripts.") - message_admins("[key_name_admin(usr)] reset NTTC scripts.") - feedback_add_details("admin_verb","RAT2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/list_ssds_afks() - set category = "Admin" - set name = "List SSDs and AFKs" - set desc = "Lists SSD and AFK players" - - if(!check_rights(R_ADMIN)) - return - - /* ======== SSD Section ========= */ - var/msg = "SSD & AFK Report" - msg += "SSD Players:
    " - msg += "" - var/mins_ssd - var/job_string - var/key_string - var/role_string - var/obj_count = 0 - var/obj_string = "" - for(var/mob/living/carbon/human/H in GLOB.living_mob_list) - if(!isLivingSSD(H)) - continue - mins_ssd = round((world.time - H.last_logout) / 600) - if(H.job) - job_string = H.job - else - job_string = "-" - key_string = H.key - if(job_string in command_positions) - job_string = "" + job_string + "" - role_string = "-" - obj_count = 0 - obj_string = "" - if(H.mind) - if(H.mind.special_role) - role_string = "[H.mind.special_role]" - if(!H.key && H.mind.key) - key_string = H.mind.key - for(var/datum/objective/O in GLOB.all_objectives) - if(O.target == H.mind) - obj_count++ - if(obj_count > 0) - obj_string = "
    Obj Target" - msg += "" - msg += "" - if(istype(H.loc, /obj/machinery/cryopod)) - msg += "" - else - msg += "" - msg += "" - msg += "
    KeyReal NameJobMins SSDSpecial RoleAreaPPNCryo
    [key_string][H.real_name][job_string][mins_ssd][role_string][obj_string][get_area(H)][ADMIN_PP(H,"PP")]De-SpawnCryo

    " - - /* ======== AFK Section ========= */ - msg += "AFK Players:
    " - msg += "" - var/mins_afk - for(var/mob/living/carbon/human/H in GLOB.living_mob_list) - if(H.client == null || H.stat == DEAD) // No clientless or dead - continue - mins_afk = round(H.client.inactivity / 600) - if(mins_afk < config.list_afk_minimum) - continue - if(H.job) - job_string = H.job - else - job_string = "-" - key_string = H.key - if(job_string in command_positions) - job_string = "" + job_string + "" - role_string = "-" - obj_count = 0 - obj_string = "" - if(H.mind) - if(H.mind.special_role) - role_string = "[H.mind.special_role]" - if(!H.key && H.mind.key) - key_string = H.mind.key - for(var/datum/objective/O in GLOB.all_objectives) - if(O.target == H.mind) - obj_count++ - if(obj_count > 0) - obj_string = "
    Obj Target" - msg += "" - msg += "" - if(istype(H.loc, /obj/machinery/cryopod)) - msg += "" - else - msg += "" - msg += "" - msg += "
    KeyReal NameJobMins AFKSpecial RoleAreaPPNCryo
    [key_string][H.real_name][job_string][mins_afk][role_string][obj_string][get_area(H)][ADMIN_PP(H,"PP")]De-SpawnCryo
    " - src << browse(msg, "window=Player_ssd_afk_check;size=600x300") - -/client/proc/toggle_ert_calling() - set category = "Event" - set name = "Toggle ERT" - - set desc = "Toggle the station's ability to call a response team." - if(!check_rights(R_EVENT)) - return - - if(SSticker.mode.ert_disabled) - SSticker.mode.ert_disabled = 0 - to_chat(usr, "ERT has been Enabled.") - log_admin("Admin [key_name(src)] has enabled ERT calling.") - message_admins("Admin [key_name_admin(usr)] has enabled ERT calling.", 1) - else - SSticker.mode.ert_disabled = 1 - to_chat(usr, "ERT has been Disabled.") - log_admin("Admin [key_name(src)] has disabled ERT calling.") - message_admins("Admin [key_name_admin(usr)] has disabled ERT calling.", 1) - -/client/proc/show_tip() - set category = "Admin" - set name = "Show Custom Tip" - set desc = "Sends a tip (that you specify) to all players. After all \ - you're the experienced player here." - - if(!check_rights(R_ADMIN)) - return - - var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null - if(!input) - return - - if(!SSticker) - return - - SSticker.selected_tip = input - - // If we've already tipped, then send it straight away. - if(SSticker.tipped) - SSticker.send_tip_of_the_round() - - message_admins("[key_name_admin(usr)] sent a Tip of the round.") - log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.") - -/client/proc/modify_goals() - set category = "Event" - set name = "Modify Station Goals" - - if(!check_rights(R_EVENT)) - return - - holder.modify_goals() - -/datum/admins/proc/modify_goals() - if(!SSticker || !SSticker.mode) - to_chat(usr, "This verb can only be used if the round has started.") - return - - var/dat = "" - for(var/datum/station_goal/S in SSticker.mode.station_goals) - dat += "[S.name] - Announce | Remove
    " - dat += "
    Add New Goal" - usr << browse(dat, "window=goals;size=400x400") - -/// 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/availible_traits = list() - - switch(add_or_remove) - if("Add") - for(var/key in GLOB.traits_by_type) - if(istype(D,key)) - availible_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 - availible_traits[name] = trait - - var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in availible_traits - if(!chosen_trait) - return - chosen_trait = availible_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) \ No newline at end of file +/client/proc/cmd_admin_drop_everything(mob/M as mob in GLOB.mob_list) + set category = null + set name = "Drop Everything" + + if(!check_rights(R_DEBUG|R_ADMIN)) + return + + var/confirm = alert(src, "Make [M] drop everything?", "Message", "Yes", "No") + if(confirm != "Yes") + return + + for(var/obj/item/W in M) + M.unEquip(W) + + log_admin("[key_name(usr)] made [key_name(M)] drop everything!") + message_admins("[key_name_admin(usr)] made [key_name_admin(M)] drop everything!", 1) + feedback_add_details("admin_verb","DEVR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_prison(mob/M as mob in GLOB.mob_list) + set category = "Admin" + set name = "Prison" + + if(!check_rights(R_ADMIN)) + return + + if(ismob(M)) + if(istype(M, /mob/living/silicon/ai)) + alert("The AI can't be sent to prison you jerk!", null, null, null, null, null) + return + //strip their stuff before they teleport into a cell :downs: + for(var/obj/item/W in M) + M.unEquip(W) + //teleport person to cell + M.Paralyse(5) + sleep(5) //so they black out before warping + M.loc = pick(GLOB.prisonwarp) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/prisoner = M + prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) + prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) + spawn(50) + to_chat(M, "You have been sent to the prison station!") + log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") + message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) + feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_subtle_message(mob/M as mob in GLOB.mob_list) + set category = "Event" + set name = "Subtle Message" + + if(!ismob(M)) + return + + if(!check_rights(R_SERVER|R_EVENT)) + return + + var/msg = clean_input("Message:", text("Subtle PM to [M.key]")) + + if(!msg) + return + + msg = admin_pencode_to_html(msg) + + if(usr) + if(usr.client) + if(usr.client.holder) + to_chat(M, "You hear a voice in your head... [msg]") + + log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") + message_admins("SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]", 1) + feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_mentor_check_new_players() //Allows mentors / admins to determine who the newer players are. + set category = "Admin" + set name = "Check new Players" + + if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) + return + + var/age = alert(src, "Age check", "Show accounts yonger then _____ days","7", "30" , "All") + + if(age == "All") + age = 9999999 + else + age = text2num(age) + + var/missing_ages = 0 + var/msg = "" + for(var/client/C in GLOB.clients) + if(C.player_age == "Requires database") + missing_ages = 1 + continue + if(C.player_age < age) + if(check_rights(R_ADMIN, 0)) + msg += "[key_name_admin(C.mob)]: [C.player_age] days old
    " + else + msg += "[key_name_mentor(C.mob)]: [C.player_age] days old
    " + + if(missing_ages) + to_chat(src, "Some accounts did not have proper ages set in their clients. This function requires database to be present") + + if(msg != "") + src << browse(msg, "window=Player_age_check") + else + to_chat(src, "No matches for that age range found.") + + +/client/proc/cmd_admin_world_narrate() // Allows administrators to fluff events a little easier -- TLE + set category = "Event" + set name = "Global Narrate" + + if(!check_rights(R_SERVER|R_EVENT)) + return + + var/msg = clean_input("Message:", text("Enter the text you wish to appear to everyone:")) + + if(!msg) + return + msg = admin_pencode_to_html(msg) + to_chat(world, "[msg]") + log_admin("GlobalNarrate: [key_name(usr)] : [msg]") + message_admins("GlobalNarrate: [key_name_admin(usr)]: [msg]
    ", 1) + feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE + set category = "Event" + set name = "Direct Narrate" + + if(!check_rights(R_SERVER|R_EVENT)) + return + + if(!M) + M = input("Direct narrate to who?", "Active Players") as null|anything in get_mob_with_client_list() + + if(!M) + return + + var/msg = clean_input("Message:", text("Enter the text you wish to appear to your target:")) + + if( !msg ) + return + msg = admin_pencode_to_html(msg) + + to_chat(M, msg) + log_admin("DirectNarrate: [key_name(usr)] to ([key_name(M)]): [msg]") + message_admins("DirectNarrate: [key_name_admin(usr)] to ([key_name_admin(M)]): [msg]
    ", 1) + feedback_add_details("admin_verb","DIRN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + + + +/client/proc/cmd_admin_headset_message(mob/M in GLOB.mob_list) + set category = "Event" + set name = "Headset Message" + + admin_headset_message(M) + +/client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null) + var/mob/living/carbon/human/H = M + + if(!check_rights(R_ADMIN)) + return + + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) + to_chat(usr, "The person you are trying to contact is not wearing a headset") + return + + if(!sender) + sender = input("Who is the message from?", "Sender") as null|anything in list("Centcomm", "Syndicate") + if(!sender) + return + + message_admins("[key_name_admin(src)] has started answering [key_name_admin(H)]'s [sender] request.") + var/input = clean_input("Please enter a message to reply to [key_name(H)] via their headset.", "Outgoing message from [sender]", "") + if(!input) + message_admins("[key_name_admin(src)] decided not to answer [key_name_admin(H)]'s [sender] request.") + return + + log_admin("[key_name(src)] replied to [key_name(H)]'s [sender] message with the message [input].") + message_admins("[key_name_admin(src)] replied to [key_name_admin(H)]'s [sender] message with: \"[input]\"") + to_chat(H, "Incoming priority transmission from [sender == "Syndicate" ? "your benefactor" : "Central Command"]. Message as follows[sender == "Syndicate" ? ", agent." : ":"] [input]") + + + + +/client/proc/cmd_admin_godmode(mob/M as mob in GLOB.mob_list) + set category = "Admin" + set name = "Godmode" + + if(!check_rights(R_ADMIN)) + return + + M.status_flags ^= GODMODE + to_chat(usr, "Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]") + + log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") + message_admins("[key_name_admin(usr)] has toggled [key_name_admin(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]", 1) + feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) + if(automute) + if(!config.automute_on) + return + else + if(!usr || !usr.client) + return + if(!check_rights(R_ADMIN|R_MOD)) + to_chat(usr, "Error: cmd_admin_mute: You don't have permission to do this.") + return + if(!M.client) + to_chat(usr, "Error: cmd_admin_mute: This mob doesn't have a client tied to it.") + if(!M.client) + return + + var/muteunmute + var/mute_string + + switch(mute_type) + if(MUTE_IC) mute_string = "IC (say and emote)" + if(MUTE_OOC) mute_string = "OOC" + if(MUTE_PRAY) mute_string = "pray" + if(MUTE_ADMINHELP) mute_string = "adminhelp, admin PM and ASAY" + if(MUTE_DEADCHAT) mute_string = "deadchat and DSAY" + if(MUTE_ALL) mute_string = "everything" + else return + + if(automute) + muteunmute = "auto-muted" + M.client.prefs.muted |= mute_type + log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]") + message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1) + to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") + feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + + if(M.client.prefs.muted & mute_type) + muteunmute = "unmuted" + M.client.prefs.muted &= ~mute_type + else + muteunmute = "muted" + M.client.prefs.muted |= mute_type + + log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]") + message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1) + to_chat(M, "You have been [muteunmute] from [mute_string].") + feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_add_random_ai_law() + set category = "Event" + set name = "Add Random AI Law" + + if(!check_rights(R_EVENT)) + return + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm != "Yes") return + log_admin("[key_name(src)] has added a random AI law.") + message_admins("[key_name_admin(src)] has added a random AI law.") + + var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") + var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) + + new /datum/event/ion_storm(0, announce_ion_laws) + feedback_add_details("admin_verb","ION") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_antagHUD_use() + set category = "Server" + set name = "Toggle antagHUD usage" + set desc = "Toggles antagHUD usage for observers" + + if(!check_rights(R_SERVER)) + return + + var/action="" + if(config.antag_hud_allowed) + for(var/mob/dead/observer/g in get_ghosts()) + if(!g.client.holder) //Remove the verb from non-admin ghosts + g.verbs -= /mob/dead/observer/verb/toggle_antagHUD + if(g.antagHUD) + g.antagHUD = 0 // Disable it on those that have it enabled + g.has_enabled_antagHUD = 2 // We'll allow them to respawn + to_chat(g, "The Administrator has disabled AntagHUD ") + config.antag_hud_allowed = 0 + to_chat(src, "AntagHUD usage has been disabled") + action = "disabled" + else + for(var/mob/dead/observer/g in get_ghosts()) + if(!g.client.holder) // Add the verb back for all non-admin ghosts + g.verbs += /mob/dead/observer/verb/toggle_antagHUD + to_chat(g, "The Administrator has enabled AntagHUD ")// Notify all observers they can now use AntagHUD + + config.antag_hud_allowed = 1 + action = "enabled" + to_chat(src, "AntagHUD usage has been enabled") + + + log_admin("[key_name(usr)] has [action] antagHUD usage for observers") + message_admins("Admin [key_name_admin(usr)] has [action] antagHUD usage for observers", 1) + +/client/proc/toggle_antagHUD_restrictions() + set category = "Server" + set name = "Toggle antagHUD Restrictions" + set desc = "Restricts players that have used antagHUD from being able to join this round." + + if(!check_rights(R_SERVER)) + return + + var/action="" + if(config.antag_hud_restricted) + for(var/mob/dead/observer/g in get_ghosts()) + to_chat(g, "The administrator has lifted restrictions on joining the round if you use AntagHUD") + action = "lifted restrictions" + config.antag_hud_restricted = 0 + to_chat(src, "AntagHUD restrictions have been lifted") + else + for(var/mob/dead/observer/g in get_ghosts()) + to_chat(g, "The administrator has placed restrictions on joining the round if you use AntagHUD") + to_chat(g, "Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions ") + g.antagHUD = 0 + g.has_enabled_antagHUD = 0 + action = "placed restrictions" + config.antag_hud_restricted = 1 + to_chat(src, "AntagHUD restrictions have been enabled") + + log_admin("[key_name(usr)] has [action] on joining the round if they use AntagHUD") + message_admins("Admin [key_name_admin(usr)] has [action] on joining the round if they use AntagHUD", 1) + +/* +If a guy was gibbed and you want to revive him, this is a good way to do so. +Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. +Traitors and the like can also be revived with the previous role mostly intact. +/N */ +/client/proc/respawn_character() + set category = "Event" + set name = "Respawn Character" + set desc = "Respawn a person that has been gibbed/dusted/killed. They must be a ghost for this to work and preferably should not have a body to go back into." + + if(!check_rights(R_SPAWN)) + return + + var/input = ckey(input(src, "Please specify which key will be respawned.", "Key", "")) + if(!input) + return + + var/mob/dead/observer/G_found + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.ckey == input) + G_found = G + break + + if(!G_found)//If a ghost was not found. + to_chat(usr, "There is no active key like that in the game or the person is not currently a ghost.") + return + + if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something + //Check if they were an alien + if(G_found.mind.assigned_role=="Alien") + if(alert("This character appears to have been an alien. Would you like to respawn them as such?",,"Yes","No")=="Yes") + var/turf/T + if(GLOB.xeno_spawn.len) T = pick(GLOB.xeno_spawn) + else T = pick(GLOB.latejoin) + + var/mob/living/carbon/alien/new_xeno + switch(G_found.mind.special_role)//If they have a mind, we can determine which caste they were. + if("Hunter") new_xeno = new /mob/living/carbon/alien/humanoid/hunter(T) + if("Sentinel") new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(T) + if("Drone") new_xeno = new /mob/living/carbon/alien/humanoid/drone(T) + if("Queen") new_xeno = new /mob/living/carbon/alien/humanoid/queen(T) + else//If we don't know what special role they have, for whatever reason, or they're a larva. + create_xeno(G_found.ckey) + return + + //Now to give them their mind back. + G_found.mind.transfer_to(new_xeno) //be careful when doing stuff like this! I've already checked the mind isn't in use + new_xeno.key = G_found.key + to_chat(new_xeno, "You have been fully respawned. Enjoy the game.") + message_admins("[key_name_admin(usr)] has respawned [new_xeno.key] as a filthy xeno.", 1) + return //all done. The ghost is auto-deleted + + var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))//The mob being spawned. + + var/datum/data/record/record_found //Referenced to later to either randomize or not randomize the character. + if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something + /*Try and locate a record for the person being respawned through data_core. + This isn't an exact science but it does the trick more often than not.*/ + var/id = md5("[G_found.real_name][G_found.mind.assigned_role]") + for(var/datum/data/record/t in GLOB.data_core.locked) + if(t.fields["id"]==id) + record_found = t//We shall now reference the record. + break + + if(record_found)//If they have a record we can determine a few things. + new_character.real_name = record_found.fields["name"] + new_character.change_gender(record_found.fields["sex"]) + new_character.age = record_found.fields["age"] + new_character.dna.blood_type = record_found.fields["blood_type"] + else + new_character.change_gender(pick(MALE,FEMALE)) + var/datum/preferences/A = new() + A.real_name = G_found.real_name + A.copy_to(new_character) + + if(!new_character.real_name) + new_character.real_name = random_name(new_character.gender) + new_character.name = new_character.real_name + + if(G_found.mind && !G_found.mind.active) + G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use + new_character.mind.special_verbs = list() + else + new_character.mind_initialize() + if(!new_character.mind.assigned_role) new_character.mind.assigned_role = "Civilian"//If they somehow got a null assigned role. + + //DNA + if(record_found)//Pull up their name from database records if they did have a mind. + new_character.dna = new()//Let's first give them a new DNA. + new_character.dna.unique_enzymes = record_found.fields["b_dna"]//Enzymes are based on real name but we'll use the record for conformity. + + // I HATE BYOND. HATE. HATE. - N3X + var/list/newSE= record_found.fields["enzymes"] + var/list/newUI = record_found.fields["identity"] + new_character.dna.SE = newSE.Copy() //This is the default of enzymes so I think it's safe to go with. + new_character.dna.UpdateSE() + new_character.UpdateAppearance(newUI.Copy())//Now we configure their appearance based on their unique identity, same as with a DNA machine or somesuch. + else//If they have no records, we just do a random DNA for them, based on their random appearance/savefile. + new_character.dna.ready_dna(new_character) + + new_character.key = G_found.key + + /* + The code below functions with the assumption that the mob is already a traitor if they have a special role. + So all it does is re-equip the mob with powers and/or items. Or not, if they have no special role. + If they don't have a mind, they obviously don't have a special role. + */ + + //Now for special roles and equipment. + switch(new_character.mind.special_role) + if("traitor") + if(new_character.mind.has_antag_datum(/datum/antagonist/traitor)) + var/datum/antagonist/traitor/T = new_character.mind.has_antag_datum(/datum/antagonist/traitor) + T.equip_traitor(src) + else + new_character.mind.add_antag_datum(/datum/antagonist/traitor) + if("Wizard") + new_character.loc = pick(GLOB.wizardstart) + //ticker.mode.learn_basic_spells(new_character) + SSticker.mode.equip_wizard(new_character) + if("Syndicate") + var/obj/effect/landmark/synd_spawn = locate("landmark*Syndicate-Spawn") + if(synd_spawn) + new_character.loc = get_turf(synd_spawn) + call(/datum/game_mode/proc/equip_syndicate)(new_character) + + if("Death Commando")//Leaves them at late-join spawn. + new_character.equip_death_commando() + new_character.internal = new_character.s_store + new_character.update_action_buttons_icon() + else//They may also be a cyborg or AI. + switch(new_character.mind.assigned_role) + if("Cyborg")//More rigging to make em' work and check if they're traitor. + new_character = new_character.Robotize() + if(new_character.mind.special_role=="traitor") + new_character.mind.add_antag_datum(/datum/antagonist/traitor) + if("AI") + new_character = new_character.AIize() + var/mob/living/silicon/ai/ai_character = new_character + ai_character.moveToAILandmark() + if(new_character.mind.special_role=="traitor") + new_character.mind.add_antag_datum(/datum/antagonist/traitor) + //Add aliens. + else + SSjobs.AssignRank(new_character, new_character.mind.assigned_role, 0) + SSjobs.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them. + + //Announces the character on all the systems, based on the record. + if(!issilicon(new_character))//If they are not a cyborg/AI. + if(!record_found && new_character.mind.assigned_role != new_character.mind.special_role)//If there are no records for them. If they have a record, this info is already in there. Offstation special characters announced anyway. + //Power to the user! + if(alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,"No","Yes")=="Yes") + GLOB.data_core.manifest_inject(new_character) + + if(alert(new_character,"Would you like an active AI to announce this character?",,"No","Yes")=="Yes") + call(/mob/new_player/proc/AnnounceArrival)(new_character, new_character.mind.assigned_role) + + message_admins("[key_name_admin(usr)] has respawned [key_name_admin(G_found)] as [new_character.real_name].", 1) + + to_chat(new_character, "You have been fully respawned. Enjoy the game.") + + feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return new_character + +//I use this proc for respawn character too. /N +/proc/create_xeno(ckey) + if(!ckey) + var/list/candidates = list() + for(var/mob/M in GLOB.player_list) + if(M.stat != DEAD) continue //we are not dead! + if(!(ROLE_ALIEN in M.client.prefs.be_special)) continue //we don't want to be an alium + if(jobban_isbanned(M, "alien") || jobban_isbanned(M, "Syndicate")) continue //we are jobbanned + if(M.client.is_afk()) continue //we are afk + if(M.mind && M.mind.current && M.mind.current.stat != DEAD) continue //we have a live body we are tied to + candidates += M.ckey + if(candidates.len) + ckey = input("Pick the player you want to respawn as a xeno.", "Suitable Candidates") as null|anything in candidates + else + to_chat(usr, "Error: create_xeno(): no suitable candidates.") + if(!istext(ckey)) return 0 + + var/alien_caste = input(usr, "Please choose which caste to spawn.","Pick a caste",null) as null|anything in list("Queen","Hunter","Sentinel","Drone","Larva") + var/obj/effect/landmark/spawn_here = GLOB.xeno_spawn.len ? pick(GLOB.xeno_spawn) : pick(GLOB.latejoin) + var/mob/living/carbon/alien/new_xeno + switch(alien_caste) + if("Queen") new_xeno = new /mob/living/carbon/alien/humanoid/queen/large(spawn_here) + if("Hunter") new_xeno = new /mob/living/carbon/alien/humanoid/hunter(spawn_here) + if("Sentinel") new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(spawn_here) + if("Drone") new_xeno = new /mob/living/carbon/alien/humanoid/drone(spawn_here) + if("Larva") new_xeno = new /mob/living/carbon/alien/larva(spawn_here) + else return 0 + + new_xeno.ckey = ckey + message_admins("[key_name_admin(usr)] has spawned [ckey] as a filthy xeno [alien_caste].", 1) + return 1 + + +/client/proc/get_ghosts(var/notify = 0,var/what = 2) + // what = 1, return ghosts ass list. + // what = 2, return mob list + + var/list/mobs = list() + var/list/ghosts = list() + var/list/sortmob = sortAtom(GLOB.mob_list) // get the mob list. + var/any=0 + for(var/mob/dead/observer/M in sortmob) + mobs.Add(M) //filter it where it's only ghosts + any = 1 //if no ghosts show up, any will just be 0 + if(!any) + if(notify) + to_chat(src, "There doesn't appear to be any ghosts for you to select.") + return + + for(var/mob/M in mobs) + var/name = M.name + ghosts[name] = M //get the name of the mob for the popup list + if(what==1) + return ghosts + else + return mobs + +/client/proc/cmd_admin_add_freeform_ai_law() + set category = "Event" + set name = "Add Custom AI law" + + if(!check_rights(R_EVENT)) + return + + var/input = clean_input("Please enter anything you want the AI to do. Anything. Serious.", "What?", "") + if(!input) + return + + log_admin("Admin [key_name(usr)] has added a new AI law - [input]") + message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]") + + var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") + var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) + + new /datum/event/ion_storm(0, announce_ion_laws, input) + + feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in GLOB.mob_list) + set category = "Event" + set name = "Rejuvenate" + + if(!check_rights(R_REJUVINATE)) + return + + if(!mob) + return + if(!istype(M)) + alert("Cannot revive a ghost") + return + M.revive() + + log_admin("[key_name(usr)] healed / revived [key_name(M)]") + message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(M)]!", 1) + feedback_add_details("admin_verb","REJU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_create_centcom_report() + set category = "Event" + set name = "Create Communications Report" + + if(!check_rights(R_SERVER|R_EVENT)) + return + +//the stuff on the list is |"report type" = "report title"|, if that makes any sense + var/list/MsgType = list("Central Command Report" = "Nanotrasen Update", + "Syndicate Communique" = "Syndicate Message", + "Space Wizard Federation Message" = "Sorcerous Message", + "Enemy Communications" = "Unknown Message", + "Custom" = "Cryptic Message") + + var/list/MsgSound = list("Beep" = 'sound/misc/notice2.ogg', + "Enemy Communications Intercepted" = 'sound/AI/intercept2.ogg', + "New Command Report Created" = 'sound/AI/commandreport.ogg') + + var/type = input(usr, "Pick a type of report to send", "Report Type", "") as anything in MsgType + + if(type == "Custom") + type = clean_input("What would you like the report type to be?", "Report Type", "Encrypted Transmission") + + var/customname = input(usr, "Pick a title for the report.", "Title", MsgType[type]) as text|null + if(!customname) + return + var/input = input(usr, "Please enter anything you want. Anything. Serious.", "What's the message?") as message|null + if(!input) + return + + switch(alert("Should this be announced to the general population?",,"Yes","No", "Cancel")) + if("Yes") + var/beepsound = input(usr, "What sound should the announcement make?", "Announcement Sound", "") as anything in MsgSound + + GLOB.command_announcement.Announce(input, customname, MsgSound[beepsound], , , type) + print_command_report(input, "[command_name()] Update") + if("No") + //same thing as the blob stuff - it's not public, so it's classified, dammit + GLOB.command_announcer.autosay("A classified message has been printed out at all communication consoles."); + print_command_report(input, "Classified [command_name()] Update") + else + return + + log_admin("[key_name(src)] has created a communications report: [input]") + message_admins("[key_name_admin(src)] has created a communications report", 1) + feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in view()) + set category = "Admin" + set name = "Delete" + + if(!check_rights(R_ADMIN)) + return + + admin_delete(A) + +/client/proc/admin_delete(datum/D) + if(istype(D) && !D.can_vv_delete()) + to_chat(src, "[D] rejected your deletion") + return + var/atom/A = D + var/coords = istype(A) ? "at ([A.x], [A.y], [A.z])" : "" + if(alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", "Yes", "No") == "Yes") + log_admin("[key_name(usr)] deleted [D] [coords]") + message_admins("[key_name_admin(usr)] deleted [D] [coords]", 1) + feedback_add_details("admin_verb","DEL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(isturf(D)) + var/turf/T = D + T.ChangeTurf(T.baseturf) + else + qdel(D) + +/client/proc/cmd_admin_list_open_jobs() + set category = "Admin" + set name = "List free slots" + + if(!check_rights(R_ADMIN)) + return + + if(SSjobs) + var/currentpositiontally + var/totalpositiontally + to_chat(src, "Job Name: Filled job slot / Total job slots (Free job slots)") + for(var/datum/job/job in SSjobs.occupations) + to_chat(src, "[job.title]: [job.current_positions] / \ + [job.total_positions == -1 ? "UNLIMITED" : job.total_positions] \ + ([job.total_positions == -1 ? "UNLIMITED" : job.total_positions - job.current_positions])") + if(job.total_positions != -1) // Only count position that isn't unlimited + currentpositiontally += job.current_positions + totalpositiontally += job.total_positions + to_chat(src, "Currently filled job slots (Excluding unlimited): [currentpositiontally] / [totalpositiontally] ([totalpositiontally - currentpositiontally])") + feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in view()) + set category = "Event" + set name = "Explosion" + + if(!check_rights(R_DEBUG|R_EVENT)) + return + + var/devastation = input("Range of total devastation. -1 to none", text("Input")) as num|null + if(devastation == null) return + var/heavy = input("Range of heavy impact. -1 to none", text("Input")) as num|null + if(heavy == null) return + var/light = input("Range of light impact. -1 to none", text("Input")) as num|null + if(light == null) return + var/flash = input("Range of flash. -1 to none", text("Input")) as num|null + if(flash == null) return + var/flames = input("Range of flames. -1 to none", text("Input")) as num|null + if(flames == null) return + + if((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1) || (flames != -1)) + if((devastation > 20) || (heavy > 20) || (light > 20) || (flames > 20)) + if(alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", "Yes", "No") == "No") + return + + explosion(O, devastation, heavy, light, flash, null, null,flames) + log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") + feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + else + return + +/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in view()) + set category = "Event" + set name = "EM Pulse" + + if(!check_rights(R_DEBUG|R_EVENT)) + return + + var/heavy = input("Range of heavy pulse.", text("Input")) as num|null + if(heavy == null) return + var/light = input("Range of light pulse.", text("Input")) as num|null + if(light == null) return + + if(heavy || light) + + empulse(O, heavy, light) + log_admin("[key_name(usr)] created an EM pulse ([heavy], [light]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an EM pulse ([heavy], [light]) at ([O.x],[O.y],[O.z])", 1) + feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + return + else + return + +/client/proc/cmd_admin_gib(mob/M as mob in GLOB.mob_list) + set category = "Admin" + set name = "Gib" + + if(!check_rights(R_ADMIN|R_EVENT)) + return + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm != "Yes") return + //Due to the delay here its easy for something to have happened to the mob + if(!M) return + + log_admin("[key_name(usr)] has gibbed [key_name(M)]") + message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]", 1) + + if(istype(M, /mob/dead/observer)) + gibs(M.loc) + return + + M.gib() + feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_gib_self() + set name = "Gibself" + set category = "Event" + + if(!check_rights(R_ADMIN|R_EVENT)) + return + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm == "Yes") + if(istype(mob, /mob/dead/observer)) // so they don't spam gibs everywhere + return + else + mob.gib() + + log_admin("[key_name(usr)] used gibself.") + message_admins("[key_name_admin(usr)] used gibself.", 1) + feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_check_contents(mob/living/M as mob in GLOB.mob_list) + set name = "Check Contents" + set category = null + + if(!check_rights(R_ADMIN)) + return + + var/list/L = M.get_contents() + for(var/t in L) + to_chat(usr, "[t]") + feedback_add_details("admin_verb","CC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_view_range() + set category = "Admin" + set name = "Change View Range" + set desc = "switches between 1x and custom views" + + if(!check_rights(R_ADMIN)) + return + + if(view == world.view) + view = input("Select view range:", "View Range", world.view) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128) + else + view = world.view + + log_admin("[key_name(usr)] changed their view range to [view].") + //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI + + feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/admin_call_shuttle() + + set category = "Admin" + set name = "Call Shuttle" + + if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) + return + + if(!check_rights(R_ADMIN)) + return + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm != "Yes") return + + if(alert("Set Shuttle Recallable (Select Yes unless you know what this does)", "Recallable?", "Yes", "No") == "Yes") + SSshuttle.emergency.canRecall = TRUE + else + SSshuttle.emergency.canRecall = FALSE + + SSshuttle.emergency.request() + + feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-called the emergency shuttle.") + message_admins("[key_name_admin(usr)] admin-called the emergency shuttle.") + return + +/client/proc/admin_cancel_shuttle() + set category = "Admin" + set name = "Cancel Shuttle" + + if(!check_rights(R_ADMIN)) + return + if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes") return + + if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) + return + + if(SSshuttle.emergency.canRecall == FALSE) + if(alert("Shuttle is currently set to be nonrecallable. Recalling may break things. Respect Recall Status?", "Override Recall Status?", "Yes", "No") == "Yes") + return + else + var/keepStatus = alert("Maintain recall status on future shuttle calls?", "Maintain Status?", "Yes", "No") == "Yes" //Keeps or drops recallability + SSshuttle.emergency.canRecall = TRUE // must be true for cancel proc to work + SSshuttle.emergency.cancel() + if(keepStatus) + SSshuttle.emergency.canRecall = FALSE // restores original status + else + SSshuttle.emergency.cancel() + + feedback_add_details("admin_verb","CCSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") + message_admins("[key_name_admin(usr)] admin-recalled the emergency shuttle.") + return + +/client/proc/admin_deny_shuttle() + set category = "Admin" + set name = "Toggle Deny Shuttle" + + if(!SSticker) + return + + if(!check_rights(R_ADMIN)) + return + + if(SSshuttle) + SSshuttle.emergencyNoEscape = !SSshuttle.emergencyNoEscape + + log_admin("[key_name(src)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") + message_admins("[key_name_admin(usr)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") + +/client/proc/cmd_admin_attack_log(mob/M as mob in GLOB.mob_list) + set category = "Admin" + set name = "Attack Log" + + if(!check_rights(R_ADMIN)) + return + + to_chat(usr, text("Attack Log for []", mob)) + for(var/t in M.attack_log_old) + to_chat(usr, t) + feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/everyone_random() + set category = "Event" + set name = "Make Everyone Random" + set desc = "Make everyone have a random appearance. You can only use this before rounds!" + + if(!check_rights(R_SERVER|R_EVENT)) + return + + if(SSticker && SSticker.mode) + to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") + return + + if(SSticker.random_players) + SSticker.random_players = 0 + message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.", 1) + to_chat(usr, "Disabled.") + return + + + var/notifyplayers = alert(src, "Do you want to notify the players?", "Options", "Yes", "No", "Cancel") + if(notifyplayers == "Cancel") + return + + log_admin("Admin [key_name(src)] has forced the players to have random appearances.") + message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.", 1) + + if(notifyplayers == "Yes") + to_chat(world, "Admin [usr.key] has forced the players to have completely random identities!") + + to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") + + SSticker.random_players = 1 + feedback_add_details("admin_verb","MER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_random_events() + set category = "Event" + set name = "Toggle random events on/off" + + set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" + if(!check_rights(R_SERVER|R_EVENT)) + return + + if(!config.allow_random_events) + config.allow_random_events = 1 + to_chat(usr, "Random events enabled") + message_admins("Admin [key_name_admin(usr)] has enabled random events.", 1) + else + config.allow_random_events = 0 + to_chat(usr, "Random events disabled") + message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1) + feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/reset_all_tcs() + set category = "Admin" + set name = "Reset NTTC Configuration" + set desc = "Resets NTTC to the default configuration." + + if(!check_rights(R_ADMIN)) + return + + var/confirm = alert(src, "You sure you want to reset NTTC?", "Confirm", "Yes", "No") + if(confirm != "Yes") + return + + GLOB.nttc_config.reset() + log_admin("[key_name(usr)] reset NTTC scripts.") + message_admins("[key_name_admin(usr)] reset NTTC scripts.") + feedback_add_details("admin_verb","RAT2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/list_ssds_afks() + set category = "Admin" + set name = "List SSDs and AFKs" + set desc = "Lists SSD and AFK players" + + if(!check_rights(R_ADMIN)) + return + + /* ======== SSD Section ========= */ + var/msg = "SSD & AFK Report" + msg += "SSD Players:
    " + msg += "" + var/mins_ssd + var/job_string + var/key_string + var/role_string + var/obj_count = 0 + var/obj_string = "" + for(var/mob/living/carbon/human/H in GLOB.living_mob_list) + if(!isLivingSSD(H)) + continue + mins_ssd = round((world.time - H.last_logout) / 600) + if(H.job) + job_string = H.job + else + job_string = "-" + key_string = H.key + if(job_string in GLOB.command_positions) + job_string = "" + job_string + "" + role_string = "-" + obj_count = 0 + obj_string = "" + if(H.mind) + if(H.mind.special_role) + role_string = "[H.mind.special_role]" + if(!H.key && H.mind.key) + key_string = H.mind.key + for(var/datum/objective/O in GLOB.all_objectives) + if(O.target == H.mind) + obj_count++ + if(obj_count > 0) + obj_string = "
    Obj Target" + msg += "" + msg += "" + if(istype(H.loc, /obj/machinery/cryopod)) + msg += "" + else + msg += "" + msg += "" + msg += "
    KeyReal NameJobMins SSDSpecial RoleAreaPPNCryo
    [key_string][H.real_name][job_string][mins_ssd][role_string][obj_string][get_area(H)][ADMIN_PP(H,"PP")]De-SpawnCryo

    " + + /* ======== AFK Section ========= */ + msg += "AFK Players:
    " + msg += "" + var/mins_afk + for(var/mob/living/carbon/human/H in GLOB.living_mob_list) + if(H.client == null || H.stat == DEAD) // No clientless or dead + continue + mins_afk = round(H.client.inactivity / 600) + if(mins_afk < config.list_afk_minimum) + continue + if(H.job) + job_string = H.job + else + job_string = "-" + key_string = H.key + if(job_string in GLOB.command_positions) + job_string = "" + job_string + "" + role_string = "-" + obj_count = 0 + obj_string = "" + if(H.mind) + if(H.mind.special_role) + role_string = "[H.mind.special_role]" + if(!H.key && H.mind.key) + key_string = H.mind.key + for(var/datum/objective/O in GLOB.all_objectives) + if(O.target == H.mind) + obj_count++ + if(obj_count > 0) + obj_string = "
    Obj Target" + msg += "" + msg += "" + if(istype(H.loc, /obj/machinery/cryopod)) + msg += "" + else + msg += "" + msg += "" + msg += "
    KeyReal NameJobMins AFKSpecial RoleAreaPPNCryo
    [key_string][H.real_name][job_string][mins_afk][role_string][obj_string][get_area(H)][ADMIN_PP(H,"PP")]De-SpawnCryo
    " + src << browse(msg, "window=Player_ssd_afk_check;size=600x300") + +/client/proc/toggle_ert_calling() + set category = "Event" + set name = "Toggle ERT" + + set desc = "Toggle the station's ability to call a response team." + if(!check_rights(R_EVENT)) + return + + if(SSticker.mode.ert_disabled) + SSticker.mode.ert_disabled = 0 + to_chat(usr, "ERT has been Enabled.") + log_admin("Admin [key_name(src)] has enabled ERT calling.") + message_admins("Admin [key_name_admin(usr)] has enabled ERT calling.", 1) + else + SSticker.mode.ert_disabled = 1 + to_chat(usr, "ERT has been Disabled.") + log_admin("Admin [key_name(src)] has disabled ERT calling.") + message_admins("Admin [key_name_admin(usr)] has disabled ERT calling.", 1) + +/client/proc/show_tip() + set category = "Admin" + set name = "Show Custom Tip" + set desc = "Sends a tip (that you specify) to all players. After all \ + you're the experienced player here." + + if(!check_rights(R_ADMIN)) + return + + var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null + if(!input) + return + + if(!SSticker) + return + + SSticker.selected_tip = input + + // If we've already tipped, then send it straight away. + if(SSticker.tipped) + SSticker.send_tip_of_the_round() + + message_admins("[key_name_admin(usr)] sent a Tip of the round.") + log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.") + +/client/proc/modify_goals() + set category = "Event" + set name = "Modify Station Goals" + + if(!check_rights(R_EVENT)) + return + + holder.modify_goals() + +/datum/admins/proc/modify_goals() + if(!SSticker || !SSticker.mode) + to_chat(usr, "This verb can only be used if the round has started.") + return + + var/dat = "" + for(var/datum/station_goal/S in SSticker.mode.station_goals) + dat += "[S.name] - Announce | Remove
    " + dat += "
    Add New Goal" + usr << browse(dat, "window=goals;size=400x400") + +/// 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/availible_traits = list() + + switch(add_or_remove) + if("Add") + for(var/key in GLOB.traits_by_type) + if(istype(D,key)) + availible_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 + availible_traits[name] = trait + + var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in availible_traits + if(!chosen_trait) + return + chosen_trait = availible_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) diff --git a/code/modules/admin/verbs/serialization.dm b/code/modules/admin/verbs/serialization.dm index 1c087b9e8fc..6443363f1d0 100644 --- a/code/modules/admin/verbs/serialization.dm +++ b/code/modules/admin/verbs/serialization.dm @@ -23,4 +23,4 @@ var/json_text = input("Enter the JSON code:","Text") as message|null if(json_text) - json_to_object(json_text, get_turf(usr)) \ No newline at end of file + json_to_object(json_text, get_turf(usr)) diff --git a/code/modules/admin/verbs/space_transitions.dm b/code/modules/admin/verbs/space_transitions.dm index eda17996c65..4589e5354b5 100644 --- a/code/modules/admin/verbs/space_transitions.dm +++ b/code/modules/admin/verbs/space_transitions.dm @@ -13,7 +13,7 @@ message_admins("[key_name_admin(usr)] re-assigned all space transitions") - space_manager.do_transition_setup() + GLOB.space_manager.do_transition_setup() log_admin("[key_name(usr)] re-assigned all space transitions") feedback_add_details("admin_verb","SPCRST") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -37,5 +37,5 @@ message_admins("[key_name_admin(usr)] made a space map") - space_manager.map_as_turfs(get_turf(usr)) + GLOB.space_manager.map_as_turfs(get_turf(usr)) log_admin("[key_name(usr)] made a space map") diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm index 4b555bc724a..25874d03360 100644 --- a/code/modules/admin/verbs/striketeam.dm +++ b/code/modules/admin/verbs/striketeam.dm @@ -1,204 +1,204 @@ -//STRIKE TEAMS - -var/const/commandos_possible = 6 //if more Commandos are needed in the future -var/global/sent_strike_team = 0 - -/client/proc/strike_team() - if(!SSticker) - to_chat(usr, "The game hasn't started yet!") - return - if(sent_strike_team == 1) - to_chat(usr, "CentComm is already sending a team.") - return - if(alert("Do you want to send in the CentComm death squad? Once enabled, this is irreversible.",,"Yes","No")!="Yes") - return - alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned commandos have internals cameras which are viewable through a monitor inside the Spec. Ops. Office. The first one selected/spawned will be the team leader.") - - message_admins("[key_name_admin(usr)] has started to spawn a CentComm DeathSquad.", 1) - - var/input = null - while(!input) - input = sanitize(copytext(input(src, "Please specify which mission the death commando squad shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) - if(!input) - if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") - return - - if(sent_strike_team) - to_chat(usr, "Looks like someone beat you to it.") - return - - // Find the nuclear auth code - var/nuke_code - var/temp_code - for(var/obj/machinery/nuclearbomb/N in world) - temp_code = text2num(N.r_code) - if(temp_code)//if it's actually a number. It won't convert any non-numericals. - nuke_code = N.r_code - break - - // Find ghosts willing to be DS - var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, commandos_possible, "Join the DeathSquad?",, 21, 600, 1, role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) - if(!commando_ghosts.len) - to_chat(usr, "Nobody volunteered to join the DeathSquad.") - return - - sent_strike_team = 1 - - // Spawns commandos and equips them. - var/commando_number = commandos_possible //for selecting a leader - var/is_leader = TRUE // set to FALSE after leader is spawned - - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - - if(commando_number <= 0) - break - - if(L.name == "Commando") - - if(!commando_ghosts.len) - break - - var/use_ds_borg = FALSE - var/mob/ghost_mob = pick(commando_ghosts) - commando_ghosts -= ghost_mob - if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) - continue - - if(!is_leader) - var/new_dstype = alert(ghost_mob.client, "Select Deathsquad Type.", "DS Character Generation", "Organic", "Cyborg") - if(new_dstype == "Cyborg") - use_ds_borg = TRUE - - if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) // Have to re-check this due to the above alert() call - continue - - if(use_ds_borg) - var/mob/living/silicon/robot/deathsquad/R = new() - R.forceMove(get_turf(L)) - var/rnum = rand(1,1000) - var/borgname = "Epsilon [rnum]" - R.name = borgname - R.custom_name = borgname - R.real_name = R.name - R.mind = new - R.mind.current = R - R.mind.original = R - R.mind.assigned_role = SPECIAL_ROLE_DEATHSQUAD - R.mind.special_role = SPECIAL_ROLE_DEATHSQUAD - R.mind.offstation_role = TRUE - if(!(R.mind in SSticker.minds)) - SSticker.minds += R.mind - SSticker.mode.traitors += R.mind - R.key = ghost_mob.key - if(nuke_code) - R.mind.store_memory("Nuke Code: [nuke_code].") - R.mind.store_memory("Mission: [input].") - to_chat(R, "You are a Special Operations cyborg, in the service of Central Command. \nYour current mission is: [input]") - else - var/mob/living/carbon/human/new_commando = create_death_commando(L, is_leader) - new_commando.mind.key = ghost_mob.key - new_commando.key = ghost_mob.key - new_commando.internal = new_commando.s_store - new_commando.update_action_buttons_icon() - if(nuke_code) - new_commando.mind.store_memory("Nuke Code: [nuke_code].") - new_commando.mind.store_memory("Mission: [input].") - to_chat(new_commando, "You are a Special Ops [is_leader ? "TEAM LEADER" : "commando"] in the service of Central Command. Check the table ahead for detailed instructions.\nYour current mission is: [input]") - - is_leader = FALSE - commando_number-- - - //Spawns the rest of the commando gear. - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Commando_Manual") - //new /obj/item/gun/energy/pulse_rifle(L.loc) - var/obj/item/paper/P = new(L.loc) - P.info = "

    Good morning soldier!. This compact guide will familiarize you with standard operating procedure. There are three basic rules to follow:
    #1 Work as a team.
    #2 Accomplish your objective at all costs.
    #3 Leave no witnesses.
    You are fully equipped and stocked for your mission--before departing on the Spec. Ops. Shuttle due South, make sure that all operatives are ready. Actual mission objective will be relayed to you by Central Command through your headsets.
    If deemed appropriate, Central Command will also allow members of your team to equip assault power-armor for the mission. You will find the armor storage due West of your position. Once you are ready to leave, utilize the Special Operations shuttle console and toggle the hull doors via the other console.

    In the event that the team does not accomplish their assigned objective in a timely manner, or finds no other way to do so, attached below are instructions on how to operate a Nanotrasen Nuclear Device. Your operations LEADER is provided with a nuclear authentication disk and a pin-pointer for this reason. You may easily recognize them by their rank: Lieutenant, Captain, or Major. The nuclear device itself will be present somewhere on your destination.

    Hello and thank you for choosing Nanotrasen for your nuclear information needs. Today's crash course will deal with the operation of a Fission Class Nanotrasen made Nuclear Device.
    First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE. Pressing any button on the compacted bomb will cause it to extend and bolt itself into place. If this is done to unbolt it one must completely log in which at this time may not be possible.
    To make the device functional:
    #1 Place bomb in designated detonation zone
    #2 Extend and anchor bomb (attack with hand).
    #3 Insert Nuclear Auth. Disk into slot.
    #4 Type numeric code into keypad ([nuke_code]).
    Note: If you make a mistake press R to reset the device.
    #5 Press the E button to log onto the device.
    You now have activated the device. To deactivate the buttons at anytime, for example when you have already prepped the bomb for detonation, remove the authentication disk OR press the R on the keypad. Now the bomb CAN ONLY be detonated using the timer. A manual detonation is not an option.
    Note: Toggle off the SAFETY.
    Use the - - and + + to set a detonation time between 5 seconds and 10 minutes. Then press the timer toggle button to start the countdown. Now remove the authentication disk so that the buttons deactivate.
    Note: THE BOMB IS STILL SET AND WILL DETONATE
    Now before you remove the disk if you need to move the bomb you can: Toggle off the anchor, move it, and re-anchor.

    The nuclear authorization code is: [nuke_code ? nuke_code : "None provided"]

    Good luck, soldier!

    " - P.name = "Spec. Ops Manual" - P.icon = "pamphlet-ds" - var/obj/item/stamp/centcom/stamp = new - P.stamp(stamp) - qdel(stamp) - - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Commando-Bomb") - new /obj/effect/spawner/newbomb/timer/syndicate(L.loc) - qdel(L) - - message_admins("[key_name_admin(usr)] has spawned a CentComm DeathSquad.", 1) - log_admin("[key_name(usr)] used Spawn Death Squad.") - return 1 - -/client/proc/create_death_commando(obj/spawn_location, is_leader = FALSE) - var/mob/living/carbon/human/new_commando = new(spawn_location.loc) - var/commando_leader_rank = pick("Lieutenant", "Captain", "Major") - var/commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") - var/commando_name = pick(GLOB.last_names) - - var/datum/preferences/A = new()//Randomize appearance for the commando. - if(is_leader) - A.age = rand(35,45) - A.real_name = "[commando_leader_rank] [commando_name]" - else - A.real_name = "[commando_rank] [commando_name]" - A.copy_to(new_commando) - - - new_commando.dna.ready_dna(new_commando)//Creates DNA. - - //Creates mind stuff. - new_commando.mind_initialize() - new_commando.mind.assigned_role = SPECIAL_ROLE_DEATHSQUAD - new_commando.mind.special_role = SPECIAL_ROLE_DEATHSQUAD - SSticker.mode.traitors |= new_commando.mind//Adds them to current traitor list. Which is really the extra antagonist list. - new_commando.equip_death_commando(is_leader) - return new_commando - -/mob/living/carbon/human/proc/equip_death_commando(is_leader = FALSE) - - var/obj/item/radio/R = new /obj/item/radio/headset/alt(src) - R.set_frequency(DTH_FREQ) - equip_to_slot_or_del(R, slot_l_ear) - if(is_leader) - equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(src), slot_w_uniform) - else - equip_to_slot_or_del(new /obj/item/clothing/under/color/green(src), slot_w_uniform) - equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/advance(src), slot_shoes) - equip_to_slot_or_del(new /obj/item/clothing/suit/space/deathsquad(src), slot_wear_suit) - equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) - equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/deathsquad(src), slot_head) - equip_to_slot_or_del(new /obj/item/clothing/mask/gas/sechailer/swat(src), slot_wear_mask) - equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) - - equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) - equip_to_slot_or_del(new /obj/item/storage/box(src), slot_in_backpack) - - equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/storage/box/flashbangs(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/flashlight(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) - if(is_leader) - equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) - else - equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) - - - equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber(src), slot_l_store) - equip_to_slot_or_del(new /obj/item/shield/energy(src), slot_r_store) - equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/double/full(src), slot_s_store) - equip_to_slot_or_del(new /obj/item/gun/projectile/revolver/mateba(src), slot_belt) - equip_to_slot_or_del(new /obj/item/gun/energy/pulse(src), slot_r_hand) - - var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(src) - L.implant(src) - - var/obj/item/card/id/W = new(src) - W.name = "[real_name]'s ID Card" - W.icon_state = "deathsquad" - W.assignment = "Death Commando" - W.access = get_centcom_access(W.assignment) - W.registered_name = real_name - equip_to_slot_or_del(W, slot_wear_id) - - return 1 +//STRIKE TEAMS + +#define COMMANDOS_POSSIBLE 6 //if more Commandos are needed in the future +GLOBAL_VAR_INIT(sent_strike_team, 0) + +/client/proc/strike_team() + if(!SSticker) + to_chat(usr, "The game hasn't started yet!") + return + if(GLOB.sent_strike_team == 1) + to_chat(usr, "CentComm is already sending a team.") + return + if(alert("Do you want to send in the CentComm death squad? Once enabled, this is irreversible.",,"Yes","No")!="Yes") + return + alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned commandos have internals cameras which are viewable through a monitor inside the Spec. Ops. Office. The first one selected/spawned will be the team leader.") + + message_admins("[key_name_admin(usr)] has started to spawn a CentComm DeathSquad.", 1) + + var/input = null + while(!input) + input = sanitize(copytext(input(src, "Please specify which mission the death commando squad shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) + if(!input) + if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") + return + + if(GLOB.sent_strike_team) + to_chat(usr, "Looks like someone beat you to it.") + return + + // Find the nuclear auth code + var/nuke_code + var/temp_code + for(var/obj/machinery/nuclearbomb/N in world) + temp_code = text2num(N.r_code) + if(temp_code)//if it's actually a number. It won't convert any non-numericals. + nuke_code = N.r_code + break + + // Find ghosts willing to be DS + var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) + if(!commando_ghosts.len) + to_chat(usr, "Nobody volunteered to join the DeathSquad.") + return + + GLOB.sent_strike_team = 1 + + // Spawns commandos and equips them. + var/commando_number = COMMANDOS_POSSIBLE //for selecting a leader + var/is_leader = TRUE // set to FALSE after leader is spawned + + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + + if(commando_number <= 0) + break + + if(L.name == "Commando") + + if(!commando_ghosts.len) + break + + var/use_ds_borg = FALSE + var/mob/ghost_mob = pick(commando_ghosts) + commando_ghosts -= ghost_mob + if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) + continue + + if(!is_leader) + var/new_dstype = alert(ghost_mob.client, "Select Deathsquad Type.", "DS Character Generation", "Organic", "Cyborg") + if(new_dstype == "Cyborg") + use_ds_borg = TRUE + + if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) // Have to re-check this due to the above alert() call + continue + + if(use_ds_borg) + var/mob/living/silicon/robot/deathsquad/R = new() + R.forceMove(get_turf(L)) + var/rnum = rand(1,1000) + var/borgname = "Epsilon [rnum]" + R.name = borgname + R.custom_name = borgname + R.real_name = R.name + R.mind = new + R.mind.current = R + R.mind.original = R + R.mind.assigned_role = SPECIAL_ROLE_DEATHSQUAD + R.mind.special_role = SPECIAL_ROLE_DEATHSQUAD + R.mind.offstation_role = TRUE + if(!(R.mind in SSticker.minds)) + SSticker.minds += R.mind + SSticker.mode.traitors += R.mind + R.key = ghost_mob.key + if(nuke_code) + R.mind.store_memory("Nuke Code: [nuke_code].") + R.mind.store_memory("Mission: [input].") + to_chat(R, "You are a Special Operations cyborg, in the service of Central Command. \nYour current mission is: [input]") + else + var/mob/living/carbon/human/new_commando = create_death_commando(L, is_leader) + new_commando.mind.key = ghost_mob.key + new_commando.key = ghost_mob.key + new_commando.internal = new_commando.s_store + new_commando.update_action_buttons_icon() + if(nuke_code) + new_commando.mind.store_memory("Nuke Code: [nuke_code].") + new_commando.mind.store_memory("Mission: [input].") + to_chat(new_commando, "You are a Special Ops [is_leader ? "TEAM LEADER" : "commando"] in the service of Central Command. Check the table ahead for detailed instructions.\nYour current mission is: [input]") + + is_leader = FALSE + commando_number-- + + //Spawns the rest of the commando gear. + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Commando_Manual") + //new /obj/item/gun/energy/pulse_rifle(L.loc) + var/obj/item/paper/P = new(L.loc) + P.info = "

    Good morning soldier!. This compact guide will familiarize you with standard operating procedure. There are three basic rules to follow:
    #1 Work as a team.
    #2 Accomplish your objective at all costs.
    #3 Leave no witnesses.
    You are fully equipped and stocked for your mission--before departing on the Spec. Ops. Shuttle due South, make sure that all operatives are ready. Actual mission objective will be relayed to you by Central Command through your headsets.
    If deemed appropriate, Central Command will also allow members of your team to equip assault power-armor for the mission. You will find the armor storage due West of your position. Once you are ready to leave, utilize the Special Operations shuttle console and toggle the hull doors via the other console.

    In the event that the team does not accomplish their assigned objective in a timely manner, or finds no other way to do so, attached below are instructions on how to operate a Nanotrasen Nuclear Device. Your operations LEADER is provided with a nuclear authentication disk and a pin-pointer for this reason. You may easily recognize them by their rank: Lieutenant, Captain, or Major. The nuclear device itself will be present somewhere on your destination.

    Hello and thank you for choosing Nanotrasen for your nuclear information needs. Today's crash course will deal with the operation of a Fission Class Nanotrasen made Nuclear Device.
    First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE. Pressing any button on the compacted bomb will cause it to extend and bolt itself into place. If this is done to unbolt it one must completely log in which at this time may not be possible.
    To make the device functional:
    #1 Place bomb in designated detonation zone
    #2 Extend and anchor bomb (attack with hand).
    #3 Insert Nuclear Auth. Disk into slot.
    #4 Type numeric code into keypad ([nuke_code]).
    Note: If you make a mistake press R to reset the device.
    #5 Press the E button to log onto the device.
    You now have activated the device. To deactivate the buttons at anytime, for example when you have already prepped the bomb for detonation, remove the authentication disk OR press the R on the keypad. Now the bomb CAN ONLY be detonated using the timer. A manual detonation is not an option.
    Note: Toggle off the SAFETY.
    Use the - - and + + to set a detonation time between 5 seconds and 10 minutes. Then press the timer toggle button to start the countdown. Now remove the authentication disk so that the buttons deactivate.
    Note: THE BOMB IS STILL SET AND WILL DETONATE
    Now before you remove the disk if you need to move the bomb you can: Toggle off the anchor, move it, and re-anchor.

    The nuclear authorization code is: [nuke_code ? nuke_code : "None provided"]

    Good luck, soldier!

    " + P.name = "Spec. Ops Manual" + P.icon = "pamphlet-ds" + var/obj/item/stamp/centcom/stamp = new + P.stamp(stamp) + qdel(stamp) + + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Commando-Bomb") + new /obj/effect/spawner/newbomb/timer/syndicate(L.loc) + qdel(L) + + message_admins("[key_name_admin(usr)] has spawned a CentComm DeathSquad.", 1) + log_admin("[key_name(usr)] used Spawn Death Squad.") + return 1 + +/client/proc/create_death_commando(obj/spawn_location, is_leader = FALSE) + var/mob/living/carbon/human/new_commando = new(spawn_location.loc) + var/commando_leader_rank = pick("Lieutenant", "Captain", "Major") + var/commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") + var/commando_name = pick(GLOB.last_names) + + var/datum/preferences/A = new()//Randomize appearance for the commando. + if(is_leader) + A.age = rand(35,45) + A.real_name = "[commando_leader_rank] [commando_name]" + else + A.real_name = "[commando_rank] [commando_name]" + A.copy_to(new_commando) + + + new_commando.dna.ready_dna(new_commando)//Creates DNA. + + //Creates mind stuff. + new_commando.mind_initialize() + new_commando.mind.assigned_role = SPECIAL_ROLE_DEATHSQUAD + new_commando.mind.special_role = SPECIAL_ROLE_DEATHSQUAD + SSticker.mode.traitors |= new_commando.mind//Adds them to current traitor list. Which is really the extra antagonist list. + new_commando.equip_death_commando(is_leader) + return new_commando + +/mob/living/carbon/human/proc/equip_death_commando(is_leader = FALSE) + + var/obj/item/radio/R = new /obj/item/radio/headset/alt(src) + R.set_frequency(DTH_FREQ) + equip_to_slot_or_del(R, slot_l_ear) + if(is_leader) + equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(src), slot_w_uniform) + else + equip_to_slot_or_del(new /obj/item/clothing/under/color/green(src), slot_w_uniform) + equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/advance(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/clothing/suit/space/deathsquad(src), slot_wear_suit) + equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) + equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/deathsquad(src), slot_head) + equip_to_slot_or_del(new /obj/item/clothing/mask/gas/sechailer/swat(src), slot_wear_mask) + equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) + + equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) + equip_to_slot_or_del(new /obj/item/storage/box(src), slot_in_backpack) + + equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/storage/box/flashbangs(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/flashlight(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) + if(is_leader) + equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) + else + equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) + + + equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber(src), slot_l_store) + equip_to_slot_or_del(new /obj/item/shield/energy(src), slot_r_store) + equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/double/full(src), slot_s_store) + equip_to_slot_or_del(new /obj/item/gun/projectile/revolver/mateba(src), slot_belt) + equip_to_slot_or_del(new /obj/item/gun/energy/pulse(src), slot_r_hand) + + var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(src) + L.implant(src) + + var/obj/item/card/id/W = new(src) + W.name = "[real_name]'s ID Card" + W.icon_state = "deathsquad" + W.assignment = "Death Commando" + W.access = get_centcom_access(W.assignment) + W.registered_name = real_name + equip_to_slot_or_del(W, slot_wear_id) + + return 1 diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm index 611dbcfd97a..73ee5edb82d 100644 --- a/code/modules/admin/verbs/striketeam_syndicate.dm +++ b/code/modules/admin/verbs/striketeam_syndicate.dm @@ -1,169 +1,169 @@ -//STRIKE TEAMS - -var/const/syndicate_commandos_possible = 6 //if more Commandos are needed in the future -var/global/sent_syndicate_strike_team = 0 -/client/proc/syndicate_strike_team() - set category = "Event" - set name = "Spawn Syndicate Strike Team" - set desc = "Spawns a squad of commandos in the Syndicate Mothership if you want to run an admin event." - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - if(!SSticker) - alert("The game hasn't started yet!") - return - if(sent_syndicate_strike_team == 1) - alert("The Syndicate are already sending a team, Mr. Dumbass.") - return - if(alert("Do you want to send in the Syndicate Strike Team? Once enabled, this is irreversible.",,"Yes","No")=="No") - return - alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned syndicates have internals cameras which are viewable through a monitor inside the Syndicate Mothership Bridge. Assigning the team's detailed task is recommended from there. The first one selected/spawned will be the team leader.") - - message_admins("[key_name_admin(usr)] has started to spawn a Syndicate Strike Team.", 1) - - var/input = null - while(!input) - input = sanitize(copytext(input(src, "Please specify which mission the syndicate strike team shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) - if(!input) - if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") - return - - if(sent_syndicate_strike_team) - to_chat(src, "Looks like someone beat you to it.") - return - - var/syndicate_commando_number = syndicate_commandos_possible //for selecting a leader - var/is_leader = TRUE // set to FALSE after leader is spawned - - // Find the nuclear auth code - var/nuke_code - var/temp_code - for(var/obj/machinery/nuclearbomb/N in world) - temp_code = text2num(N.r_code) - if(temp_code)//if it's actually a number. It won't convert any non-numericals. - nuke_code = N.r_code - break - - // Find ghosts willing to be SST - var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, syndicate_commandos_possible, "Join the Syndicate Strike Team?",, 21, 600, 1, role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) - if(!commando_ghosts.len) - to_chat(usr, "Nobody volunteered to join the SST.") - return - - sent_syndicate_strike_team = 1 - - //Spawns commandos and equips them. - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - - if(syndicate_commando_number <= 0) - break - - if(L.name == "Syndicate-Commando") - - if(!commando_ghosts.len) - break - - var/mob/ghost_mob = pick(commando_ghosts) - commando_ghosts -= ghost_mob - - if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) - continue - - var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, is_leader) - - if(!new_syndicate_commando) - continue - - new_syndicate_commando.key = ghost_mob.key - new_syndicate_commando.internal = new_syndicate_commando.s_store - new_syndicate_commando.update_action_buttons_icon() - - //So they don't forget their code or mission. - if(nuke_code) - new_syndicate_commando.mind.store_memory("Nuke Code: [nuke_code].") - new_syndicate_commando.mind.store_memory("Mission: [input].") - - to_chat(new_syndicate_commando, "You are an Elite Syndicate [is_leader ? "TEAM LEADER" : "commando"] in the service of the Syndicate. \nYour current mission is: [input]") - new_syndicate_commando.faction += "syndicate" - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.join_hud(new_syndicate_commando.mind.current) - set_antag_hud(new_syndicate_commando.mind.current, "hudoperative") - new_syndicate_commando.regenerate_icons() - is_leader = FALSE - syndicate_commando_number-- - - message_admins("[key_name_admin(usr)] has spawned a Syndicate strike squad.", 1) - log_admin("[key_name(usr)] used Spawn Syndicate Squad.") - feedback_add_details("admin_verb","SDTHS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/create_syndicate_death_commando(obj/spawn_location, is_leader = FALSE) - var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) - var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") - var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") - var/syndicate_commando_name = pick(GLOB.last_names) - - var/datum/preferences/A = new()//Randomize appearance for the commando. - if(is_leader) - A.age = rand(35,45) - A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" - else - A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" - A.copy_to(new_syndicate_commando) - - new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. - - //Creates mind stuff. - new_syndicate_commando.mind_initialize() - new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.offstation_role = TRUE - SSticker.mode.traitors |= new_syndicate_commando.mind //Adds them to current traitor list. Which is really the extra antagonist list. - new_syndicate_commando.equip_syndicate_commando(is_leader) - qdel(spawn_location) - return new_syndicate_commando - -/mob/living/carbon/human/proc/equip_syndicate_commando(is_leader = FALSE, full_gear = FALSE) - var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt/syndteam(src) - R.set_frequency(SYNDTEAM_FREQ) - equip_to_slot_or_del(R, slot_l_ear) - equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(src), slot_w_uniform) - if(!full_gear) - equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(src), slot_shoes) - equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) - - equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) - equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(src), slot_in_backpack) - - equip_to_slot_or_del(new /obj/item/gun/projectile/revolver(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) - if(is_leader) - equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) - else - equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/card/emag(src), slot_r_store) - equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber/red(src), slot_l_store) - - if(full_gear) - equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(src), slot_wear_mask) - equip_to_slot_or_del(new /obj/item/clothing/suit/space/hardsuit/syndi/elite/sst(src), slot_wear_suit) - equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) - equip_to_slot_or_del(new /obj/item/storage/belt/military/sst(src), slot_belt) - equip_to_slot_or_del(new /obj/item/tank/jetpack/oxygen/harness(src), slot_s_store) - equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/syndie/advance(src), slot_shoes) - equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/l6_saw(src), slot_r_hand) - equip_to_slot_or_del(new /obj/item/ammo_box/magazine/mm556x45(src), slot_in_backpack) - - var/obj/item/implant/dust/D = new /obj/item/implant/dust(src) - D.implant(src) - var/obj/item/card/id/syndicate/W = new(src) //Untrackable by AI - W.name = "[real_name]'s ID Card" - W.icon_state = "syndie" - W.assignment = "Syndicate Commando" - W.access += get_syndicate_access(W.assignment) - W.registered_name = real_name - equip_to_slot_or_del(W, slot_wear_id) - - return 1 +//STRIKE TEAMS + +#define SYNDICATE_COMMANDOS_POSSIBLE 6 //if more Commandos are needed in the future +GLOBAL_VAR_INIT(sent_syndicate_strike_team, 0) +/client/proc/syndicate_strike_team() + set category = "Event" + set name = "Spawn Syndicate Strike Team" + set desc = "Spawns a squad of commandos in the Syndicate Mothership if you want to run an admin event." + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + if(!SSticker) + alert("The game hasn't started yet!") + return + if(GLOB.sent_syndicate_strike_team == 1) + alert("The Syndicate are already sending a team, Mr. Dumbass.") + return + if(alert("Do you want to send in the Syndicate Strike Team? Once enabled, this is irreversible.",,"Yes","No")=="No") + return + alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned syndicates have internals cameras which are viewable through a monitor inside the Syndicate Mothership Bridge. Assigning the team's detailed task is recommended from there. The first one selected/spawned will be the team leader.") + + message_admins("[key_name_admin(usr)] has started to spawn a Syndicate Strike Team.", 1) + + var/input = null + while(!input) + input = sanitize(copytext(input(src, "Please specify which mission the syndicate strike team shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) + if(!input) + if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") + return + + if(GLOB.sent_syndicate_strike_team) + to_chat(src, "Looks like someone beat you to it.") + return + + var/syndicate_commando_number = SYNDICATE_COMMANDOS_POSSIBLE //for selecting a leader + var/is_leader = TRUE // set to FALSE after leader is spawned + + // Find the nuclear auth code + var/nuke_code + var/temp_code + for(var/obj/machinery/nuclearbomb/N in world) + temp_code = text2num(N.r_code) + if(temp_code)//if it's actually a number. It won't convert any non-numericals. + nuke_code = N.r_code + break + + // Find ghosts willing to be SST + var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) + if(!commando_ghosts.len) + to_chat(usr, "Nobody volunteered to join the SST.") + return + + GLOB.sent_syndicate_strike_team = 1 + + //Spawns commandos and equips them. + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + + if(syndicate_commando_number <= 0) + break + + if(L.name == "Syndicate-Commando") + + if(!commando_ghosts.len) + break + + var/mob/ghost_mob = pick(commando_ghosts) + commando_ghosts -= ghost_mob + + if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) + continue + + var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, is_leader) + + if(!new_syndicate_commando) + continue + + new_syndicate_commando.key = ghost_mob.key + new_syndicate_commando.internal = new_syndicate_commando.s_store + new_syndicate_commando.update_action_buttons_icon() + + //So they don't forget their code or mission. + if(nuke_code) + new_syndicate_commando.mind.store_memory("Nuke Code: [nuke_code].") + new_syndicate_commando.mind.store_memory("Mission: [input].") + + to_chat(new_syndicate_commando, "You are an Elite Syndicate [is_leader ? "TEAM LEADER" : "commando"] in the service of the Syndicate. \nYour current mission is: [input]") + new_syndicate_commando.faction += "syndicate" + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] + opshud.join_hud(new_syndicate_commando.mind.current) + set_antag_hud(new_syndicate_commando.mind.current, "hudoperative") + new_syndicate_commando.regenerate_icons() + is_leader = FALSE + syndicate_commando_number-- + + message_admins("[key_name_admin(usr)] has spawned a Syndicate strike squad.", 1) + log_admin("[key_name(usr)] used Spawn Syndicate Squad.") + feedback_add_details("admin_verb","SDTHS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/create_syndicate_death_commando(obj/spawn_location, is_leader = FALSE) + var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) + var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") + var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") + var/syndicate_commando_name = pick(GLOB.last_names) + + var/datum/preferences/A = new()//Randomize appearance for the commando. + if(is_leader) + A.age = rand(35,45) + A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" + else + A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" + A.copy_to(new_syndicate_commando) + + new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. + + //Creates mind stuff. + new_syndicate_commando.mind_initialize() + new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.offstation_role = TRUE + SSticker.mode.traitors |= new_syndicate_commando.mind //Adds them to current traitor list. Which is really the extra antagonist list. + new_syndicate_commando.equip_syndicate_commando(is_leader) + qdel(spawn_location) + return new_syndicate_commando + +/mob/living/carbon/human/proc/equip_syndicate_commando(is_leader = FALSE, full_gear = FALSE) + var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt/syndteam(src) + R.set_frequency(SYNDTEAM_FREQ) + equip_to_slot_or_del(R, slot_l_ear) + equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(src), slot_w_uniform) + if(!full_gear) + equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) + + equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) + equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(src), slot_in_backpack) + + equip_to_slot_or_del(new /obj/item/gun/projectile/revolver(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) + if(is_leader) + equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) + else + equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/card/emag(src), slot_r_store) + equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber/red(src), slot_l_store) + + if(full_gear) + equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(src), slot_wear_mask) + equip_to_slot_or_del(new /obj/item/clothing/suit/space/hardsuit/syndi/elite/sst(src), slot_wear_suit) + equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) + equip_to_slot_or_del(new /obj/item/storage/belt/military/sst(src), slot_belt) + equip_to_slot_or_del(new /obj/item/tank/jetpack/oxygen/harness(src), slot_s_store) + equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/syndie/advance(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/l6_saw(src), slot_r_hand) + equip_to_slot_or_del(new /obj/item/ammo_box/magazine/mm556x45(src), slot_in_backpack) + + var/obj/item/implant/dust/D = new /obj/item/implant/dust(src) + D.implant(src) + var/obj/item/card/id/syndicate/W = new(src) //Untrackable by AI + W.name = "[real_name]'s ID Card" + W.icon_state = "syndie" + W.assignment = "Syndicate Commando" + W.access += get_syndicate_access(W.assignment) + W.registered_name = real_name + equip_to_slot_or_del(W, slot_wear_id) + + return 1 diff --git a/code/modules/admin/verbs/ticklag.dm b/code/modules/admin/verbs/ticklag.dm index 316adf935b0..e19291c159e 100644 --- a/code/modules/admin/verbs/ticklag.dm +++ b/code/modules/admin/verbs/ticklag.dm @@ -1,21 +1,21 @@ -//Merged Doohl's and the existing ticklag as they both had good elements about them ~Carn - -/client/proc/ticklag() - set category = "Debug" - set name = "Set Ticklag" - set desc = "Sets a new tick lag. Recommend you don't mess with this too much! Stable, time-tested ticklag value is 0.9" - - if(!check_rights(R_DEBUG)) return - - var/newtick = input("Sets a new tick lag. Please don't mess with this too much! The stable, time-tested ticklag value is 0.9","Lag of Tick", world.tick_lag) as num|null - //I've used ticks of 2 before to help with serious singulo lags - if(newtick && newtick <= 2 && newtick > 0) - log_admin("[key_name(src)] has modified world.tick_lag to [newtick]", 0) - message_admins("[key_name_admin(src)] has modified world.tick_lag to [newtick]", 0) - world.tick_lag = newtick - feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - else - to_chat(src, "Error: ticklag(): Invalid world.ticklag value. No changes made.") - - +//Merged Doohl's and the existing ticklag as they both had good elements about them ~Carn + +/client/proc/ticklag() + set category = "Debug" + set name = "Set Ticklag" + set desc = "Sets a new tick lag. Recommend you don't mess with this too much! Stable, time-tested ticklag value is 0.9" + + if(!check_rights(R_DEBUG)) return + + var/newtick = input("Sets a new tick lag. Please don't mess with this too much! The stable, time-tested ticklag value is 0.9","Lag of Tick", world.tick_lag) as num|null + //I've used ticks of 2 before to help with serious singulo lags + if(newtick && newtick <= 2 && newtick > 0) + log_admin("[key_name(src)] has modified world.tick_lag to [newtick]", 0) + message_admins("[key_name_admin(src)] has modified world.tick_lag to [newtick]", 0) + world.tick_lag = newtick + feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + else + to_chat(src, "Error: ticklag(): Invalid world.ticklag value. No changes made.") + + diff --git a/code/modules/admin/verbs/toggledebugverbs.dm b/code/modules/admin/verbs/toggledebugverbs.dm index 85c61ac978f..216f5c3e4b0 100644 --- a/code/modules/admin/verbs/toggledebugverbs.dm +++ b/code/modules/admin/verbs/toggledebugverbs.dm @@ -1,4 +1,4 @@ -var/list/admin_verbs_show_debug_verbs = list( +GLOBAL_LIST_INIT(admin_verbs_show_debug_verbs, list( /client/proc/camera_view, /client/proc/sec_camera_report, /client/proc/intercom_view, @@ -22,7 +22,7 @@ var/list/admin_verbs_show_debug_verbs = list( /client/proc/admin_redo_space_transitions, /client/proc/make_turf_space_map, /client/proc/vv_by_ref -) +)) // Would be nice to make this a permanent admin pref so we don't need to click it each time /client/proc/enable_debug_verbs() @@ -32,6 +32,6 @@ var/list/admin_verbs_show_debug_verbs = list( if(!check_rights(R_DEBUG)) return - verbs += admin_verbs_show_debug_verbs + verbs += GLOB.admin_verbs_show_debug_verbs feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/tripAI.dm b/code/modules/admin/verbs/tripAI.dm index 98a4bf82e98..5eedd9f2811 100644 --- a/code/modules/admin/verbs/tripAI.dm +++ b/code/modules/admin/verbs/tripAI.dm @@ -1,22 +1,22 @@ -/client/proc/triple_ai() - set category = "Event" - set name = "Create AI Triumvirate" - - if(SSticker.current_state > GAME_STATE_PREGAME) - to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") - return - - if(SSjobs && SSticker) - var/datum/job/job = SSjobs.GetJob("AI") - if(!job) - to_chat(usr, "Unable to locate the AI job") - return - if(SSticker.triai) - SSticker.triai = 0 - to_chat(usr, "Only one AI will be spawned at round start.") - message_admins("[key_name_admin(usr)] has toggled off triple AIs at round start.", 1) - else - SSticker.triai = 1 - to_chat(usr, "There will be an AI Triumvirate at round start.") - message_admins("[key_name_admin(usr)] has toggled on triple AIs at round start.", 1) - return +/client/proc/triple_ai() + set category = "Event" + set name = "Create AI Triumvirate" + + if(SSticker.current_state > GAME_STATE_PREGAME) + to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") + return + + if(SSjobs && SSticker) + var/datum/job/job = SSjobs.GetJob("AI") + if(!job) + to_chat(usr, "Unable to locate the AI job") + return + if(SSticker.triai) + SSticker.triai = 0 + to_chat(usr, "Only one AI will be spawned at round start.") + message_admins("[key_name_admin(usr)] has toggled off triple AIs at round start.", 1) + else + SSticker.triai = 1 + to_chat(usr, "There will be an AI Triumvirate at round start.") + message_admins("[key_name_admin(usr)] has toggled on triple AIs at round start.", 1) + return diff --git a/code/modules/admin/verbs/vox_raiders.dm b/code/modules/admin/verbs/vox_raiders.dm index 3e42ebe65fb..048939a7c6d 100644 --- a/code/modules/admin/verbs/vox_raiders.dm +++ b/code/modules/admin/verbs/vox_raiders.dm @@ -1,4 +1,4 @@ -var/global/vox_tick = 1 +GLOBAL_VAR_INIT(vox_tick, 1) /mob/living/carbon/human/proc/equip_vox_raider() @@ -10,7 +10,7 @@ var/global/vox_tick = 1 equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/vox(src), slot_shoes) // REPLACE THESE WITH CODED VOX ALTERNATIVES. equip_to_slot_or_del(new /obj/item/clothing/gloves/color/yellow/vox(src), slot_gloves) // AS ABOVE. - switch(vox_tick) + switch(GLOB.vox_tick) if(1) // Vox raider! equip_to_slot_or_del(new /obj/item/clothing/suit/space/vox/carapace(src), slot_wear_suit) equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/vox/carapace(src), slot_head) @@ -59,7 +59,7 @@ var/global/vox_tick = 1 W.registered_user = src equip_to_slot_or_del(W, slot_wear_id) - vox_tick++ - if(vox_tick > 4) vox_tick = 1 + GLOB.vox_tick++ + if(GLOB.vox_tick > 4) GLOB.vox_tick = 1 return 1 diff --git a/code/modules/admin/watchlist.dm b/code/modules/admin/watchlist.dm index a908c2c264b..002a3b7ffe4 100644 --- a/code/modules/admin/watchlist.dm +++ b/code/modules/admin/watchlist.dm @@ -6,7 +6,7 @@ if(!new_ckey) return new_ckey = sanitizeSQL(new_ckey) - var/DBQuery/query_watchfind = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[new_ckey]'") + var/DBQuery/query_watchfind = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[new_ckey]'") if(!query_watchfind.Execute()) var/err = query_watchfind.ErrorMsg() log_game("SQL ERROR obtaining ckey from player table. Error : \[[err]\]\n") @@ -29,7 +29,7 @@ if(!adminckey) return var/admin_sql_ckey = sanitizeSQL(adminckey) - var/DBQuery/query_watchadd = dbcon.NewQuery("INSERT INTO [format_table_name("watch")] (ckey, reason, adminckey, timestamp) VALUES ('[target_sql_ckey]', '[reason]', '[admin_sql_ckey]', '[timestamp]')") + var/DBQuery/query_watchadd = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("watch")] (ckey, reason, adminckey, timestamp) VALUES ('[target_sql_ckey]', '[reason]', '[admin_sql_ckey]', '[timestamp]')") if(!query_watchadd.Execute()) var/err = query_watchadd.ErrorMsg() log_game("SQL ERROR during adding new watch entry. Error : \[[err]\]\n") @@ -43,7 +43,7 @@ if(!check_rights(R_ADMIN)) return var/target_sql_ckey = sanitizeSQL(target_ckey) - var/DBQuery/query_watchdel = dbcon.NewQuery("DELETE FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_watchdel = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") if(!query_watchdel.Execute()) var/err = query_watchdel.ErrorMsg() log_game("SQL ERROR during removing watch entry. Error : \[[err]\]\n") @@ -57,7 +57,7 @@ if(!check_rights(R_ADMIN)) return var/target_sql_ckey = sanitizeSQL(target_ckey) - var/DBQuery/query_watchreason = dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_watchreason = GLOB.dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") if(!query_watchreason.Execute()) var/err = query_watchreason.ErrorMsg() log_game("SQL ERROR obtaining reason from watch table. Error : \[[err]\]\n") @@ -71,7 +71,7 @@ var/sql_ckey = sanitizeSQL(usr.ckey) var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from \"[watch_reason]\" to \"[new_reason]\"" edit_text = sanitizeSQL(edit_text) - var/DBQuery/query_watchupdate = dbcon.NewQuery("UPDATE [format_table_name("watch")] SET reason = '[new_reason]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_watchupdate = GLOB.dbcon.NewQuery("UPDATE [format_table_name("watch")] SET reason = '[new_reason]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE ckey = '[target_sql_ckey]'") if(!query_watchupdate.Execute()) var/err = query_watchupdate.ErrorMsg() log_game("SQL ERROR editing watchlist reason. Error : \[[err]\]\n") @@ -96,7 +96,7 @@ else search = "^." search = sanitizeSQL(search) - var/DBQuery/query_watchlist = dbcon.NewQuery("SELECT ckey, reason, adminckey, timestamp, last_editor FROM [format_table_name("watch")] WHERE ckey REGEXP '[search]' ORDER BY ckey") + var/DBQuery/query_watchlist = GLOB.dbcon.NewQuery("SELECT ckey, reason, adminckey, timestamp, last_editor FROM [format_table_name("watch")] WHERE ckey REGEXP '[search]' ORDER BY ckey") if(!query_watchlist.Execute()) var/err = query_watchlist.ErrorMsg() log_game("SQL ERROR obtaining ckey, reason, adminckey, timestamp, last_editor from watch table. Error : \[[err]\]\n") @@ -115,7 +115,7 @@ /proc/check_watchlist(target_ckey) var/target_sql_ckey = sanitizeSQL(target_ckey) - var/DBQuery/query_watch = dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_watch = GLOB.dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") if(!query_watch.Execute()) var/err = query_watch.ErrorMsg() log_game("SQL ERROR obtaining reason from watch table. Error : \[[err]\]\n") diff --git a/code/modules/alarm/camera_alarm.dm b/code/modules/alarm/camera_alarm.dm index 6d8f151f0b8..bef53ad466f 100644 --- a/code/modules/alarm/camera_alarm.dm +++ b/code/modules/alarm/camera_alarm.dm @@ -1,2 +1,2 @@ /datum/alarm_handler/camera - category = "Camera Alarms" \ No newline at end of file + category = "Camera Alarms" diff --git a/code/modules/alarm/motion_alarm.dm b/code/modules/alarm/motion_alarm.dm index 0acbbe5f221..fd7e6febe48 100644 --- a/code/modules/alarm/motion_alarm.dm +++ b/code/modules/alarm/motion_alarm.dm @@ -1,2 +1,2 @@ /datum/alarm_handler/motion - category = "Motion Alarms" \ No newline at end of file + category = "Motion Alarms" diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 39053fa79d9..4a6fe7d6640 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -134,4 +134,4 @@ GLOBAL_LIST_EMPTY(antagonists) //Displayed at the end of roundend_category section /datum/antagonist/proc/roundend_report_footer() - return \ No newline at end of file + return diff --git a/code/modules/antagonists/_common/antag_helpers.dm b/code/modules/antagonists/_common/antag_helpers.dm index 134ba1d7d90..af00cbf3190 100644 --- a/code/modules/antagonists/_common/antag_helpers.dm +++ b/code/modules/antagonists/_common/antag_helpers.dm @@ -16,4 +16,4 @@ continue var/datum/team/T = A.get_team() if(!team_type || istype(T, team_type)) - . |= T \ No newline at end of file + . |= T diff --git a/code/modules/antagonists/_common/antag_hud.dm b/code/modules/antagonists/_common/antag_hud.dm index 89ad9af15ab..0cb800aa38d 100644 --- a/code/modules/antagonists/_common/antag_hud.dm +++ b/code/modules/antagonists/_common/antag_hud.dm @@ -47,11 +47,11 @@ newhud.join_hud(current) /datum/mind/proc/leave_all_huds() - for(var/datum/atom_hud/antag/hud in huds) + for(var/datum/atom_hud/antag/hud in GLOB.huds) if(current in hud.hudusers) hud.leave_hud(current) - for(var/datum/atom_hud/data/hud in huds) + for(var/datum/atom_hud/data/hud in GLOB.huds) if(current in hud.hudusers) hud.remove_hud_from(current) @@ -77,4 +77,4 @@ /datum/mindslaves/proc/leave_serv_hud(datum/mind/free_mind) thrallhud.leave_hud(free_mind.current) - set_antag_hud(free_mind.current, null) \ No newline at end of file + set_antag_hud(free_mind.current, null) diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index d57c3124f61..beefc41735b 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -266,7 +266,7 @@ to_chat(user, "The sludge does not respond to your attempt to awake it. Perhaps you should try again later.") /obj/item/antag_spawner/morph/spawn_antag(client/C, turf/T, type = "", mob/user) - var/mob/living/simple_animal/hostile/morph/wizard/M = new /mob/living/simple_animal/hostile/morph/wizard(pick(xeno_spawn)) + var/mob/living/simple_animal/hostile/morph/wizard/M = new /mob/living/simple_animal/hostile/morph/wizard(pick(GLOB.xeno_spawn)) M.key = C.key M.mind.assigned_role = SPECIAL_ROLE_MORPH M.mind.special_role = SPECIAL_ROLE_MORPH diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm index 7d08e18834c..1d30a6fe334 100644 --- a/code/modules/antagonists/survivalist/survivalist.dm +++ b/code/modules/antagonists/survivalist/survivalist.dm @@ -38,4 +38,4 @@ var/datum/objective/steal_five_of_type/summon_magic/magic = new magic.owner = owner objectives += magic - ..() \ No newline at end of file + ..() diff --git a/code/modules/antagonists/traitor/datum_mindslave.dm b/code/modules/antagonists/traitor/datum_mindslave.dm index 5efd35715ad..6fcb4bac63a 100644 --- a/code/modules/antagonists/traitor/datum_mindslave.dm +++ b/code/modules/antagonists/traitor/datum_mindslave.dm @@ -44,10 +44,10 @@ owner.objectives -= O /datum/antagonist/mindslave/proc/update_mindslave_icons_added() - var/datum/atom_hud/antag/traitorhud = huds[ANTAG_HUD_TRAITOR] + var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] traitorhud.join_hud(owner.current, null) set_antag_hud(owner.current, "hudmindslave") /datum/antagonist/mindslave/proc/update_mindslave_icons_removed() - var/datum/atom_hud/antag/traitorhud = huds[ANTAG_HUD_TRAITOR] + var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] traitorhud.leave_hud(owner.current, null) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 9e6f1b79bfa..e13fb701317 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -118,7 +118,7 @@ var/objective_amount = config.traitor_objectives_amount - + if(is_hijacker && objective_count <= objective_amount) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount if (!(locate(/datum/objective/hijack) in objectives)) var/datum/objective/hijack/hijack_objective = new @@ -152,8 +152,7 @@ var/objective_count = 0 var/try_again = TRUE - if(prob(30)) - objective_count += forge_single_objective() + objective_count += forge_single_objective() for(var/i = objective_count, i < config.traitor_objectives_amount) var/datum/objective/assassinate/kill_objective = new @@ -188,7 +187,7 @@ destroy_objective.owner = owner destroy_objective.find_target() if("[destroy_objective]" in assigned_targets) // Is this target already in their list of assigned targets? If so, don't add this objective and return - return 0 + return 0 else if(destroy_objective.target) // Is the target a real one and not null? If so, add it to our list of targets to avoid duplicate targets assigned_targets.Add("[destroy_objective.target]") // This logic is applied to all traitor objectives including steal objectives add_objective(destroy_objective) @@ -222,7 +221,7 @@ else if(kill_objective.target) assigned_targets.Add("[kill_objective.target]") add_objective(kill_objective) - + else var/datum/objective/steal/steal_objective = new steal_objective.owner = owner @@ -232,32 +231,13 @@ else if(steal_objective.steal_target) assigned_targets.Add("[steal_objective.steal_target]") add_objective(steal_objective) - + /datum/antagonist/traitor/proc/forge_single_AI_objective() . = 1 - var/special_pick = rand(1,2) - switch(special_pick) - if(1) // AI hijack - var/datum/objective/block/block_objective = new - block_objective.owner = owner - add_objective(block_objective) - if(2) // Protect and strand a target - var/datum/objective/protect/yandere_one = new - yandere_one.owner = owner - yandere_one.find_target() - - if("[yandere_one.target]" in assigned_targets) - return 0 - else if(yandere_one.target) - assigned_targets.Add("[yandere_one.target]") - - add_objective(yandere_one) - var/datum/objective/maroon/yandere_two = new - yandere_two.owner = owner - yandere_two.target = yandere_one.target - yandere_two.explanation_text = "Prevent [yandere_one.target], the [yandere_one.target.assigned_role] from escaping alive." - add_objective(yandere_two) + var/datum/objective/block/block_objective = new + block_objective.owner = owner + add_objective(block_objective) /datum/antagonist/traitor/greet() @@ -271,13 +251,13 @@ /datum/antagonist/traitor/proc/update_traitor_icons_added(datum/mind/traitor_mind) - var/datum/atom_hud/antag/traitorhud = huds[ANTAG_HUD_TRAITOR] + var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] traitorhud.join_hud(owner.current, null) set_antag_hud(owner.current, "hudsyndicate") /datum/antagonist/traitor/proc/update_traitor_icons_removed(datum/mind/traitor_mind) - var/datum/atom_hud/antag/traitorhud = huds[ANTAG_HUD_TRAITOR] + var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] traitorhud.leave_hud(owner.current, null) set_antag_hud(owner.current, null) @@ -293,7 +273,7 @@ if(should_equip) equip_traitor() owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE) - + /datum/antagonist/traitor/proc/give_codewords() if(!owner.current) @@ -329,7 +309,7 @@ if(traitor_kind == TRAITOR_HUMAN) var/mob/living/carbon/human/traitor_mob = owner.current - + // find a radio! toolbox(es), backpack, belt, headset var/obj/item/R = locate(/obj/item/pda) in traitor_mob.contents //Hide the uplink in a PDA if available, otherwise radio if(!R) diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm index 1697eb897c9..cb6fad39d34 100644 --- a/code/modules/antagonists/wishgranter/wishgranter.dm +++ b/code/modules/antagonists/wishgranter/wishgranter.dm @@ -22,57 +22,57 @@ if(!istype(H)) return H.ignore_gene_stability = TRUE - H.dna.SetSEState(HULKBLOCK, TRUE) - genemutcheck(H, HULKBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.hulkblock, TRUE) + genemutcheck(H, GLOB.hulkblock, null, MUTCHK_FORCED) - H.dna.SetSEState(XRAYBLOCK, TRUE) - genemutcheck(H, XRAYBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.xrayblock, TRUE) + genemutcheck(H, GLOB.xrayblock, null, MUTCHK_FORCED) - H.dna.SetSEState(FIREBLOCK, TRUE) - genemutcheck(H, FIREBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.fireblock, TRUE) + genemutcheck(H, GLOB.fireblock, null, MUTCHK_FORCED) - H.dna.SetSEState(COLDBLOCK, TRUE) - genemutcheck(H, COLDBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.coldblock, TRUE) + genemutcheck(H, GLOB.coldblock, null, MUTCHK_FORCED) - H.dna.SetSEState(TELEBLOCK, TRUE) - genemutcheck(H, TELEBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.teleblock, TRUE) + genemutcheck(H, GLOB.teleblock, null, MUTCHK_FORCED) - H.dna.SetSEState(INCREASERUNBLOCK, TRUE) - genemutcheck(H, INCREASERUNBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.increaserunblock, TRUE) + genemutcheck(H, GLOB.increaserunblock, null, MUTCHK_FORCED) - H.dna.SetSEState(BREATHLESSBLOCK, TRUE) - genemutcheck(H, BREATHLESSBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.breathlessblock, TRUE) + genemutcheck(H, GLOB.breathlessblock, null, MUTCHK_FORCED) - H.dna.SetSEState(REGENERATEBLOCK, TRUE) - genemutcheck(H, REGENERATEBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.regenerateblock, TRUE) + genemutcheck(H, GLOB.regenerateblock, null, MUTCHK_FORCED) - H.dna.SetSEState(SHOCKIMMUNITYBLOCK, TRUE) - genemutcheck(H, SHOCKIMMUNITYBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.shockimmunityblock, TRUE) + genemutcheck(H, GLOB.shockimmunityblock, null, MUTCHK_FORCED) - H.dna.SetSEState(SMALLSIZEBLOCK, TRUE) - genemutcheck(H, SMALLSIZEBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.smallsizeblock, TRUE) + genemutcheck(H, GLOB.smallsizeblock, null, MUTCHK_FORCED) - H.dna.SetSEState(SOBERBLOCK, TRUE) - genemutcheck(H, SOBERBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.soberblock, TRUE) + genemutcheck(H, GLOB.soberblock, null, MUTCHK_FORCED) - H.dna.SetSEState(PSYRESISTBLOCK, TRUE) - genemutcheck(H, PSYRESISTBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.psyresistblock, TRUE) + genemutcheck(H, GLOB.psyresistblock, null, MUTCHK_FORCED) - H.dna.SetSEState(SHADOWBLOCK, TRUE) - genemutcheck(H, SHADOWBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.shadowblock, TRUE) + genemutcheck(H, GLOB.shadowblock, null, MUTCHK_FORCED) - H.dna.SetSEState(CRYOBLOCK, TRUE) - genemutcheck(H, CRYOBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.cryoblock, TRUE) + genemutcheck(H, GLOB.cryoblock, null, MUTCHK_FORCED) - H.dna.SetSEState(EATBLOCK, TRUE) - genemutcheck(H, EATBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.eatblock, TRUE) + genemutcheck(H, GLOB.eatblock, null, MUTCHK_FORCED) - H.dna.SetSEState(JUMPBLOCK, TRUE) - genemutcheck(H, JUMPBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.jumpblock, TRUE) + genemutcheck(H, GLOB.jumpblock, null, MUTCHK_FORCED) - H.dna.SetSEState(IMMOLATEBLOCK, TRUE) - genemutcheck(H, IMMOLATEBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.immolateblock, TRUE) + genemutcheck(H, GLOB.immolateblock, null, MUTCHK_FORCED) H.mutations.Add(LASER) H.update_mutations() - H.update_body() \ No newline at end of file + H.update_body() diff --git a/code/modules/arcade/arcade_prize.dm b/code/modules/arcade/arcade_prize.dm index a91e392e125..832d1c46649 100644 --- a/code/modules/arcade/arcade_prize.dm +++ b/code/modules/arcade/arcade_prize.dm @@ -20,7 +20,7 @@ opening = 1 playsound(src.loc, 'sound/items/bubblewrap.ogg', 30, 1, extrarange = -4, falloff = 10) icon_state = "prizeconfetti" - src.color = pick(random_color_list) + src.color = pick(GLOB.random_color_list) var/prize_inside = pick(possible_contents) spawn(10) user.unEquip(src) @@ -85,4 +85,4 @@ if(25 to 74) icon_state = "tickets_3" // Buncha tickets else - icon_state = "tickets_4" // Ticket snake \ No newline at end of file + icon_state = "tickets_4" // Ticket snake diff --git a/code/modules/arcade/claw_game.dm b/code/modules/arcade/claw_game.dm index 7339b31dc77..6ee5c83b89c 100644 --- a/code/modules/arcade/claw_game.dm +++ b/code/modules/arcade/claw_game.dm @@ -1,4 +1,4 @@ -/var/claw_game_html = null +GLOBAL_VAR(claw_game_html) /obj/machinery/arcade/claw name = "Claw Game" @@ -30,8 +30,8 @@ component_parts += new /obj/item/stack/sheet/glass(null, 1) RefreshParts() - if(!claw_game_html) - claw_game_html = file2text('code/modules/arcade/crane.html') + if(!GLOB.claw_game_html) + GLOB.claw_game_html = file2text('code/modules/arcade/crane.html') /obj/machinery/arcade/claw/RefreshParts() var/bin_upgrades = 0 @@ -58,14 +58,14 @@ atom_say("WINNER!") new /obj/item/toy/prizeball(get_turf(src)) playsound(src.loc, 'sound/arcade/win.ogg', 50, 1, extrarange = -3, falloff = 10) - addtimer(CALLBACK(src, .update_icon), 10) + addtimer(CALLBACK(src, .proc/update_icon), 10) /obj/machinery/arcade/claw/start_play(mob/user as mob) ..() user << browse_rsc('page.css') for(var/i in 1 to img_resources.len) user << browse_rsc(img_resources[i]) - var/my_game_html = replacetext(claw_game_html, "/* ref src */", UID()) + var/my_game_html = replacetext(GLOB.claw_game_html, "/* ref src */", UID()) user << browse(my_game_html, "window=[window_name];size=915x600;can_resize=0") /obj/machinery/arcade/claw/Topic(href, list/href_list) @@ -76,4 +76,4 @@ if(!isnull(prize_won)) close_game() if(prize_won == "1") - win() \ No newline at end of file + win() diff --git a/code/modules/arcade/mob_hunt/battle_computer.dm b/code/modules/arcade/mob_hunt/battle_computer.dm index 0d6e91b60d9..94337a5cfac 100644 --- a/code/modules/arcade/mob_hunt/battle_computer.dm +++ b/code/modules/arcade/mob_hunt/battle_computer.dm @@ -302,4 +302,4 @@ to_chat(user, "This mob is already at maximum health!") return patient.mob_data.cur_health = patient.mob_data.max_health - to_chat(user, "[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!") \ No newline at end of file + to_chat(user, "[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!") diff --git a/code/modules/arcade/mob_hunt/mob_datums.dm b/code/modules/arcade/mob_hunt/mob_datums.dm index dad3f528796..a50fab183e6 100644 --- a/code/modules/arcade/mob_hunt/mob_datums.dm +++ b/code/modules/arcade/mob_hunt/mob_datums.dm @@ -108,7 +108,7 @@ /datum/mob_hunt/proc/get_possible_areas() var/list/possible_areas = list() //setup, sets all station areas (and subtypes) to weight 1 - for(var/A in the_station_areas) + for(var/A in GLOB.the_station_areas) if(A == /area/holodeck) //don't allow holodeck areas as possible spawns since it will allow it to spawn in the holodeck rooms on z2 as well continue if(A in possible_areas) @@ -498,4 +498,4 @@ turf_blacklist = list() area_whitelist = list() turf_whitelist = list() - lifetime = 2000 \ No newline at end of file + lifetime = 2000 diff --git a/code/modules/arcade/mob_hunt/mob_type_datums.dm b/code/modules/arcade/mob_hunt/mob_type_datums.dm index 9d27a844ea8..0ae84b1b764 100644 --- a/code/modules/arcade/mob_hunt/mob_type_datums.dm +++ b/code/modules/arcade/mob_hunt/mob_type_datums.dm @@ -253,4 +253,4 @@ TYPE_FLYING, TYPE_BLUESPACE, TYPE_STEEL) - immunity = list(TYPE_POISON) \ No newline at end of file + immunity = list(TYPE_POISON) diff --git a/code/modules/arcade/page.css b/code/modules/arcade/page.css index 17e77bcb038..e3dfcac1cce 100644 --- a/code/modules/arcade/page.css +++ b/code/modules/arcade/page.css @@ -89,4 +89,4 @@ body { .button { cursor: pointer; -} \ No newline at end of file +} diff --git a/code/modules/arcade/prize_counter.dm b/code/modules/arcade/prize_counter.dm index 4d1d6973909..9c8a4dea685 100644 --- a/code/modules/arcade/prize_counter.dm +++ b/code/modules/arcade/prize_counter.dm @@ -140,11 +140,11 @@ th.cost.toomuch {background:maroon;} "} - for(var/datum/prize_item/item in global_prizes.prizes) + for(var/datum/prize_item/item in GLOB.global_prizes.prizes) var/cost_class="affordable" if(item.cost>tickets) cost_class="toomuch" - var/itemID = global_prizes.prizes.Find(item) + var/itemID = GLOB.global_prizes.prizes.Find(item) var/row_color="light" if(itemID%2 == 0) row_color="dark" @@ -185,12 +185,12 @@ th.cost.toomuch {background:maroon;} if(href_list["buy"]) var/itemID = text2num(href_list["buy"]) - var/datum/prize_item/item = global_prizes.prizes[itemID] + var/datum/prize_item/item = GLOB.global_prizes.prizes[itemID] var/sure = alert(usr,"Are you sure you wish to purchase [item.name] for [item.cost] tickets?","You sure?","Yes","No") in list("Yes","No") if(sure=="No") updateUsrDialog() return - if(!global_prizes.PlaceOrder(src, itemID)) + if(!GLOB.global_prizes.PlaceOrder(src, itemID)) to_chat(usr, "Unable to complete the exchange.") else to_chat(usr, "You've successfully purchased the item.") diff --git a/code/modules/arcade/prize_datums.dm b/code/modules/arcade/prize_datums.dm index 8a3d598bb3d..1edad36b8f3 100644 --- a/code/modules/arcade/prize_datums.dm +++ b/code/modules/arcade/prize_datums.dm @@ -1,5 +1,4 @@ - -var/global/datum/prizes/global_prizes = new +GLOBAL_DATUM_INIT(global_prizes, /datum/prizes, new()) /datum/prizes var/list/prizes = list() @@ -14,7 +13,7 @@ var/global/datum/prizes/global_prizes = new return if(!prize_counter) return 0 - var/datum/prize_item/item = global_prizes.prizes[itemID] + var/datum/prize_item/item = GLOB.global_prizes.prizes[itemID] if(!item) return 0 if(prize_counter.tickets >= item.cost) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index b2a44940053..827e7893396 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -1,150 +1,150 @@ -/obj/item/assembly - name = "assembly" - desc = "A small electronic device that should never exist." - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "" - flags = CONDUCT - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL = 100) - throwforce = 2 - throw_speed = 3 - throw_range = 10 - origin_tech = "magnets=1;engineering=1" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - - var/bomb_name = "bomb" // used for naming bombs / mines - - var/secured = TRUE - var/list/attached_overlays = null - var/obj/item/assembly_holder/holder = null - var/cooldown = FALSE //To prevent spam - var/wires = WIRE_RECEIVE | WIRE_PULSE - var/datum/wires/connected = null // currently only used by timer/signaler - - var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() - var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder - var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly - var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() - var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message - -/obj/item/assembly/proc/activate() //What the device does when turned on - return - -/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs - return - -/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct - return - -/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here - return - -/obj/item/assembly/proc/attach_assembly(obj/A, mob/user) //Called when an assembly is attacked by another - return - -/obj/item/assembly/proc/process_cooldown() //Called via spawn(10) to have it count down the cooldown var - return - -/obj/item/assembly/proc/holder_movement() //Called when the holder is moved - return - -/obj/item/assembly/proc/describe() // Called by grenades to describe the state of the trigger (time left, etc) - return "The trigger assembly looks broken!" - -/obj/item/assembly/interact(mob/user) //Called when attack_self is called - return - -/obj/item/assembly/process_cooldown() - cooldown-- - if(cooldown <= 0) - return FALSE - spawn(10) - process_cooldown() - return TRUE - -/obj/item/assembly/Destroy() - if(istype(loc, /obj/item/assembly_holder) || istype(holder)) - var/obj/item/assembly_holder/A = loc - if(A.a_left == src) - A.a_left = null - else if(A.a_right == src) - A.a_right = null - holder = null - return ..() - -/obj/item/assembly/pulsed(radio = FALSE) - if(holder && (wires & WIRE_RECEIVE)) - activate() - if(radio && (wires & WIRE_RADIO_RECEIVE)) - activate() - return TRUE - -/obj/item/assembly/pulse(radio = FALSE) - if(holder && (wires & WIRE_PULSE)) - holder.process_activation(src, 1, 0) - if(holder && (wires & WIRE_PULSE_SPECIAL)) - holder.process_activation(src, 0, 1) - if(istype(loc, /obj/item/grenade)) // This is a hack. Todo: Manage this better -Sayu - var/obj/item/grenade/G = loc - G.prime() // Adios, muchachos - return TRUE - -/obj/item/assembly/activate() - if(!secured || cooldown > 0) - return FALSE - cooldown = 2 - spawn(10) - process_cooldown() - return TRUE - -/obj/item/assembly/toggle_secure() - secured = !secured - update_icon() - return secured - -/obj/item/assembly/attach_assembly(obj/item/assembly/A, mob/user) - holder = new /obj/item/assembly_holder(get_turf(src)) - if(holder.attach(A, src, user)) - to_chat(user, "You attach [A] to [src]!") - return TRUE - return FALSE - -/obj/item/assembly/attackby(obj/item/W, mob/user, params) - if(isassembly(W)) - var/obj/item/assembly/A = W - if(!A.secured && !secured) - attach_assembly(A, user) - return - - return ..() - -/obj/item/assembly/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(toggle_secure()) - to_chat(user, "[src] is ready!") - else - to_chat(user, "[src] can now be attached!") - -/obj/item/assembly/process() - STOP_PROCESSING(SSobj, src) - -/obj/item/assembly/examine(mob/user) - . = ..() - if(in_range(src, user) || loc == user) - if(secured) - . += "[src] is ready!" - else - . += "[src] can be attached!" - -/obj/item/assembly/attack_self(mob/user) - if(!user) - return - user.set_machine(src) - interact(user) - return TRUE - -/obj/item/assembly/interact(mob/user) - return \ No newline at end of file +/obj/item/assembly + name = "assembly" + desc = "A small electronic device that should never exist." + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "" + flags = CONDUCT + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL = 100) + throwforce = 2 + throw_speed = 3 + throw_range = 10 + origin_tech = "magnets=1;engineering=1" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + + var/bomb_name = "bomb" // used for naming bombs / mines + + var/secured = TRUE + var/list/attached_overlays = null + var/obj/item/assembly_holder/holder = null + var/cooldown = FALSE //To prevent spam + var/wires = WIRE_RECEIVE | WIRE_PULSE + var/datum/wires/connected = null // currently only used by timer/signaler + + var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() + var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder + var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly + var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() + var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message + +/obj/item/assembly/proc/activate() //What the device does when turned on + return + +/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs + return + +/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct + return + +/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here + return + +/obj/item/assembly/proc/attach_assembly(obj/A, mob/user) //Called when an assembly is attacked by another + return + +/obj/item/assembly/proc/process_cooldown() //Called via spawn(10) to have it count down the cooldown var + return + +/obj/item/assembly/proc/holder_movement() //Called when the holder is moved + return + +/obj/item/assembly/proc/describe() // Called by grenades to describe the state of the trigger (time left, etc) + return "The trigger assembly looks broken!" + +/obj/item/assembly/interact(mob/user) //Called when attack_self is called + return + +/obj/item/assembly/process_cooldown() + cooldown-- + if(cooldown <= 0) + return FALSE + spawn(10) + process_cooldown() + return TRUE + +/obj/item/assembly/Destroy() + if(istype(loc, /obj/item/assembly_holder) || istype(holder)) + var/obj/item/assembly_holder/A = loc + if(A.a_left == src) + A.a_left = null + else if(A.a_right == src) + A.a_right = null + holder = null + return ..() + +/obj/item/assembly/pulsed(radio = FALSE) + if(holder && (wires & WIRE_RECEIVE)) + activate() + if(radio && (wires & WIRE_RADIO_RECEIVE)) + activate() + return TRUE + +/obj/item/assembly/pulse(radio = FALSE) + if(holder && (wires & WIRE_PULSE)) + holder.process_activation(src, 1, 0) + if(holder && (wires & WIRE_PULSE_SPECIAL)) + holder.process_activation(src, 0, 1) + if(istype(loc, /obj/item/grenade)) // This is a hack. Todo: Manage this better -Sayu + var/obj/item/grenade/G = loc + G.prime() // Adios, muchachos + return TRUE + +/obj/item/assembly/activate() + if(!secured || cooldown > 0) + return FALSE + cooldown = 2 + spawn(10) + process_cooldown() + return TRUE + +/obj/item/assembly/toggle_secure() + secured = !secured + update_icon() + return secured + +/obj/item/assembly/attach_assembly(obj/item/assembly/A, mob/user) + holder = new /obj/item/assembly_holder(get_turf(src)) + if(holder.attach(A, src, user)) + to_chat(user, "You attach [A] to [src]!") + return TRUE + return FALSE + +/obj/item/assembly/attackby(obj/item/W, mob/user, params) + if(isassembly(W)) + var/obj/item/assembly/A = W + if(!A.secured && !secured) + attach_assembly(A, user) + return + + return ..() + +/obj/item/assembly/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(toggle_secure()) + to_chat(user, "[src] is ready!") + else + to_chat(user, "[src] can now be attached!") + +/obj/item/assembly/process() + STOP_PROCESSING(SSobj, src) + +/obj/item/assembly/examine(mob/user) + . = ..() + if(in_range(src, user) || loc == user) + if(secured) + . += "[src] is ready!" + else + . += "[src] can be attached!" + +/obj/item/assembly/attack_self(mob/user) + if(!user) + return + user.set_machine(src) + interact(user) + return TRUE + +/obj/item/assembly/interact(mob/user) + return diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index d5039af0a84..bf62e2a828a 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -1,180 +1,180 @@ -/obj/item/onetankbomb - name = "bomb" - icon = 'icons/obj/tank.dmi' - item_state = "assembly" - throwforce = 5 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 2 - throw_range = 4 - flags = CONDUCT //Copied this from old code, so this may or may not be necessary - var/status = 0 //0 - not readied //1 - bomb finished with welder - var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device - var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank - origin_tech = "materials=1;engineering=1" - -/obj/item/onetankbomb/examine(mob/user) - . = ..() - . += bombtank.examine(user) - -/obj/item/onetankbomb/update_icon() - if(bombtank) - icon_state = bombtank.icon_state - if(bombassembly) - overlays += bombassembly.icon_state - overlays += bombassembly.overlays - overlays += "bomb_assembly" - -/obj/item/onetankbomb/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/analyzer)) - bombtank.attackby(W, user, params) - return - return ..() - -/obj/item/onetankbomb/wrench_act(mob/user, obj/item/I) //This is basically bomb assembly code inverted. apparently it works. - if(status) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - to_chat(user, "You disassemble [src].") - bombassembly.loc = user.loc - bombassembly.master = null - bombassembly = null - bombtank.loc = user.loc - bombtank.master = null - bombtank = null - qdel(src) - -/obj/item/onetankbomb/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, volume = I.tool_volume)) - return - if(!status) - status = TRUE - investigate_log("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) - msg_admin_attack("[key_name_admin(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", ATKLOG_FEW) - log_game("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature - T0C]") - to_chat(user, "A pressure hole has been bored to [bombtank] valve. [bombtank] can now be ignited.") - else - status = FALSE - investigate_log("[key_name(user)] unwelded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) - to_chat(user, "The hole has been closed.") - - -/obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly - bombassembly.attack_self(user, 1) - add_fingerprint(user) - return - -/obj/item/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. - visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - sleep(10) - if(!src) - return - if(status) - bombtank.detonate() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file - else - bombtank.release() - -/obj/item/onetankbomb/HasProximity(atom/movable/AM) - if(bombassembly) - bombassembly.HasProximity(AM) - -/obj/item/onetankbomb/Crossed(atom/movable/AM, oldloc) //for mousetraps - if(bombassembly) - bombassembly.Crossed(AM, oldloc) - -/obj/item/onetankbomb/on_found(mob/finder) //for mousetraps - if(bombassembly) - bombassembly.on_found(finder) - -/obj/item/onetankbomb/hear_talk(mob/living/M, list/message_pieces) - if(bombassembly) - bombassembly.hear_talk(M, message_pieces) - -/obj/item/onetankbomb/hear_message(mob/living/M, msg) - if(bombassembly) - bombassembly.hear_message(M, msg) - -// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ---------- - -/obj/item/tank/proc/bomb_assemble(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb - var/obj/item/assembly_holder/S = W - var/mob/M = user - if(!S.secured) //Check if the assembly is secured - return - if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it - return - - var/obj/item/onetankbomb/R = new /obj/item/onetankbomb(loc) - - M.drop_item() //Remove the assembly from your hands - M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it - M.put_in_hands(R) //Equips the bomb if possible, or puts it on the floor. - - R.bombassembly = S //Tell the bomb about its assembly part - S.master = R //Tell the assembly about its new owner - S.loc = R //Move the assembly out of the fucking way - - R.bombtank = src //Same for tank - master = R - loc = R - R.update_icon() - return - -/obj/item/tank/proc/detonate() //This happens when a bomb is told to explode - var/fuel_moles = air_contents.toxins + air_contents.oxygen/6 - var/strength = 1 - - var/turf/ground_zero = get_turf(loc) - loc = null - - if(air_contents.temperature > (T0C + 400)) - strength = (fuel_moles/15) - - if(strength >=1) - explosion(ground_zero, round(strength,1), round(strength*2,1), round(strength*3,1), round(strength*4,1)) - else if(strength >=0.5) - explosion(ground_zero, 0, 1, 2, 4) - else if(strength >=0.2) - explosion(ground_zero, -1, 0, 1, 2) - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - else if(air_contents.temperature > (T0C + 250)) - strength = (fuel_moles/20) - - if(strength >=1) - explosion(ground_zero, 0, round(strength,1), round(strength*2,1), round(strength*3,1)) - else if(strength >=0.5) - explosion(ground_zero, -1, 0, 1, 2) - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - else if(air_contents.temperature > (T0C + 100)) - strength = (fuel_moles/25) - - if(strength >=1) - explosion(ground_zero, -1, 0, round(strength,1), round(strength*3,1)) - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - air_update_turf() - if(master) - qdel(master) - qdel(src) - -/obj/item/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out. - var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles()) - var/turf/simulated/T = get_turf(src) - if(!T) - return - T.assume_air(removed) - air_update_turf() +/obj/item/onetankbomb + name = "bomb" + icon = 'icons/obj/tank.dmi' + item_state = "assembly" + throwforce = 5 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 2 + throw_range = 4 + flags = CONDUCT //Copied this from old code, so this may or may not be necessary + var/status = 0 //0 - not readied //1 - bomb finished with welder + var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device + var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank + origin_tech = "materials=1;engineering=1" + +/obj/item/onetankbomb/examine(mob/user) + . = ..() + . += bombtank.examine(user) + +/obj/item/onetankbomb/update_icon() + if(bombtank) + icon_state = bombtank.icon_state + if(bombassembly) + overlays += bombassembly.icon_state + overlays += bombassembly.overlays + overlays += "bomb_assembly" + +/obj/item/onetankbomb/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/analyzer)) + bombtank.attackby(W, user, params) + return + return ..() + +/obj/item/onetankbomb/wrench_act(mob/user, obj/item/I) //This is basically bomb assembly code inverted. apparently it works. + if(status) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You disassemble [src].") + bombassembly.loc = user.loc + bombassembly.master = null + bombassembly = null + bombtank.loc = user.loc + bombtank.master = null + bombtank = null + qdel(src) + +/obj/item/onetankbomb/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + if(!status) + status = TRUE + investigate_log("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) + msg_admin_attack("[key_name_admin(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", ATKLOG_FEW) + log_game("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature - T0C]") + to_chat(user, "A pressure hole has been bored to [bombtank] valve. [bombtank] can now be ignited.") + else + status = FALSE + investigate_log("[key_name(user)] unwelded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) + to_chat(user, "The hole has been closed.") + + +/obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly + bombassembly.attack_self(user, 1) + add_fingerprint(user) + return + +/obj/item/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + sleep(10) + if(!src) + return + if(status) + bombtank.detonate() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file + else + bombtank.release() + +/obj/item/onetankbomb/HasProximity(atom/movable/AM) + if(bombassembly) + bombassembly.HasProximity(AM) + +/obj/item/onetankbomb/Crossed(atom/movable/AM, oldloc) //for mousetraps + if(bombassembly) + bombassembly.Crossed(AM, oldloc) + +/obj/item/onetankbomb/on_found(mob/finder) //for mousetraps + if(bombassembly) + bombassembly.on_found(finder) + +/obj/item/onetankbomb/hear_talk(mob/living/M, list/message_pieces) + if(bombassembly) + bombassembly.hear_talk(M, message_pieces) + +/obj/item/onetankbomb/hear_message(mob/living/M, msg) + if(bombassembly) + bombassembly.hear_message(M, msg) + +// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ---------- + +/obj/item/tank/proc/bomb_assemble(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb + var/obj/item/assembly_holder/S = W + var/mob/M = user + if(!S.secured) //Check if the assembly is secured + return + if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it + return + + var/obj/item/onetankbomb/R = new /obj/item/onetankbomb(loc) + + M.drop_item() //Remove the assembly from your hands + M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it + M.put_in_hands(R) //Equips the bomb if possible, or puts it on the floor. + + R.bombassembly = S //Tell the bomb about its assembly part + S.master = R //Tell the assembly about its new owner + S.loc = R //Move the assembly out of the fucking way + + R.bombtank = src //Same for tank + master = R + loc = R + R.update_icon() + return + +/obj/item/tank/proc/detonate() //This happens when a bomb is told to explode + var/fuel_moles = air_contents.toxins + air_contents.oxygen/6 + var/strength = 1 + + var/turf/ground_zero = get_turf(loc) + loc = null + + if(air_contents.temperature > (T0C + 400)) + strength = (fuel_moles/15) + + if(strength >=1) + explosion(ground_zero, round(strength,1), round(strength*2,1), round(strength*3,1), round(strength*4,1)) + else if(strength >=0.5) + explosion(ground_zero, 0, 1, 2, 4) + else if(strength >=0.2) + explosion(ground_zero, -1, 0, 1, 2) + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + else if(air_contents.temperature > (T0C + 250)) + strength = (fuel_moles/20) + + if(strength >=1) + explosion(ground_zero, 0, round(strength,1), round(strength*2,1), round(strength*3,1)) + else if(strength >=0.5) + explosion(ground_zero, -1, 0, 1, 2) + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + else if(air_contents.temperature > (T0C + 100)) + strength = (fuel_moles/25) + + if(strength >=1) + explosion(ground_zero, -1, 0, round(strength,1), round(strength*3,1)) + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + air_update_turf() + if(master) + qdel(master) + qdel(src) + +/obj/item/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out. + var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles()) + var/turf/simulated/T = get_turf(src) + if(!T) + return + T.assume_air(removed) + air_update_turf() diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm index 6648a9ce22d..a83df5501c9 100644 --- a/code/modules/assembly/health.dm +++ b/code/modules/assembly/health.dm @@ -104,4 +104,4 @@ return attack_self(user) - return \ No newline at end of file + return diff --git a/code/modules/assembly/helpers.dm b/code/modules/assembly/helpers.dm index eef33d2763d..9b5e93931a7 100644 --- a/code/modules/assembly/helpers.dm +++ b/code/modules/assembly/helpers.dm @@ -1,44 +1,44 @@ -/proc/isassembly(O) - if(istype(O, /obj/item/assembly)) - return 1 - return 0 - -/proc/isigniter(O) - if(istype(O, /obj/item/assembly/igniter)) - return 1 - return 0 - -/proc/isinfared(O) - if(istype(O, /obj/item/assembly/infra)) - return 1 - return 0 - -/proc/isprox(O) - if(istype(O, /obj/item/assembly/prox_sensor)) - return 1 - return 0 - -/proc/issignaler(O) - if(istype(O, /obj/item/assembly/signaler)) - return 1 - return 0 - -/proc/istimer(O) - if(istype(O, /obj/item/assembly/timer)) - return 1 - return 0 - -/* -Name: IsSpecialAssembly -Desc: If true is an object that can be attached to an assembly holder but is a special thing like a plasma can or door -*/ - -/obj/proc/IsSpecialAssembly() - return 0 - -/* -Name: IsAssemblyHolder -Desc: If true is an object that can hold an assemblyholder object -*/ -/obj/proc/IsAssemblyHolder() - return 0 \ No newline at end of file +/proc/isassembly(O) + if(istype(O, /obj/item/assembly)) + return 1 + return 0 + +/proc/isigniter(O) + if(istype(O, /obj/item/assembly/igniter)) + return 1 + return 0 + +/proc/isinfared(O) + if(istype(O, /obj/item/assembly/infra)) + return 1 + return 0 + +/proc/isprox(O) + if(istype(O, /obj/item/assembly/prox_sensor)) + return 1 + return 0 + +/proc/issignaler(O) + if(istype(O, /obj/item/assembly/signaler)) + return 1 + return 0 + +/proc/istimer(O) + if(istype(O, /obj/item/assembly/timer)) + return 1 + return 0 + +/* +Name: IsSpecialAssembly +Desc: If true is an object that can be attached to an assembly holder but is a special thing like a plasma can or door +*/ + +/obj/proc/IsSpecialAssembly() + return 0 + +/* +Name: IsAssemblyHolder +Desc: If true is an object that can hold an assemblyholder object +*/ +/obj/proc/IsAssemblyHolder() + return 0 diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 236ee94bd9d..6a6cd7e8d8e 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -1,197 +1,197 @@ -/obj/item/assembly_holder - name = "Assembly" - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "holder" - item_state = "assembly" - flags = CONDUCT - throwforce = 5 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 3 - throw_range = 10 - - var/secured = FALSE - var/obj/item/assembly/a_left = null - var/obj/item/assembly/a_right = null - -/obj/item/assembly_holder/proc/attach(obj/item/D, obj/item/D2, mob/user) - return - -/obj/item/assembly_holder/proc/process_activation(var/obj/item/D) - return - -/obj/item/assembly_holder/IsAssemblyHolder() - return TRUE - -/obj/item/assembly_holder/Destroy() - if(a_left) - a_left.holder = null - if(a_right) - a_right.holder = null - return ..() - -/obj/item/assembly_holder/attach(obj/item/D, obj/item/D2, mob/user) - if(!D || !D2) - return FALSE - if(!isassembly(D) || !isassembly(D2)) - return FALSE - var/obj/item/assembly/A1 = D - var/obj/item/assembly/A2 = D2 - if(A1.secured || A2.secured) - return FALSE - if(!A1.remove_item_from_storage(src)) - if(user) - user.remove_from_mob(A1) - A1.loc = src - if(!A2.remove_item_from_storage(src)) - if(user) - user.remove_from_mob(A2) - A2.loc = src - A1.holder = src - A2.holder = src - a_left = A1 - a_right = A2 - name = "[A1.name]-[A2.name] assembly" - update_icon() - return TRUE - - -/obj/item/assembly_holder/update_icon() - overlays.Cut() - if(a_left) - overlays += "[a_left.icon_state]_left" - for(var/O in a_left.attached_overlays) - overlays += "[O]_l" - if(a_right) - overlays += "[a_right.icon_state]_right" - for(var/O in a_right.attached_overlays) - overlays += "[O]_r" - if(master) - master.update_icon() - - -/obj/item/assembly_holder/examine(mob/user) - . = ..() - if(in_range(src, user) || loc == user) - if(secured) - . += "[src] is ready!" - else - . += "[src] can be attached!" - - -/obj/item/assembly_holder/HasProximity(atom/movable/AM) - if(a_left) - a_left.HasProximity(AM) - if(a_right) - a_right.HasProximity(AM) - - -/obj/item/assembly_holder/Crossed(atom/movable/AM, oldloc) - if(a_left) - a_left.Crossed(AM, oldloc) - if(a_right) - a_right.Crossed(AM, oldloc) - -/obj/item/assembly_holder/on_found(mob/finder) - if(a_left) - a_left.on_found(finder) - if(a_right) - a_right.on_found(finder) - - -/obj/item/assembly_holder/hear_talk(mob/living/M, list/message_pieces) - if(a_left) - a_left.hear_talk(M, message_pieces) - if(a_right) - a_right.hear_talk(M, message_pieces) - -/obj/item/assembly_holder/hear_message(mob/living/M, msg) - if(a_left) - a_left.hear_message(M, msg) - if(a_right) - a_right.hear_message(M, msg) - -/obj/item/assembly_holder/proc/process_movement() // infrared beams and prox sensors - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - -/obj/item/assembly_holder/Move() - . = ..() - process_movement() - return - -/obj/item/assembly_holder/pickup() - . = ..() - process_movement() - -/obj/item/assembly_holder/Bump() - ..() - process_movement() - -/obj/item/assembly_holder/throw_impact() // called when a throw stops - ..() - process_movement() - -/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - ..() - return - -/obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/I) - if(!a_left || !a_right) - to_chat(user, "BUG:Assembly part missing, please report this!") - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - a_left.toggle_secure() - a_right.toggle_secure() - secured = !secured - if(secured) - to_chat(user, "[src] is ready!") - else - to_chat(user, "[src] can now be taken apart!") - update_icon() - -/obj/item/assembly_holder/attack_self(mob/user) - add_fingerprint(user) - if(secured) - if(!a_left || !a_right) - to_chat(user, "Assembly part missing!") - return - if(istype(a_left, a_right.type))//If they are the same type it causes issues due to window code - switch(alert("Which side would you like to use?",,"Left","Right")) - if("Left") - a_left.attack_self(user) - if("Right") - a_right.attack_self(user) - return - else - a_left.attack_self(user) - a_right.attack_self(user) - else - var/turf/T = get_turf(src) - if(!T) - return FALSE - if(a_left) - a_left.holder = null - a_left.loc = T - if(a_right) - a_right.holder = null - a_right.loc = T - qdel(src) - - -/obj/item/assembly_holder/process_activation(obj/D, normal = TRUE, special = TRUE) - if(!D) - return FALSE - if(normal && a_right && a_left) - if(a_right != D) - a_right.pulsed(0) - if(a_left != D) - a_left.pulsed(0) - if(master) - master.receive_signal() - return TRUE \ No newline at end of file +/obj/item/assembly_holder + name = "Assembly" + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "holder" + item_state = "assembly" + flags = CONDUCT + throwforce = 5 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 10 + + var/secured = FALSE + var/obj/item/assembly/a_left = null + var/obj/item/assembly/a_right = null + +/obj/item/assembly_holder/proc/attach(obj/item/D, obj/item/D2, mob/user) + return + +/obj/item/assembly_holder/proc/process_activation(var/obj/item/D) + return + +/obj/item/assembly_holder/IsAssemblyHolder() + return TRUE + +/obj/item/assembly_holder/Destroy() + if(a_left) + a_left.holder = null + if(a_right) + a_right.holder = null + return ..() + +/obj/item/assembly_holder/attach(obj/item/D, obj/item/D2, mob/user) + if(!D || !D2) + return FALSE + if(!isassembly(D) || !isassembly(D2)) + return FALSE + var/obj/item/assembly/A1 = D + var/obj/item/assembly/A2 = D2 + if(A1.secured || A2.secured) + return FALSE + if(!A1.remove_item_from_storage(src)) + if(user) + user.remove_from_mob(A1) + A1.loc = src + if(!A2.remove_item_from_storage(src)) + if(user) + user.remove_from_mob(A2) + A2.loc = src + A1.holder = src + A2.holder = src + a_left = A1 + a_right = A2 + name = "[A1.name]-[A2.name] assembly" + update_icon() + return TRUE + + +/obj/item/assembly_holder/update_icon() + overlays.Cut() + if(a_left) + overlays += "[a_left.icon_state]_left" + for(var/O in a_left.attached_overlays) + overlays += "[O]_l" + if(a_right) + overlays += "[a_right.icon_state]_right" + for(var/O in a_right.attached_overlays) + overlays += "[O]_r" + if(master) + master.update_icon() + + +/obj/item/assembly_holder/examine(mob/user) + . = ..() + if(in_range(src, user) || loc == user) + if(secured) + . += "[src] is ready!" + else + . += "[src] can be attached!" + + +/obj/item/assembly_holder/HasProximity(atom/movable/AM) + if(a_left) + a_left.HasProximity(AM) + if(a_right) + a_right.HasProximity(AM) + + +/obj/item/assembly_holder/Crossed(atom/movable/AM, oldloc) + if(a_left) + a_left.Crossed(AM, oldloc) + if(a_right) + a_right.Crossed(AM, oldloc) + +/obj/item/assembly_holder/on_found(mob/finder) + if(a_left) + a_left.on_found(finder) + if(a_right) + a_right.on_found(finder) + + +/obj/item/assembly_holder/hear_talk(mob/living/M, list/message_pieces) + if(a_left) + a_left.hear_talk(M, message_pieces) + if(a_right) + a_right.hear_talk(M, message_pieces) + +/obj/item/assembly_holder/hear_message(mob/living/M, msg) + if(a_left) + a_left.hear_message(M, msg) + if(a_right) + a_right.hear_message(M, msg) + +/obj/item/assembly_holder/proc/process_movement() // infrared beams and prox sensors + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + +/obj/item/assembly_holder/Move() + . = ..() + process_movement() + return + +/obj/item/assembly_holder/pickup() + . = ..() + process_movement() + +/obj/item/assembly_holder/Bump() + ..() + process_movement() + +/obj/item/assembly_holder/throw_impact() // called when a throw stops + ..() + process_movement() + +/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + ..() + return + +/obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/I) + if(!a_left || !a_right) + to_chat(user, "BUG:Assembly part missing, please report this!") + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + a_left.toggle_secure() + a_right.toggle_secure() + secured = !secured + if(secured) + to_chat(user, "[src] is ready!") + else + to_chat(user, "[src] can now be taken apart!") + update_icon() + +/obj/item/assembly_holder/attack_self(mob/user) + add_fingerprint(user) + if(secured) + if(!a_left || !a_right) + to_chat(user, "Assembly part missing!") + return + if(istype(a_left, a_right.type))//If they are the same type it causes issues due to window code + switch(alert("Which side would you like to use?",,"Left","Right")) + if("Left") + a_left.attack_self(user) + if("Right") + a_right.attack_self(user) + return + else + a_left.attack_self(user) + a_right.attack_self(user) + else + var/turf/T = get_turf(src) + if(!T) + return FALSE + if(a_left) + a_left.holder = null + a_left.loc = T + if(a_right) + a_right.holder = null + a_right.loc = T + qdel(src) + + +/obj/item/assembly_holder/process_activation(obj/D, normal = TRUE, special = TRUE) + if(!D) + return FALSE + if(normal && a_right && a_left) + if(a_right != D) + a_right.pulsed(0) + if(a_left != D) + a_left.pulsed(0) + if(master) + master.receive_signal() + return TRUE diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index 3bbfe65bbc0..b81acbbe2b0 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -1,45 +1,45 @@ -/obj/item/assembly/igniter - name = "igniter" - desc = "A small electronic device able to ignite combustable substances." - icon_state = "igniter" - materials = list(MAT_METAL=500, MAT_GLASS=50) - origin_tech = "magnets=1" - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - -/obj/item/assembly/igniter/New() - ..() - sparks.set_up(2, 0, src) - sparks.attach(src) - -/obj/item/assembly/igniter/Destroy() - QDEL_NULL(sparks) - return ..() - - -/obj/item/assembly/igniter/describe() - return "The igniter is [secured ? "secured." : "unsecured."]" - - -/obj/item/assembly/igniter/activate() - if(!..()) - return FALSE//Cooldown check - var/turf/location = get_turf(loc) - if(location) - location.hotspot_expose(1000,1000) - if(istype(loc, /obj/item/assembly_holder)) - if(istype(loc.loc, /obj/structure/reagent_dispensers/fueltank)) - var/obj/structure/reagent_dispensers/fueltank/tank = loc.loc - if(tank) - tank.boom(TRUE) - if(istype(loc.loc, /obj/item/reagent_containers/glass/beaker)) - var/obj/item/reagent_containers/glass/beaker/beakerbomb = loc.loc - if(beakerbomb) - beakerbomb.heat_beaker() - sparks.start() - return TRUE - - -/obj/item/assembly/igniter/attack_self(mob/user) - activate() - add_fingerprint(user) - return +/obj/item/assembly/igniter + name = "igniter" + desc = "A small electronic device able to ignite combustable substances." + icon_state = "igniter" + materials = list(MAT_METAL=500, MAT_GLASS=50) + origin_tech = "magnets=1" + var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + +/obj/item/assembly/igniter/New() + ..() + sparks.set_up(2, 0, src) + sparks.attach(src) + +/obj/item/assembly/igniter/Destroy() + QDEL_NULL(sparks) + return ..() + + +/obj/item/assembly/igniter/describe() + return "The igniter is [secured ? "secured." : "unsecured."]" + + +/obj/item/assembly/igniter/activate() + if(!..()) + return FALSE//Cooldown check + var/turf/location = get_turf(loc) + if(location) + location.hotspot_expose(1000,1000) + if(istype(loc, /obj/item/assembly_holder)) + if(istype(loc.loc, /obj/structure/reagent_dispensers/fueltank)) + var/obj/structure/reagent_dispensers/fueltank/tank = loc.loc + if(tank) + tank.boom(TRUE) + if(istype(loc.loc, /obj/item/reagent_containers/glass/beaker)) + var/obj/item/reagent_containers/glass/beaker/beakerbomb = loc.loc + if(beakerbomb) + beakerbomb.heat_beaker() + sparks.start() + return TRUE + + +/obj/item/assembly/igniter/attack_self(mob/user) + activate() + add_fingerprint(user) + return diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index b9d3ee2afc1..19263e010ab 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -1,286 +1,286 @@ -/obj/item/assembly/infra - name = "infrared emitter" - desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." - icon_state = "infrared" - materials = list(MAT_METAL=1000, MAT_GLASS=500) - origin_tech = "magnets=2;materials=2" - - bomb_name = "tripwire mine" - - secured = FALSE // toggle_secure()'ed in New() for correct adding to processing_objects, won't work otherwise - dir = EAST - var/on = FALSE - var/visible = TRUE - var/obj/effect/beam/i_beam/first = null - var/obj/effect/beam/i_beam/last = null - var/max_nesting_level = 10 - var/turf/fire_location - var/emission_cycles = 0 - var/emission_cap = 20 - -/obj/item/assembly/infra/Destroy() - if(first) - QDEL_NULL(first) - last = null - fire_location = null - return ..() - -/obj/item/assembly/infra/describe() - return "The assembly is [secured ? "secure" : "not secure"]. The infrared trigger is [on ? "on" : "off"]." - -/obj/item/assembly/infra/examine(mob/user) - . = ..() - . += describe() - -/obj/item/assembly/infra/activate() - if(!..()) - return FALSE//Cooldown check - on = !on - update_icon() - return TRUE - -/obj/item/assembly/infra/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - on = FALSE - if(first) - qdel(first) - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/assembly/infra/New() - ..() - if(!secured) - toggle_secure() - -/obj/item/assembly/infra/proc/arm() // Forces the device to arm no matter its current state. - if(!secured) // Checked because arm() might be called sometime after the object is spawned. - toggle_secure() - on = 1 - -/obj/item/assembly/infra/update_icon() - overlays.Cut() - attached_overlays = list() - if(on) - overlays += "infrared_on" - attached_overlays += "infrared_on" - - if(holder) - holder.update_icon() - -/obj/item/assembly/infra/process() - var/turf/T = get_turf(src) - if(first && (!on || !fire_location || fire_location != T || emission_cycles >= emission_cap)) - qdel(first) - return - if(!on) - return - if(!secured) - return - if(first && last) - last.process() - emission_cycles++ - return - if(T) - fire_location = T - emission_cycles = 0 - var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(T) - I.master = src - I.density = 1 - I.dir = dir - I.update_icon() - first = I - step(I, I.dir) - if(first) - I.density = FALSE - I.vis_spread(visible) - I.limit = 8 - I.process() - -/obj/item/assembly/infra/attack_hand() - qdel(first) - ..() - -/obj/item/assembly/infra/Move() - var/t = dir - . = ..() - dir = t - qdel(first) - -/obj/item/assembly/infra/holder_movement() - if(!holder) - return FALSE - qdel(first) - return TRUE - -/obj/item/assembly/infra/equipped(var/mob/user, var/slot) - qdel(first) - return ..() - -/obj/item/assembly/infra/pickup(mob/user) - qdel(first) - return ..() - -/obj/item/assembly/infra/proc/trigger_beam() - if(!secured || !on || cooldown > 0) - return FALSE - pulse(0) - audible_message("[bicon(src)] *beep* *beep*", null, 3) - if(first) - qdel(first) - cooldown = 2 - spawn(10) - process_cooldown() - -/obj/item/assembly/infra/interact(mob/user)//TODO: change this this to the wire control panel - if(!secured) return - user.set_machine(src) - var/dat = {"Infrared Laser - Status: [on ? "On" : "Off"]
    - Visibility: [visible ? "Visible" : "Invisible"]
    - Current Direction: [capitalize(dir2text(dir))]
    -
    -

    Refresh -

    Close"} - var/datum/browser/popup = new(user, "infra", name, 400, 400) - popup.set_content(dat) - popup.open(0) - onclose(user, "infra") - -/obj/item/assembly/infra/Topic(href, href_list) - ..() - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - usr << browse(null, "window=infra") - onclose(usr, "infra") - return - if(href_list["state"]) - on = !(on) - update_icon() - if(href_list["visible"]) - visible = !(visible) - if(first) - first.vis_spread(visible) - if(href_list["rotate"]) - rotate() - if(href_list["close"]) - usr << browse(null, "window=infra") - return - if(usr) - attack_self(usr) - -/obj/item/assembly/infra/verb/rotate()//This could likely be better - set name = "Rotate Infrared Laser" - set category = "Object" - set src in usr - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - dir = turn(dir, 90) - - if(usr.machine == src) - interact(usr) - - if(first) - qdel(first) - - - -/obj/item/assembly/infra/armed/New() - ..() - spawn(3) - if(holder) - if(holder.master) - dir = holder.master.dir - arm() - -/obj/item/assembly/infra/armed/stealth - visible = FALSE - - -/***************************IBeam*********************************/ - -/obj/effect/beam/i_beam - name = "i beam" - icon = 'icons/obj/projectiles.dmi' - icon_state = "ibeam" - var/obj/effect/beam/i_beam/next = null - var/obj/effect/beam/i_beam/previous = null - var/obj/item/assembly/infra/master = null - var/limit = null - var/visible = FALSE - var/left = null - var/life_cycles = 0 - var/life_cap = 20 - anchored = TRUE - pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE - - -/obj/effect/beam/i_beam/proc/hit() - if(master) - master.trigger_beam() - qdel(src) - -/obj/effect/beam/i_beam/proc/vis_spread(v) - visible = v - if(next) - next.vis_spread(v) - -/obj/effect/beam/i_beam/update_icon() - transform = turn(matrix(), dir2angle(dir)) - -/obj/effect/beam/i_beam/process() - life_cycles++ - if(loc.density || !master || life_cycles >= life_cap) - qdel(src) - return - if(left > 0) - left-- - if(left < 1) - if(!(visible)) - invisibility = 101 - else - invisibility = FALSE - else - invisibility = FALSE - - if(!next && (limit > 0)) - var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(loc) - I.master = master - I.density = 1 - I.dir = dir - I.update_icon() - I.previous = src - next = I - step(I, I.dir) - if(next) - I.density = FALSE - I.vis_spread(visible) - I.limit = limit - 1 - master.last = I - I.process() - -/obj/effect/beam/i_beam/Bump() - qdel(src) - -/obj/effect/beam/i_beam/Bumped() - hit() - -/obj/effect/beam/i_beam/Crossed(atom/movable/AM, oldloc) - if(!isobj(AM) && !isliving(AM)) - return - if(istype(AM, /obj/effect)) - return - hit() - -/obj/effect/beam/i_beam/Destroy() - if(master.first == src) - master.first = null - QDEL_NULL(next) - if(previous) - previous.next = null - master.last = previous - return ..() +/obj/item/assembly/infra + name = "infrared emitter" + desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." + icon_state = "infrared" + materials = list(MAT_METAL=1000, MAT_GLASS=500) + origin_tech = "magnets=2;materials=2" + + bomb_name = "tripwire mine" + + secured = FALSE // toggle_secure()'ed in New() for correct adding to processing_objects, won't work otherwise + dir = EAST + var/on = FALSE + var/visible = TRUE + var/obj/effect/beam/i_beam/first = null + var/obj/effect/beam/i_beam/last = null + var/max_nesting_level = 10 + var/turf/fire_location + var/emission_cycles = 0 + var/emission_cap = 20 + +/obj/item/assembly/infra/Destroy() + if(first) + QDEL_NULL(first) + last = null + fire_location = null + return ..() + +/obj/item/assembly/infra/describe() + return "The assembly is [secured ? "secure" : "not secure"]. The infrared trigger is [on ? "on" : "off"]." + +/obj/item/assembly/infra/examine(mob/user) + . = ..() + . += describe() + +/obj/item/assembly/infra/activate() + if(!..()) + return FALSE//Cooldown check + on = !on + update_icon() + return TRUE + +/obj/item/assembly/infra/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + on = FALSE + if(first) + qdel(first) + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/assembly/infra/New() + ..() + if(!secured) + toggle_secure() + +/obj/item/assembly/infra/proc/arm() // Forces the device to arm no matter its current state. + if(!secured) // Checked because arm() might be called sometime after the object is spawned. + toggle_secure() + on = 1 + +/obj/item/assembly/infra/update_icon() + overlays.Cut() + attached_overlays = list() + if(on) + overlays += "infrared_on" + attached_overlays += "infrared_on" + + if(holder) + holder.update_icon() + +/obj/item/assembly/infra/process() + var/turf/T = get_turf(src) + if(first && (!on || !fire_location || fire_location != T || emission_cycles >= emission_cap)) + qdel(first) + return + if(!on) + return + if(!secured) + return + if(first && last) + last.process() + emission_cycles++ + return + if(T) + fire_location = T + emission_cycles = 0 + var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(T) + I.master = src + I.density = 1 + I.dir = dir + I.update_icon() + first = I + step(I, I.dir) + if(first) + I.density = FALSE + I.vis_spread(visible) + I.limit = 8 + I.process() + +/obj/item/assembly/infra/attack_hand() + qdel(first) + ..() + +/obj/item/assembly/infra/Move() + var/t = dir + . = ..() + dir = t + qdel(first) + +/obj/item/assembly/infra/holder_movement() + if(!holder) + return FALSE + qdel(first) + return TRUE + +/obj/item/assembly/infra/equipped(var/mob/user, var/slot) + qdel(first) + return ..() + +/obj/item/assembly/infra/pickup(mob/user) + qdel(first) + return ..() + +/obj/item/assembly/infra/proc/trigger_beam() + if(!secured || !on || cooldown > 0) + return FALSE + pulse(0) + audible_message("[bicon(src)] *beep* *beep*", null, 3) + if(first) + qdel(first) + cooldown = 2 + spawn(10) + process_cooldown() + +/obj/item/assembly/infra/interact(mob/user)//TODO: change this this to the wire control panel + if(!secured) return + user.set_machine(src) + var/dat = {"Infrared Laser + Status: [on ? "On" : "Off"]
    + Visibility: [visible ? "Visible" : "Invisible"]
    + Current Direction: [capitalize(dir2text(dir))]
    +
    +

    Refresh +

    Close"} + var/datum/browser/popup = new(user, "infra", name, 400, 400) + popup.set_content(dat) + popup.open(0) + onclose(user, "infra") + +/obj/item/assembly/infra/Topic(href, href_list) + ..() + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + usr << browse(null, "window=infra") + onclose(usr, "infra") + return + if(href_list["state"]) + on = !(on) + update_icon() + if(href_list["visible"]) + visible = !(visible) + if(first) + first.vis_spread(visible) + if(href_list["rotate"]) + rotate() + if(href_list["close"]) + usr << browse(null, "window=infra") + return + if(usr) + attack_self(usr) + +/obj/item/assembly/infra/verb/rotate()//This could likely be better + set name = "Rotate Infrared Laser" + set category = "Object" + set src in usr + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + dir = turn(dir, 90) + + if(usr.machine == src) + interact(usr) + + if(first) + qdel(first) + + + +/obj/item/assembly/infra/armed/New() + ..() + spawn(3) + if(holder) + if(holder.master) + dir = holder.master.dir + arm() + +/obj/item/assembly/infra/armed/stealth + visible = FALSE + + +/***************************IBeam*********************************/ + +/obj/effect/beam/i_beam + name = "i beam" + icon = 'icons/obj/projectiles.dmi' + icon_state = "ibeam" + var/obj/effect/beam/i_beam/next = null + var/obj/effect/beam/i_beam/previous = null + var/obj/item/assembly/infra/master = null + var/limit = null + var/visible = FALSE + var/left = null + var/life_cycles = 0 + var/life_cap = 20 + anchored = TRUE + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + + +/obj/effect/beam/i_beam/proc/hit() + if(master) + master.trigger_beam() + qdel(src) + +/obj/effect/beam/i_beam/proc/vis_spread(v) + visible = v + if(next) + next.vis_spread(v) + +/obj/effect/beam/i_beam/update_icon() + transform = turn(matrix(), dir2angle(dir)) + +/obj/effect/beam/i_beam/process() + life_cycles++ + if(loc.density || !master || life_cycles >= life_cap) + qdel(src) + return + if(left > 0) + left-- + if(left < 1) + if(!(visible)) + invisibility = 101 + else + invisibility = FALSE + else + invisibility = FALSE + + if(!next && (limit > 0)) + var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(loc) + I.master = master + I.density = 1 + I.dir = dir + I.update_icon() + I.previous = src + next = I + step(I, I.dir) + if(next) + I.density = FALSE + I.vis_spread(visible) + I.limit = limit - 1 + master.last = I + I.process() + +/obj/effect/beam/i_beam/Bump() + qdel(src) + +/obj/effect/beam/i_beam/Bumped() + hit() + +/obj/effect/beam/i_beam/Crossed(atom/movable/AM, oldloc) + if(!isobj(AM) && !isliving(AM)) + return + if(istype(AM, /obj/effect)) + return + hit() + +/obj/effect/beam/i_beam/Destroy() + if(master.first == src) + master.first = null + QDEL_NULL(next) + if(previous) + previous.next = null + master.last = previous + return ..() diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index f4869f10d33..35e80e53d18 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -1,144 +1,144 @@ -/obj/item/assembly/mousetrap - name = "mousetrap" - desc = "A handy little spring-loaded trap for catching pesty rodents." - icon_state = "mousetrap" - materials = list(MAT_METAL=100) - origin_tech = "combat=1;materials=2;engineering=1" - var/armed = FALSE - - bomb_name = "contact mine" - -/obj/item/assembly/mousetrap/examine(mob/user) - . = ..() - if(armed) - . += "It looks like it's armed." - -/obj/item/assembly/mousetrap/activate() - if(..()) - armed = !armed - if(!armed) - if(ishuman(usr)) - var/mob/living/carbon/human/user = usr - if((user.getBrainLoss() >= 60 || (CLUMSY in user.mutations)) && prob(50)) - to_chat(user, "Your hand slips, setting off the trigger.") - pulse(0) - update_icon() - if(usr) - playsound(usr.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) - -/obj/item/assembly/mousetrap/describe() - return "The pressure switch is [armed ? "primed" : "safe"]." - -/obj/item/assembly/mousetrap/update_icon() - if(armed) - icon_state = "mousetraparmed" - else - icon_state = "mousetrap" - if(holder) - holder.update_icon() - -/obj/item/assembly/mousetrap/proc/triggered(mob/target, type = "feet") - if(!armed) - return - var/obj/item/organ/external/affecting = null - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(PIERCEIMMUNE in H.dna.species.species_traits) - playsound(src, 'sound/effects/snap.ogg', 50, TRUE) - armed = FALSE - update_icon() - pulse(FALSE) - return FALSE - switch(type) - if("feet") - if(!H.shoes) - affecting = H.get_organ(pick("l_leg", "r_leg")) - H.Weaken(3) - if("l_hand", "r_hand") - if(!H.gloves) - affecting = H.get_organ(type) - H.Stun(3) - if(affecting) - affecting.receive_damage(1, 0) - else if(ismouse(target)) - var/mob/living/simple_animal/mouse/M = target - visible_message("SPLAT!") - M.splat() - playsound(loc, 'sound/effects/snap.ogg', 50, 1) - layer = MOB_LAYER - 0.2 - armed = FALSE - update_icon() - pulse(0) - -/obj/item/assembly/mousetrap/attack_self(mob/living/user) - if(!armed) - to_chat(user, "You arm [src].") - else - if((user.getBrainLoss() >= 60 || (CLUMSY in user.mutations)) && prob(50)) - var/which_hand = "l_hand" - if(!user.hand) - which_hand = "r_hand" - triggered(user, which_hand) - user.visible_message("[user] accidentally sets off [src], breaking [user.p_their()] fingers.", \ - "You accidentally trigger [src]!") - return - to_chat(user, "You disarm [src].") - armed = !armed - update_icon() - playsound(user.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) - -/obj/item/assembly/mousetrap/attack_hand(mob/living/user) - if(armed) - if((user.getBrainLoss() >= 60 || CLUMSY in user.mutations) && prob(50)) - var/which_hand = "l_hand" - if(!user.hand) - which_hand = "r_hand" - triggered(user, which_hand) - user.visible_message("[user] accidentally sets off [src], breaking [user.p_their()] fingers.", \ - "You accidentally trigger [src]!") - return - ..() - -/obj/item/assembly/mousetrap/Crossed(atom/movable/AM, oldloc) - if(armed) - if(ishuman(AM)) - var/mob/living/carbon/H = AM - if(H.m_intent == MOVE_INTENT_RUN) - triggered(H) - H.visible_message("[H] accidentally steps on [src].", \ - "You accidentally step on [src]") - else if(ismouse(AM)) - triggered(AM) - else if(AM.density) // For mousetrap grenades, set off by anything heavy - triggered(AM) - ..() - -/obj/item/assembly/mousetrap/on_found(mob/finder) - if(armed) - finder.visible_message("[finder] accidentally sets off [src], breaking [finder.p_their()] fingers.", \ - "You accidentally trigger [src]!") - triggered(finder, finder.hand ? "l_hand" : "r_hand") - return TRUE //end the search! - return FALSE - -/obj/item/assembly/mousetrap/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(!armed) - return ..() - visible_message("[src] is triggered by [AM].") - triggered(null) - -/obj/item/assembly/mousetrap/armed - icon_state = "mousetraparmed" - armed = 1 - - -/obj/item/assembly/mousetrap/verb/hide_under() - set src in oview(1) - set name = "Hide" - set category = "Object" - - if(usr.stat) - return - - layer = TURF_LAYER+0.2 - to_chat(usr, "You hide [src].") +/obj/item/assembly/mousetrap + name = "mousetrap" + desc = "A handy little spring-loaded trap for catching pesty rodents." + icon_state = "mousetrap" + materials = list(MAT_METAL=100) + origin_tech = "combat=1;materials=2;engineering=1" + var/armed = FALSE + + bomb_name = "contact mine" + +/obj/item/assembly/mousetrap/examine(mob/user) + . = ..() + if(armed) + . += "It looks like it's armed." + +/obj/item/assembly/mousetrap/activate() + if(..()) + armed = !armed + if(!armed) + if(ishuman(usr)) + var/mob/living/carbon/human/user = usr + if((user.getBrainLoss() >= 60 || (CLUMSY in user.mutations)) && prob(50)) + to_chat(user, "Your hand slips, setting off the trigger.") + pulse(0) + update_icon() + if(usr) + playsound(usr.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) + +/obj/item/assembly/mousetrap/describe() + return "The pressure switch is [armed ? "primed" : "safe"]." + +/obj/item/assembly/mousetrap/update_icon() + if(armed) + icon_state = "mousetraparmed" + else + icon_state = "mousetrap" + if(holder) + holder.update_icon() + +/obj/item/assembly/mousetrap/proc/triggered(mob/target, type = "feet") + if(!armed) + return + var/obj/item/organ/external/affecting = null + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(PIERCEIMMUNE in H.dna.species.species_traits) + playsound(src, 'sound/effects/snap.ogg', 50, TRUE) + armed = FALSE + update_icon() + pulse(FALSE) + return FALSE + switch(type) + if("feet") + if(!H.shoes) + affecting = H.get_organ(pick("l_leg", "r_leg")) + H.Weaken(3) + if("l_hand", "r_hand") + if(!H.gloves) + affecting = H.get_organ(type) + H.Stun(3) + if(affecting) + affecting.receive_damage(1, 0) + else if(ismouse(target)) + var/mob/living/simple_animal/mouse/M = target + visible_message("SPLAT!") + M.splat() + playsound(loc, 'sound/effects/snap.ogg', 50, 1) + layer = MOB_LAYER - 0.2 + armed = FALSE + update_icon() + pulse(0) + +/obj/item/assembly/mousetrap/attack_self(mob/living/user) + if(!armed) + to_chat(user, "You arm [src].") + else + if((user.getBrainLoss() >= 60 || (CLUMSY in user.mutations)) && prob(50)) + var/which_hand = "l_hand" + if(!user.hand) + which_hand = "r_hand" + triggered(user, which_hand) + user.visible_message("[user] accidentally sets off [src], breaking [user.p_their()] fingers.", \ + "You accidentally trigger [src]!") + return + to_chat(user, "You disarm [src].") + armed = !armed + update_icon() + playsound(user.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) + +/obj/item/assembly/mousetrap/attack_hand(mob/living/user) + if(armed) + if((user.getBrainLoss() >= 60 || (CLUMSY in user.mutations)) && prob(50)) + var/which_hand = "l_hand" + if(!user.hand) + which_hand = "r_hand" + triggered(user, which_hand) + user.visible_message("[user] accidentally sets off [src], breaking [user.p_their()] fingers.", \ + "You accidentally trigger [src]!") + return + ..() + +/obj/item/assembly/mousetrap/Crossed(atom/movable/AM, oldloc) + if(armed) + if(ishuman(AM)) + var/mob/living/carbon/H = AM + if(H.m_intent == MOVE_INTENT_RUN) + triggered(H) + H.visible_message("[H] accidentally steps on [src].", \ + "You accidentally step on [src]") + else if(ismouse(AM)) + triggered(AM) + else if(AM.density) // For mousetrap grenades, set off by anything heavy + triggered(AM) + ..() + +/obj/item/assembly/mousetrap/on_found(mob/finder) + if(armed) + finder.visible_message("[finder] accidentally sets off [src], breaking [finder.p_their()] fingers.", \ + "You accidentally trigger [src]!") + triggered(finder, finder.hand ? "l_hand" : "r_hand") + return TRUE //end the search! + return FALSE + +/obj/item/assembly/mousetrap/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(!armed) + return ..() + visible_message("[src] is triggered by [AM].") + triggered(null) + +/obj/item/assembly/mousetrap/armed + icon_state = "mousetraparmed" + armed = 1 + + +/obj/item/assembly/mousetrap/verb/hide_under() + set src in oview(1) + set name = "Hide" + set category = "Object" + + if(usr.stat) + return + + layer = TURF_LAYER+0.2 + to_chat(usr, "You hide [src].") diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index e6ae3ec7bc4..7e777f235b1 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -1,134 +1,134 @@ -/obj/item/assembly/prox_sensor - name = "proximity sensor" - desc = "Used for scanning and alerting when someone enters a certain proximity." - icon_state = "prox" - materials = list(MAT_METAL = 800, MAT_GLASS = 200) - origin_tech = "magnets=1;engineering=1" - - secured = 0 - - bomb_name = "proximity mine" - - var/scanning = 0 - var/timing = 0 - var/time = 10 - -/obj/item/assembly/prox_sensor/describe() - if(timing) - return "The proximity sensor is arming." - return "The proximity sensor is [scanning ? "armed" : "disarmed"]." - -/obj/item/assembly/prox_sensor/activate() - if(!..()) - return FALSE //Cooldown check - timing = !timing - update_icon() - return FALSE - -/obj/item/assembly/prox_sensor/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - scanning = 0 - timing = 0 - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/assembly/prox_sensor/HasProximity(atom/movable/AM) - if(!isobj(AM) && !isliving(AM)) - return - if(istype(AM, /obj/effect)) - return - if(AM.move_speed < 12) - sense() - -/obj/item/assembly/prox_sensor/proc/sense() - if(!secured || !scanning || cooldown > 0) - return FALSE - pulse(0) - visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - cooldown = 2 - spawn(10) - process_cooldown() - -/obj/item/assembly/prox_sensor/process() - if(timing && (time >= 0)) - time-- - if(timing && time <= 0) - timing = 0 - toggle_scan() - time = 10 - -/obj/item/assembly/prox_sensor/dropped() - ..() - spawn(0) - sense() - return - -/obj/item/assembly/prox_sensor/proc/toggle_scan() - if(!secured) - return FALSE - scanning = !scanning - update_icon() - -/obj/item/assembly/prox_sensor/update_icon() - overlays.Cut() - attached_overlays = list() - if(timing) - overlays += "prox_timing" - attached_overlays += "prox_timing" - if(scanning) - overlays += "prox_scanning" - attached_overlays += "prox_scanning" - if(holder) - holder.update_icon() - -/obj/item/assembly/prox_sensor/Move() - ..() - sense() - -/obj/item/assembly/prox_sensor/holder_movement() - sense() - -/obj/item/assembly/prox_sensor/interact(mob/user)//TODO: Change this to the wires thingy - if(!secured) - user.show_message("The [name] is unsecured!") - return FALSE - var/second = time % 60 - var/minute = (time - second) / 60 - var/dat = text("Proximity Sensor\n[] []:[]\n- - + +\n", (timing ? "Arming" : "Not Arming"), minute, second) - dat += "
    [scanning?"Armed":"Unarmed"] (Movement sensor active when armed!)" - dat += "

    Refresh" - dat += "

    Close" - var/datum/browser/popup = new(user, "prox", name, 400, 400) - popup.set_content(dat) - popup.open(0) - onclose(user, "prox") - -/obj/item/assembly/prox_sensor/Topic(href, href_list) - ..() - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - usr << browse(null, "window=prox") - onclose(usr, "prox") - return - - if(href_list["scanning"]) - toggle_scan() - - if(href_list["time"]) - timing = text2num(href_list["time"]) - update_icon() - - if(href_list["tp"]) - var/tp = text2num(href_list["tp"]) - time += tp - time = min(max(round(time), 0), 600) - - if(href_list["close"]) - usr << browse(null, "window=prox") - return - - if(usr) - attack_self(usr) \ No newline at end of file +/obj/item/assembly/prox_sensor + name = "proximity sensor" + desc = "Used for scanning and alerting when someone enters a certain proximity." + icon_state = "prox" + materials = list(MAT_METAL = 800, MAT_GLASS = 200) + origin_tech = "magnets=1;engineering=1" + + secured = 0 + + bomb_name = "proximity mine" + + var/scanning = 0 + var/timing = 0 + var/time = 10 + +/obj/item/assembly/prox_sensor/describe() + if(timing) + return "The proximity sensor is arming." + return "The proximity sensor is [scanning ? "armed" : "disarmed"]." + +/obj/item/assembly/prox_sensor/activate() + if(!..()) + return FALSE //Cooldown check + timing = !timing + update_icon() + return FALSE + +/obj/item/assembly/prox_sensor/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + scanning = 0 + timing = 0 + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/assembly/prox_sensor/HasProximity(atom/movable/AM) + if(!isobj(AM) && !isliving(AM)) + return + if(istype(AM, /obj/effect)) + return + if(AM.move_speed < 12) + sense() + +/obj/item/assembly/prox_sensor/proc/sense() + if(!secured || !scanning || cooldown > 0) + return FALSE + pulse(0) + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + cooldown = 2 + spawn(10) + process_cooldown() + +/obj/item/assembly/prox_sensor/process() + if(timing && (time >= 0)) + time-- + if(timing && time <= 0) + timing = 0 + toggle_scan() + time = 10 + +/obj/item/assembly/prox_sensor/dropped() + ..() + spawn(0) + sense() + return + +/obj/item/assembly/prox_sensor/proc/toggle_scan() + if(!secured) + return FALSE + scanning = !scanning + update_icon() + +/obj/item/assembly/prox_sensor/update_icon() + overlays.Cut() + attached_overlays = list() + if(timing) + overlays += "prox_timing" + attached_overlays += "prox_timing" + if(scanning) + overlays += "prox_scanning" + attached_overlays += "prox_scanning" + if(holder) + holder.update_icon() + +/obj/item/assembly/prox_sensor/Move() + ..() + sense() + +/obj/item/assembly/prox_sensor/holder_movement() + sense() + +/obj/item/assembly/prox_sensor/interact(mob/user)//TODO: Change this to the wires thingy + if(!secured) + user.show_message("The [name] is unsecured!") + return FALSE + var/second = time % 60 + var/minute = (time - second) / 60 + var/dat = text("Proximity Sensor\n[] []:[]\n- - + +\n", (timing ? "Arming" : "Not Arming"), minute, second) + dat += "
    [scanning?"Armed":"Unarmed"] (Movement sensor active when armed!)" + dat += "

    Refresh" + dat += "

    Close" + var/datum/browser/popup = new(user, "prox", name, 400, 400) + popup.set_content(dat) + popup.open(0) + onclose(user, "prox") + +/obj/item/assembly/prox_sensor/Topic(href, href_list) + ..() + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + usr << browse(null, "window=prox") + onclose(usr, "prox") + return + + if(href_list["scanning"]) + toggle_scan() + + if(href_list["time"]) + timing = text2num(href_list["time"]) + update_icon() + + if(href_list["tp"]) + var/tp = text2num(href_list["tp"]) + time += tp + time = min(max(round(time), 0), 600) + + if(href_list["close"]) + usr << browse(null, "window=prox") + return + + if(usr) + attack_self(usr) diff --git a/code/modules/assembly/shock_kit.dm b/code/modules/assembly/shock_kit.dm index 7ee6844deea..d054ea8d3f5 100644 --- a/code/modules/assembly/shock_kit.dm +++ b/code/modules/assembly/shock_kit.dm @@ -1,46 +1,46 @@ -/obj/item/assembly/shock_kit - name = "electrohelmet assembly" - desc = "This appears to be made from both an electropack and a helmet." - icon = 'icons/obj/assemblies.dmi' - icon_state = "shock_kit" - var/obj/item/clothing/head/helmet/part1 = null - var/obj/item/radio/electropack/part2 = null - var/status = 0 - w_class = WEIGHT_CLASS_HUGE - flags = CONDUCT - -/obj/item/assembly/shock_kit/Destroy() - QDEL_NULL(part1) - QDEL_NULL(part2) - return ..() - -/obj/item/assembly/shock_kit/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/wrench) && !status) - var/turf/T = loc - if(ismob(T)) - T = T.loc - part1.loc = T - part2.loc = T - part1.master = null - part2.master = null - part1 = null - part2 = null - qdel(src) - return - if(istype(W, /obj/item/screwdriver)) - status = !status - to_chat(user, "[src] is now [status ? "secured" : "unsecured"]!") - add_fingerprint(user) - return - -/obj/item/assembly/shock_kit/attack_self(mob/user as mob) - part1.attack_self(user, status) - part2.attack_self(user, status) - add_fingerprint(user) - return - -/obj/item/assembly/shock_kit/receive_signal() - if(istype(loc, /obj/structure/chair/e_chair)) - var/obj/structure/chair/e_chair/C = loc - C.shock() - return +/obj/item/assembly/shock_kit + name = "electrohelmet assembly" + desc = "This appears to be made from both an electropack and a helmet." + icon = 'icons/obj/assemblies.dmi' + icon_state = "shock_kit" + var/obj/item/clothing/head/helmet/part1 = null + var/obj/item/radio/electropack/part2 = null + var/status = 0 + w_class = WEIGHT_CLASS_HUGE + flags = CONDUCT + +/obj/item/assembly/shock_kit/Destroy() + QDEL_NULL(part1) + QDEL_NULL(part2) + return ..() + +/obj/item/assembly/shock_kit/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/wrench) && !status) + var/turf/T = loc + if(ismob(T)) + T = T.loc + part1.loc = T + part2.loc = T + part1.master = null + part2.master = null + part1 = null + part2 = null + qdel(src) + return + if(istype(W, /obj/item/screwdriver)) + status = !status + to_chat(user, "[src] is now [status ? "secured" : "unsecured"]!") + add_fingerprint(user) + return + +/obj/item/assembly/shock_kit/attack_self(mob/user as mob) + part1.attack_self(user, status) + part2.attack_self(user, status) + add_fingerprint(user) + return + +/obj/item/assembly/shock_kit/receive_signal() + if(istype(loc, /obj/structure/chair/e_chair)) + var/obj/structure/chair/e_chair/C = loc + C.shock() + return diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 29f79cae1a7..f774d28921f 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -1,177 +1,177 @@ -/obj/item/assembly/signaler - name = "remote signaling device" - desc = "Used to remotely activate devices." - icon_state = "signaller" - item_state = "signaler" - materials = list(MAT_METAL=400, MAT_GLASS=120) - origin_tech = "magnets=1;bluespace=1" - wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE - - secured = 1 - var/receiving = FALSE - - bomb_name = "remote-control bomb" - - var/code = 30 - var/frequency = RSD_FREQ - var/delay = 0 - var/datum/radio_frequency/radio_connection - var/airlock_wire = null - -/obj/item/assembly/signaler/New() - ..() - if(SSradio) - set_frequency(frequency) - -/obj/item/assembly/signaler/Initialize() - ..() - if(SSradio) - set_frequency(frequency) - -/obj/item/assembly/signaler/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/item/assembly/signaler/describe() - return "[src]'s power light is [receiving ? "on" : "off"]" - -/obj/item/assembly/signaler/activate() - if(cooldown > 0) - return FALSE - cooldown = 2 - spawn(10) - process_cooldown() - - signal() - return TRUE - -/obj/item/assembly/signaler/update_icon() - if(holder) - holder.update_icon() - return - -/obj/item/assembly/signaler/interact(mob/user, flag1) - var/t1 = "-------" - var/dat = {" - - "} - if(!flag1) - dat += {" - Send Signal
    - Receiver is [receiving?"on":"off"]
    - "} - dat += {" - Frequency/Code for signaler:
    - Frequency: - - - - - [format_frequency(frequency)] - + - +
    - - Code: - - - - - [code] - + - +
    - [t1] -
    - "} - var/datum/browser/popup = new(user, "radio", name, 400, 400) - popup.set_content(dat) - popup.open(0) - onclose(user, "radio") - -/obj/item/assembly/signaler/Topic(href, href_list) - ..() - - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - usr << browse(null, "window=radio") - onclose(usr, "radio") - return - - if(href_list["freq"]) - var/new_frequency = (frequency + text2num(href_list["freq"])) - if(new_frequency < RADIO_LOW_FREQ || new_frequency > RADIO_HIGH_FREQ) - new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - set_frequency(new_frequency) - - if(href_list["code"]) - code += text2num(href_list["code"]) - code = round(code) - code = min(100, code) - code = max(1, code) - if(href_list["receive"]) - receiving = !receiving - - if(href_list["send"]) - spawn( 0 ) - signal() - - if(usr) - attack_self(usr) - -/obj/item/assembly/signaler/proc/signal() - if(!radio_connection) - return - - var/datum/signal/signal = new - signal.source = src - signal.encryption = code - signal.data["message"] = "ACTIVATE" - radio_connection.post_signal(src, signal) - - var/time = time2text(world.realtime,"hh:mm:ss") - var/turf/T = get_turf(src) - if(usr) - lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") - -/obj/item/assembly/signaler/pulse(var/radio = FALSE) - if(connected && wires) - connected.Pulse(src) - else - return ..(radio) - -/obj/item/assembly/signaler/receive_signal(datum/signal/signal) - if(!receiving || !signal) - return FALSE - - if(signal.encryption != code) - return FALSE - - if(!(wires & WIRE_RADIO_RECEIVE)) - return FALSE - pulse(1) - - for(var/mob/O in hearers(1, loc)) - O.show_message("[bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) - return TRUE - -/obj/item/assembly/signaler/proc/set_frequency(new_frequency) - if(!SSradio) - sleep(20) - if(!SSradio) - return - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_CHAT) - -// Embedded signaller used in anomalies. -/obj/item/assembly/signaler/anomaly - name = "anomaly core" - desc = "The neutralized core of an anomaly. It'd probably be valuable for research." - icon_state = "anomaly core" - item_state = "electronic" - resistance_flags = FIRE_PROOF - receiving = TRUE - -/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal) - if(..()) - for(var/obj/effect/anomaly/A in orange(0, src)) - A.anomalyNeutralize() - -/obj/item/assembly/signaler/anomaly/attack_self() - return +/obj/item/assembly/signaler + name = "remote signaling device" + desc = "Used to remotely activate devices." + icon_state = "signaller" + item_state = "signaler" + materials = list(MAT_METAL=400, MAT_GLASS=120) + origin_tech = "magnets=1;bluespace=1" + wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE + + secured = 1 + var/receiving = FALSE + + bomb_name = "remote-control bomb" + + var/code = 30 + var/frequency = RSD_FREQ + var/delay = 0 + var/datum/radio_frequency/radio_connection + var/airlock_wire = null + +/obj/item/assembly/signaler/New() + ..() + if(SSradio) + set_frequency(frequency) + +/obj/item/assembly/signaler/Initialize() + ..() + if(SSradio) + set_frequency(frequency) + +/obj/item/assembly/signaler/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/item/assembly/signaler/describe() + return "[src]'s power light is [receiving ? "on" : "off"]" + +/obj/item/assembly/signaler/activate() + if(cooldown > 0) + return FALSE + cooldown = 2 + spawn(10) + process_cooldown() + + signal() + return TRUE + +/obj/item/assembly/signaler/update_icon() + if(holder) + holder.update_icon() + return + +/obj/item/assembly/signaler/interact(mob/user, flag1) + var/t1 = "-------" + var/dat = {" + + "} + if(!flag1) + dat += {" + Send Signal
    + Receiver is [receiving?"on":"off"]
    + "} + dat += {" + Frequency/Code for signaler:
    + Frequency: + - + - + [format_frequency(frequency)] + + + +
    + + Code: + - + - + [code] + + + +
    + [t1] +
    + "} + var/datum/browser/popup = new(user, "radio", name, 400, 400) + popup.set_content(dat) + popup.open(0) + onclose(user, "radio") + +/obj/item/assembly/signaler/Topic(href, href_list) + ..() + + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + usr << browse(null, "window=radio") + onclose(usr, "radio") + return + + if(href_list["freq"]) + var/new_frequency = (frequency + text2num(href_list["freq"])) + if(new_frequency < RADIO_LOW_FREQ || new_frequency > RADIO_HIGH_FREQ) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + + if(href_list["code"]) + code += text2num(href_list["code"]) + code = round(code) + code = min(100, code) + code = max(1, code) + if(href_list["receive"]) + receiving = !receiving + + if(href_list["send"]) + spawn( 0 ) + signal() + + if(usr) + attack_self(usr) + +/obj/item/assembly/signaler/proc/signal() + if(!radio_connection) + return + + var/datum/signal/signal = new + signal.source = src + signal.encryption = code + signal.data["message"] = "ACTIVATE" + radio_connection.post_signal(src, signal) + + var/time = time2text(world.realtime,"hh:mm:ss") + var/turf/T = get_turf(src) + if(usr) + GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") + +/obj/item/assembly/signaler/pulse(var/radio = FALSE) + if(connected && wires) + connected.Pulse(src) + else + return ..(radio) + +/obj/item/assembly/signaler/receive_signal(datum/signal/signal) + if(!receiving || !signal) + return FALSE + + if(signal.encryption != code) + return FALSE + + if(!(wires & WIRE_RADIO_RECEIVE)) + return FALSE + pulse(1) + + for(var/mob/O in hearers(1, loc)) + O.show_message("[bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) + return TRUE + +/obj/item/assembly/signaler/proc/set_frequency(new_frequency) + if(!SSradio) + sleep(20) + if(!SSradio) + return + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_CHAT) + +// Embedded signaller used in anomalies. +/obj/item/assembly/signaler/anomaly + name = "anomaly core" + desc = "The neutralized core of an anomaly. It'd probably be valuable for research." + icon_state = "anomaly core" + item_state = "electronic" + resistance_flags = FIRE_PROOF + receiving = TRUE + +/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal) + if(..()) + for(var/obj/effect/anomaly/A in orange(0, src)) + A.anomalyNeutralize() + +/obj/item/assembly/signaler/anomaly/attack_self() + return diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index 2c2216fa9d0..cab132ab747 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -1,127 +1,127 @@ -/obj/item/assembly/timer - name = "timer" - desc = "Used to time things. Works well with contraptions which has to count down. Tick tock." - icon_state = "timer" - materials = list(MAT_METAL=500, MAT_GLASS=50) - origin_tech = "magnets=1;engineering=1" - - secured = FALSE - - bomb_name = "time bomb" - - var/timing = FALSE - var/time = 10 - var/repeat = FALSE - var/set_time = 10 - -/obj/item/assembly/timer/describe() - if(timing) - return "The timer is counting down from [time]!" - return "The timer is set for [time] seconds." - -/obj/item/assembly/timer/activate() - if(!..()) - return FALSE//Cooldown check - timing = !timing - update_icon() - return FALSE - -/obj/item/assembly/timer/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - timing = FALSE - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/assembly/timer/proc/timer_end() - if(!secured || cooldown > 0) - return FALSE - pulse(0) - if(loc) - loc.visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - cooldown = 2 - spawn(10) - process_cooldown() - -/obj/item/assembly/timer/process() - if(timing && (time > 0)) - time -= 2 // 2 seconds per process() - if(timing && time <= 0) - timing = repeat - timer_end() - time = set_time - -/obj/item/assembly/timer/update_icon() - overlays.Cut() - attached_overlays = list() - if(timing) - overlays += "timer_timing" - attached_overlays += "timer_timing" - if(holder) - holder.update_icon() - -/obj/item/assembly/timer/interact(mob/user as mob)//TODO: Have this use the wires - if(!secured) - user.show_message("The [name] is unsecured!") - return FALSE - var/second = time % 60 - var/minute = (time - second) / 60 - var/set_second = set_time % 60 - var/set_minute = (set_time - set_second) / 60 - if(second < 10) second = "0[second]" - if(set_second < 10) set_second = "0[set_second]" - - var/dat = {" - -

    Timing Unit

    - [minute]:[second] [timing?"Stop":"Start"] Reset
    - Repeat: [repeat?"On":"Off"]
    - Timer set for - - - [set_minute]:[set_second] + + -
    -
    -

    - Refresh -

    - Close"} - var/datum/browser/popup = new(user, "timer", name, 400, 400) - popup.set_content(dat) - popup.open(0) - onclose(user, "timer") - -/obj/item/assembly/timer/Topic(href, href_list) - ..() - if(usr.incapacitated() || !in_range(loc, usr)) - usr << browse(null, "window=timer") - onclose(usr, "timer") - return - - if(href_list["time"]) - timing = !timing - if(timing && istype(holder, /obj/item/transfer_valve)) - message_admins("[key_name_admin(usr)] activated [src] attachment on [holder].") - investigate_log("[key_name(usr)] activated [src] attachment for [loc]", INVESTIGATE_BOMB) - log_game("[key_name(usr)] activated [src] attachment for [loc]") - update_icon() - if(href_list["reset"]) - time = set_time - - if(href_list["repeat"]) - repeat = !repeat - - if(href_list["tp"]) - var/tp = text2num(href_list["tp"]) - set_time += tp - set_time = min(max(round(set_time), 6), 600) - if(!timing) - time = set_time - - if(href_list["close"]) - usr << browse(null, "window=timer") - return - - if(usr) - attack_self(usr) +/obj/item/assembly/timer + name = "timer" + desc = "Used to time things. Works well with contraptions which has to count down. Tick tock." + icon_state = "timer" + materials = list(MAT_METAL=500, MAT_GLASS=50) + origin_tech = "magnets=1;engineering=1" + + secured = FALSE + + bomb_name = "time bomb" + + var/timing = FALSE + var/time = 10 + var/repeat = FALSE + var/set_time = 10 + +/obj/item/assembly/timer/describe() + if(timing) + return "The timer is counting down from [time]!" + return "The timer is set for [time] seconds." + +/obj/item/assembly/timer/activate() + if(!..()) + return FALSE//Cooldown check + timing = !timing + update_icon() + return FALSE + +/obj/item/assembly/timer/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + timing = FALSE + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/assembly/timer/proc/timer_end() + if(!secured || cooldown > 0) + return FALSE + pulse(0) + if(loc) + loc.visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + cooldown = 2 + spawn(10) + process_cooldown() + +/obj/item/assembly/timer/process() + if(timing && (time > 0)) + time -= 2 // 2 seconds per process() + if(timing && time <= 0) + timing = repeat + timer_end() + time = set_time + +/obj/item/assembly/timer/update_icon() + overlays.Cut() + attached_overlays = list() + if(timing) + overlays += "timer_timing" + attached_overlays += "timer_timing" + if(holder) + holder.update_icon() + +/obj/item/assembly/timer/interact(mob/user as mob)//TODO: Have this use the wires + if(!secured) + user.show_message("The [name] is unsecured!") + return FALSE + var/second = time % 60 + var/minute = (time - second) / 60 + var/set_second = set_time % 60 + var/set_minute = (set_time - set_second) / 60 + if(second < 10) second = "0[second]" + if(set_second < 10) set_second = "0[set_second]" + + var/dat = {" + +

    Timing Unit

    + [minute]:[second] [timing?"Stop":"Start"] Reset
    + Repeat: [repeat?"On":"Off"]
    + Timer set for + - - [set_minute]:[set_second] + + +
    +
    +

    + Refresh +

    + Close"} + var/datum/browser/popup = new(user, "timer", name, 400, 400) + popup.set_content(dat) + popup.open(0) + onclose(user, "timer") + +/obj/item/assembly/timer/Topic(href, href_list) + ..() + if(usr.incapacitated() || !in_range(loc, usr)) + usr << browse(null, "window=timer") + onclose(usr, "timer") + return + + if(href_list["time"]) + timing = !timing + if(timing && istype(holder, /obj/item/transfer_valve)) + message_admins("[key_name_admin(usr)] activated [src] attachment on [holder].") + investigate_log("[key_name(usr)] activated [src] attachment for [loc]", INVESTIGATE_BOMB) + log_game("[key_name(usr)] activated [src] attachment for [loc]") + update_icon() + if(href_list["reset"]) + time = set_time + + if(href_list["repeat"]) + repeat = !repeat + + if(href_list["tp"]) + var/tp = text2num(href_list["tp"]) + set_time += tp + set_time = min(max(round(set_time), 6), 600) + if(!timing) + time = set_time + + if(href_list["close"]) + usr << browse(null, "window=timer") + return + + if(usr) + attack_self(usr) diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index a20cdb89caf..3119b55e28a 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -74,4 +74,4 @@ /obj/item/assembly/voice/noise/hear_message(mob/living/M as mob, msg) pulse(0) var/turf/T = get_turf(src) //otherwise it won't work in hand - T.visible_message("[bicon(src)] beeps!") \ No newline at end of file + T.visible_message("[bicon(src)] beeps!") diff --git a/code/modules/atmos_automation/console.dm b/code/modules/atmos_automation/console.dm index a810d3b08c5..8d23bb390aa 100644 --- a/code/modules/atmos_automation/console.dm +++ b/code/modules/atmos_automation/console.dm @@ -54,7 +54,7 @@ /obj/machinery/computer/general_air_control/atmos_automation/proc/selectValidChildFor(datum/automation/parent, mob/user, list/valid_returntypes) var/list/choices=list() - for(var/childtype in automation_types) + for(var/childtype in GLOB.automation_types) var/datum/automation/A = new childtype(src) if(A.returntype == null) continue @@ -217,7 +217,7 @@ testing("AAC: Null cData in root JS array.") continue var/Atype=text2path(cData["type"]) - if(!(Atype in automation_types)) + if(!(Atype in GLOB.automation_types)) testing("AAC: Unrecognized Atype [Atype].") continue var/datum/automation/A = new Atype(src) diff --git a/code/modules/atmos_automation/implementation/digital_valves.dm b/code/modules/atmos_automation/implementation/digital_valves.dm index a143c0e9869..dc0d0b2be00 100644 --- a/code/modules/atmos_automation/implementation/digital_valves.dm +++ b/code/modules/atmos_automation/implementation/digital_valves.dm @@ -41,4 +41,4 @@ return valve = input("Select a valve:", "Sensor Data", valve) as null|anything in valves parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/implementation/emitters.dm b/code/modules/atmos_automation/implementation/emitters.dm index 100d68708cd..e6315fbceb2 100644 --- a/code/modules/atmos_automation/implementation/emitters.dm +++ b/code/modules/atmos_automation/implementation/emitters.dm @@ -39,4 +39,4 @@ return emitter = input("Select an emitter:", "Emitter", emitter) as null|anything in emitters parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/implementation/injectors.dm b/code/modules/atmos_automation/implementation/injectors.dm index cc17dd27921..d6747f31447 100644 --- a/code/modules/atmos_automation/implementation/injectors.dm +++ b/code/modules/atmos_automation/implementation/injectors.dm @@ -80,4 +80,4 @@ injector_names|=I.id_tag injector = input("Select an injector:", "Sensor Data", injector) as null|anything in injector_names parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/implementation/scrubbers.dm b/code/modules/atmos_automation/implementation/scrubbers.dm index 4188773a6a4..54829d01cea 100644 --- a/code/modules/atmos_automation/implementation/scrubbers.dm +++ b/code/modules/atmos_automation/implementation/scrubbers.dm @@ -84,13 +84,13 @@ parent.updateUsrDialog() return 1 -var/global/list/gas_labels=list( +GLOBAL_LIST_INIT(gas_labels, list( "co2" = "CO2", "tox" = "Plasma", "n2o" = "N2O", "o2" = "O2", "n2" = "N2" -) +)) /datum/automation/set_scrubber_gasses name="Scrubber: Gasses" @@ -131,7 +131,7 @@ var/global/list/gas_labels=list( GetText() var/txt = "Set Scrubber [fmtString(scrubber)] to scrub " for(var/gas in gasses) - txt += " [gas_labels[gas]] ([gasses[gas] ? "on" : "off"])," + txt += " [GLOB.gas_labels[gas]] ([gasses[gas] ? "on" : "off"])," return txt Topic(href,href_list) @@ -150,4 +150,4 @@ var/global/list/gas_labels=list( injector_names|=S.id_tag scrubber = input("Select a scrubber:", "Scrubbers", scrubber) as null|anything in injector_names parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/implementation/sensors.dm b/code/modules/atmos_automation/implementation/sensors.dm index 487298aa266..e1ef348a617 100644 --- a/code/modules/atmos_automation/implementation/sensors.dm +++ b/code/modules/atmos_automation/implementation/sensors.dm @@ -22,7 +22,7 @@ field = json["field"] Evaluate() - if(sensor && field && sensor in parent.sensor_information) + if(sensor && field && (sensor in parent.sensor_information)) return parent.sensor_information[sensor][field] return 0 @@ -53,4 +53,4 @@ sensor_list|=M.id_tag sensor = input("Select a sensor:", "Sensor Data", field) as null|anything in sensor_list parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/statements.dm b/code/modules/atmos_automation/statements.dm index 72937e18eae..3f5ed0fb6a7 100644 --- a/code/modules/atmos_automation/statements.dm +++ b/code/modules/atmos_automation/statements.dm @@ -1,4 +1,4 @@ -var/global/automation_types = subtypesof(/datum/automation) +GLOBAL_LIST_INIT(automation_types, subtypesof(/datum/automation)) #define AUTOM_RT_NULL 0 #define AUTOM_RT_NUM 1 @@ -56,7 +56,7 @@ var/global/automation_types = subtypesof(/datum/automation) if(isnull(cData) || !("type" in cData)) return null var/Atype=text2path(cData["type"]) - if(!(Atype in automation_types)) + if(!(Atype in GLOB.automation_types)) return null var/datum/automation/A = new Atype(parent) A.Import(cData) @@ -70,7 +70,7 @@ var/global/automation_types = subtypesof(/datum/automation) . += null continue var/Atype=text2path(cData["type"]) - if(!(Atype in automation_types)) + if(!(Atype in GLOB.automation_types)) continue var/datum/automation/A = new Atype(parent) A.Import(cData) @@ -454,4 +454,4 @@ var/global/automation_types = subtypesof(/datum/automation) if(href_list["set_value"]) value = input("Set a value:", "Static Value", value) as num parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 7131b79b03b..bac8d64d9f2 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -1,602 +1,601 @@ -//These are meant for spawning on maps, namely Away Missions. - -//If someone can do this in a neater way, be my guest-Kor - -//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). - -/obj/effect/mob_spawn - name = "Unknown" - density = TRUE - anchored = TRUE - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - var/mob_type = null - var/mob_name = "unidentified entity" - var/mob_gender = null - var/death = TRUE //Kill the mob - var/roundstart = TRUE //fires on initialize - var/instant = FALSE //fires on New - var/flavour_text = "The mapper forgot to set this!" - var/faction = null - var/permanent = FALSE //If true, the spawner will not disappear upon running out of uses. - var/random = FALSE //Don't set a name or gender, just go random - var/objectives = null - var/uses = 1 //how many times can we spawn from it. set to -1 for infinite. - var/brute_damage = 0 - var/oxy_damage = 0 - var/burn_damage = 0 - var/datum/disease/disease = null //Do they start with a pre-spawned disease? - var/mob_color //Change the mob's color - var/assignedrole - var/banType = ROLE_GHOST - var/ghost_usable = TRUE - var/offstation_role = TRUE // If set to true, the role of the user's mind will be set to offstation - -/obj/effect/mob_spawn/attack_ghost(mob/user) - var/mob/dead/observer/O = user - if(SSticker.current_state != GAME_STATE_PLAYING || !loc || !ghost_usable) - return - if(!uses) - to_chat(user, "This spawner is out of charges!") - return - if(jobban_isbanned(user, banType)) - to_chat(user, "You are jobanned!") - return - if(cannotPossess(user)) - to_chat(user, "Upon using the antagHUD you forfeited the ability to join the round.") - return - if(!O.can_reenter_corpse) - to_chat(user, "You have forfeited the right to respawn.") - return - var/ghost_role = alert("Become [mob_name]? (Warning, You can no longer be cloned!)",,"Yes","No") - if(ghost_role == "No") - return - if(!species_prompt()) - return - if(!loc || !uses || QDELETED(src) || QDELETED(user)) - to_chat(user, "The [name] is no longer usable!") - return - log_game("[user.ckey] became [mob_name]") - create(ckey = user.ckey) - -/obj/effect/mob_spawn/Initialize(mapload) - . = ..() - if(instant || roundstart) //at some point we should probably re-introduce the (ticker && ticker.current_state > GAME_STATE_SETTING_UP) portion of this check, but for now it was preventing the corpses from spawning at roundstart and resulting in ghost role spawners that made dead bodies. - create() - else if(ghost_usable) - GLOB.poi_list |= src - LAZYADD(GLOB.mob_spawners[name], src) - -/obj/effect/mob_spawn/Destroy() - GLOB.poi_list -= src - var/list/spawners = GLOB.mob_spawners[name] - LAZYREMOVE(spawners, src) - if(!LAZYLEN(spawners)) - GLOB.mob_spawners -= name - return ..() - -/obj/effect/mob_spawn/proc/species_prompt() - return TRUE - -/obj/effect/mob_spawn/proc/special(mob/M) - return - -/obj/effect/mob_spawn/proc/equip(mob/M) - return - -/obj/effect/mob_spawn/proc/create(ckey, flavour = TRUE, name) - var/mob/living/M = new mob_type(get_turf(src)) //living mobs only - var/mob/living/carbon/human/H = M - if(H && !H.dna) - H.Initialize(null) - if(!random) - M.real_name = mob_name ? mob_name : M.name - if(!mob_gender) - mob_gender = pick(MALE, FEMALE) - M.gender = mob_gender - if(faction) - M.faction = list(faction) - if(disease) - M.ForceContractDisease(new disease) - if(death) - M.death() //Kills the new mob - - M.adjustOxyLoss(oxy_damage) - M.adjustBruteLoss(brute_damage) - M.adjustFireLoss(burn_damage) - M.color = mob_color - equip(M, TRUE) - - if(ckey) - M.ckey = ckey - if(flavour) - to_chat(M, "[flavour_text]") - var/datum/mind/MM = M.mind - if(objectives) - for(var/objective in objectives) - MM.objectives += new/datum/objective(objective) - if(assignedrole) - M.mind.assigned_role = assignedrole - M.mind.offstation_role = offstation_role - special(M, name) - MM.name = M.real_name - if(uses > 0) - uses-- - if(!permanent && !uses) - qdel(src) - -// Base version - place these on maps/templates. -/obj/effect/mob_spawn/human - mob_type = /mob/living/carbon/human - //Human specific stuff. - var/mob_species = null //Set species - var/allow_species_pick = FALSE - var/list/pickable_species = list("Human", "Vulpkanin", "Tajaran", "Unathi", "Skrell", "Diona") - var/datum/outfit/outfit = /datum/outfit //If this is a path, it will be instanced in Initialize() - var/disable_pda = TRUE - var/disable_sensors = TRUE - //All of these only affect the ID that the outfit has placed in the ID slot - var/id_job = null //Such as "Clown" or "Chef." This just determines what the ID reads as, not their access - var/id_access = null //This is for access. See access.dm for which jobs give what access. Use "Captain" if you want it to be all access. - var/id_access_list = null //Allows you to manually add access to an ID card. - assignedrole = "Ghost Role" - - var/husk = null - //these vars are for lazy mappers to override parts of the outfit - //these cannot be null by default, or mappers cannot set them to null if they want nothing in that slot - var/uniform = -1 - var/r_hand = -1 - var/l_hand = -1 - var/suit = -1 - var/shoes = -1 - var/gloves = -1 - var/ears = -1 - var/glasses = -1 - var/mask = -1 - var/head = -1 - var/belt = -1 - var/r_pocket = -1 - var/l_pocket = -1 - var/back = -1 - var/id = -1 - var/neck = -1 - var/pda = -1 - var/backpack_contents = -1 - var/suit_store = -1 - - var/hair_style - var/facial_hair_style - var/skin_tone - - -/obj/effect/mob_spawn/human/Initialize() - if(ispath(outfit)) - outfit = new outfit() - if(!outfit) - outfit = new /datum/outfit - if(!mob_name) - mob_name = id_job - return ..() - -/obj/effect/mob_spawn/human/species_prompt() - if(allow_species_pick) - var/selected_species = input("Select a species", "Species Selection") as null|anything in pickable_species - if(!selected_species) - return TRUE // You didn't pick, so just continue on with the spawning process as a human - var/datum/species/S = GLOB.all_species[selected_species] - mob_species = S.type - return TRUE - -/obj/effect/mob_spawn/human/equip(mob/living/carbon/human/H) - if(mob_species) - H.set_species(mob_species) - - if(husk) - H.ChangeToHusk() - else //Because for some reason I can't track down, things are getting turned into husks even if husk = false. It's in some damage proc somewhere. - H.mutations.Remove(HUSK) - H.underwear = "Nude" - H.undershirt = "Nude" - H.socks = "Nude" - var/obj/item/organ/external/head/D = H.get_organ("head") - if(istype(D)) - if(hair_style) - D.h_style = hair_style - else - D.h_style = random_hair_style(gender, D.dna.species.name) - D.hair_colour = rand_hex_color() - if(facial_hair_style) - D.f_style = facial_hair_style - else - D.f_style = random_facial_hair_style(gender, D.dna.species.name) - D.facial_colour = rand_hex_color() - if(skin_tone) - H.change_skin_tone(skin_tone) - else - H.change_skin_tone(random_skin_tone()) - H.change_skin_color(rand_hex_color()) - H.update_hair() - H.update_fhair() - H.update_body() - H.update_dna() - H.regenerate_icons() - if(outfit) - var/static/list/slots = list("uniform", "r_hand", "l_hand", "suit", "shoes", "gloves", "ears", "glasses", "mask", "head", "belt", "r_pocket", "l_pocket", "back", "id", "neck", "backpack_contents", "suit_store") - for(var/slot in slots) - var/T = vars[slot] - if(!isnum(T)) - outfit.vars[slot] = T - H.equipOutfit(outfit) - var/list/del_types = list(/obj/item/pda, /obj/item/radio/headset) - for(var/del_type in del_types) - var/obj/item/I = locate(del_type) in H - qdel(I) - - if(disable_pda) - // We don't want corpse PDAs to show up in the messenger list. - var/obj/item/pda/PDA = locate(/obj/item/pda) in H - if(PDA) - var/datum/data/pda/app/messenger/M = PDA.find_program(/datum/data/pda/app/messenger) - M.toff = 1 - if(disable_sensors) - // Using crew monitors to find corpses while creative makes finding certain ruins too easy. - var/obj/item/clothing/under/C = H.w_uniform - if(istype(C)) - C.sensor_mode = SUIT_SENSOR_OFF - - var/obj/item/card/id/W = H.wear_id - if(W) - if(id_access) - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(J.title == id_access) - W.access = J.get_access() - break - if(id_access_list) - if(!islist(W.access)) - W.access = list() - W.access |= id_access_list - if(id_job) - W.assignment = id_job - W.registered_name = H.real_name - W.update_label() - -//Instant version - use when spawning corpses during runtime -/obj/effect/mob_spawn/human/corpse - roundstart = FALSE - instant = TRUE - -/obj/effect/mob_spawn/human/corpse/damaged - brute_damage = 1000 - - -/obj/effect/mob_spawn/human/alive - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "sleeper" - death = FALSE - roundstart = FALSE //you could use these for alive fake humans on roundstart but this is more common scenario - - -//Non-human spawners - -/obj/effect/mob_spawn/mouse - name = "sleeper" - mob_name = "space mouse" - mob_type = /mob/living/simple_animal/mouse - death = FALSE - roundstart = FALSE - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "sleeper" - flavour_text = "Squeak!" - -/obj/effect/mob_spawn/cow - name = "sleeper" - mob_name = "space cow" - mob_type = /mob/living/simple_animal/cow - death = FALSE - roundstart = FALSE - mob_gender = FEMALE - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "sleeper" - flavour_text = "Moo!" - - -///////////Civilians////////////////////// - -/obj/effect/mob_spawn/human/corpse/assistant - name = "Assistant" - mob_name = "Assistant" - id_job = "Assistant" - outfit = /datum/outfit/job/assistant - -/obj/effect/mob_spawn/human/corpse/assistant/beesease_infection - disease = /datum/disease/beesease - -/obj/effect/mob_spawn/human/corpse/assistant/brainrot_infection - disease = /datum/disease/brainrot - -/obj/effect/mob_spawn/human/corpse/assistant/spanishflu_infection - disease = /datum/disease/fluspanish - -/obj/effect/mob_spawn/human/cook - name = "Cook" - mob_name = "Chef" - id_job = "Chef" - outfit = /datum/outfit/job/chef - -/obj/effect/mob_spawn/human/doctor - name = "Doctor" - mob_name = "Medical Doctor" - id_job = "Medical Doctor" - outfit = /datum/outfit/job/doctor - -/obj/effect/mob_spawn/human/doctor/alive - death = FALSE - roundstart = FALSE - random = TRUE - name = "sleeper" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "sleeper" - flavour_text = "You are a space doctor!" - assignedrole = "Space Doctor" - -/obj/effect/mob_spawn/human/doctor/alive/equip(mob/living/carbon/human/H) - ..() - // Remove radio and PDA so they wouldn't annoy station crew. - var/list/del_types = list(/obj/item/pda, /obj/item/radio/headset) - for(var/del_type in del_types) - var/obj/item/I = locate(del_type) in H - qdel(I) - -/obj/effect/mob_spawn/human/engineer - name = "Engineer" - mob_name = "Engineer" - id_job = "Engineer" - outfit = /datum/outfit/job/engineer - -/obj/effect/mob_spawn/human/engineer/hardsuit - outfit = /datum/outfit/job/engineer/suit - -/datum/outfit/job/engineer/suit - name = "Station Engineer" - - uniform = /obj/item/clothing/under/rank/engineer - belt = /obj/item/storage/belt/utility/full - suit = /obj/item/clothing/suit/space/hardsuit/engine - shoes = /obj/item/clothing/shoes/workboots - mask = /obj/item/clothing/mask/breath - id = /obj/item/card/id/engineering - l_pocket = /obj/item/t_scanner - - backpack = /obj/item/storage/backpack/industrial - - -/obj/effect/mob_spawn/human/clown - name = "Clown" - mob_name = "Clown" - id_job = "Clown" - outfit = /datum/outfit/job/clown - -/obj/effect/mob_spawn/human/clown/Initialize() - mob_name = pick(GLOB.clown_names) - return ..() - -/obj/effect/mob_spawn/human/corpse/clownmili - name = "Clown Soldier" - outfit = /datum/outfit/clownsoldier - -/obj/effect/mob_spawn/human/corpse/clownmili/Initialize() - mob_name = "Officer [pick(GLOB.clown_names)]" - return ..() - -/obj/effect/mob_spawn/human/corpse/clownoff - name = "Clown Officer" - outfit = /datum/outfit/clownofficer - -/obj/effect/mob_spawn/human/corpse/clownoff/Initialize() - mob_name = "Honk Specialist [pick(GLOB.clown_names)]" - return ..() - - -/datum/outfit/clownsoldier - name = "Clown Soldier" - uniform = /obj/item/clothing/under/soldieruniform - suit = /obj/item/clothing/suit/soldiercoat - shoes = /obj/item/clothing/shoes/clown_shoes - l_ear = /obj/item/radio/headset - mask = /obj/item/clothing/mask/gas/clown_hat - l_pocket = /obj/item/bikehorn - back = /obj/item/storage/backpack/clown - head = /obj/item/clothing/head/stalhelm - -/datum/outfit/clownofficer - name = "Clown Officer" - uniform = /obj/item/clothing/under/officeruniform - suit = /obj/item/clothing/suit/officercoat - shoes = /obj/item/clothing/shoes/clown_shoes - l_ear = /obj/item/radio/headset - mask = /obj/item/clothing/mask/gas/clown_hat - l_pocket = /obj/item/bikehorn - back = /obj/item/storage/backpack/clown - head = /obj/item/clothing/head/naziofficer - -/obj/effect/mob_spawn/human/mime - name = "Mime" - mob_name = "Mime" - id_job = "Mime" - outfit = /datum/outfit/job/mime - -/obj/effect/mob_spawn/human/mime/Initialize() - mob_name = pick(GLOB.mime_names) - return ..() - -/obj/effect/mob_spawn/human/scientist - name = "Scientist" - mob_name = "Scientist" - id_job = "Scientist" - outfit = /datum/outfit/job/scientist - -/obj/effect/mob_spawn/human/miner - name = "Shaft Miner" - mob_name = "Shaft Miner" - id_job = "Shaft Miner" - outfit = /datum/outfit/job/mining/suit - -/datum/outfit/job/mining/suit - name = "Shaft Miner" - suit = /obj/item/clothing/suit/space/hardsuit/mining - uniform = /obj/item/clothing/under/rank/miner - gloves = /obj/item/clothing/gloves/fingerless - shoes = /obj/item/clothing/shoes/workboots - l_ear = /obj/item/radio/headset/headset_cargo/mining - id = /obj/item/card/id/supply - l_pocket = /obj/item/reagent_containers/food/pill/patch/styptic - r_pocket = /obj/item/flashlight/seclite - -/obj/effect/mob_spawn/human/miner/explorer - outfit = /datum/outfit/job/mining/equipped - -/obj/effect/mob_spawn/human/bartender - name = "Space Bartender" - mob_name = "Bartender" - id_job = "Bartender" - id_access_list = list(access_bar) - outfit = /datum/outfit/spacebartender - -/obj/effect/mob_spawn/human/bartender/alive - death = FALSE - roundstart = FALSE - random = TRUE - allow_species_pick = TRUE - name = "bartender sleeper" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "sleeper" - flavour_text = "You are a space bartender! Time to mix drinks and change lives." - assignedrole = "Space Bartender" - -/obj/effect/mob_spawn/human/beach/alive/lifeguard - flavour_text = "You're a spunky lifeguard! It's up to you to make sure nobody drowns or gets eaten by sharks and stuff." - mob_gender = "female" - name = "lifeguard sleeper" - id_job = "Lifeguard" - uniform = /obj/item/clothing/under/shorts/red - -/datum/outfit/spacebartender - name = "Space Bartender" - uniform = /obj/item/clothing/under/rank/bartender - suit = /obj/item/clothing/suit/armor/vest - belt = /obj/item/storage/belt/bandolier/full - shoes = /obj/item/clothing/shoes/black - glasses = /obj/item/clothing/glasses/sunglasses/reagent - id = /obj/item/card/id - - -/obj/effect/mob_spawn/human/beach - outfit = /datum/outfit/beachbum - -/obj/effect/mob_spawn/human/beach/alive - death = FALSE - roundstart = FALSE - random = TRUE - allow_species_pick = TRUE - mob_name = "Beach Bum" - name = "beach bum sleeper" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "sleeper" - flavour_text = "You are a beach bum!" - assignedrole = "Beach Bum" - -/datum/outfit/beachbum - name = "Beach Bum" - glasses = /obj/item/clothing/glasses/sunglasses - uniform = /obj/item/clothing/under/shorts/red - -/////////////////Spooky Undead////////////////////// - -/obj/effect/mob_spawn/human/skeleton - name = "skeletal remains" - mob_name = "skeleton" - mob_species = /datum/species/skeleton - mob_gender = NEUTER - -/obj/effect/mob_spawn/human/skeleton/alive - death = FALSE - roundstart = FALSE - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - flavour_text = "By unknown powers, your skeletal remains have been reanimated! Walk this mortal plain and terrorize all living adventurers who dare cross your path." - assignedrole = "Skeleton" - -/////////////////Officers////////////////////// - -/obj/effect/mob_spawn/human/bridgeofficer - name = "Bridge Officer" - mob_name = "Bridge Officer" - id_job = "Bridge Officer" - id_access = "Captain" - outfit = /datum/outfit/nanotrasenbridgeofficercorpse - -/datum/outfit/nanotrasenbridgeofficercorpse - name = "Bridge Officer Corpse" - l_ear = /obj/item/radio/headset/heads/hop - uniform = /obj/item/clothing/under/rank/centcom_officer - suit = /obj/item/clothing/suit/armor/bulletproof - shoes = /obj/item/clothing/shoes/black - glasses = /obj/item/clothing/glasses/sunglasses - id = /obj/item/card/id - - -/obj/effect/mob_spawn/human/commander - name = "Commander" - mob_name = "Commander" - id_job = "Commander" - id_access = "Captain" - outfit = /datum/outfit/nanotrasencommandercorpse - -/datum/outfit/nanotrasencommandercorpse - name = "Commander Corpse" - - uniform = /obj/item/clothing/under/rank/centcom/officer - gloves = /obj/item/clothing/gloves/color/white - shoes = /obj/item/clothing/shoes/centcom - head = /obj/item/clothing/head/beret/centcom/officer - glasses = /obj/item/clothing/glasses/hud/security/sunglasses - id = /obj/item/card/id/centcom - - - -/obj/effect/mob_spawn/human/abductor - name = "abductor" - mob_name = "alien" - mob_species = /datum/species/abductor - outfit = /datum/outfit/abductorcorpse - -/datum/outfit/abductorcorpse - name = "Abductor Corpse" - uniform = /obj/item/clothing/under/color/grey - shoes = /obj/item/clothing/shoes/combat - -//For ghost bar. -/obj/effect/mob_spawn/human/alive/space_bar_patron - name = "Bar cryogenics" - mob_name = "Bar patron" - random = TRUE - permanent = TRUE - uses = -1 - outfit = /datum/outfit/spacebartender - assignedrole = "Space Bar Patron" - -/obj/effect/mob_spawn/human/alive/space_bar_patron/attack_hand(mob/user) - var/despawn = alert("Return to cryosleep? (Warning, Your mob will be deleted!)",,"Yes","No") - if(despawn == "No" || !loc || !Adjacent(user)) - return - user.visible_message("[user.name] climbs back into cryosleep...") - qdel(user) - -/datum/outfit/cryobartender - name = "Cryogenic Bartender" - uniform = /obj/item/clothing/under/rank/bartender - back = /obj/item/storage/backpack - shoes = /obj/item/clothing/shoes/black - suit = /obj/item/clothing/suit/armor/vest - glasses = /obj/item/clothing/glasses/sunglasses/reagent +//These are meant for spawning on maps, namely Away Missions. + +//If someone can do this in a neater way, be my guest-Kor + +//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). + +/obj/effect/mob_spawn + name = "Unknown" + density = TRUE + anchored = TRUE + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + var/mob_type = null + var/mob_name = "unidentified entity" + var/mob_gender = null + var/death = TRUE //Kill the mob + var/roundstart = TRUE //fires on initialize + var/instant = FALSE //fires on New + var/flavour_text = "The mapper forgot to set this!" + var/faction = null + var/permanent = FALSE //If true, the spawner will not disappear upon running out of uses. + var/random = FALSE //Don't set a name or gender, just go random + var/objectives = null + var/uses = 1 //how many times can we spawn from it. set to -1 for infinite. + var/brute_damage = 0 + var/oxy_damage = 0 + var/burn_damage = 0 + var/datum/disease/disease = null //Do they start with a pre-spawned disease? + var/mob_color //Change the mob's color + var/assignedrole + var/banType = ROLE_GHOST + var/ghost_usable = TRUE + var/offstation_role = TRUE // If set to true, the role of the user's mind will be set to offstation + +/obj/effect/mob_spawn/attack_ghost(mob/user) + var/mob/dead/observer/O = user + if(SSticker.current_state != GAME_STATE_PLAYING || !loc || !ghost_usable) + return + if(!uses) + to_chat(user, "This spawner is out of charges!") + return + if(jobban_isbanned(user, banType)) + to_chat(user, "You are jobanned!") + return + if(cannotPossess(user)) + to_chat(user, "Upon using the antagHUD you forfeited the ability to join the round.") + return + if(!O.can_reenter_corpse) + to_chat(user, "You have forfeited the right to respawn.") + return + var/ghost_role = alert("Become [mob_name]? (Warning, You can no longer be cloned!)",,"Yes","No") + if(ghost_role == "No") + return + if(!species_prompt()) + return + if(!loc || !uses || QDELETED(src) || QDELETED(user)) + to_chat(user, "The [name] is no longer usable!") + return + log_game("[user.ckey] became [mob_name]") + create(ckey = user.ckey) + +/obj/effect/mob_spawn/Initialize(mapload) + . = ..() + if(instant || roundstart) //at some point we should probably re-introduce the (ticker && ticker.current_state > GAME_STATE_SETTING_UP) portion of this check, but for now it was preventing the corpses from spawning at roundstart and resulting in ghost role spawners that made dead bodies. + create() + else if(ghost_usable) + GLOB.poi_list |= src + LAZYADD(GLOB.mob_spawners[name], src) + +/obj/effect/mob_spawn/Destroy() + GLOB.poi_list -= src + var/list/spawners = GLOB.mob_spawners[name] + LAZYREMOVE(spawners, src) + if(!LAZYLEN(spawners)) + GLOB.mob_spawners -= name + return ..() + +/obj/effect/mob_spawn/proc/species_prompt() + return TRUE + +/obj/effect/mob_spawn/proc/special(mob/M) + return + +/obj/effect/mob_spawn/proc/equip(mob/M) + return + +/obj/effect/mob_spawn/proc/create(ckey, flavour = TRUE, name) + var/mob/living/M = new mob_type(get_turf(src)) //living mobs only + var/mob/living/carbon/human/H = M + if(H && !H.dna) + H.Initialize(null) + if(!random) + M.real_name = mob_name ? mob_name : M.name + if(!mob_gender) + mob_gender = pick(MALE, FEMALE) + M.gender = mob_gender + if(faction) + M.faction = list(faction) + if(disease) + M.ForceContractDisease(new disease) + if(death) + M.death() //Kills the new mob + + M.adjustOxyLoss(oxy_damage) + M.adjustBruteLoss(brute_damage) + M.adjustFireLoss(burn_damage) + M.color = mob_color + equip(M, TRUE) + + if(ckey) + M.ckey = ckey + if(flavour) + to_chat(M, "[flavour_text]") + var/datum/mind/MM = M.mind + if(objectives) + for(var/objective in objectives) + MM.objectives += new/datum/objective(objective) + if(assignedrole) + M.mind.assigned_role = assignedrole + M.mind.offstation_role = offstation_role + special(M, name) + MM.name = M.real_name + if(uses > 0) + uses-- + if(!permanent && !uses) + qdel(src) + +// Base version - place these on maps/templates. +/obj/effect/mob_spawn/human + mob_type = /mob/living/carbon/human + //Human specific stuff. + var/mob_species = null //Set species + var/allow_species_pick = FALSE + var/list/pickable_species = list("Human", "Vulpkanin", "Tajaran", "Unathi", "Skrell", "Diona") + var/datum/outfit/outfit = /datum/outfit //If this is a path, it will be instanced in Initialize() + var/disable_pda = TRUE + var/disable_sensors = TRUE + //All of these only affect the ID that the outfit has placed in the ID slot + var/id_job = null //Such as "Clown" or "Chef." This just determines what the ID reads as, not their access + var/id_access = null //This is for access. See access.dm for which jobs give what access. Use "Captain" if you want it to be all access. + var/id_access_list = null //Allows you to manually add access to an ID card. + assignedrole = "Ghost Role" + + var/husk = null + //these vars are for lazy mappers to override parts of the outfit + //these cannot be null by default, or mappers cannot set them to null if they want nothing in that slot + var/uniform = -1 + var/r_hand = -1 + var/l_hand = -1 + var/suit = -1 + var/shoes = -1 + var/gloves = -1 + var/ears = -1 + var/glasses = -1 + var/mask = -1 + var/head = -1 + var/belt = -1 + var/r_pocket = -1 + var/l_pocket = -1 + var/back = -1 + var/id = -1 + var/neck = -1 + var/pda = -1 + var/backpack_contents = -1 + var/suit_store = -1 + var/hair_style + var/facial_hair_style + var/skin_tone + + var/list/del_types = list(/obj/item/pda, /obj/item/radio/headset) + +/obj/effect/mob_spawn/human/Initialize() + if(ispath(outfit)) + outfit = new outfit() + if(!outfit) + outfit = new /datum/outfit + if(!mob_name) + mob_name = id_job + return ..() + +/obj/effect/mob_spawn/human/species_prompt() + if(allow_species_pick) + var/selected_species = input("Select a species", "Species Selection") as null|anything in pickable_species + if(!selected_species) + return TRUE // You didn't pick, so just continue on with the spawning process as a human + var/datum/species/S = GLOB.all_species[selected_species] + mob_species = S.type + return TRUE + +/obj/effect/mob_spawn/human/equip(mob/living/carbon/human/H) + if(mob_species) + H.set_species(mob_species) + + if(husk) + H.ChangeToHusk() + else //Because for some reason I can't track down, things are getting turned into husks even if husk = false. It's in some damage proc somewhere. + H.mutations.Remove(HUSK) + H.underwear = "Nude" + H.undershirt = "Nude" + H.socks = "Nude" + var/obj/item/organ/external/head/D = H.get_organ("head") + if(istype(D)) + if(hair_style) + D.h_style = hair_style + else + D.h_style = random_hair_style(gender, D.dna.species.name) + D.hair_colour = rand_hex_color() + if(facial_hair_style) + D.f_style = facial_hair_style + else + D.f_style = random_facial_hair_style(gender, D.dna.species.name) + D.facial_colour = rand_hex_color() + if(skin_tone) + H.change_skin_tone(skin_tone) + else + H.change_skin_tone(random_skin_tone()) + H.change_skin_color(rand_hex_color()) + H.update_hair() + H.update_fhair() + H.update_body() + H.update_dna() + H.regenerate_icons() + if(outfit) + var/static/list/slots = list("uniform", "r_hand", "l_hand", "suit", "shoes", "gloves", "ears", "glasses", "mask", "head", "belt", "r_pocket", "l_pocket", "back", "id", "neck", "backpack_contents", "suit_store") + for(var/slot in slots) + var/T = vars[slot] + if(!isnum(T)) + outfit.vars[slot] = T + H.equipOutfit(outfit) + for(var/del_type in del_types) + var/obj/item/I = locate(del_type) in H + qdel(I) + + if(disable_pda) + // We don't want corpse PDAs to show up in the messenger list. + var/obj/item/pda/PDA = locate(/obj/item/pda) in H + if(PDA) + var/datum/data/pda/app/messenger/M = PDA.find_program(/datum/data/pda/app/messenger) + M.toff = 1 + if(disable_sensors) + // Using crew monitors to find corpses while creative makes finding certain ruins too easy. + var/obj/item/clothing/under/C = H.w_uniform + if(istype(C)) + C.sensor_mode = SUIT_SENSOR_OFF + + var/obj/item/card/id/W = H.wear_id + if(W) + if(id_access) + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(J.title == id_access) + W.access = J.get_access() + break + if(id_access_list) + if(!islist(W.access)) + W.access = list() + W.access |= id_access_list + if(id_job) + W.assignment = id_job + W.registered_name = H.real_name + W.update_label() + +//Instant version - use when spawning corpses during runtime +/obj/effect/mob_spawn/human/corpse + roundstart = FALSE + instant = TRUE + +/obj/effect/mob_spawn/human/corpse/damaged + brute_damage = 1000 + + +/obj/effect/mob_spawn/human/alive + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper" + death = FALSE + roundstart = FALSE //you could use these for alive fake humans on roundstart but this is more common scenario + + +//Non-human spawners + +/obj/effect/mob_spawn/mouse + name = "sleeper" + mob_name = "space mouse" + mob_type = /mob/living/simple_animal/mouse + death = FALSE + roundstart = FALSE + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper" + flavour_text = "Squeak!" + +/obj/effect/mob_spawn/cow + name = "sleeper" + mob_name = "space cow" + mob_type = /mob/living/simple_animal/cow + death = FALSE + roundstart = FALSE + mob_gender = FEMALE + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper" + flavour_text = "Moo!" + + +///////////Civilians////////////////////// + +/obj/effect/mob_spawn/human/corpse/assistant + name = "Assistant" + mob_name = "Assistant" + id_job = "Assistant" + outfit = /datum/outfit/job/assistant + +/obj/effect/mob_spawn/human/corpse/assistant/beesease_infection + disease = /datum/disease/beesease + +/obj/effect/mob_spawn/human/corpse/assistant/brainrot_infection + disease = /datum/disease/brainrot + +/obj/effect/mob_spawn/human/corpse/assistant/spanishflu_infection + disease = /datum/disease/fluspanish + +/obj/effect/mob_spawn/human/cook + name = "Cook" + mob_name = "Chef" + id_job = "Chef" + outfit = /datum/outfit/job/chef + +/obj/effect/mob_spawn/human/doctor + name = "Doctor" + mob_name = "Medical Doctor" + id_job = "Medical Doctor" + outfit = /datum/outfit/job/doctor + +/obj/effect/mob_spawn/human/doctor/alive + death = FALSE + roundstart = FALSE + random = TRUE + name = "sleeper" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper" + flavour_text = "You are a space doctor!" + assignedrole = "Space Doctor" + +/obj/effect/mob_spawn/human/doctor/alive/equip(mob/living/carbon/human/H) + ..() + // Remove radio and PDA so they wouldn't annoy station crew. + var/list/del_types = list(/obj/item/pda, /obj/item/radio/headset) + for(var/del_type in del_types) + var/obj/item/I = locate(del_type) in H + qdel(I) + +/obj/effect/mob_spawn/human/engineer + name = "Engineer" + mob_name = "Engineer" + id_job = "Engineer" + outfit = /datum/outfit/job/engineer + +/obj/effect/mob_spawn/human/engineer/hardsuit + outfit = /datum/outfit/job/engineer/suit + +/datum/outfit/job/engineer/suit + name = "Station Engineer" + + uniform = /obj/item/clothing/under/rank/engineer + belt = /obj/item/storage/belt/utility/full + suit = /obj/item/clothing/suit/space/hardsuit/engine + shoes = /obj/item/clothing/shoes/workboots + mask = /obj/item/clothing/mask/breath + id = /obj/item/card/id/engineering + l_pocket = /obj/item/t_scanner + + backpack = /obj/item/storage/backpack/industrial + + +/obj/effect/mob_spawn/human/clown + name = "Clown" + mob_name = "Clown" + id_job = "Clown" + outfit = /datum/outfit/job/clown + +/obj/effect/mob_spawn/human/clown/Initialize() + mob_name = pick(GLOB.clown_names) + return ..() + +/obj/effect/mob_spawn/human/corpse/clownmili + name = "Clown Soldier" + outfit = /datum/outfit/clownsoldier + +/obj/effect/mob_spawn/human/corpse/clownmili/Initialize() + mob_name = "Officer [pick(GLOB.clown_names)]" + return ..() + +/obj/effect/mob_spawn/human/corpse/clownoff + name = "Clown Officer" + outfit = /datum/outfit/clownofficer + +/obj/effect/mob_spawn/human/corpse/clownoff/Initialize() + mob_name = "Honk Specialist [pick(GLOB.clown_names)]" + return ..() + + +/datum/outfit/clownsoldier + name = "Clown Soldier" + uniform = /obj/item/clothing/under/soldieruniform + suit = /obj/item/clothing/suit/soldiercoat + shoes = /obj/item/clothing/shoes/clown_shoes + l_ear = /obj/item/radio/headset + mask = /obj/item/clothing/mask/gas/clown_hat + l_pocket = /obj/item/bikehorn + back = /obj/item/storage/backpack/clown + head = /obj/item/clothing/head/stalhelm + +/datum/outfit/clownofficer + name = "Clown Officer" + uniform = /obj/item/clothing/under/officeruniform + suit = /obj/item/clothing/suit/officercoat + shoes = /obj/item/clothing/shoes/clown_shoes + l_ear = /obj/item/radio/headset + mask = /obj/item/clothing/mask/gas/clown_hat + l_pocket = /obj/item/bikehorn + back = /obj/item/storage/backpack/clown + head = /obj/item/clothing/head/naziofficer + +/obj/effect/mob_spawn/human/mime + name = "Mime" + mob_name = "Mime" + id_job = "Mime" + outfit = /datum/outfit/job/mime + +/obj/effect/mob_spawn/human/mime/Initialize() + mob_name = pick(GLOB.mime_names) + return ..() + +/obj/effect/mob_spawn/human/scientist + name = "Scientist" + mob_name = "Scientist" + id_job = "Scientist" + outfit = /datum/outfit/job/scientist + +/obj/effect/mob_spawn/human/miner + name = "Shaft Miner" + mob_name = "Shaft Miner" + id_job = "Shaft Miner" + outfit = /datum/outfit/job/mining/suit + +/datum/outfit/job/mining/suit + name = "Shaft Miner" + suit = /obj/item/clothing/suit/space/hardsuit/mining + uniform = /obj/item/clothing/under/rank/miner + gloves = /obj/item/clothing/gloves/fingerless + shoes = /obj/item/clothing/shoes/workboots + l_ear = /obj/item/radio/headset/headset_cargo/mining + id = /obj/item/card/id/supply + l_pocket = /obj/item/reagent_containers/food/pill/patch/styptic + r_pocket = /obj/item/flashlight/seclite + +/obj/effect/mob_spawn/human/miner/explorer + outfit = /datum/outfit/job/mining/equipped + +/obj/effect/mob_spawn/human/bartender + name = "Space Bartender" + mob_name = "Bartender" + id_job = "Bartender" + id_access_list = list(ACCESS_BAR) + outfit = /datum/outfit/spacebartender + +/obj/effect/mob_spawn/human/bartender/alive + death = FALSE + roundstart = FALSE + random = TRUE + allow_species_pick = TRUE + name = "bartender sleeper" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper" + flavour_text = "You are a space bartender! Time to mix drinks and change lives." + assignedrole = "Space Bartender" + +/obj/effect/mob_spawn/human/beach/alive/lifeguard + flavour_text = "You're a spunky lifeguard! It's up to you to make sure nobody drowns or gets eaten by sharks and stuff." + mob_gender = "female" + name = "lifeguard sleeper" + id_job = "Lifeguard" + uniform = /obj/item/clothing/under/shorts/red + +/datum/outfit/spacebartender + name = "Space Bartender" + uniform = /obj/item/clothing/under/rank/bartender + suit = /obj/item/clothing/suit/armor/vest + belt = /obj/item/storage/belt/bandolier/full + shoes = /obj/item/clothing/shoes/black + glasses = /obj/item/clothing/glasses/sunglasses/reagent + id = /obj/item/card/id + + +/obj/effect/mob_spawn/human/beach + outfit = /datum/outfit/beachbum + +/obj/effect/mob_spawn/human/beach/alive + death = FALSE + roundstart = FALSE + random = TRUE + allow_species_pick = TRUE + mob_name = "Beach Bum" + name = "beach bum sleeper" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper" + flavour_text = "You are a beach bum!" + assignedrole = "Beach Bum" + +/datum/outfit/beachbum + name = "Beach Bum" + glasses = /obj/item/clothing/glasses/sunglasses + uniform = /obj/item/clothing/under/shorts/red + +/////////////////Spooky Undead////////////////////// + +/obj/effect/mob_spawn/human/skeleton + name = "skeletal remains" + mob_name = "skeleton" + mob_species = /datum/species/skeleton + mob_gender = NEUTER + +/obj/effect/mob_spawn/human/skeleton/alive + death = FALSE + roundstart = FALSE + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + flavour_text = "By unknown powers, your skeletal remains have been reanimated! Walk this mortal plain and terrorize all living adventurers who dare cross your path." + assignedrole = "Skeleton" + +/////////////////Officers////////////////////// + +/obj/effect/mob_spawn/human/bridgeofficer + name = "Bridge Officer" + mob_name = "Bridge Officer" + id_job = "Bridge Officer" + id_access = "Captain" + outfit = /datum/outfit/nanotrasenbridgeofficercorpse + +/datum/outfit/nanotrasenbridgeofficercorpse + name = "Bridge Officer Corpse" + l_ear = /obj/item/radio/headset/heads/hop + uniform = /obj/item/clothing/under/rank/centcom_officer + suit = /obj/item/clothing/suit/armor/bulletproof + shoes = /obj/item/clothing/shoes/black + glasses = /obj/item/clothing/glasses/sunglasses + id = /obj/item/card/id + + +/obj/effect/mob_spawn/human/commander + name = "Commander" + mob_name = "Commander" + id_job = "Commander" + id_access = "Captain" + outfit = /datum/outfit/nanotrasencommandercorpse + +/datum/outfit/nanotrasencommandercorpse + name = "Commander Corpse" + + uniform = /obj/item/clothing/under/rank/centcom/officer + gloves = /obj/item/clothing/gloves/color/white + shoes = /obj/item/clothing/shoes/centcom + head = /obj/item/clothing/head/beret/centcom/officer + glasses = /obj/item/clothing/glasses/hud/security/sunglasses + id = /obj/item/card/id/centcom + + + +/obj/effect/mob_spawn/human/abductor + name = "abductor" + mob_name = "alien" + mob_species = /datum/species/abductor + outfit = /datum/outfit/abductorcorpse + +/datum/outfit/abductorcorpse + name = "Abductor Corpse" + uniform = /obj/item/clothing/under/color/grey + shoes = /obj/item/clothing/shoes/combat + +//For ghost bar. +/obj/effect/mob_spawn/human/alive/space_bar_patron + name = "Bar cryogenics" + mob_name = "Bar patron" + random = TRUE + permanent = TRUE + uses = -1 + outfit = /datum/outfit/spacebartender + assignedrole = "Space Bar Patron" + +/obj/effect/mob_spawn/human/alive/space_bar_patron/attack_hand(mob/user) + var/despawn = alert("Return to cryosleep? (Warning, Your mob will be deleted!)",,"Yes","No") + if(despawn == "No" || !loc || !Adjacent(user)) + return + user.visible_message("[user.name] climbs back into cryosleep...") + qdel(user) + +/datum/outfit/cryobartender + name = "Cryogenic Bartender" + uniform = /obj/item/clothing/under/rank/bartender + back = /obj/item/storage/backpack + shoes = /obj/item/clothing/shoes/black + suit = /obj/item/clothing/suit/armor/vest + glasses = /obj/item/clothing/glasses/sunglasses/reagent diff --git a/code/modules/awaymissions/exile.dm b/code/modules/awaymissions/exile.dm index e808c5fe382..5f750c16144 100644 --- a/code/modules/awaymissions/exile.dm +++ b/code/modules/awaymissions/exile.dm @@ -1,44 +1,44 @@ -//Exile implants will allow you to use the station gate, but not return home. -//This will allow security to exile badguys/for badguys to exile their kill targets - -/obj/item/implant/exile - name = "exile implant" - desc = "Prevents you from returning from away missions" - origin_tech = "materials=2;biotech=3;magnets=2;bluespace=3" - activated = 0 - -/obj/item/implant/exile/get_data() - var/dat = {"Implant Specifications:
    - Name: Nanotrasen Employee Exile Implant
    - Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant
    "} - return dat - - -/obj/item/implanter/exile - name = "implanter (exile)" - -/obj/item/implanter/exile/New() - imp = new /obj/item/implant/exile( src ) - ..() - -/obj/item/implantcase/exile - name = "implant case - 'Exile'" - desc = "A glass case containing an exile implant." - -/obj/item/implantcase/exile/New() - imp = new /obj/item/implant/exile(src) - ..() - - -/obj/structure/closet/secure_closet/exile - name = "exile implants" - req_access = list(access_armory) - -/obj/structure/closet/secure_closet/exile/New() - ..() - new /obj/item/implanter/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) \ No newline at end of file +//Exile implants will allow you to use the station gate, but not return home. +//This will allow security to exile badguys/for badguys to exile their kill targets + +/obj/item/implant/exile + name = "exile implant" + desc = "Prevents you from returning from away missions" + origin_tech = "materials=2;biotech=3;magnets=2;bluespace=3" + activated = 0 + +/obj/item/implant/exile/get_data() + var/dat = {"Implant Specifications:
    + Name: Nanotrasen Employee Exile Implant
    + Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant
    "} + return dat + + +/obj/item/implanter/exile + name = "implanter (exile)" + +/obj/item/implanter/exile/New() + imp = new /obj/item/implant/exile( src ) + ..() + +/obj/item/implantcase/exile + name = "implant case - 'Exile'" + desc = "A glass case containing an exile implant." + +/obj/item/implantcase/exile/New() + imp = new /obj/item/implant/exile(src) + ..() + + +/obj/structure/closet/secure_closet/exile + name = "exile implants" + req_access = list(ACCESS_ARMORY) + +/obj/structure/closet/secure_closet/exile/New() + ..() + new /obj/item/implanter/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 8bacd9a839c..d7e298e8c96 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -1,295 +1,295 @@ -var/obj/machinery/gateway/centerstation/the_gateway = null -/obj/machinery/gateway - name = "gateway" - desc = "A mysterious gateway built by unknown hands, it allows for faster than light travel to far-flung locations." - icon = 'icons/obj/machines/gateway.dmi' - icon_state = "off" - density = 1 - anchored = 1 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/active = 0 - -/obj/machinery/gateway/Initialize() - ..() - update_icon() - update_density_from_dir() - -/obj/machinery/gateway/proc/update_density_from_dir() - if(dir == 2) - density = 0 - - -/obj/machinery/gateway/update_icon() - if(active) - icon_state = "on" - return - icon_state = "off" - - - -//this is da important part wot makes things go -/obj/machinery/gateway/centerstation - density = 1 - icon_state = "offcenter" - use_power = IDLE_POWER_USE - - //warping vars - var/list/linked = list() - var/ready = 0 //have we got all the parts for a gateway? - var/wait = 0 //this just grabs world.time at world start - var/obj/machinery/gateway/centeraway/awaygate = null - -/obj/machinery/gateway/centerstation/New() - ..() - if(!the_gateway) - the_gateway = src - -/obj/machinery/gateway/centerstation/Initialize() - ..() - update_icon() - wait = world.time + config.gateway_delay //+ thirty minutes default - awaygate = locate(/obj/machinery/gateway/centeraway) in world - -/obj/machinery/gateway/centerstation/update_density_from_dir() - return - -/obj/machinery/gateway/centerstation/Destroy() - if(the_gateway == src) - the_gateway = null - return ..() - -/obj/machinery/gateway/centerstation/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - - - -/obj/machinery/gateway/centerstation/process() - if(stat & (NOPOWER)) - if(active) toggleoff() - return - - if(active) - use_power(5000) - - -/obj/machinery/gateway/centerstation/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) - if(!ready) - return - if(linked.len != 8) - return - if(!powered()) - return - if(!awaygate) - awaygate = locate(/obj/machinery/gateway/centeraway) in world - if(!awaygate) - to_chat(user, "Error: No destination found.") - return - if(world.time < wait) - to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centerstation/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centerstation/attack_hand(mob/user as mob) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -//okay, here's the good teleporting stuff -/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) - if(!ready) - return - if(!active) - return - if(!awaygate) - return - - if(awaygate.calibrated) - M.forceMove(get_step(awaygate.loc, SOUTH)) - M.dir = SOUTH - return - else - var/obj/effect/landmark/dest = pick(awaydestinations) - if(dest) - M.forceMove(dest.loc) - M.dir = SOUTH - use_power(5000) - return - - -/obj/machinery/gateway/centerstation/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W,/obj/item/multitool)) - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - return ..() - -/////////////////////////////////////Away//////////////////////// - - -/obj/machinery/gateway/centeraway - density = 1 - icon_state = "offcenter" - use_power = NO_POWER_USE - var/calibrated = 1 - var/list/linked = list() //a list of the connected gateway chunks - var/ready = 0 - var/obj/machinery/gateway/centeraway/stationgate = null - - -/obj/machinery/gateway/centeraway/Initialize() - ..() - update_icon() - stationgate = locate(/obj/machinery/gateway/centerstation) in world - - -/obj/machinery/gateway/centeraway/update_density_from_dir() - return - -/obj/machinery/gateway/centeraway/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - - -/obj/machinery/gateway/centeraway/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) - if(!ready) - return - if(linked.len != 8) - return - if(!stationgate) - stationgate = locate(/obj/machinery/gateway/centerstation) in world - if(!stationgate) - to_chat(user, "Error: No destination found.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centeraway/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centeraway/attack_hand(mob/user as mob) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -/obj/machinery/gateway/centeraway/Bumped(atom/movable/AM) - if(!ready) - return - if(!active) - return - if(!stationgate || QDELETED(stationgate)) - return - if(isliving(AM)) - if(exilecheck(AM)) - return - else - for(var/mob/living/L in AM.contents) - if(exilecheck(L)) - atom_say("Rejecting [AM]: Exile implant detected in contained lifeform.") - return - if(AM.has_buckled_mobs()) - for(var/mob/living/L in AM.buckled_mobs) - if(exilecheck(L)) - atom_say("Rejecting [AM]: Exile implant detected in close proximity lifeform.") - return - AM.forceMove(get_step(stationgate.loc, SOUTH)) - AM.setDir(SOUTH) - if(ismob(AM)) - var/mob/M = AM - if(M.client) - M.client.move_delay = max(world.time + 5, M.client.move_delay) - -/obj/machinery/gateway/centeraway/proc/exilecheck(var/mob/living/carbon/M) - for(var/obj/item/implant/exile/E in M)//Checking that there is an exile implant in the contents - if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket - to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") - return 1 - return 0 - -/obj/machinery/gateway/centeraway/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W,/obj/item/multitool)) - if(calibrated) - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - else - to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") - calibrated = 1 - return - return ..() \ No newline at end of file +GLOBAL_DATUM_INIT(the_gateway, /obj/machinery/gateway/centerstation, null) +/obj/machinery/gateway + name = "gateway" + desc = "A mysterious gateway built by unknown hands, it allows for faster than light travel to far-flung locations." + icon = 'icons/obj/machines/gateway.dmi' + icon_state = "off" + density = 1 + anchored = 1 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/active = 0 + +/obj/machinery/gateway/Initialize() + ..() + update_icon() + update_density_from_dir() + +/obj/machinery/gateway/proc/update_density_from_dir() + if(dir == 2) + density = 0 + + +/obj/machinery/gateway/update_icon() + if(active) + icon_state = "on" + return + icon_state = "off" + + + +//this is da important part wot makes things go +/obj/machinery/gateway/centerstation + density = 1 + icon_state = "offcenter" + use_power = IDLE_POWER_USE + + //warping vars + var/list/linked = list() + var/ready = 0 //have we got all the parts for a gateway? + var/wait = 0 //this just grabs world.time at world start + var/obj/machinery/gateway/centeraway/awaygate = null + +/obj/machinery/gateway/centerstation/New() + ..() + if(!GLOB.the_gateway) + GLOB.the_gateway = src + +/obj/machinery/gateway/centerstation/Initialize() + ..() + update_icon() + wait = world.time + config.gateway_delay //+ thirty minutes default + awaygate = locate(/obj/machinery/gateway/centeraway) in world + +/obj/machinery/gateway/centerstation/update_density_from_dir() + return + +/obj/machinery/gateway/centerstation/Destroy() + if(GLOB.the_gateway == src) + GLOB.the_gateway = null + return ..() + +/obj/machinery/gateway/centerstation/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + + + +/obj/machinery/gateway/centerstation/process() + if(stat & (NOPOWER)) + if(active) toggleoff() + return + + if(active) + use_power(5000) + + +/obj/machinery/gateway/centerstation/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in GLOB.alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) + if(!ready) + return + if(linked.len != 8) + return + if(!powered()) + return + if(!awaygate) + awaygate = locate(/obj/machinery/gateway/centeraway) in world + if(!awaygate) + to_chat(user, "Error: No destination found.") + return + if(world.time < wait) + to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centerstation/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centerstation/attack_hand(mob/user as mob) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +//okay, here's the good teleporting stuff +/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) + if(!ready) + return + if(!active) + return + if(!awaygate) + return + + if(awaygate.calibrated) + M.forceMove(get_step(awaygate.loc, SOUTH)) + M.dir = SOUTH + return + else + var/obj/effect/landmark/dest = pick(GLOB.awaydestinations) + if(dest) + M.forceMove(dest.loc) + M.dir = SOUTH + use_power(5000) + return + + +/obj/machinery/gateway/centerstation/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W,/obj/item/multitool)) + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") + return + return ..() + +/////////////////////////////////////Away//////////////////////// + + +/obj/machinery/gateway/centeraway + density = 1 + icon_state = "offcenter" + use_power = NO_POWER_USE + var/calibrated = 1 + var/list/linked = list() //a list of the connected gateway chunks + var/ready = 0 + var/obj/machinery/gateway/centeraway/stationgate = null + + +/obj/machinery/gateway/centeraway/Initialize() + ..() + update_icon() + stationgate = locate(/obj/machinery/gateway/centerstation) in world + + +/obj/machinery/gateway/centeraway/update_density_from_dir() + return + +/obj/machinery/gateway/centeraway/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + + +/obj/machinery/gateway/centeraway/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in GLOB.alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) + if(!ready) + return + if(linked.len != 8) + return + if(!stationgate) + stationgate = locate(/obj/machinery/gateway/centerstation) in world + if(!stationgate) + to_chat(user, "Error: No destination found.") + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centeraway/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centeraway/attack_hand(mob/user as mob) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +/obj/machinery/gateway/centeraway/Bumped(atom/movable/AM) + if(!ready) + return + if(!active) + return + if(!stationgate || QDELETED(stationgate)) + return + if(isliving(AM)) + if(exilecheck(AM)) + return + else + for(var/mob/living/L in AM.contents) + if(exilecheck(L)) + atom_say("Rejecting [AM]: Exile implant detected in contained lifeform.") + return + if(AM.has_buckled_mobs()) + for(var/mob/living/L in AM.buckled_mobs) + if(exilecheck(L)) + atom_say("Rejecting [AM]: Exile implant detected in close proximity lifeform.") + return + AM.forceMove(get_step(stationgate.loc, SOUTH)) + AM.setDir(SOUTH) + if(ismob(AM)) + var/mob/M = AM + if(M.client) + M.client.move_delay = max(world.time + 5, M.client.move_delay) + +/obj/machinery/gateway/centeraway/proc/exilecheck(var/mob/living/carbon/M) + for(var/obj/item/implant/exile/E in M)//Checking that there is an exile implant in the contents + if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket + to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") + return 1 + return 0 + +/obj/machinery/gateway/centeraway/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W,/obj/item/multitool)) + if(calibrated) + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") + return + else + to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") + calibrated = 1 + return + return ..() diff --git a/code/modules/awaymissions/loot.dm b/code/modules/awaymissions/loot.dm index 6497d62648a..266eb53c815 100644 --- a/code/modules/awaymissions/loot.dm +++ b/code/modules/awaymissions/loot.dm @@ -1,25 +1,25 @@ -/obj/effect/spawner/away/lootdrop - icon = 'icons/mob/screen_gen.dmi' - icon_state = "x2" - var/lootcount = 1 //how many items will be spawned - var/lootdoubles = 0 //if the same item can be spawned twice - var/loot = "" //a list of possible items to spawn- a string of paths - -/obj/effect/spawner/away/lootdrop/Initialize() - ..() - var/list/things = params2list(loot) - - if(things && things.len) - for(var/i = lootcount, i > 0, i--) - if(!things.len) - return - - var/loot_spawn = pick(things) - var/loot_path = text2path(loot_spawn) - - if(!loot_path || !lootdoubles) - things.Remove(loot_spawn) - continue - - new loot_path(get_turf(src)) - qdel(src) \ No newline at end of file +/obj/effect/spawner/away/lootdrop + icon = 'icons/mob/screen_gen.dmi' + icon_state = "x2" + var/lootcount = 1 //how many items will be spawned + var/lootdoubles = 0 //if the same item can be spawned twice + var/loot = "" //a list of possible items to spawn- a string of paths + +/obj/effect/spawner/away/lootdrop/Initialize() + ..() + var/list/things = params2list(loot) + + if(things && things.len) + for(var/i = lootcount, i > 0, i--) + if(!things.len) + return + + var/loot_spawn = pick(things) + var/loot_path = text2path(loot_spawn) + + if(!loot_path || !lootdoubles) + things.Remove(loot_spawn) + continue + + new loot_path(get_turf(src)) + qdel(src) diff --git a/code/modules/awaymissions/map_rng.dm b/code/modules/awaymissions/map_rng.dm index 77954eafb9f..0c1d776fa7b 100644 --- a/code/modules/awaymissions/map_rng.dm +++ b/code/modules/awaymissions/map_rng.dm @@ -17,7 +17,7 @@ if(tname) template_name = tname if(template_name) - template = map_templates[template_name] + template = GLOB.map_templates[template_name] /obj/effect/landmark/map_loader/Initialize() . = ..() @@ -47,5 +47,5 @@ ..() if(template_list) template_name = safepick(splittext(template_list, ";")) - template = map_templates[template_name] + template = GLOB.map_templates[template_name] load(template) diff --git a/code/modules/awaymissions/maploader/dmm_suite.dm b/code/modules/awaymissions/maploader/dmm_suite.dm index 21c2dac0dc8..1eee3d8fc34 100644 --- a/code/modules/awaymissions/maploader/dmm_suite.dm +++ b/code/modules/awaymissions/maploader/dmm_suite.dm @@ -1,5 +1,4 @@ -var/global/dmm_suite/maploader = new - +GLOBAL_DATUM_INIT(maploader, /dmm_suite, new()) dmm_suite{ /* @@ -70,4 +69,4 @@ dmm_suite{ // map_name: A valid name for the map to be saved, such as "castle" (Required). // flags: Any, or a combination, of several bit flags (Optional, see documentation). } - } \ No newline at end of file + } diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index 4d5d464e136..9a7cf6b6405 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -4,8 +4,8 @@ //As of 3.6.2016 //global datum that will preload variables on atoms instanciation -var/global/use_preloader = FALSE -var/global/dmm_suite/preloader/_preloader = new +GLOBAL_VAR_INIT(use_preloader, FALSE) +GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) /dmm_suite // These regexes are global - meaning that starting the maploader again mid-load will @@ -96,7 +96,7 @@ var/global/dmm_suite/preloader/_preloader = new if(cropMap) continue else - space_manager.increase_max_zlevel_to(zcrd) //create a new z_level if needed + GLOB.space_manager.increase_max_zlevel_to(zcrd) //create a new z_level if needed bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrdStart) bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) @@ -156,10 +156,10 @@ var/global/dmm_suite/preloader/_preloader = new CHECK_TICK catch(var/exception/e) - _preloader.reset() + GLOB._preloader.reset() throw e - _preloader.reset() + GLOB._preloader.reset() log_debug("Loaded map in [stop_watch(watch)]s.") qdel(LM) if(bounds[MAP_MINX] == 1.#INF) // Shouldn't need to check every item @@ -271,14 +271,14 @@ var/global/dmm_suite/preloader/_preloader = new throw EXCEPTION("Oh no, I thought this was an area!") var/atom/instance - _preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation + GLOB._preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation instance = LM.area_path_to_real_area(members[index]) if(crds) instance.contents.Add(crds) - if(use_preloader && instance) - _preloader.load(instance) + if(GLOB.use_preloader && instance) + GLOB._preloader.load(instance) //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect @@ -316,7 +316,7 @@ var/global/dmm_suite/preloader/_preloader = new //Instance an atom at (x,y,z) and gives it the variables in attributes /dmm_suite/proc/instance_atom(path,list/attributes, x, y, z) var/atom/instance - _preloader.setup(attributes, path) + GLOB._preloader.setup(attributes, path) var/turf/T = locate(x,y,z) if(T) @@ -328,8 +328,8 @@ var/global/dmm_suite/preloader/_preloader = new else instance = new path (T)//first preloader pass - if(use_preloader && instance)//second preloader pass, for those atoms that don't ..() in New() - _preloader.load(instance) + if(GLOB.use_preloader && instance)//second preloader pass, for those atoms that don't ..() in New() + GLOB._preloader.load(instance) return instance @@ -439,7 +439,7 @@ var/global/dmm_suite/preloader/_preloader = new json_ready = 0 if("map_json_data" in the_attributes) json_ready = 1 - use_preloader = TRUE + GLOB.use_preloader = TRUE attributes = the_attributes target_path = path @@ -458,11 +458,11 @@ var/global/dmm_suite/preloader/_preloader = new if(islist(value)) value = deepCopyList(value) what.vars[attribute] = value - use_preloader = FALSE + GLOB.use_preloader = FALSE // If the map loader fails, make this safe /dmm_suite/preloader/proc/reset() - use_preloader = FALSE + GLOB.use_preloader = FALSE attributes = list() target_path = null diff --git a/code/modules/awaymissions/mission_code/UO71-terrorspiders.dm b/code/modules/awaymissions/mission_code/UO71-terrorspiders.dm index 6c822c80c03..b41ec901573 100644 --- a/code/modules/awaymissions/mission_code/UO71-terrorspiders.dm +++ b/code/modules/awaymissions/mission_code/UO71-terrorspiders.dm @@ -224,7 +224,7 @@ name = "ID Upgrade Machine" icon_state = "guest" icon_screen = "pass" - var/list/access_to_give = list(access_away01) + var/list/access_to_give = list(ACCESS_AWAY01) var/beenused = 0 var/door_to_open = "UO71_Start" diff --git a/code/modules/awaymissions/mission_code/academy.dm b/code/modules/awaymissions/mission_code/academy.dm index f8d66058199..7cddddd8b08 100644 --- a/code/modules/awaymissions/mission_code/academy.dm +++ b/code/modules/awaymissions/mission_code/academy.dm @@ -108,7 +108,7 @@ if(3) //Swarm of creatures T.visible_message("A swarm of creatures surround [user]!") - for(var/direction in alldirs) + for(var/direction in GLOB.alldirs) new /mob/living/simple_animal/hostile/netherworld(get_step(get_turf(user),direction)) if(4) //Destroy Equipment @@ -131,7 +131,7 @@ T.visible_message("Unseen forces throw [user]!") user.Stun(6) user.adjustBruteLoss(50) - var/throw_dir = cardinal + var/throw_dir = GLOB.cardinal var/atom/throw_target = get_edge_target_turf(user, throw_dir) user.throw_at(throw_target, 200, 4) if(8) @@ -160,7 +160,7 @@ //Mad Dosh T.visible_message("Mad dosh shoots out of [src]!") var/turf/Start = get_turf(src) - for(var/direction in alldirs) + for(var/direction in GLOB.alldirs) var/turf/dirturf = get_step(Start,direction) if(rand(0,1)) new /obj/item/stack/spacecash/c1000(dirturf) @@ -255,7 +255,7 @@ if(!target_mob) return var/turf/Start = get_turf(user) - for(var/direction in alldirs) + for(var/direction in GLOB.alldirs) var/turf/T = get_step(Start,direction) if(!T.density) target_mob.Move(T) diff --git a/code/modules/awaymissions/mission_code/beach.dm b/code/modules/awaymissions/mission_code/beach.dm index 2da10ecbd98..9baaae33aae 100644 --- a/code/modules/awaymissions/mission_code/beach.dm +++ b/code/modules/awaymissions/mission_code/beach.dm @@ -25,4 +25,4 @@ W.dir = dir spawn(1) W.loc = get_step(W, dir) - water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE) \ No newline at end of file + water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE) diff --git a/code/modules/awaymissions/mission_code/blackmarketpackers.dm b/code/modules/awaymissions/mission_code/blackmarketpackers.dm index 73e288b56ed..fa4e6081594 100644 --- a/code/modules/awaymissions/mission_code/blackmarketpackers.dm +++ b/code/modules/awaymissions/mission_code/blackmarketpackers.dm @@ -23,4 +23,4 @@ /area/awaymission/BMPship/Gate name = "\improper Gateway Block" icon_state = "away4" - requires_power = TRUE \ No newline at end of file + requires_power = TRUE diff --git a/code/modules/awaymissions/mission_code/centcomAway.dm b/code/modules/awaymissions/mission_code/centcomAway.dm index 1dc26f932e0..bd9890aa43a 100644 --- a/code/modules/awaymissions/mission_code/centcomAway.dm +++ b/code/modules/awaymissions/mission_code/centcomAway.dm @@ -60,4 +60,4 @@ teams never did figure out what happened that last time... and I can't wrap my head \ around it myself. Why would a shuttle full of evacuees all snap and beat each other \ to death the moment they reached safety?
    \ - - D. Cereza" \ No newline at end of file + - D. Cereza" diff --git a/code/modules/awaymissions/mission_code/evil_santa.dm b/code/modules/awaymissions/mission_code/evil_santa.dm index 1597491b800..19ec3ad2dc5 100644 --- a/code/modules/awaymissions/mission_code/evil_santa.dm +++ b/code/modules/awaymissions/mission_code/evil_santa.dm @@ -20,4 +20,4 @@ Couldn't make it back to my shack. That gun would have helped, if only I brought it...
    \ Cave-in has me trapped in here, I just hope the distress signal reaches help in time...
    \
    \ - He knows. He knows. He knows. He knows. He knows. He knows. He knows. He knows. He knows." \ No newline at end of file + He knows. He knows. He knows. He knows. He knows. He knows. He knows. He knows. He knows." diff --git a/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm b/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm index 8dc3692179e..bb1aae5f8a8 100644 --- a/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm +++ b/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm @@ -107,4 +107,4 @@ /obj/structure/showcase/machinery/oldpod/used name = "opened cryogenic pod" - desc = "A cryogenic pod that has recently discharged its occupant. The pod appears non-functional." \ No newline at end of file + desc = "A cryogenic pod that has recently discharged its occupant. The pod appears non-functional." diff --git a/code/modules/awaymissions/mission_code/ruins/gps.dm b/code/modules/awaymissions/mission_code/ruins/gps.dm new file mode 100644 index 00000000000..fa26eea5d0d --- /dev/null +++ b/code/modules/awaymissions/mission_code/ruins/gps.dm @@ -0,0 +1,13 @@ +//local gps units to let the ruins be found easier. + +/obj/item/gps/ruin + name = "navigation console" + desc = "A console for navigation in local space, gives off a weak signal that can be picked up if sufficiently close." + icon = 'icons/obj/terminals.dmi' + icon_state = "gps_console" + anchored = TRUE + local = TRUE + gpstag = "Unknown Signal" + +/obj/item/gps/ruin/attack_hand(mob/user) + attack_self(user) diff --git a/code/modules/awaymissions/mission_code/ruins/oldstation.dm b/code/modules/awaymissions/mission_code/ruins/oldstation.dm index 83cdfd8ac5c..e939cf06f32 100644 --- a/code/modules/awaymissions/mission_code/ruins/oldstation.dm +++ b/code/modules/awaymissions/mission_code/ruins/oldstation.dm @@ -18,7 +18,7 @@ desc = "A perfectly retrograde identification card. Looks like it could use some flavor." icon = 'icons/obj/card.dmi' icon_state = "retro" - access = list(access_away01) + access = list(ACCESS_AWAY01) /obj/item/card/id/away/old/sec name = "Security Officer ID" @@ -44,7 +44,7 @@ name = "APC Access ID" desc = "A special ID card that allows access to APC terminals." icon_state = "centcom_old" - access = list(access_engine_equip) + access = list(ACCESS_ENGINE_EQUIP) /obj/item/storage/backpack/old max_combined_w_class = 12 @@ -364,4 +364,4 @@ /area/ruin/space/ancientstation/hivebot name = "Hivebot Mothership" - icon_state = "teleporter" \ No newline at end of file + icon_state = "teleporter" diff --git a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm index 72b45e45e6e..c5b56805213 100644 --- a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm +++ b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm @@ -14,4 +14,4 @@ /obj/item/spellbook/oneuse/summonitem = 20, /obj/item/spellbook/oneuse/forcewall = 10, /obj/item/soulstone = 15 //spooky wizard stuff - ) \ No newline at end of file + ) diff --git a/code/modules/awaymissions/mission_code/spacebattle.dm b/code/modules/awaymissions/mission_code/spacebattle.dm index 146597fe457..c68a61a6748 100644 --- a/code/modules/awaymissions/mission_code/spacebattle.dm +++ b/code/modules/awaymissions/mission_code/spacebattle.dm @@ -32,4 +32,4 @@ name = "\improper Syndicate Fighter" /area/awaymission/spacebattle/secret - name = "\improper Hidden Chamber" \ No newline at end of file + name = "\improper Hidden Chamber" diff --git a/code/modules/awaymissions/mission_code/spacehotel.dm b/code/modules/awaymissions/mission_code/spacehotel.dm index e9c74d73ea9..45ebf9700ca 100644 --- a/code/modules/awaymissions/mission_code/spacehotel.dm +++ b/code/modules/awaymissions/mission_code/spacehotel.dm @@ -276,4 +276,4 @@ spawn(300) if(D.occupant == deadbeat) // they still haven't checked out... - checkout(roomid) \ No newline at end of file + checkout(roomid) diff --git a/code/modules/awaymissions/mission_code/stationCollision.dm b/code/modules/awaymissions/mission_code/stationCollision.dm index 8d6a7fabe05..1beba373724 100644 --- a/code/modules/awaymissions/mission_code/stationCollision.dm +++ b/code/modules/awaymissions/mission_code/stationCollision.dm @@ -94,11 +94,11 @@ obj/item/gun/energy/laser/retro/sc_retro */ //These vars hold the code itself, they'll be generated at round-start -var/sc_safecode1 = "[rand(0,9)]" -var/sc_safecode2 = "[rand(0,9)]" -var/sc_safecode3 = "[rand(0,9)]" -var/sc_safecode4 = "[rand(0,9)]" -var/sc_safecode5 = "[rand(0,9)]" +GLOBAL_VAR_INIT(sc_safecode1, "[rand(0,9)]") // Do these even need to be strings? Probably for the best +GLOBAL_VAR_INIT(sc_safecode2, "[rand(0,9)]") +GLOBAL_VAR_INIT(sc_safecode3, "[rand(0,9)]") +GLOBAL_VAR_INIT(sc_safecode4, "[rand(0,9)]") +GLOBAL_VAR_INIT(sc_safecode5, "[rand(0,9)]") //Pieces of paper actually containing the hints /obj/item/paper/sc_safehint_paper_prison @@ -106,13 +106,13 @@ var/sc_safecode5 = "[rand(0,9)]" /obj/item/paper/sc_safehint_paper_prison/New() ..() - info = "The ink is smudged, you can only make out a couple numbers: '[sc_safecode1]**[sc_safecode4]*'" + info = "The ink is smudged, you can only make out a couple numbers: '[GLOB.sc_safecode1]**[GLOB.sc_safecode4]*'" /obj/item/paper/sc_safehint_paper_hydro name = "shredded paper" /obj/item/paper/sc_safehint_paper_hydro/New() ..() - info = "Although the paper is shredded, you can clearly see the number: '[sc_safecode2]'" + info = "Although the paper is shredded, you can clearly see the number: '[GLOB.sc_safecode2]'" /obj/item/paper/sc_safehint_paper_caf name = "blood-soaked paper" @@ -124,7 +124,7 @@ var/sc_safecode5 = "[rand(0,9)]" /obj/item/paper/sc_safehint_paper_bible/New() ..() info = {"It would appear that the pen hidden with the paper had leaked ink over the paper. - However you can make out the last three digits:'[sc_safecode3][sc_safecode4][sc_safecode5]' + However you can make out the last three digits:
    '[GLOB.sc_safecode3][GLOB.sc_safecode4][GLOB.sc_safecode5]' "} /obj/item/paper/sc_safehint_paper_shuttle @@ -148,7 +148,7 @@ var/sc_safecode5 = "[rand(0,9)]" /obj/item/storage/secure/safe/sc_ssafe/New() ..() - l_code = "[sc_safecode1][sc_safecode2][sc_safecode3][sc_safecode4][sc_safecode5]" + l_code = "[GLOB.sc_safecode1][GLOB.sc_safecode2][GLOB.sc_safecode3][GLOB.sc_safecode4][GLOB.sc_safecode5]" l_set = 1 new /obj/item/gun/energy/mindflayer(src) new /obj/item/soulstone(src) @@ -199,4 +199,4 @@ var/sc_safecode5 = "[rand(0,9)]" return /obj/singularity/narsie/sc_Narsie/ex_act() - return \ No newline at end of file + return diff --git a/code/modules/awaymissions/pamphlet.dm b/code/modules/awaymissions/pamphlet.dm index 27ef5097e58..ffbb188ada2 100644 --- a/code/modules/awaymissions/pamphlet.dm +++ b/code/modules/awaymissions/pamphlet.dm @@ -1,38 +1,38 @@ -/obj/item/paper/pamphlet - name = "pamphlet" - icon_state = "pamphlet" - info = "Welcome to the Nanotrasen Gateway project...
    \ - Congratulations! If you're reading this, you and your superiors have decided that you're \ - ready to commit to a life spent colonising the rolling hills of far away worlds. You \ - must be ready for a lifetime of adventure, a little bit of hard work, and an award \ - winning dental plan- but that's not all the Nanotrasen Gateway project has to offer.
    \ -
    Because we care about you, we feel it is only fair to make sure you know the risks \ - before you commit to joining the Nanotrasen Gateway project. All away destinations have \ - been fully scanned by a Nanotrasen expeditionary team, and are certified to be 100% safe. \ - We've even left a case of space beer along with the basic materials you'll need to expand \ - Nanotrasen's operational area and start your new life.

    \ - Gateway Operation Basics
    \ - All Nanotrasen approved Gateways operate on the same basic principals. They operate off \ - area equipment power as you would expect, but they also require a backup wire with at least \ - 128, 000 Watts of power running through it. Without this supply, it cannot safely function \ - and will reject all attempts at operation.

    \ - Once it is correctly setup, and once it has enough power to operate, the Gateway will begin \ - searching for an output location. The amount of time this takes is variable, but the Gateway \ - interface will give you an estimate accurate to the minute. Power loss will not interrupt the \ - searching process. Influenza will not interrupt the searching process. Temporal anomalies \ - may cause the estimate to be inaccurate, but will not interrupt the searching process.

    \ - Life On The Other Side
    \ - Once you have traversed the Gateway, you may experience some disorientation. Do not panic. \ - This is a normal side effect of travelling vast distances in a short period of time. You should \ - survey the immediate area, and attempt to locate your complimentary case of space beer. Our \ - expeditionary teams have ensured the complete safety of all away locations, but in a small \ - number of cases, the Gateway they have established may not be immediately obvious. \ - Do not panic if you cannot locate the return Gateway. Begin colonisation of the destination. \ -

    A New World
    \ - As a participant in the Nanotrasen Gateway Project, you will be on the frontiers of space. \ - Though complete safety is assured, participants are advised to prepare for inhospitable \ - environs." - -//we don't want the silly text overlay! -/obj/item/paper/pamphlet/update_icon() - return \ No newline at end of file +/obj/item/paper/pamphlet + name = "pamphlet" + icon_state = "pamphlet" + info = "Welcome to the Nanotrasen Gateway project...
    \ + Congratulations! If you're reading this, you and your superiors have decided that you're \ + ready to commit to a life spent colonising the rolling hills of far away worlds. You \ + must be ready for a lifetime of adventure, a little bit of hard work, and an award \ + winning dental plan- but that's not all the Nanotrasen Gateway project has to offer.
    \ +
    Because we care about you, we feel it is only fair to make sure you know the risks \ + before you commit to joining the Nanotrasen Gateway project. All away destinations have \ + been fully scanned by a Nanotrasen expeditionary team, and are certified to be 100% safe. \ + We've even left a case of space beer along with the basic materials you'll need to expand \ + Nanotrasen's operational area and start your new life.

    \ + Gateway Operation Basics
    \ + All Nanotrasen approved Gateways operate on the same basic principals. They operate off \ + area equipment power as you would expect, but they also require a backup wire with at least \ + 128, 000 Watts of power running through it. Without this supply, it cannot safely function \ + and will reject all attempts at operation.

    \ + Once it is correctly setup, and once it has enough power to operate, the Gateway will begin \ + searching for an output location. The amount of time this takes is variable, but the Gateway \ + interface will give you an estimate accurate to the minute. Power loss will not interrupt the \ + searching process. Influenza will not interrupt the searching process. Temporal anomalies \ + may cause the estimate to be inaccurate, but will not interrupt the searching process.

    \ + Life On The Other Side
    \ + Once you have traversed the Gateway, you may experience some disorientation. Do not panic. \ + This is a normal side effect of travelling vast distances in a short period of time. You should \ + survey the immediate area, and attempt to locate your complimentary case of space beer. Our \ + expeditionary teams have ensured the complete safety of all away locations, but in a small \ + number of cases, the Gateway they have established may not be immediately obvious. \ + Do not panic if you cannot locate the return Gateway. Begin colonisation of the destination. \ +

    A New World
    \ + As a participant in the Nanotrasen Gateway Project, you will be on the frontiers of space. \ + Though complete safety is assured, participants are advised to prepare for inhospitable \ + environs." + +//we don't want the silly text overlay! +/obj/item/paper/pamphlet/update_icon() + return diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm index c406a76cb73..3b31a8bbcfc 100644 --- a/code/modules/awaymissions/zlevel.dm +++ b/code/modules/awaymissions/zlevel.dm @@ -1,4 +1,4 @@ -var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away_mission_config.txt") +GLOBAL_LIST_INIT(potentialRandomZlevels, generateMapList(filename = "config/away_mission_config.txt")) // Call this before you remove the last dirt on a z level - that way, all objects // will have proper atmos and other important enviro things @@ -39,27 +39,27 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away T.ChangeTurf(T.baseturf) /proc/createRandomZlevel() - if(awaydestinations.len) //crude, but it saves another var! + if(GLOB.awaydestinations.len) //crude, but it saves another var! return - if(potentialRandomZlevels && potentialRandomZlevels.len) + if(GLOB.potentialRandomZlevels && GLOB.potentialRandomZlevels.len) var/watch = start_watch() log_startup_progress("Loading away mission...") - var/map = pick(potentialRandomZlevels) + var/map = pick(GLOB.potentialRandomZlevels) var/file = file(map) if(isfile(file)) - var/zlev = space_manager.add_new_zlevel(AWAY_MISSION, linkage = UNAFFECTED, traits = list(AWAY_LEVEL,BLOCK_TELEPORT)) - space_manager.add_dirt(zlev) - maploader.load_map(file, z_offset = zlev) + var/zlev = GLOB.space_manager.add_new_zlevel(AWAY_MISSION, linkage = UNAFFECTED, traits = list(AWAY_LEVEL,BLOCK_TELEPORT)) + GLOB.space_manager.add_dirt(zlev) + GLOB.maploader.load_map(file, z_offset = zlev) late_setup_level(block(locate(1, 1, zlev), locate(world.maxx, world.maxy, zlev))) - space_manager.remove_dirt(zlev) + GLOB.space_manager.remove_dirt(zlev) log_world(" Away mission loaded: [map]") for(var/obj/effect/landmark/L in GLOB.landmarks_list) if(L.name != "awaystart") continue - awaydestinations.Add(L) + GLOB.awaydestinations.Add(L) log_startup_progress(" Away mission loaded in [stop_watch(watch)]s.") @@ -69,22 +69,22 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away /proc/createALLZlevels() - if(awaydestinations.len) //crude, but it saves another var! + if(GLOB.awaydestinations.len) //crude, but it saves another var! return - if(potentialRandomZlevels && potentialRandomZlevels.len) + if(GLOB.potentialRandomZlevels && GLOB.potentialRandomZlevels.len) var/watch = start_watch() log_startup_progress("Loading away missions...") - for(var/map in potentialRandomZlevels) + for(var/map in GLOB.potentialRandomZlevels) var/file = file(map) if(isfile(file)) log_startup_progress("Loading away mission: [map]") - var/zlev = space_manager.add_new_zlevel() - space_manager.add_dirt(zlev) - maploader.load_map(file, z_offset = zlev) + var/zlev = GLOB.space_manager.add_new_zlevel() + GLOB.space_manager.add_dirt(zlev) + GLOB.maploader.load_map(file, z_offset = zlev) late_setup_level(block(locate(1, 1, zlev), locate(world.maxx, world.maxy, zlev))) - space_manager.remove_dirt(zlev) + GLOB.space_manager.remove_dirt(zlev) log_world(" Away mission loaded: [map]") //map_transition_config.Add(AWAY_MISSION_LIST) @@ -92,7 +92,7 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away for(var/obj/effect/landmark/L in GLOB.landmarks_list) if(L.name != "awaystart") continue - awaydestinations.Add(L) + GLOB.awaydestinations.Add(L) log_startup_progress(" Away mission loaded in [stop_watch(watch)]s.") watch = start_watch() @@ -172,87 +172,3 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away new /obj/effect/landmark/ruin(central_turf, src) return TRUE return FALSE - -/proc/seedRuins(list/z_levels = null, budget = 0, whitelist = /area/space, list/potentialRuins) - if(!z_levels || !z_levels.len) - WARNING("No Z levels provided - Not generating ruins") - return - - for(var/zl in z_levels) - var/turf/T = locate(1, 1, zl) - if(!T) - WARNING("Z level [zl] does not exist - Not generating ruins") - return - - var/list/ruins = potentialRuins.Copy() - - var/list/forced_ruins = list() //These go first on the z level associated (same random one by default) - var/list/ruins_availible = list() //we can try these in the current pass - var/forced_z //If set we won't pick z level and use this one instead. - - //Set up the starting ruin list - for(var/key in ruins) - var/datum/map_template/ruin/R = ruins[key] - if(R.cost > budget) //Why would you do that - continue - if(R.always_place) - forced_ruins[R] = -1 - if(R.unpickable) - continue - ruins_availible[R] = R.placement_weight - - while(budget > 0 && (ruins_availible.len || forced_ruins.len)) - var/datum/map_template/ruin/current_pick - var/forced = FALSE - if(forced_ruins.len) //We have something we need to load right now, so just pick it - for(var/ruin in forced_ruins) - current_pick = ruin - if(forced_ruins[ruin] > 0) //Load into designated z - forced_z = forced_ruins[ruin] - forced = TRUE - break - else //Otherwise just pick random one - current_pick = pickweight(ruins_availible) - - var/placement_tries = PLACEMENT_TRIES - var/failed_to_place = TRUE - var/z_placed = 0 - while(placement_tries > 0) - placement_tries-- - z_placed = pick(z_levels) - if(!current_pick.try_to_place(forced_z ? forced_z : z_placed,whitelist)) - continue - else - failed_to_place = FALSE - break - - //That's done remove from priority even if it failed - if(forced) - //TODO : handle forced ruins with multiple variants - forced_ruins -= current_pick - forced = FALSE - - if(failed_to_place) - for(var/datum/map_template/ruin/R in ruins_availible) - if(R.id == current_pick.id) - ruins_availible -= R - log_world("Failed to place [current_pick.name] ruin.") - else - budget -= current_pick.cost - if(!current_pick.allow_duplicates) - for(var/datum/map_template/ruin/R in ruins_availible) - if(R.id == current_pick.id) - ruins_availible -= R - if(current_pick.never_spawn_with) - for(var/blacklisted_type in current_pick.never_spawn_with) - for(var/possible_exclusion in ruins_availible) - if(istype(possible_exclusion,blacklisted_type)) - ruins_availible -= possible_exclusion - forced_z = 0 - - //Update the availible list - for(var/datum/map_template/ruin/R in ruins_availible) - if(R.cost > budget) - ruins_availible -= R - - log_world("Ruin loader finished with [budget] left to spend.") diff --git a/code/modules/awaymissions/zvis.dm b/code/modules/awaymissions/zvis.dm index 102554f2e6e..b35b0fb6ffc 100644 --- a/code/modules/awaymissions/zvis.dm +++ b/code/modules/awaymissions/zvis.dm @@ -370,4 +370,4 @@ screen_loc = "CENTER[ox >= 0 ? "+" : ""][ox],CENTER[oy >= 0 ? "+" : ""][oy]" /obj/effect/view_portal_dummy/attack_ghost(mob/user) - owner.attack_ghost(user) \ No newline at end of file + owner.attack_ghost(user) diff --git a/code/modules/buildmode/README.md b/code/modules/buildmode/README.md index 37ce2ce6b1b..015e6ce6a66 100644 --- a/code/modules/buildmode/README.md +++ b/code/modules/buildmode/README.md @@ -294,4 +294,4 @@ Existing varieties: + *Left click a location*: - Cause an explosion where you clicked. \ No newline at end of file + Cause an explosion where you clicked. diff --git a/code/modules/buildmode/bm_mode.dm b/code/modules/buildmode/bm_mode.dm index de4c09a192d..0169df7cd62 100644 --- a/code/modules/buildmode/bm_mode.dm +++ b/code/modules/buildmode/bm_mode.dm @@ -47,11 +47,11 @@ overlaystate = "blueOverlay" preview += image('icons/turf/overlays.dmi', T, overlaystate) BM.holder.images += preview - return T + return T /datum/buildmode_mode/proc/highlight_region(region) BM.holder.images -= preview - for(var/t in region) + for(var/T in region) preview += image('icons/turf/overlays.dmi', T, "redOverlay") BM.holder.images += preview @@ -84,4 +84,4 @@ deselect_region() /datum/buildmode_mode/proc/handle_selected_region(mob/user, params) - return \ No newline at end of file + return diff --git a/code/modules/buildmode/buildmode.dm b/code/modules/buildmode/buildmode.dm index 7f43f20c934..6c260c41cb2 100644 --- a/code/modules/buildmode/buildmode.dm +++ b/code/modules/buildmode/buildmode.dm @@ -125,4 +125,4 @@ #undef BM_SWITCHSTATE_NONE #undef BM_SWITCHSTATE_MODE -#undef BM_SWITCHSTATE_DIR \ No newline at end of file +#undef BM_SWITCHSTATE_DIR diff --git a/code/modules/buildmode/buttons.dm b/code/modules/buildmode/buttons.dm index c07be97a9eb..2bec31f8650 100644 --- a/code/modules/buildmode/buttons.dm +++ b/code/modules/buildmode/buttons.dm @@ -85,4 +85,4 @@ /obj/screen/buildmode/quit/Click() bd.quit() - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/buildmode/effects/line.dm b/code/modules/buildmode/effects/line.dm index 4b59e8a673c..b49d35af095 100644 --- a/code/modules/buildmode/effects/line.dm +++ b/code/modules/buildmode/effects/line.dm @@ -25,4 +25,4 @@ cl.images -= I cl = null QDEL_NULL(I) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/buildmode/submodes/advanced.dm b/code/modules/buildmode/submodes/advanced.dm index fadd46753bb..c2e64fb488e 100644 --- a/code/modules/buildmode/submodes/advanced.dm +++ b/code/modules/buildmode/submodes/advanced.dm @@ -56,4 +56,4 @@ if(isobj(object)) log_admin("Build Mode: [key_name(user)] deleted [object] at ([object.x],[object.y],[object.z])") qdel(object) - \ No newline at end of file + diff --git a/code/modules/buildmode/submodes/atmos.dm b/code/modules/buildmode/submodes/atmos.dm index f12fdb1eb1a..b3a07374846 100644 --- a/code/modules/buildmode/submodes/atmos.dm +++ b/code/modules/buildmode/submodes/atmos.dm @@ -69,4 +69,4 @@ T.air_update_turf() // admin log - log_admin("Build Mode: [key_name(user)] changed the atmos of region [COORD(cornerA)] to [COORD(cornerB)]. T: [temperature], P: [pressure], Ox: [oxygen]%, N2: [nitrogen]%, Plsma: [plasma]%, CO2: [cdiox]%, N2O: [nitrox]%. [ctrl_click ? "Overwrote base unsimulated turf gases." : ""]") \ No newline at end of file + log_admin("Build Mode: [key_name(user)] changed the atmos of region [COORD(cornerA)] to [COORD(cornerB)]. T: [temperature], P: [pressure], Ox: [oxygen]%, N2: [nitrogen]%, Plsma: [plasma]%, CO2: [cdiox]%, N2O: [nitrox]%. [ctrl_click ? "Overwrote base unsimulated turf gases." : ""]") diff --git a/code/modules/buildmode/submodes/fill.dm b/code/modules/buildmode/submodes/fill.dm index b143b2c9b90..3c06a199e47 100644 --- a/code/modules/buildmode/submodes/fill.dm +++ b/code/modules/buildmode/submodes/fill.dm @@ -46,4 +46,4 @@ T.ChangeTurf(objholder) else var/obj/A = new objholder(T) - A.setDir(BM.build_dir) \ No newline at end of file + A.setDir(BM.build_dir) diff --git a/code/modules/buildmode/submodes/link.dm b/code/modules/buildmode/submodes/link.dm index 136ffad6433..25a6d92bfa3 100644 --- a/code/modules/buildmode/submodes/link.dm +++ b/code/modules/buildmode/submodes/link.dm @@ -46,17 +46,21 @@ if(!M.id || M.id == "") M.id = input(user, "Please select an ID for the button", "Buildmode", "") if(!M.id || M.id == "") - goto line_jump + speed_execute() + return if(P.id_tag == M.id && P.id_tag && P.id_tag != "") P.id_tag = null to_chat(user, "[P] unlinked.") - goto line_jump + speed_execute() + return if(!M.normaldoorcontrol) if(link_lines.len && alert(user, "Warning: This will disable links to connected pod doors. Continue?", "Buildmode", "Yes", "No") == "No") - goto line_jump + speed_execute() + return M.normaldoorcontrol = 1 if(P.id_tag && alert(user, "Warning: This will unlink something else from the door. Continue?", "Buildmode", "Yes", "No") == "No") - goto line_jump + speed_execute() + return P.id_tag = M.id if(istype(link_obj, /obj/machinery/door_control) && istype(object, /obj/machinery/door/poddoor)) var/obj/machinery/door_control/M = link_obj @@ -64,24 +68,29 @@ if(!M.id || M.id == "") M.id = input(user, "Please select an ID for the button", "Buildmode", "") if(!M.id || M.id == "") - goto line_jump + speed_execute() + return if(P.id_tag == M.id && P.id_tag && P.id_tag != "") P.id_tag = null to_chat(user, "[P] unlinked.") - goto line_jump + speed_execute() + return if(M.normaldoorcontrol) if(link_lines.len && alert(user, "Warning: This will disable links to connected airlocks. Continue?", "Buildmode", "Yes", "No") == "No") - goto line_jump + speed_execute() + return M.normaldoorcontrol = 0 if(!M.id || M.id == "") M.id = input(user, "Please select an ID for the button", "Buildmode", "") if(!M.id || M.id == "") - goto line_jump + speed_execute() + return if(P.id_tag && P.id_tag != 1 && alert(user, "Warning: This will unlink something else from the door. Continue?", "Buildmode", "Yes", "No") == "No") - goto line_jump + speed_execute() + return P.id_tag = M.id - line_jump // For the goto +/datum/buildmode_mode/link/proc/speed_execute() // For exiting out of hell clear_lines() if(istype(link_obj, /obj/machinery/door_control)) diff --git a/code/modules/buildmode/submodes/mapgen.dm b/code/modules/buildmode/submodes/mapgen.dm index 22be429c2ce..df6f6b73ed0 100644 --- a/code/modules/buildmode/submodes/mapgen.dm +++ b/code/modules/buildmode/submodes/mapgen.dm @@ -37,4 +37,4 @@ highlight_region(G.map) var/confirm = alert("Are you sure you want run the map generator?", "Run generator", "Yes", "No") if(confirm == "Yes") - G.generate() \ No newline at end of file + G.generate() diff --git a/code/modules/buildmode/submodes/save.dm b/code/modules/buildmode/submodes/save.dm index 43a1f9af174..34a57e1f7e4 100644 --- a/code/modules/buildmode/submodes/save.dm +++ b/code/modules/buildmode/submodes/save.dm @@ -3,7 +3,7 @@ use_corner_selection = TRUE var/use_json = TRUE - + /datum/buildmode_mode/save/show_help(mob/user) to_chat(user, "***********************************************************") to_chat(user, "Left Mouse Button on turf/obj/mob = Select corner") @@ -23,6 +23,6 @@ if(use_json) map_flags = 32 // Magic number defined in `writer.dm` that I can't use directly // because #defines are for some reason our coding standard - var/our_map = maploader.save_map(cornerA, cornerB, map_name, map_flags) + var/our_map = GLOB.maploader.save_map(cornerA, cornerB, map_name, map_flags) user << ftp(our_map) // send the map they've made! Or are stealing, whatever to_chat(user, "Map saving complete! [our_map]") diff --git a/code/modules/busy_space/air_traffic.dm b/code/modules/busy_space/air_traffic.dm index b5cbf8e6fda..e21414e8ed3 100644 --- a/code/modules/busy_space/air_traffic.dm +++ b/code/modules/busy_space/air_traffic.dm @@ -1,6 +1,5 @@ //Cactus, Speedbird, Dynasty, oh my - -var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller +GLOBAL_DATUM_INIT(atc, /datum/lore/atc_controller, new) /datum/lore/atc_controller var/delay_max = 10 MINUTES //Maximum amount of tiem between ATC messages. Default is 10 mins. @@ -30,29 +29,29 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller /datum/lore/atc_controller/proc/msg(var/message,var/sender) ASSERT(message) - global_announcer.autosay("[message]", sender ? sender : "[using_map.station_short] Space Control") + GLOB.global_announcer.autosay("[message]", sender ? sender : "[GLOB.using_map.station_short] Space Control") /datum/lore/atc_controller/proc/reroute_traffic(var/yes = 1) if(yes) if(!squelched) - msg("Rerouting traffic away from [using_map.station_name].") + msg("Rerouting traffic away from [GLOB.using_map.station_name].") squelched = TRUE else if(squelched) - msg("Resuming normal traffic routing around [using_map.station_name].") + msg("Resuming normal traffic routing around [GLOB.using_map.station_name].") squelched = FALSE /datum/lore/atc_controller/proc/shift_ending(var/evac = 0) - msg("Automated Shuttle departing [using_map.station_name] for [using_map.dock_name] on routine transfer route.", "NT Automated Shuttle") + msg("Automated Shuttle departing [GLOB.using_map.station_name] for [GLOB.using_map.dock_name] on routine transfer route.", "NT Automated Shuttle") sleep(5 SECONDS) - msg("Automated Shuttle, cleared to complete routine transfer from [using_map.station_name] to [using_map.dock_name].") + msg("Automated Shuttle, cleared to complete routine transfer from [GLOB.using_map.station_name] to [GLOB.using_map.dock_name].") /datum/lore/atc_controller/proc/random_convo() - var/one = pick(loremaster.organizations) //These will pick an index, not an instance - var/two = pick(loremaster.organizations) + var/one = pick(GLOB.loremaster.organizations) //These will pick an index, not an instance + var/two = pick(GLOB.loremaster.organizations) - var/datum/lore/organization/source = loremaster.organizations[one] //Resolve to the instances - var/datum/lore/organization/dest = loremaster.organizations[two] + var/datum/lore/organization/source = GLOB.loremaster.organizations[one] //Resolve to the instances + var/datum/lore/organization/dest = GLOB.loremaster.organizations[two] //Let's get some mission parameters var/owner = source.short_name //Use the short name @@ -62,13 +61,13 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller var/destname = pick(dest.destination_names) //Pick a random holding from the destination var/combined_name = "[owner] [prefix] [shipname]" - var/alt_atc_names = list("[using_map.station_short] TraCon", "[using_map.station_short] Control", "[using_map.station_short] STC", "[using_map.station_short] Airspace") - var/wrong_atc_names = list("Sol Command", "Orion Control", "[using_map.dock_name]") + var/alt_atc_names = list("[GLOB.using_map.station_short] TraCon", "[GLOB.using_map.station_short] Control", "[GLOB.using_map.station_short] STC", "[GLOB.using_map.station_short] Airspace") + var/wrong_atc_names = list("Sol Command", "Orion Control", "[GLOB.using_map.dock_name]") var/mission_noun = list("flight", "mission", "route") var/request_verb = list("requesting", "calling for", "asking for") //First response is 'yes', second is 'no' - var/requests = list("[using_map.station_short] transit clearance" = list("cleared to transit", "unable to approve, contact regional on 953.5"), + var/requests = list("[GLOB.using_map.station_short] transit clearance" = list("cleared to transit", "unable to approve, contact regional on 953.5"), "planetary flight rules" = list("cleared planetary flight rules", "unable to approve planetary flight rules due to traffic"), "special flight rules" = list("cleared special flight rules", "unable to approve special flight rules for your traffic class"), "current solar weather info" = list("sending you the relevant information via tightbeam", "cannot fulfill your request at the moment"), @@ -104,20 +103,20 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller if("wrong_freq") callname = pick(wrong_atc_names) full_request = "[callname], this is [combined_name] on a [mission] [pick(mission_noun)] to [destname], [pick(request_verb)] [request]." - full_response = "[combined_name], this is [using_map.station_short] TraCon, wrong frequency. Switch to [rand(700,999)].[rand(1,9)]." - full_closure = "[using_map.station_short] TraCon, copy, apologies." + full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, wrong frequency. Switch to [rand(700,999)].[rand(1,9)]." + full_closure = "[GLOB.using_map.station_short] TraCon, copy, apologies." if("wrong_lang") //Can't implement this until autosay has language support if("emerg") var/problem = pick("hull breaches on multiple decks","unknown life forms on board","a drive about to go critical","asteroids impacting the hull","a total loss of engine power","people trying to board the ship") full_request = "Mayday, mayday, mayday, this is [combined_name] declaring an emergency! We have [problem]!" var/rand_freq = "[rand(700,999)].[rand(1,9)]" - full_response = "[combined_name], this is [using_map.station_short] TraCon, copy. Switch to emergency responder channel [rand_freq]." - full_closure = "Roger, [using_map.station_short] TraCon, contacting [rand_freq]." + full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, copy. Switch to emergency responder channel [rand_freq]." + full_closure = "Roger, [GLOB.using_map.station_short] TraCon, contacting [rand_freq]." else full_request = "[callname], this is [combined_name] on a [mission] [pick(mission_noun)] to [destname], [pick(request_verb)] [request]." - full_response = "[combined_name], this is [using_map.station_short] TraCon, [response]." //Station TraCon always calls themselves TraCon - full_closure = "[using_map.station_short] TraCon, [yes ? "thank you" : "copy"], good day." //They always copy what TraCon called themselves in the end when they realize they said it wrong + full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, [response]." //Station TraCon always calls themselves TraCon + full_closure = "[GLOB.using_map.station_short] TraCon, [yes ? "thank you" : "copy"], good day." //They always copy what TraCon called themselves in the end when they realize they said it wrong //Ship sends request to ATC msg(full_request,"[prefix] [shipname]") @@ -126,4 +125,4 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg(full_response) sleep(5 SECONDS) //Ship sends response to ATC - msg(full_closure,"[prefix] [shipname]") \ No newline at end of file + msg(full_closure,"[prefix] [shipname]") diff --git a/code/modules/busy_space/loremaster.dm b/code/modules/busy_space/loremaster.dm index 28dca0055b0..77b8a8ed53d 100644 --- a/code/modules/busy_space/loremaster.dm +++ b/code/modules/busy_space/loremaster.dm @@ -1,6 +1,5 @@ //I AM THE LOREMASTER, ARE YOU THE GATEKEEPER? - -var/datum/lore/loremaster/loremaster = new/datum/lore/loremaster +GLOBAL_DATUM_INIT(loremaster, /datum/lore/loremaster, new) /datum/lore/loremaster var/list/organizations = list() diff --git a/code/modules/busy_space/organizations.dm b/code/modules/busy_space/organizations.dm index f80e82938ec..c39e92ea49e 100644 --- a/code/modules/busy_space/organizations.dm +++ b/code/modules/busy_space/organizations.dm @@ -123,7 +123,7 @@ ..() spawn(1) // BYOND shenanigans means using_map is not initialized yet. Wait a tick. // Get rid of the current map from the list, so ships flying in don't say they're coming to the current map. - var/string_to_test = "[using_map.station_name] in [using_map.starsys_name]" + var/string_to_test = "[GLOB.using_map.station_name] in [GLOB.using_map.starsys_name]" if(string_to_test in destination_names) destination_names.Remove(string_to_test) @@ -546,4 +546,4 @@ "Xarxis 5", "Haverick", "Darsing", - "Norfolk") \ No newline at end of file + "Norfolk") diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 57a6df32ab6..e1e68bd0617 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -147,16 +147,16 @@ You can set verify to TRUE if you want send() to sleep until the client has the //These datums are used to populate the asset cache, the proc "register()" does this. //all of our asset datums, used for referring to these later -/var/global/list/asset_datums = list() +GLOBAL_LIST_EMPTY(asset_datums) //get a assetdatum or make a new one /proc/get_asset_datum(var/type) - if(!(type in asset_datums)) + if(!(type in GLOB.asset_datums)) return new type() - return asset_datums[type] + return GLOB.asset_datums[type] /datum/asset/New() - asset_datums[type] = src + GLOB.asset_datums[type] = src /datum/asset/proc/register() return @@ -195,7 +195,8 @@ You can set verify to TRUE if you want send() to sleep until the client has the "large_stamp-rep.png" = 'icons/paper_icons/large_stamp-rep.png', "large_stamp-magistrate.png"= 'icons/paper_icons/large_stamp-magistrate.png', "talisman.png" = 'icons/paper_icons/talisman.png', - "ntlogo.png" = 'icons/paper_icons/ntlogo.png' + "ntlogo.png" = 'icons/paper_icons/ntlogo.png', + "syndielogo.png" ='icons/paper_icons/syndielogo.png' ) /datum/asset/simple/chess @@ -313,14 +314,14 @@ You can set verify to TRUE if you want send() to sleep until the client has the if(!(state in list("cap", "connector", "dtvalve", "dual-port vent", "dvalve", "filter", "he", "heunary", "injector", "junction", "manifold", "mixer", "tvalve", "mvalve", "passive vent", "passivegate", "pump", "scrubber", "simple", "universal", "uvent", "volumepump"))) //Basically all the pipes we want sprites for continue if(state in list("he", "simple")) - for(var/D in alldirs) + for(var/D in GLOB.alldirs) assets["[state]-[dir2text(D)].png"] = icon('icons/obj/pipe-item.dmi', state, D) - for(var/D in cardinal) + for(var/D in GLOB.cardinal) assets["[state]-[dir2text(D)].png"] = icon('icons/obj/pipe-item.dmi', state, D) for(var/state in icon_states('icons/obj/pipes/disposal.dmi')) if(!(state in list("pipe-c", "pipe-j1", "pipe-s", "pipe-t", "pipe-y", "intake", "outlet", "pipe-j1s"))) //Pipes we want sprites for continue - for(var/D in cardinal) + for(var/D in GLOB.cardinal) assets["[state]-[dir2text(D)].png"] = icon('icons/obj/pipes/disposal.dmi', state, D) for(var/asset_name in assets) register_asset(asset_name, assets[asset_name]) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 0feca103c12..a063fafd736 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -28,13 +28,6 @@ var/adminhelped = 0 - // var/gc_destroyed //Time when this object was destroyed. [Inherits from datum] - -#ifdef TESTING - var/running_find_references - var/last_find_references = 0 -#endif - /////////////// //SOUND STUFF// /////////////// @@ -99,4 +92,4 @@ var/client_keysend_amount = 0 var/next_keysend_reset = 0 var/next_keysend_trip_reset = 0 - var/keysend_tripped = FALSE \ No newline at end of file + var/keysend_tripped = FALSE diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 962be47cae9..561b0b48149 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -319,16 +319,16 @@ if(!config.disable_localhost_admin) if(is_connecting_from_localhost()) new /datum/admins("!LOCALHOST!", R_HOST, ckey) // Makes localhost rank - holder = admin_datums[ckey] + holder = GLOB.admin_datums[ckey] if(holder) GLOB.admins += src holder.owner = src //preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum) - prefs = preferences_datums[ckey] + prefs = GLOB.preferences_datums[ckey] if(!prefs) prefs = new /datum/preferences(src) - preferences_datums[ckey] = prefs + GLOB.preferences_datums[ckey] = prefs else prefs.parent = src prefs.last_ip = address //these are gonna be used for banning @@ -339,8 +339,8 @@ spawn() // Goonchat does some non-instant checks in start() chatOutput.start() - if( (world.address == address || !address) && !host ) - host = key + if( (world.address == address || !address) && !GLOB.host ) + GLOB.host = key world.update_status() if(holder) @@ -373,13 +373,11 @@ send_resources() if(prefs.toggles & UI_DARKMODE) // activates dark mode if its flagged. -AA07 - if(establish_db_connection()) - activate_darkmode() + activate_darkmode() + else + // activate_darkmode() calls the CL update button proc, so we dont want it double called + SSchangelog.UpdatePlayerChangelogButton(src) - if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates. -CP - if(establish_db_connection()) - to_chat(src, "Changelog has changed since your last visit.") - update_changelog_button() if(prefs.toggles & DISABLE_KARMA) // activates if karma is disabled if(establish_db_connection()) @@ -393,10 +391,10 @@ check_forum_link() - if(custom_event_msg && custom_event_msg != "") + if(GLOB.custom_event_msg && GLOB.custom_event_msg != "") to_chat(src, "

    Custom Event

    ") to_chat(src, "

    A custom event is taking place. OOC Info:

    ") - to_chat(src, "[html_encode(custom_event_msg)]") + to_chat(src, "[html_encode(GLOB.custom_event_msg)]") to_chat(src, "
    ") if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them. @@ -414,13 +412,20 @@ if(M.client) playercount += 1 - if(playercount >= 150 && GLOB.panic_bunker_enabled == 0) - GLOB.panic_bunker_enabled = 1 - message_admins("Panic bunker has been automatically enabled due to playercount surpassing 150") + // Update the state of the panic bunker based on current playercount + var/threshold = config.panic_bunker_threshold + + if((playercount > threshold) && (GLOB.panic_bunker_enabled == FALSE)) + GLOB.panic_bunker_enabled = TRUE + message_admins("Panic bunker has been automatically enabled due to playercount rising above [threshold]") + + if((playercount < threshold) && (GLOB.panic_bunker_enabled == TRUE)) + GLOB.panic_bunker_enabled = FALSE + message_admins("Panic bunker has been automatically disabled due to playercount dropping below [threshold]") /client/proc/is_connecting_from_localhost() var/localhost_addresses = list("127.0.0.1", "::1") // Adresses - if(!isnull(address) && address in localhost_addresses) + if(!isnull(address) && (address in localhost_addresses)) return TRUE return FALSE @@ -445,7 +450,7 @@ return establish_db_connection() - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) return if(check_rights(R_ADMIN, 0, mob)) // Yes, the mob is required, regardless of other examples in this file, it won't work otherwise @@ -454,7 +459,7 @@ return //Donator stuff. - var/DBQuery/query_donor_select = dbcon.NewQuery("SELECT ckey, tier, active FROM `[format_table_name("donators")]` WHERE ckey = '[ckey]'") + var/DBQuery/query_donor_select = GLOB.dbcon.NewQuery("SELECT ckey, tier, active FROM `[format_table_name("donators")]` WHERE ckey = '[ckey]'") query_donor_select.Execute() while(query_donor_select.NextRow()) if(!text2num(query_donor_select.item[3])) @@ -475,11 +480,11 @@ establish_db_connection() - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) return - var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM [format_table_name("player")] WHERE ckey = '[ckey]'") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM [format_table_name("player")] WHERE ckey = '[ckey]'") query.Execute() var/sql_id = 0 player_age = 0 // New players won't have an entry so knowing we have a connection we set this to zero to be updated if there is a record. @@ -489,7 +494,7 @@ break - var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = '[address]'") + var/DBQuery/query_ip = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = '[address]'") query_ip.Execute() related_accounts_ip = list() while(query_ip.NextRow()) @@ -497,7 +502,7 @@ related_accounts_ip.Add("[query_ip.item[1]]") - var/DBQuery/query_cid = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]'") + var/DBQuery/query_cid = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]'") query_cid.Execute() related_accounts_cid = list() while(query_cid.NextRow()) @@ -539,7 +544,7 @@ if(sql_id) //Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables - var/DBQuery/query_update = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastseen = Now(), ip = '[sql_ip]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]") + var/DBQuery/query_update = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastseen = Now(), ip = '[sql_ip]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]") if(!query_update.Execute()) var/err = query_update.ErrorMsg() log_game("SQL ERROR during log_client_to_db (update). Error : \[[err]\]\n") @@ -549,19 +554,19 @@ // Check new peeps for panic bunker if(GLOB.panic_bunker_enabled) - message_admins("Failed Login: [key] - New account attempting to connect during panic bunker") - src << "Sorry but the server is currently not accepting connections from never before seen players. Please try again later." + var/threshold = config.panic_bunker_threshold + src << "Server is not accepting connections from never-before-seen players until player count is less than [threshold]. Please try again later." del(src) return // Dont insert or they can just go in again - var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')") + var/DBQuery/query_insert = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')") if(!query_insert.Execute()) var/err = query_insert.ErrorMsg() log_game("SQL ERROR during log_client_to_db (insert). Error : \[[err]\]\n") message_admins("SQL ERROR during log_client_to_db (insert). Error : \[[err]\]\n") // Log player connections to DB - var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]`(`datetime`,`ckey`,`ip`,`computerid`) VALUES(Now(),'[ckey]','[sql_ip]','[sql_computerid]');") + var/DBQuery/query_accesslog = GLOB.dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]`(`datetime`,`ckey`,`ip`,`computerid`) VALUES(Now(),'[ckey]','[sql_ip]','[sql_computerid]');") query_accesslog.Execute() /client/proc/check_ip_intel() @@ -609,14 +614,14 @@ to_chat(src, "You have no verified forum account. VERIFY FORUM ACCOUNT") /client/proc/create_oauth_token() - var/DBQuery/query_find_token = dbcon.NewQuery("SELECT token FROM [format_table_name("oauth_tokens")] WHERE ckey = '[ckey]' limit 1") + var/DBQuery/query_find_token = GLOB.dbcon.NewQuery("SELECT token FROM [format_table_name("oauth_tokens")] WHERE ckey = '[ckey]' limit 1") if(!query_find_token.Execute()) log_debug("create_oauth_token: failed db read") return if(query_find_token.NextRow()) return query_find_token.item[1] var/tokenstr = md5("[ckey][rand()]") - var/DBQuery/query_insert_token = dbcon.NewQuery("INSERT INTO [format_table_name("oauth_tokens")] (ckey, token) VALUES('[ckey]','[tokenstr]')") + var/DBQuery/query_insert_token = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("oauth_tokens")] (ckey, token) VALUES('[ckey]','[tokenstr]')") if(!query_insert_token.Execute()) return return tokenstr @@ -631,7 +636,7 @@ if(!fromban) to_chat(src, "Your forum account is already set.") return - var/DBQuery/query_find_link = dbcon.NewQuery("SELECT fuid FROM [format_table_name("player")] WHERE ckey = '[ckey]' limit 1") + var/DBQuery/query_find_link = GLOB.dbcon.NewQuery("SELECT fuid FROM [format_table_name("player")] WHERE ckey = '[ckey]' limit 1") if(!query_find_link.Execute()) log_debug("link_forum_account: failed db read") return @@ -675,7 +680,7 @@ var/oldcid = cidcheck[ckey] if(!oldcid) - var/DBQuery/query_cidcheck = dbcon.NewQuery("SELECT computerid FROM [format_table_name("player")] WHERE ckey = '[ckey]'") + var/DBQuery/query_cidcheck = GLOB.dbcon.NewQuery("SELECT computerid FROM [format_table_name("player")] WHERE ckey = '[ckey]'") query_cidcheck.Execute() var/lastcid = computer_id @@ -740,7 +745,7 @@ var/const/adminckey = "CID-Error" // Check for notes in the last day - only 1 note per 24 hours - var/DBQuery/query_get_notes = dbcon.NewQuery("SELECT id from [format_table_name("notes")] WHERE ckey = '[ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW()") + var/DBQuery/query_get_notes = GLOB.dbcon.NewQuery("SELECT id from [format_table_name("notes")] WHERE ckey = '[ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW()") if(!query_get_notes.Execute()) var/err = query_get_notes.ErrorMsg() log_game("SQL ERROR obtaining id from notes table. Error : \[[err]\]\n") @@ -749,7 +754,7 @@ return // Only add a note if their most recent note isn't from the randomizer blocker, either - query_get_notes = dbcon.NewQuery("SELECT adminckey FROM [format_table_name("notes")] WHERE ckey = '[ckey]' ORDER BY timestamp DESC LIMIT 1") + query_get_notes = GLOB.dbcon.NewQuery("SELECT adminckey FROM [format_table_name("notes")] WHERE ckey = '[ckey]' ORDER BY timestamp DESC LIMIT 1") if(!query_get_notes.Execute()) var/err = query_get_notes.ErrorMsg() log_game("SQL ERROR obtaining adminckey from notes table. Error : \[[err]\]\n") @@ -818,7 +823,7 @@ // IF YOU CHANGE ANYTHING IN ACTIVATE, MAKE SURE IT HAS A DEACTIVATE METHOD, -AA07 /client/proc/activate_darkmode() ///// BUTTONS ///// - update_changelog_button() + SSchangelog.UpdatePlayerChangelogButton(src) /* Rpane */ winset(src, "rpane.textb", "background-color=#40628a;text-color=#FFFFFF") winset(src, "rpane.infob", "background-color=#40628a;text-color=#FFFFFF") @@ -850,7 +855,7 @@ /client/proc/deactivate_darkmode() ///// BUTTONS ///// - update_changelog_button() + SSchangelog.UpdatePlayerChangelogButton(src) /* Rpane */ winset(src, "rpane.textb", "background-color=none;text-color=#000000") winset(src, "rpane.infob", "background-color=none;text-color=#000000") @@ -880,22 +885,6 @@ ///// NOTIFY USER ///// to_chat(src, "Darkmode Disabled") // what a sick fuck -// Better changelog button handling -/client/proc/update_changelog_button() - if(establish_db_connection()) - if(prefs.lastchangelog != changelog_hash) - winset(src, "rpane.changelog", "background-color=#bb7700;text-color=#FFFFFF;font-style=bold") - else - if(prefs.toggles & UI_DARKMODE) - winset(src, "rpane.changelog", "background-color=#40628a;text-color=#FFFFFF") - else - winset(src, "rpane.changelog", "background-color=none;text-color=#000000") - else - if(prefs.toggles & UI_DARKMODE) - winset(src, "rpane.changelog", "background-color=#40628a;text-color=#FFFFFF") - else - winset(src, "rpane.changelog", "background-color=none;text-color=#000000") - /client/proc/generate_clickcatcher() if(!void) void = new() @@ -913,7 +902,7 @@ return FALSE if(M && M.player_logged < SSD_WARNING_TIMER) return FALSE - to_chat(src, "Interacting with SSD players is against server rules unless you've ahelped first for permission. If you have, confirm that and proceed.") + to_chat(src, "Are you taking this person to cryo or giving them medical treatment? If you are, confirm that and proceed. Interacting with SSD players in other ways is against server rules unless you've ahelped first for permission.") return TRUE #undef SSD_WARNING_TIMER diff --git a/code/modules/client/message.dm b/code/modules/client/message.dm index ba09e7e1567..5c612339621 100644 --- a/code/modules/client/message.dm +++ b/code/modules/client/message.dm @@ -7,4 +7,4 @@ proc/addclientmessage(var/ckey, var/message) var/list/L = GLOB.clientmessages[ckey] if(!L) GLOB.clientmessages[ckey] = L = list() - L += message \ No newline at end of file + L += message diff --git a/code/modules/client/preference/loadout/gear_tweaks.dm b/code/modules/client/preference/loadout/gear_tweaks.dm index 6458a51b9a3..9446008b762 100644 --- a/code/modules/client/preference/loadout/gear_tweaks.dm +++ b/code/modules/client/preference/loadout/gear_tweaks.dm @@ -16,8 +16,7 @@ /* * Color adjustment */ - -var/datum/gear_tweak/color/gear_tweak_free_color_choice = new() +GLOBAL_DATUM_INIT(gear_tweak_free_color_choice, /datum/gear_tweak/color, new()) /datum/gear_tweak/color var/list/valid_colors diff --git a/code/modules/client/preference/loadout/loadout.dm b/code/modules/client/preference/loadout/loadout.dm index 63825cc1237..1091b38a11c 100644 --- a/code/modules/client/preference/loadout/loadout.dm +++ b/code/modules/client/preference/loadout/loadout.dm @@ -1,5 +1,5 @@ -var/list/loadout_categories = list() -var/list/gear_datums = list() +GLOBAL_LIST_EMPTY(loadout_categories) +GLOBAL_LIST_EMPTY(gear_datums) /datum/loadout_category var/category = "" @@ -30,15 +30,15 @@ var/list/gear_datums = list() error("Loadout - Missing path definition: [G]") continue - if(!loadout_categories[use_category]) - loadout_categories[use_category] = new /datum/loadout_category(use_category) - var/datum/loadout_category/LC = loadout_categories[use_category] - gear_datums[use_name] = new geartype - LC.gear[use_name] = gear_datums[use_name] + if(!GLOB.loadout_categories[use_category]) + GLOB.loadout_categories[use_category] = new /datum/loadout_category(use_category) + var/datum/loadout_category/LC = GLOB.loadout_categories[use_category] + GLOB.gear_datums[use_name] = new geartype + LC.gear[use_name] = GLOB.gear_datums[use_name] - loadout_categories = sortAssoc(loadout_categories) - for(var/loadout_category in loadout_categories) - var/datum/loadout_category/LC = loadout_categories[loadout_category] + GLOB.loadout_categories = sortAssoc(GLOB.loadout_categories) + for(var/loadout_category in GLOB.loadout_categories) + var/datum/loadout_category/LC = GLOB.loadout_categories[loadout_category] LC.gear = sortAssoc(LC.gear) return 1 diff --git a/code/modules/client/preference/loadout/loadout_cosmetics.dm b/code/modules/client/preference/loadout/loadout_cosmetics.dm index b7ac6c39493..0375de2de53 100644 --- a/code/modules/client/preference/loadout/loadout_cosmetics.dm +++ b/code/modules/client/preference/loadout/loadout_cosmetics.dm @@ -21,4 +21,4 @@ /datum/gear/lipstick/lime display_name = "lipstick, lime" - path = /obj/item/lipstick/lime \ No newline at end of file + path = /obj/item/lipstick/lime diff --git a/code/modules/client/preference/loadout/loadout_donor.dm b/code/modules/client/preference/loadout/loadout_donor.dm index 830b10b7055..7d43bd6b86a 100644 --- a/code/modules/client/preference/loadout/loadout_donor.dm +++ b/code/modules/client/preference/loadout/loadout_donor.dm @@ -93,4 +93,4 @@ display_name = "Gold ID Decal" path = /obj/item/id_decal/gold donator_tier = 4 - cost = 4 \ No newline at end of file + cost = 4 diff --git a/code/modules/client/preference/loadout/loadout_general.dm b/code/modules/client/preference/loadout/loadout_general.dm index 50105813fd6..8c08063ad7e 100644 --- a/code/modules/client/preference/loadout/loadout_general.dm +++ b/code/modules/client/preference/loadout/loadout_general.dm @@ -29,7 +29,7 @@ /datum/gear/sechud display_name = "a classic security HUD" path = /obj/item/clothing/glasses/hud/security - allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot", "Internal Affairs Agent") + allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot", "Internal Affairs Agent","Magistrate") /datum/gear/matches display_name = "a box of matches" @@ -42,7 +42,7 @@ /datum/gear/doublecards display_name = "a double deck of standard cards" path = /obj/item/deck/doublecards - + /datum/gear/tarot display_name = "a deck of tarot cards" path = /obj/item/deck/tarot diff --git a/code/modules/client/preference/loadout/loadout_glasses.dm b/code/modules/client/preference/loadout/loadout_glasses.dm index 94f2555bdba..a84af914c49 100644 --- a/code/modules/client/preference/loadout/loadout_glasses.dm +++ b/code/modules/client/preference/loadout/loadout_glasses.dm @@ -21,4 +21,4 @@ /datum/gear/glasses/prescription display_name = "Prescription glasses" - path = /obj/item/clothing/glasses/regular \ No newline at end of file + path = /obj/item/clothing/glasses/regular diff --git a/code/modules/client/preference/loadout/loadout_gloves.dm b/code/modules/client/preference/loadout/loadout_gloves.dm index cd8d5a7c8e1..879cb840506 100644 --- a/code/modules/client/preference/loadout/loadout_gloves.dm +++ b/code/modules/client/preference/loadout/loadout_gloves.dm @@ -5,4 +5,4 @@ /datum/gear/gloves/fingerless display_name = "Fingerless Gloves" - path = /obj/item/clothing/gloves/fingerless \ No newline at end of file + path = /obj/item/clothing/gloves/fingerless diff --git a/code/modules/client/preference/loadout/loadout_shoes.dm b/code/modules/client/preference/loadout/loadout_shoes.dm index 5031f4333fb..ab000b209ac 100644 --- a/code/modules/client/preference/loadout/loadout_shoes.dm +++ b/code/modules/client/preference/loadout/loadout_shoes.dm @@ -55,4 +55,4 @@ /datum/gear/shoes/whiteshoes display_name = "White shoes" - path = /obj/item/clothing/shoes/white \ No newline at end of file + path = /obj/item/clothing/shoes/white diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index eb1587ff28c..ae66958c4cf 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -1,6 +1,7 @@ -var/list/preferences_datums = list() +GLOBAL_LIST_EMPTY(preferences_datums) +GLOBAL_PROTECT(preferences_datums) // These feel like something that shouldnt be fucked with -var/global/list/special_role_times = list( //minimum age (in days) for accounts to play these roles +GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts to play these roles ROLE_PAI = 0, ROLE_POSIBRAIN = 0, ROLE_GUARDIAN = 0, @@ -24,7 +25,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts ROLE_GSPIDER = 21, ROLE_ABDUCTOR = 30, ROLE_DEVIL = 14 -) +)) /proc/player_old_enough_antag(client/C, role) if(available_in_days_antag(C, role)) @@ -42,7 +43,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return 0 if(!isnum(C.player_age)) return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced - var/minimal_player_age_antag = special_role_times[num2text(role)] + var/minimal_player_age_antag = GLOB.special_role_times[num2text(role)] if(!isnum(minimal_player_age_antag)) return 0 @@ -55,8 +56,8 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return max(0, days - C.player_age) return 0 -#define MAX_SAVE_SLOTS 20 // Save slots for regular players -#define MAX_SAVE_SLOTS_MEMBER 20 // Save slots for BYOND members +#define MAX_SAVE_SLOTS 30 // Save slots for regular players +#define MAX_SAVE_SLOTS_MEMBER 30 // Save slots for BYOND members #define TAB_CHAR 0 #define TAB_GAME 1 @@ -78,7 +79,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/last_id //game-preferences - var/lastchangelog = "" //Saved changlog filesize to detect if there was a change + var/lastchangelog = "1" //Saved changlog timestamp (unix epoch) to detect if there was a change. Dont set this to 0 unless you want the last changelog date to be 4x longer than the expected lifespan of the universe. var/exp var/ooccolor = "#b82e00" var/list/be_special = list() //Special role selection @@ -199,7 +200,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/unlock_content = 0 //Gear stuff - var/list/gear = list() + var/list/loadout_gear = list() var/gear_tab = "General" // Parallax var/parallax = PARALLAX_HIGH @@ -343,11 +344,11 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += "Eyes: " dat += "Color [color_square(e_colour)]
    " - if((S.bodyflags & HAS_SKIN_COLOR) || body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) //admins can always fuck with this, because they are admins + if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) //admins can always fuck with this, because they are admins dat += "Body Color: " dat += "Color [color_square(s_colour)]
    " - if(body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) + if(GLOB.body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) dat += "Body Accessory: " dat += "[body_accessory ? "[body_accessory]" : "None"]
    " @@ -413,10 +414,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts switch(status) if("cyborg") var/datum/robolimb/R - if(rlimb_data[name] && all_robolimbs[rlimb_data[name]]) - R = all_robolimbs[rlimb_data[name]] + if(rlimb_data[name] && GLOB.all_robolimbs[rlimb_data[name]]) + R = GLOB.all_robolimbs[rlimb_data[name]] else - R = basic_robolimb + R = GLOB.basic_robolimb dat += "\t[R.company] [organ_name] prosthesis" if("amputated") dat += "\tAmputated [organ_name]" @@ -461,7 +462,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += "Ghost Sight: [(toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
    " dat += "Ghost PDA: [(toggles & CHAT_GHOSTPDA) ? "All PDA Messages" : "No PDA Messages"]
    " if(check_rights(R_ADMIN,0)) - dat += "OOC Color:     Change
    " + dat += "OOC Color:     Change
    " if(config.allow_Metadata) dat += "OOC Notes: Edit
    " dat += "Parallax (Fancy Space): " @@ -488,7 +489,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += "You are banned from special roles." be_special = list() else - for(var/i in special_roles) + for(var/i in GLOB.special_roles) if(jobban_isbanned(user, i)) dat += "Be [capitalize(i)]: \[BANNED]
    " else if(!player_old_enough_antag(user.client, i)) @@ -507,9 +508,9 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if(TAB_GEAR) var/total_cost = 0 var/list/type_blacklist = list() - if(gear && gear.len) - for(var/i = 1, i <= gear.len, i++) - var/datum/gear/G = gear_datums[gear[i]] + if(loadout_gear && loadout_gear.len) + for(var/i = 1, i <= loadout_gear.len, i++) + var/datum/gear/G = GLOB.gear_datums[loadout_gear[i]] if(G) if(!G.subtype_cost_overlap) if(G.subtype_path in type_blacklist) @@ -525,7 +526,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += "
    " var/firstcat = 1 - for(var/category in loadout_categories) + for(var/category in GLOB.loadout_categories) if(firstcat) firstcat = 0 else @@ -536,13 +537,13 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += " [category] " dat += "
    " - var/datum/loadout_category/LC = loadout_categories[gear_tab] + var/datum/loadout_category/LC = GLOB.loadout_categories[gear_tab] dat += "
    " dat += "
    [LC.category]
    " dat += "
    " for(var/gear_name in LC.gear) var/datum/gear/G = LC.gear[gear_name] - var/ticked = (G.display_name in gear) + var/ticked = (G.display_name in loadout_gear) if(G.donator_tier > user.client.donator_level) dat += "[G.display_name]" else @@ -575,10 +576,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts /datum/preferences/proc/get_gear_metadata(var/datum/gear/G) - . = gear[G.display_name] + . = loadout_gear[G.display_name] if(!.) . = list() - gear[G.display_name] = . + loadout_gear[G.display_name] = . /datum/preferences/proc/get_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak) var/list/metadata = get_gear_metadata(G) @@ -651,10 +652,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/available_in_days = job.available_in_days(user.client) HTML += "[rank] \[IN [(available_in_days)] DAYS]" continue - if((job_support_low & CIVILIAN) && (rank != "Civilian")) + if((job_support_low & JOB_CIVILIAN) && (rank != "Civilian")) HTML += "[rank]" continue - if((rank in command_positions) || (rank == "AI"))//Bold head jobs + if((rank in GLOB.command_positions) || (rank == "AI"))//Bold head jobs HTML += "[rank]" else HTML += "[rank]" @@ -693,7 +694,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts // HTML += "" if(rank == "Civilian")//Civilian is special - if(job_support_low & CIVILIAN) + if(job_support_low & JOB_CIVILIAN) HTML += " \[Yes]" else HTML += " \[No]" @@ -720,7 +721,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts HTML += "" for(var/i = 1, i < (limit - index), i += 1) // Finish the column so it is even - HTML += "  " + HTML += "  " HTML += "" @@ -760,7 +761,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_medsci_high = 0 job_karma_high = 0 - if(job.department_flag == SUPPORT) + if(job.department_flag == JOBCAT_SUPPORT) job_support_low &= ~job.flag job_support_med &= ~job.flag job_support_high &= ~job.flag @@ -774,7 +775,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_support_low |= job.flag return 1 - else if(job.department_flag == ENGSEC) + else if(job.department_flag == JOBCAT_ENGSEC) job_engsec_low &= ~job.flag job_engsec_med &= ~job.flag job_engsec_high &= ~job.flag @@ -788,7 +789,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_engsec_low |= job.flag return 1 - else if(job.department_flag == MEDSCI) + else if(job.department_flag == JOBCAT_MEDSCI) job_medsci_low &= ~job.flag job_medsci_med &= ~job.flag job_medsci_high &= ~job.flag @@ -802,7 +803,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_medsci_low |= job.flag return 1 - else if(job.department_flag == KARMA) + else if(job.department_flag == JOBCAT_KARMA) job_karma_low &= ~job.flag job_karma_med &= ~job.flag job_karma_high &= ~job.flag @@ -846,9 +847,6 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return 1 /datum/preferences/proc/ShowDisabilityState(mob/user, flag, label) - var/datum/species/S = GLOB.all_species[species] - if(flag == DISABILITY_FLAG_FAT && !(CAN_BE_FAT in S.species_traits)) - return "
  • [species] cannot be fat.
  • " return "
  • [label]: [disabilities & flag ? "Yes" : "No"]
  • " /datum/preferences/proc/SetDisabilities(mob/user) @@ -973,7 +971,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts /datum/preferences/proc/GetJobDepartment(var/datum/job/job, var/level) if(!job || !level) return 0 switch(job.department_flag) - if(SUPPORT) + if(JOBCAT_SUPPORT) switch(level) if(1) return job_support_high @@ -981,7 +979,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return job_support_med if(3) return job_support_low - if(MEDSCI) + if(JOBCAT_MEDSCI) switch(level) if(1) return job_medsci_high @@ -989,7 +987,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return job_medsci_med if(3) return job_medsci_low - if(ENGSEC) + if(JOBCAT_ENGSEC) switch(level) if(1) return job_engsec_high @@ -997,7 +995,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return job_engsec_med if(3) return job_engsec_low - if(KARMA) + if(JOBCAT_KARMA) switch(level) if(1) return job_karma_high @@ -1027,7 +1025,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_karma_high = 0 switch(job.department_flag) - if(SUPPORT) + if(JOBCAT_SUPPORT) switch(level) if(2) job_support_high = job.flag @@ -1037,7 +1035,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_support_low &= ~job.flag else job_support_low |= job.flag - if(MEDSCI) + if(JOBCAT_MEDSCI) switch(level) if(2) job_medsci_high = job.flag @@ -1047,7 +1045,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_medsci_low &= ~job.flag else job_medsci_low |= job.flag - if(ENGSEC) + if(JOBCAT_ENGSEC) switch(level) if(2) job_engsec_high = job.flag @@ -1057,7 +1055,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_engsec_low &= ~job.flag else job_engsec_low |= job.flag - if(KARMA) + if(JOBCAT_KARMA) switch(level) if(2) job_karma_high = job.flag @@ -1115,9 +1113,8 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts SetDisabilities(user) if("input") var/dflag=text2num(href_list["disability"]) - if(dflag >= 0) - if(!(dflag==DISABILITY_FLAG_FAT && !(CAN_BE_FAT in S.species_traits))) //If the disability isn't fatness, toggle it. If it IS fatness, check to see if the species can be fat before going ahead. - disabilities ^= text2num(href_list["disability"]) //MAGIC + if(dflag >= 0) // Toggle it. + disabilities ^= text2num(href_list["disability"]) //MAGIC SetDisabilities(user) else SetDisabilities(user) @@ -1160,17 +1157,17 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if(href_list["preference"] == "gear") if(href_list["toggle_gear"]) - var/datum/gear/TG = gear_datums[href_list["toggle_gear"]] - if(TG.display_name in gear) - gear -= TG.display_name + var/datum/gear/TG = GLOB.gear_datums[href_list["toggle_gear"]] + if(TG.display_name in loadout_gear) + loadout_gear -= TG.display_name else if(TG.donator_tier && user.client.donator_level < TG.donator_tier) to_chat(user, "That gear is only available at a higher donation tier than you are on.") return var/total_cost = 0 var/list/type_blacklist = list() - for(var/gear_name in gear) - var/datum/gear/G = gear_datums[gear_name] + for(var/gear_name in loadout_gear) + var/datum/gear/G = GLOB.gear_datums[gear_name] if(istype(G)) if(!G.subtype_cost_overlap) if(G.subtype_path in type_blacklist) @@ -1179,10 +1176,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts total_cost += G.cost if((total_cost + TG.cost) <= max_gear_slots) - gear += TG.display_name + loadout_gear += TG.display_name else if(href_list["gear"] && href_list["tweak"]) - var/datum/gear/gear = gear_datums[href_list["gear"]] + var/datum/gear/gear = GLOB.gear_datums[href_list["gear"]] var/datum/gear_tweak/tweak = locate(href_list["tweak"]) if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks)) return @@ -1193,7 +1190,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts else if(href_list["select_category"]) gear_tab = href_list["select_category"] else if(href_list["clear_loadout"]) - gear.Cut() + loadout_gear.Cut() ShowChoices(user) return @@ -1203,7 +1200,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/datum/robolimb/robohead if(S.bodyflags & ALL_RPARTS) var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" - robohead = all_robolimbs[head_model] + robohead = GLOB.all_robolimbs[head_model] switch(href_list["preference"]) if("name") real_name = random_name(gender,species) @@ -1320,7 +1317,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/datum/robolimb/robohead if(NS.bodyflags & ALL_RPARTS) var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" - robohead = all_robolimbs[head_model] + robohead = GLOB.all_robolimbs[head_model] //grab one of the valid hair styles for the newly chosen species h_style = random_hair_style(gender, species, robohead) @@ -1453,7 +1450,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts head_model = "Morpheus Cyberkinetics" else head_model = rlimb_data["head"] - var/datum/robolimb/robohead = all_robolimbs[head_model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model] if((species in SA.species_allowed) && robohead.is_monitor && ((SA.models_allowed && (robohead.company in SA.models_allowed)) || !SA.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. valid_hairstyles += hairstyle //Give them their hairstyles if they do. else @@ -1536,7 +1533,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts head_model = "Morpheus Cyberkinetics" else head_model = rlimb_data["head"] - var/datum/robolimb/robohead = all_robolimbs[head_model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model] if(robohead.is_monitor && M.name != "None") //If the character can have prosthetic heads and they have the default Morpheus head (or another monitor-head), no optic markings. continue else if(!robohead.is_monitor && M.name != "None") //Otherwise, if they DON'T have the default head and the head's not a monitor but the head's not in the style's list of allowed models, skip. @@ -1613,10 +1610,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if("body_accessory") var/list/possible_body_accessories = list() if(check_rights(R_ADMIN, 1, user)) - possible_body_accessories = body_accessory_by_name.Copy() + possible_body_accessories = GLOB.body_accessory_by_name.Copy() else - for(var/B in body_accessory_by_name) - var/datum/body_accessory/accessory = body_accessory_by_name[B] + for(var/B in GLOB.body_accessory_by_name) + var/datum/body_accessory/accessory = GLOB.body_accessory_by_name[B] if(!istype(accessory)) possible_body_accessories += "None" //the only null entry should be the "None" option continue @@ -1660,7 +1657,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts head_model = "Morpheus Cyberkinetics" else head_model = rlimb_data["head"] - var/datum/robolimb/robohead = all_robolimbs[head_model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model] if((species in SA.species_allowed) && robohead.is_monitor && ((SA.models_allowed && (robohead.company in SA.models_allowed)) || !SA.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do. else @@ -1750,7 +1747,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts s_tone = max(min(round(skin_c), S.icon_skin_tones.len), 1) if("skin") - if((S.bodyflags & HAS_SKIN_COLOR) || body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) + if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", s_colour) as color|null if(new_skin) s_colour = new_skin @@ -1869,7 +1866,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if(!choice) return R.company = choice - R = all_robolimbs[R.company] + R = GLOB.all_robolimbs[R.company] if(R.has_subtypes == 1) //If the company the user selected provides more than just one base model, lets handle it. var/list/robolimb_models = list() for(var/limb_type in typesof(R)) //Handling the different models of parts that manufacturers can provide. @@ -2022,7 +2019,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if("be_special") var/r = href_list["role"] - if(r in special_roles) + if(r in GLOB.special_roles) be_special ^= r if("name") @@ -2208,7 +2205,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts character.m_styles = m_styles if(body_accessory) - character.body_accessory = body_accessory_by_name["[body_accessory]"] + character.body_accessory = GLOB.body_accessory_by_name["[body_accessory]"] character.backbag = backbag @@ -2220,54 +2217,54 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts character.change_eye_color(e_colour) - if(disabilities & DISABILITY_FLAG_FAT && (CAN_BE_FAT in character.dna.species.species_traits)) - character.dna.SetSEState(FATBLOCK, TRUE, TRUE) + if(disabilities & DISABILITY_FLAG_FAT) + character.dna.SetSEState(GLOB.fatblock, TRUE, TRUE) character.overeatduration = 600 - character.dna.default_blocks.Add(FATBLOCK) + character.dna.default_blocks.Add(GLOB.fatblock) if(disabilities & DISABILITY_FLAG_NEARSIGHTED) - character.dna.SetSEState(GLASSESBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(GLASSESBLOCK) + character.dna.SetSEState(GLOB.glassesblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.glassesblock) if(disabilities & DISABILITY_FLAG_BLIND) - character.dna.SetSEState(BLINDBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(BLINDBLOCK) + character.dna.SetSEState(GLOB.blindblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.blindblock) if(disabilities & DISABILITY_FLAG_DEAF) - character.dna.SetSEState(DEAFBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(DEAFBLOCK) + character.dna.SetSEState(GLOB.deafblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.deafblock) if(disabilities & DISABILITY_FLAG_COLOURBLIND) - character.dna.SetSEState(COLOURBLINDBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(COLOURBLINDBLOCK) + character.dna.SetSEState(GLOB.colourblindblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.colourblindblock) if(disabilities & DISABILITY_FLAG_MUTE) - character.dna.SetSEState(MUTEBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(MUTEBLOCK) + character.dna.SetSEState(GLOB.muteblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.muteblock) if(disabilities & DISABILITY_FLAG_NERVOUS) - character.dna.SetSEState(NERVOUSBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(NERVOUSBLOCK) + character.dna.SetSEState(GLOB.nervousblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.nervousblock) if(disabilities & DISABILITY_FLAG_SWEDISH) - character.dna.SetSEState(SWEDEBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(SWEDEBLOCK) + character.dna.SetSEState(GLOB.swedeblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.swedeblock) if(disabilities & DISABILITY_FLAG_CHAV) - character.dna.SetSEState(CHAVBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(CHAVBLOCK) + character.dna.SetSEState(GLOB.chavblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.chavblock) if(disabilities & DISABILITY_FLAG_LISP) - character.dna.SetSEState(LISPBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(LISPBLOCK) + character.dna.SetSEState(GLOB.lispblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.lispblock) if(disabilities & DISABILITY_FLAG_DIZZY) - character.dna.SetSEState(DIZZYBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(DIZZYBLOCK) + character.dna.SetSEState(GLOB.dizzyblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.dizzyblock) if(disabilities & DISABILITY_FLAG_WINGDINGS && (CAN_WINGDINGS in character.dna.species.species_traits)) - character.dna.SetSEState(WINGDINGSBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(WINGDINGSBLOCK) + character.dna.SetSEState(GLOB.wingdingsblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.wingdingsblock) character.dna.species.handle_dna(character) @@ -2286,7 +2283,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts /datum/preferences/proc/open_load_dialog(mob/user) - var/DBQuery/query = dbcon.NewQuery("SELECT slot,real_name FROM [format_table_name("characters")] WHERE ckey='[user.ckey]' ORDER BY slot") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT slot,real_name FROM [format_table_name("characters")] WHERE ckey='[user.ckey]' ORDER BY slot") var/list/slotnames[max_save_slots] if(!query.Execute()) diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm index 6090538f2b3..811d163c955 100644 --- a/code/modules/client/preference/preferences_mysql.dm +++ b/code/modules/client/preference/preferences_mysql.dm @@ -1,6 +1,6 @@ /datum/preferences/proc/load_preferences(client/C) - var/DBQuery/query = dbcon.NewQuery({"SELECT + var/DBQuery/query = GLOB.dbcon.NewQuery({"SELECT ooccolor, UI_style, UI_style_color, @@ -84,11 +84,11 @@ // Might as well scrub out any malformed be_special list entries while we're here for(var/role in be_special) - if(!(role in special_roles)) + if(!(role in GLOB.special_roles)) log_runtime(EXCEPTION("[C.key] had a malformed role entry: '[role]'. Removing!"), src) be_special -= role - var/DBQuery/query = dbcon.NewQuery({"UPDATE [format_table_name("player")] + var/DBQuery/query = GLOB.dbcon.NewQuery({"UPDATE [format_table_name("player")] SET ooccolor='[ooccolor]', UI_style='[UI_style]', @@ -126,11 +126,11 @@ slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot)) if(slot != default_slot) default_slot = slot - var/DBQuery/firstquery = dbcon.NewQuery("UPDATE [format_table_name("player")] SET default_slot=[slot] WHERE ckey='[C.ckey]'") + var/DBQuery/firstquery = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET default_slot=[slot] WHERE ckey='[C.ckey]'") firstquery.Execute() // Let's not have this explode if you sneeze on the DB - var/DBQuery/query = dbcon.NewQuery({"SELECT + var/DBQuery/query = GLOB.dbcon.NewQuery({"SELECT OOC_Notes, real_name, name_is_always_random, @@ -259,7 +259,7 @@ //socks socks = query.item[49] body_accessory = query.item[50] - gear = params2list(query.item[51]) + loadout_gear = params2list(query.item[51]) autohiss_mode = text2num(query.item[52]) //Sanitize @@ -318,7 +318,7 @@ if(!player_alt_titles) player_alt_titles = new() if(!organ_data) src.organ_data = list() if(!rlimb_data) src.rlimb_data = list() - if(!gear) gear = list() + if(!loadout_gear) loadout_gear = list() return 1 @@ -336,14 +336,14 @@ rlimblist = list2params(rlimb_data) if(!isemptylist(player_alt_titles)) playertitlelist = list2params(player_alt_titles) - if(!isemptylist(gear)) - gearlist = list2params(gear) + if(!isemptylist(loadout_gear)) + gearlist = list2params(loadout_gear) - var/DBQuery/firstquery = dbcon.NewQuery("SELECT slot FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' ORDER BY slot") + var/DBQuery/firstquery = GLOB.dbcon.NewQuery("SELECT slot FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' ORDER BY slot") firstquery.Execute() while(firstquery.NextRow()) if(text2num(firstquery.item[1]) == default_slot) - var/DBQuery/query = dbcon.NewQuery({"UPDATE [format_table_name("characters")] SET OOC_Notes='[sanitizeSQL(metadata)]', + var/DBQuery/query = GLOB.dbcon.NewQuery({"UPDATE [format_table_name("characters")] SET OOC_Notes='[sanitizeSQL(metadata)]', real_name='[sanitizeSQL(real_name)]', name_is_always_random='[be_random_name]', gender='[gender]', @@ -406,7 +406,7 @@ return return 1 - var/DBQuery/query = dbcon.NewQuery({" + var/DBQuery/query = GLOB.dbcon.NewQuery({" INSERT INTO [format_table_name("characters")] (ckey, slot, OOC_Notes, real_name, name_is_always_random, gender, age, species, language, hair_colour, secondary_hair_colour, @@ -473,7 +473,7 @@ return 1 /datum/preferences/proc/load_random_character_slot(client/C) - var/DBQuery/query = dbcon.NewQuery("SELECT slot FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' ORDER BY slot") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT slot FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' ORDER BY slot") var/list/saves = list() if(!query.Execute()) @@ -491,17 +491,3 @@ load_character(C,pick(saves)) return 1 -/datum/preferences/proc/SetChangelog(client/C,hash) - lastchangelog=hash - if(preferences_datums[C.ckey].toggles & UI_DARKMODE) - winset(C, "rpane.changelog", "background-color=#40628a;font-color=#ffffff;font-style=none") - else - winset(C, "rpane.changelog", "background-color=none;font-style=none") - var/DBQuery/query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastchangelog='[lastchangelog]' WHERE ckey='[C.ckey]'") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during lastchangelog updating. Error : \[[err]\]\n") - message_admins("SQL ERROR during lastchangelog updating. Error : \[[err]\]\n") - to_chat(C, "Couldn't update your last seen changelog, please try again later.") - return - return 1 diff --git a/code/modules/client/preference/preferences_spawnpoints.dm b/code/modules/client/preference/preferences_spawnpoints.dm index 38b6b87ef43..5708567130e 100644 --- a/code/modules/client/preference/preferences_spawnpoints.dm +++ b/code/modules/client/preference/preferences_spawnpoints.dm @@ -1,10 +1,10 @@ -var/list/spawntypes = list() +GLOBAL_LIST_EMPTY(spawntypes) /proc/populate_spawn_points() - spawntypes = list() + // GLOB.spawntypes = list() | This is already done, is it not for(var/type in subtypesof(/datum/spawnpoint)) var/datum/spawnpoint/S = new type() - spawntypes[S.display_name] = S + GLOB.spawntypes[S.display_name] = S /datum/spawnpoint var/msg //Message to display on the arrivals computer. @@ -28,7 +28,7 @@ var/list/spawntypes = list() /datum/spawnpoint/arrivals/New() ..() - turfs = latejoin + turfs = GLOB.latejoin /datum/spawnpoint/gateway display_name = "Gateway" @@ -36,7 +36,7 @@ var/list/spawntypes = list() /datum/spawnpoint/gateway/New() ..() - turfs = latejoin_gateway + turfs = GLOB.latejoin_gateway /datum/spawnpoint/cryo display_name = "Cryogenic Storage" @@ -45,7 +45,7 @@ var/list/spawntypes = list() /datum/spawnpoint/cryo/New() ..() - turfs = latejoin_cryo + turfs = GLOB.latejoin_cryo /datum/spawnpoint/cyborg display_name = "Cyborg Storage" @@ -54,4 +54,4 @@ var/list/spawntypes = list() /datum/spawnpoint/cyborg/New() ..() - turfs = latejoin_cyborg + turfs = GLOB.latejoin_cyborg diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index d5e4b2bf3fe..5534e18e253 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -36,6 +36,15 @@ to_chat(usr, "You will [(prefs.toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers") feedback_add_details("admin_verb","THR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/verb/toggle_ai_voice_annoucements() + set name = "Hear/Silence AI Voice Announcements" + set category = "Preferences" + set desc = "Toggle hearing AI annoucements in voice form or in text form" + prefs.sound ^= SOUND_AI_VOICE + prefs.save_preferences(src) + to_chat(usr, "[(prefs.sound & SOUND_AI_VOICE) ? "You will now hear AI announcements." : "AI annoucements will now be converted to text."] ") + feedback_add_details("admin_verb","TAIvoice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + /client/proc/toggleadminhelpsound() set name = "Hear/Silence Admin Bwoinks" set category = "Preferences" diff --git a/code/modules/client/view.dm b/code/modules/client/view.dm index f4e74e203e8..ac862933afb 100644 --- a/code/modules/client/view.dm +++ b/code/modules/client/view.dm @@ -87,4 +87,4 @@ return to_chat(src, "View range set to [viewNum_to_text(view_range)]") - AddViewMod("custom", view_range) \ No newline at end of file + AddViewMod("custom", view_range) diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index f2e9e639dd6..da0873f162f 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -542,4 +542,4 @@ /obj/item/stamp/chameleon/broken/Initialize() . = ..() - chameleon_action.emp_randomise(INFINITY) \ No newline at end of file + chameleon_action.emp_randomise(INFINITY) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index adb35b0fb3b..bfa43b92fa3 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -1,803 +1,802 @@ -/obj/item/clothing - name = "clothing" - max_integrity = 200 - integrity_failure = 80 - resistance_flags = FLAMMABLE - var/list/species_restricted = null //Only these species can wear this kit. - var/scan_reagents = 0 //Can the wearer see reagents while it's equipped? - - /* - Sprites used when the clothing item is refit. This is done by setting icon_override. - For best results, if this is set then sprite_sheets should be null and vice versa, but that is by no means necessary. - Ideally, sprite_sheets_refit should be used for "hard" clothing items that can't change shape very well to fit the wearer (e.g. helmets, hardsuits), - while sprite_sheets should be used for "flexible" clothing items that do not need to be refitted (e.g. vox wearing jumpsuits). - */ - var/list/sprite_sheets_refit = null - lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' - var/alt_desc = null - var/flash_protect = 0 //What level of bright light protection item has. 1 = Flashers, Flashes, & Flashbangs | 2 = Welding | -1 = OH GOD WELDING BURNT OUT MY RETINAS - var/tint = 0 //Sets the item's level of visual impairment tint, normally set to the same as flash_protect - var/up = 0 //but seperated to allow items to protect but not impair vision, like space helmets - - var/visor_flags = 0 //flags that are added/removed when an item is adjusted up/down - var/visor_flags_inv = 0 //same as visor_flags, but for flags_inv - var/visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_VISIONFLAGS | VISOR_DARKNESSVIEW | VISOR_INVISVIEW //what to toggle when toggled with weldingvisortoggle() - - var/toggle_message = null - var/alt_toggle_message = null - var/active_sound = null - var/toggle_sound = null - var/toggle_cooldown = null - var/cooldown = 0 - var/species_disguise = null - var/magical = FALSE - -/obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc. - if(!can_use(user)) - return FALSE - - visor_toggling() - - to_chat(user, "You adjust \the [src] [up ? "up" : "down"].") - - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.head_update(src, forced = 1) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - return TRUE - -/obj/item/clothing/proc/visor_toggling() //handles all the actual toggling of flags - up = !up - flags ^= visor_flags - flags_inv ^= visor_flags_inv - flags_cover ^= initial(flags_cover) - icon_state = "[initial(icon_state)][up ? "up" : ""]" - if(visor_vars_to_toggle & VISOR_FLASHPROTECT) - flash_protect ^= initial(flash_protect) - if(visor_vars_to_toggle & VISOR_TINT) - tint ^= initial(tint) - -/obj/item/clothing/proc/can_use(mob/user) - if(user && ismob(user)) - if(!user.incapacitated()) - return TRUE - return FALSE - -//BS12: Species-restricted clothing check. -/obj/item/clothing/mob_can_equip(M as mob, slot) - - //if we can't equip the item anyway, don't bother with species_restricted (also cuts down on spam) - if(!..()) - return 0 - - // Skip species restriction checks on non-equipment slots - if(slot in list(slot_r_hand, slot_l_hand, slot_in_backpack, slot_l_store, slot_r_store)) - return 1 - - if(species_restricted && istype(M,/mob/living/carbon/human)) - - var/wearable = null - var/exclusive = null - var/mob/living/carbon/human/H = M - - if("exclude" in species_restricted) - exclusive = 1 - - if(H.dna.species) - if(exclusive) - if(!(H.dna.species.name in species_restricted)) - wearable = 1 - else - if(H.dna.species.name in species_restricted) - wearable = 1 - - if(!wearable) - to_chat(M, "Your species cannot wear [src].") - return 0 - - return 1 - -/obj/item/clothing/proc/refit_for_species(var/target_species) - //Set species_restricted list - switch(target_species) - if("Human", "Skrell") //humanoid bodytypes - species_restricted = list("exclude","Unathi","Tajaran","Diona","Vox","Wryn","Drask") - else - species_restricted = list(target_species) - - //Set icon - if(sprite_sheets && (target_species in sprite_sheets)) - icon_override = sprite_sheets[target_species] - else - icon_override = initial(icon_override) - - if(sprite_sheets_obj && (target_species in sprite_sheets_obj)) - icon = sprite_sheets_obj[target_species] - else - icon = initial(icon) - -//Ears: currently only used for headsets and earmuffs -/obj/item/clothing/ears - name = "ears" - w_class = WEIGHT_CLASS_TINY - throwforce = 2 - slot_flags = SLOT_EARS - resistance_flags = NONE - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/ears.dmi', - "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' - ) //We read you loud and skree-er. - -/obj/item/clothing/ears/attack_hand(mob/user) - if(!user) - return - - if(loc != user || !ishuman(user)) - ..() - return - - var/mob/living/carbon/human/H = user - if(H.l_ear != src && H.r_ear != src) - ..() - return - - if(!usr.canUnEquip(src)) - return - - var/obj/item/clothing/ears/O - if(slot_flags & SLOT_TWOEARS ) - O = (H.l_ear == src ? H.r_ear : H.l_ear) - user.unEquip(O) - if(!istype(src, /obj/item/clothing/ears/offear)) - qdel(O) - O = src - else - O = src - - user.unEquip(src) - - if(O) - user.put_in_hands(O) - O.add_fingerprint(user) - - if(istype(src, /obj/item/clothing/ears/offear)) - qdel(src) - - -/obj/item/clothing/ears/offear - name = "Other ear" - w_class = WEIGHT_CLASS_HUGE - icon = 'icons/mob/screen_gen.dmi' - icon_state = "block" - slot_flags = SLOT_EARS | SLOT_TWOEARS - -/obj/item/clothing/ears/offear/New(var/obj/O) - name = O.name - desc = O.desc - icon = O.icon - icon_state = O.icon_state - dir = O.dir - - -//Glasses -/obj/item/clothing/glasses - name = "glasses" - icon = 'icons/obj/clothing/glasses.dmi' - w_class = WEIGHT_CLASS_SMALL - flags_cover = GLASSESCOVERSEYES - slot_flags = SLOT_EYES - materials = list(MAT_GLASS = 250) - var/vision_flags = 0 - var/see_in_dark = 0 //Base human is 2 - var/invis_view = SEE_INVISIBLE_LIVING - var/invis_override = 0 - var/lighting_alpha - - var/emagged = 0 - var/list/color_view = null//overrides client.color while worn - var/prescription = 0 - var/prescription_upgradable = 0 - var/over_mask = FALSE //Whether or not the eyewear is rendered above the mask. Purely cosmetic. - strip_delay = 20 // but seperated to allow items to protect but not impair vision, like space helmets - put_on_delay = 25 - resistance_flags = NONE - species_restricted = list("exclude","Kidan") -/* -SEE_SELF // can see self, no matter what -SEE_MOBS // can see all mobs, no matter what -SEE_OBJS // can see all objs, no matter what -SEE_TURFS // can see all turfs (and areas), no matter what -SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are - // in a lit area (via pixel_x,y or smooth movement), can see those pixels -BLIND // can't see anything -*/ - -/obj/item/clothing/glasses/verb/adjust_eyewear() //Adjust eyewear to be worn above or below the mask. - set name = "Adjust Eyewear" - set category = "Object" - set desc = "Adjust your eyewear to be worn over or under a mask." - set src in usr - - var/mob/living/carbon/human/user = usr - if(!istype(user)) - return - if(user.incapacitated()) //Dead spessmen adjust no glasses. Resting/buckled ones do, though - return - - var/action_fluff = "You adjust \the [src]" - if(user.glasses == src) - if(!user.canUnEquip(src)) - to_chat(usr, "[src] is stuck to you!") - return - if(attack_hand(user)) //Remove the glasses for this action. Prevents logic-defying instances where glasses phase through your mask as it ascends/descends to another plane of existence. - action_fluff = "You remove \the [src] and adjust it" - - over_mask = !over_mask - to_chat(user, "[action_fluff] to be worn [over_mask ? "over" : "under"] a mask.") - -//Gloves -/obj/item/clothing/gloves - name = "gloves" - gender = PLURAL //Carn: for grammarically correct text-parsing - w_class = WEIGHT_CLASS_SMALL - icon = 'icons/obj/clothing/gloves.dmi' - siemens_coefficient = 0.50 - body_parts_covered = HANDS - slot_flags = SLOT_GLOVES - attack_verb = list("challenged") - var/transfer_prints = FALSE - var/pickpocket = 0 //Master pickpocket? - var/clipped = 0 - strip_delay = 20 - put_on_delay = 40 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/gloves.dmi', - "Drask" = 'icons/mob/species/drask/gloves.dmi' - ) - -// 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() - -/obj/item/clothing/gloves/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/wirecutters)) - if(!clipped) - playsound(src.loc, W.usesound, 100, 1) - user.visible_message("[user] snips the fingertips off [src].","You snip the fingertips off [src].") - clipped = 1 - name = "mangled [name]" - desc = "[desc] They have had the fingertips cut off of them." - update_icon() - else - to_chat(user, "[src] have already been clipped!") - return - else - return ..() - -/obj/item/clothing/under/proc/set_sensors(mob/user as mob) - var/mob/M = user - if(istype(M, /mob/dead/)) return - if(user.stat || user.restrained()) return - if(has_sensor >= 2) - to_chat(user, "The controls are locked.") - return 0 - if(has_sensor <= 0) - to_chat(user, "This suit does not have any sensors.") - return 0 - - var/list/modes = list("Off", "Binary sensors", "Vitals tracker", "Tracking beacon") - var/switchMode = input("Select a sensor mode:", "Suit Sensor Mode", modes[sensor_mode + 1]) in modes - if(get_dist(user, src) > 1) - to_chat(user, "You have moved too far away.") - return - sensor_mode = modes.Find(switchMode) - 1 - - if(src.loc == user) - switch(sensor_mode) - if(0) - to_chat(user, "You disable your suit's remote sensing equipment.") - if(1) - to_chat(user, "Your suit will now report whether you are live or dead.") - if(2) - to_chat(user, "Your suit will now report your vital lifesigns.") - if(3) - to_chat(user, "Your suit will now report your vital lifesigns as well as your coordinate position.") - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.w_uniform == src) - H.update_suit_sensors() - - else if(istype(src.loc, /mob)) - switch(sensor_mode) - if(0) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] disables [src.loc]'s remote sensing equipment.", 1) - if(1) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] turns [src.loc]'s remote sensors to binary.", 1) - if(2) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] sets [src.loc]'s sensors to track vitals.", 1) - if(3) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] sets [src.loc]'s sensors to maximum.", 1) - if(istype(src,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = src - if(H.w_uniform == src) - H.update_suit_sensors() - -/obj/item/clothing/under/verb/toggle() - set name = "Toggle Suit Sensors" - set category = "Object" - set src in usr - set_sensors(usr) - ..() - -//Head -/obj/item/clothing/head - name = "head" - icon = 'icons/obj/clothing/hats.dmi' - body_parts_covered = HEAD - slot_flags = SLOT_HEAD - var/blockTracking // Do we block AI tracking? - var/HUDType = null - - var/vision_flags = 0 - var/see_in_dark = 0 - var/lighting_alpha - - var/can_toggle = null - -//Mask -/obj/item/clothing/mask - name = "mask" - icon = 'icons/obj/clothing/masks.dmi' - body_parts_covered = HEAD - slot_flags = SLOT_MASK - var/mask_adjusted = 0 - var/adjusted_flags = null - strip_delay = 40 - put_on_delay = 40 - -//Proc that moves gas/breath masks out of the way -/obj/item/clothing/mask/proc/adjustmask(var/mob/user) - var/mob/living/carbon/human/H = usr //Used to check if the mask is on the head, to check if the hands are full, and to turn off internals if they were on when the mask was pushed out of the way. - if(user.incapacitated()) //This check allows you to adjust your masks while you're buckled into chairs or beds. - return - if(mask_adjusted) - icon_state = initial(icon_state) - gas_transfer_coefficient = initial(gas_transfer_coefficient) - permeability_coefficient = initial(permeability_coefficient) - to_chat(user, "You push \the [src] back into place.") - mask_adjusted = 0 - slot_flags = initial(slot_flags) - if(flags_inv != initial(flags_inv)) - if(initial(flags_inv) & HIDEFACE) //If the mask is one that hides the face and can be adjusted yet lost that trait when it was adjusted, make it hide the face again. - flags_inv |= HIDEFACE - if(flags != initial(flags)) - if(initial(flags) & AIRTIGHT) //If the mask is airtight and thus, one that you'd be able to run internals from yet can't because it was adjusted, make it airtight again. - flags |= AIRTIGHT - if(flags_cover != initial(flags_cover)) - if(initial(flags_cover) & MASKCOVERSMOUTH) //If the mask covers the mouth when it's down and can be adjusted yet lost that trait when it was adjusted, make it cover the mouth again. - flags_cover |= MASKCOVERSMOUTH - if(H.head == src && flags_inv == HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. - if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. - user.unEquip(src) - else //Otherwise, put it in an available hand, the active one preferentially. - src.loc = user - H.head = null - user.put_in_hands(src) - else - icon_state += "_up" - to_chat(user, "You push \the [src] out of the way.") - gas_transfer_coefficient = null - permeability_coefficient = null - mask_adjusted = 1 - if(adjusted_flags) - slot_flags = adjusted_flags - if(ishuman(user) && H.internal && !H.get_organ_slot("breathing_tube") && user.wear_mask == src) /*If the user was wearing the mask providing internals on their face at the time it was adjusted, turn off internals. - Otherwise, they adjusted it while it was in their hands or some such so we won't be needing to turn off internals.*/ - H.internal = null - H.update_action_buttons_icon() - if(flags_inv & HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. - flags_inv &= ~HIDEFACE /*Done after the above to avoid having to do a check for initial(src.flags_inv == HIDEFACE). - This reveals the user's face since the bandana will now be going on their head.*/ - if(flags_cover & MASKCOVERSMOUTH) //Mask won't cover the mouth any more since it's been pushed out of the way. Allows for CPRing with adjusted masks. - flags_cover &= ~MASKCOVERSMOUTH - if(flags & AIRTIGHT) //If the mask was airtight, it won't be anymore since you just pushed it off your face. - flags &= ~AIRTIGHT - if(user.wear_mask == src && initial(flags_inv) == HIDEFACE) //Means that you won't have to take off and put back on simple things like breath masks which, realistically, can just be pulled down off your face. - if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. - user.unEquip(src) - else //Otherwise, put it in an available hand, the active one preferentially. - src.loc = user - user.wear_mask = null - user.put_in_hands(src) - H.wear_mask_update(src, toggle_off = mask_adjusted) - usr.update_inv_wear_mask() - usr.update_inv_head() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -//Shoes -/obj/item/clothing/shoes - name = "shoes" - icon = 'icons/obj/clothing/shoes.dmi' - desc = "Comfortable-looking shoes." - gender = PLURAL //Carn: for grammatically correct text-parsing - var/chained = 0 - var/can_cut_open = 0 - var/cut_open = 0 - body_parts_covered = FEET - slot_flags = SLOT_FEET - - var/silence_steps = 0 - var/shoe_sound_footstep = 1 - var/shoe_sound = null - var/blood_state = BLOOD_STATE_NOT_BLOODY - var/list/bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) - - permeability_coefficient = 0.50 - slowdown = SHOES_SLOWDOWN - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/shoes.dmi', - "Drask" = 'icons/mob/species/drask/shoes.dmi' - ) - -/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/match) && src.loc == user) - var/obj/item/match/M = I - if(M.matchignite()) // Match isn't lit, but isn't burnt. - user.visible_message("[user] strikes a [M] on the bottom of [src], lighting it.","You strike the [M] on the bottom of [src] to light it.") - playsound(user.loc, 'sound/goonstation/misc/matchstick_light.ogg', 50, 1) - else - user.visible_message("[user] crushes the [M] into the bottom of [src], extinguishing it.","You crush the [M] into the bottom of [src], extinguishing it.") - M.dropped() - return - - if(istype(I, /obj/item/wirecutters)) - if(can_cut_open) - if(!cut_open) - playsound(src.loc, I.usesound, 100, 1) - user.visible_message("[user] cuts open the toes of [src].","You cut open the toes of [src].") - cut_open = 1 - icon_state = "[icon_state]_opentoe" - item_state = "[item_state]_opentoe" - name = "mangled [name]" - desc = "[desc] They have had their toes opened up." - update_icon() - else - to_chat(user, "[src] have already had [p_their()] toes cut open!") - return - else - return ..() - -/obj/item/clothing/shoes/proc/step_action(var/mob/living/carbon/human/H) //squeek squeek - SEND_SIGNAL(src, COMSIG_SHOES_STEP_ACTION) - if(shoe_sound) - var/turf/T = get_turf(H) - - if(!istype(H) || !istype(T)) - return 0 - - if(H.m_intent == MOVE_INTENT_RUN) - if(shoe_sound_footstep >= 2) - if(T.shoe_running_volume) - playsound(src, shoe_sound, T.shoe_running_volume, 1) - shoe_sound_footstep = 0 - else - shoe_sound_footstep++ - else if(T.shoe_walking_volume) - playsound(src, shoe_sound, T.shoe_walking_volume, 1) - - return 1 - -/obj/item/proc/negates_gravity() - return 0 - -//Suit -/obj/item/clothing/suit - icon = 'icons/obj/clothing/suits.dmi' - name = "suit" - var/fire_resist = T0C+100 - allowed = list(/obj/item/tank/emergency_oxygen) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - slot_flags = SLOT_OCLOTHING - var/blood_overlay_type = "suit" - var/suittoggled = FALSE - var/suit_adjusted = 0 - var/ignore_suitadjust = 1 - var/adjust_flavour = null - var/list/hide_tail_by_species = null - -//Proc that opens and closes jackets. -/obj/item/clothing/suit/proc/adjustsuit(var/mob/user) - if(!ignore_suitadjust) - if(!user.incapacitated()) - if(!(HULK in user.mutations)) - if(suit_adjusted) - var/flavour = "close" - icon_state = copytext(icon_state, 1, findtext(icon_state, "_open")) /*Trims the '_open' off the end of the icon state, thus avoiding a case where jackets that start open will - end up with a suffix of _open_open if adjusted twice, since their initial state is _open. */ - item_state = copytext(item_state, 1, findtext(item_state, "_open")) - if(adjust_flavour) - flavour = "[copytext(adjust_flavour, 3, length(adjust_flavour) + 1)] up" //Trims off the 'un' at the beginning of the word. unzip -> zip, unbutton->button. - to_chat(user, "You [flavour] \the [src].") - suit_adjusted = 0 //Suit is no longer adjusted. - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - else - var/flavour = "open" - icon_state += "_open" - item_state += "_open" - if(adjust_flavour) - flavour = "[adjust_flavour]" - to_chat(user, "You [flavour] \the [src].") - suit_adjusted = 1 //Suit's adjusted. - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - else - if(user.canUnEquip(src)) //Checks to see if the item can be unequipped. If so, lets shred. Otherwise, struggle and fail. - if(contents) //If the suit's got any storage capability... - for(var/obj/item/O in contents) //AVOIDING ITEM LOSS. Check through everything that's stored in the jacket and see if one of the items is a pocket. - if(istype(O, /obj/item/storage/internal)) //If it's a pocket... - if(O.contents) //Check to see if the pocket's got anything in it. - for(var/obj/item/I in O.contents) //Dump the pocket out onto the floor below the user. - user.unEquip(I,1) - - user.visible_message("[user] bellows, [pick("shredding", "ripping open", "tearing off")] [user.p_their()] jacket in a fit of rage!","You accidentally [pick("shred", "rend", "tear apart")] [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")]!") - user.unEquip(src) - qdel(src) //Now that the pockets have been emptied, we can safely destroy the jacket. - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) - else - to_chat(user, "You yank and pull at \the [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")], however you are unable to change its state!")//Yep, that's all they get. Avoids having to snowflake in a cooldown. - - return - user.update_inv_wear_suit() - else - to_chat(user, "You attempt to button up the velcro on \the [src], before promptly realising how retarded you are.") - -/obj/item/clothing/suit/equipped(var/mob/living/carbon/human/user, var/slot) //Handle tail-hiding on a by-species basis. - ..() - if(ishuman(user) && hide_tail_by_species && slot == slot_wear_suit) - if(user.dna.species.name in hide_tail_by_species) - if(!(flags_inv & HIDETAIL)) //Hide the tail if the user's species is in the hide_tail_by_species list and the tail isn't already hidden. - flags_inv |= HIDETAIL - else - if(!(initial(flags_inv) & HIDETAIL) && (flags_inv & HIDETAIL)) //Otherwise, remove the HIDETAIL flag if it wasn't already in the flags_inv to start with. - flags_inv &= ~HIDETAIL - -/obj/item/clothing/suit/ui_action_click(mob/user) //This is what happens when you click the HUD action button to adjust your suit. - if(!ignore_suitadjust) - adjustsuit(user) - else - ..() //This is required in order to ensure that the UI buttons for items that have alternate functions tied to UI buttons still work. - -/obj/item/clothing/suit/proc/special_overlays() // Does it have special overlays when worn? - return FALSE - -//Spacesuit -//Note: Everything in modules/clothing/spacesuits should have the entire suit grouped together. -// Meaning the the suit is defined directly after the corrisponding helmet. Just like below! -/obj/item/clothing/head/helmet/space - name = "Space helmet" - icon_state = "space" - desc = "A special helmet designed for work in a hazardous, low-pressure environment." - flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - item_state = "s_helmet" - permeability_coefficient = 0.01 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT - species_restricted = list("exclude","Wryn") - flash_protect = 2 - strip_delay = 50 - put_on_delay = 50 - resistance_flags = NONE - dog_fashion = null - - -/obj/item/clothing/suit/space - name = "Space suit" - desc = "A suit that protects against low pressure environments. Has a big 13 on the back." - icon_state = "space" - item_state = "s_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/flashlight,/obj/item/tank) - slowdown = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT - strip_delay = 80 - put_on_delay = 80 - resistance_flags = NONE - hide_tail_by_species = null - species_restricted = list("exclude","Wryn") - - -//Under clothing -/obj/item/clothing/under - icon = 'icons/obj/clothing/uniforms.dmi' - name = "under" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - permeability_coefficient = 0.90 - slot_flags = SLOT_ICLOTHING - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/uniform.dmi', - "Drask" = 'icons/mob/species/drask/uniform.dmi', - "Grey" = 'icons/mob/species/grey/uniform.dmi' - ) - - var/has_sensor = TRUE//For the crew computer 2 = unable to change mode - var/sensor_mode = SENSOR_OFF - var/random_sensor = TRUE - /* - 1 = Report living/dead - 2 = Report detailed damages - 3 = Report location - */ - var/list/accessories = list() - var/displays_id = 1 - var/rolled_down = 0 - var/basecolor - -/obj/item/clothing/under/rank/New() - if(random_sensor) - sensor_mode = pick(SENSOR_OFF, SENSOR_LIVING, SENSOR_VITALS, SENSOR_COORDS) - ..() - -/obj/item/clothing/under/Destroy() - QDEL_LIST(accessories) - return ..() - -/obj/item/clothing/under/proc/can_attach_accessory(obj/item/clothing/accessory/A) - if(istype(A)) - . = TRUE - else - return FALSE - - if(accessories.len) - for(var/obj/item/clothing/accessory/AC in accessories) - if((A.slot in list(ACCESSORY_SLOT_UTILITY, ACCESSORY_SLOT_ARMBAND)) && AC.slot == A.slot) - return FALSE - if(!A.allow_duplicates && AC.type == A.type) - return FALSE - -/obj/item/clothing/under/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/clothing/accessory)) - attach_accessory(I, user, TRUE) - - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - A.attackby(I, user, params) - return TRUE - - . = ..() - -/obj/item/clothing/under/proc/attach_accessory(obj/item/clothing/accessory/A, mob/user, unequip = FALSE) - if(can_attach_accessory(A)) - if(unequip && !user.unEquip(A)) // Make absolutely sure this accessory is removed from hands - return FALSE - - accessories += A - A.on_attached(src, user) - - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - H.update_inv_w_uniform() - - return TRUE - else - to_chat(user, "You cannot attach more accessories of this type to [src].") - - return FALSE - -/obj/item/clothing/under/examine(mob/user) - . = ..() - switch(sensor_mode) - if(0) - . += "Its sensors appear to be disabled." - if(1) - . += "Its binary life sensors appear to be enabled." - if(2) - . += "Its vital tracker appears to be enabled." - if(3) - . += "Its vital tracker and tracking beacon appear to be enabled." - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - . += "\A [A] is attached to it." - - -/obj/item/clothing/under/verb/rollsuit() - set name = "Roll Down Jumpsuit" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)) return - if(usr.stat) return - - if(!usr.incapacitated()) - if(copytext(item_color,-2) != "_d") - basecolor = item_color - if(basecolor + "_d_s" in icon_states('icons/mob/uniform.dmi')) - item_color = item_color == "[basecolor]" ? "[basecolor]_d" : "[basecolor]" - usr.update_inv_w_uniform() - else - to_chat(usr, "You cannot roll down this uniform!") - else - to_chat(usr, "You cannot roll down the uniform!") - -/obj/item/clothing/under/verb/removetie() - set name = "Remove Accessory" - set category = "Object" - set src in usr - handle_accessories_removal() - -/obj/item/clothing/under/proc/handle_accessories_removal() - if(!isliving(usr)) - return - if(usr.incapacitated()) - return - if(!Adjacent(usr)) - return - if(!accessories.len) - return - var/obj/item/clothing/accessory/A - if(accessories.len > 1) - A = input("Select an accessory to remove from [src]") as null|anything in accessories - else - A = accessories[1] - remove_accessory(usr,A) - -/obj/item/clothing/under/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A) - if(!(A in accessories)) - return - if(!isliving(user)) - return - if(user.incapacitated()) - return - if(!Adjacent(user)) - return - A.on_removed(user) - accessories -= A - to_chat(user, "You remove [A] from [src].") - usr.update_inv_w_uniform() - -/obj/item/clothing/under/emp_act(severity) - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - A.emp_act(severity) - ..() - -/obj/item/clothing/under/AltClick() - handle_accessories_removal() - -/obj/item/clothing/obj_destruction(damage_flag) - if(damage_flag == "bomb" || damage_flag == "melee") - var/turf/T = get_turf(src) - spawn(1) //so the shred survives potential turf change from the explosion. - var/obj/effect/decal/cleanable/shreds/Shreds = new(T) - Shreds.desc = "The sad remains of what used to be [name]." - deconstruct(FALSE) - else - ..() \ No newline at end of file +/obj/item/clothing + name = "clothing" + max_integrity = 200 + integrity_failure = 80 + resistance_flags = FLAMMABLE + var/list/species_restricted = null //Only these species can wear this kit. + var/scan_reagents = 0 //Can the wearer see reagents while it's equipped? + + /* + Sprites used when the clothing item is refit. This is done by setting icon_override. + For best results, if this is set then sprite_sheets should be null and vice versa, but that is by no means necessary. + Ideally, sprite_sheets_refit should be used for "hard" clothing items that can't change shape very well to fit the wearer (e.g. helmets, hardsuits), + while sprite_sheets should be used for "flexible" clothing items that do not need to be refitted (e.g. vox wearing jumpsuits). + */ + var/list/sprite_sheets_refit = null + lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' + var/alt_desc = null + var/flash_protect = 0 //What level of bright light protection item has. 1 = Flashers, Flashes, & Flashbangs | 2 = Welding | -1 = OH GOD WELDING BURNT OUT MY RETINAS + var/tint = 0 //Sets the item's level of visual impairment tint, normally set to the same as flash_protect + var/up = 0 //but seperated to allow items to protect but not impair vision, like space helmets + + var/visor_flags = 0 //flags that are added/removed when an item is adjusted up/down + var/visor_flags_inv = 0 //same as visor_flags, but for flags_inv + var/visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_VISIONFLAGS | VISOR_DARKNESSVIEW | VISOR_INVISVIEW //what to toggle when toggled with weldingvisortoggle() + + var/toggle_message = null + var/alt_toggle_message = null + var/active_sound = null + var/toggle_sound = null + var/toggle_cooldown = null + var/cooldown = 0 + var/species_disguise = null + var/magical = FALSE + +/obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc. + if(!can_use(user)) + return FALSE + + visor_toggling() + + to_chat(user, "You adjust \the [src] [up ? "up" : "down"].") + + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.head_update(src, forced = 1) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + return TRUE + +/obj/item/clothing/proc/visor_toggling() //handles all the actual toggling of flags + up = !up + flags ^= visor_flags + flags_inv ^= visor_flags_inv + flags_cover ^= initial(flags_cover) + icon_state = "[initial(icon_state)][up ? "up" : ""]" + if(visor_vars_to_toggle & VISOR_FLASHPROTECT) + flash_protect ^= initial(flash_protect) + if(visor_vars_to_toggle & VISOR_TINT) + tint ^= initial(tint) + +/obj/item/clothing/proc/can_use(mob/user) + if(user && ismob(user)) + if(!user.incapacitated()) + return TRUE + return FALSE + +//BS12: Species-restricted clothing check. +/obj/item/clothing/mob_can_equip(M as mob, slot) + + //if we can't equip the item anyway, don't bother with species_restricted (also cuts down on spam) + if(!..()) + return 0 + + // Skip species restriction checks on non-equipment slots + if(slot in list(slot_r_hand, slot_l_hand, slot_in_backpack, slot_l_store, slot_r_store)) + return 1 + + if(species_restricted && istype(M,/mob/living/carbon/human)) + + var/wearable = null + var/exclusive = null + var/mob/living/carbon/human/H = M + + if("exclude" in species_restricted) + exclusive = 1 + + if(H.dna.species) + if(exclusive) + if(!(H.dna.species.name in species_restricted)) + wearable = 1 + else + if(H.dna.species.name in species_restricted) + wearable = 1 + + if(!wearable) + to_chat(M, "Your species cannot wear [src].") + return 0 + + return 1 + +/obj/item/clothing/proc/refit_for_species(var/target_species) + //Set species_restricted list + switch(target_species) + if("Human", "Skrell") //humanoid bodytypes + species_restricted = list("exclude","Unathi","Tajaran","Diona","Vox","Wryn","Drask") + else + species_restricted = list(target_species) + + //Set icon + if(sprite_sheets && (target_species in sprite_sheets)) + icon_override = sprite_sheets[target_species] + else + icon_override = initial(icon_override) + + if(sprite_sheets_obj && (target_species in sprite_sheets_obj)) + icon = sprite_sheets_obj[target_species] + else + icon = initial(icon) + +//Ears: currently only used for headsets and earmuffs +/obj/item/clothing/ears + name = "ears" + w_class = WEIGHT_CLASS_TINY + throwforce = 2 + slot_flags = SLOT_EARS + resistance_flags = NONE + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/ears.dmi', + "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' + ) //We read you loud and skree-er. + +/obj/item/clothing/ears/attack_hand(mob/user) + if(!user) + return + + if(loc != user || !ishuman(user)) + ..() + return + + var/mob/living/carbon/human/H = user + if(H.l_ear != src && H.r_ear != src) + ..() + return + + if(!usr.canUnEquip(src)) + return + + var/obj/item/clothing/ears/O + if(slot_flags & SLOT_TWOEARS ) + O = (H.l_ear == src ? H.r_ear : H.l_ear) + user.unEquip(O) + if(!istype(src, /obj/item/clothing/ears/offear)) + qdel(O) + O = src + else + O = src + + user.unEquip(src) + + if(O) + user.put_in_hands(O) + O.add_fingerprint(user) + + if(istype(src, /obj/item/clothing/ears/offear)) + qdel(src) + + +/obj/item/clothing/ears/offear + name = "Other ear" + w_class = WEIGHT_CLASS_HUGE + icon = 'icons/mob/screen_gen.dmi' + icon_state = "block" + slot_flags = SLOT_EARS | SLOT_TWOEARS + +/obj/item/clothing/ears/offear/New(var/obj/O) + name = O.name + desc = O.desc + icon = O.icon + icon_state = O.icon_state + dir = O.dir + + +//Glasses +/obj/item/clothing/glasses + name = "glasses" + icon = 'icons/obj/clothing/glasses.dmi' + w_class = WEIGHT_CLASS_SMALL + flags_cover = GLASSESCOVERSEYES + slot_flags = SLOT_EYES + materials = list(MAT_GLASS = 250) + var/vision_flags = 0 + var/see_in_dark = 0 //Base human is 2 + var/invis_view = SEE_INVISIBLE_LIVING + var/invis_override = 0 + var/lighting_alpha + + var/emagged = 0 + var/list/color_view = null//overrides client.color while worn + var/prescription = 0 + var/prescription_upgradable = 0 + var/over_mask = FALSE //Whether or not the eyewear is rendered above the mask. Purely cosmetic. + strip_delay = 20 // but seperated to allow items to protect but not impair vision, like space helmets + put_on_delay = 25 + resistance_flags = NONE + species_restricted = list("exclude","Kidan") +/* +SEE_SELF // can see self, no matter what +SEE_MOBS // can see all mobs, no matter what +SEE_OBJS // can see all objs, no matter what +SEE_TURFS // can see all turfs (and areas), no matter what +SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are + // in a lit area (via pixel_x,y or smooth movement), can see those pixels +BLIND // can't see anything +*/ + +/obj/item/clothing/glasses/verb/adjust_eyewear() //Adjust eyewear to be worn above or below the mask. + set name = "Adjust Eyewear" + set category = "Object" + set desc = "Adjust your eyewear to be worn over or under a mask." + set src in usr + + var/mob/living/carbon/human/user = usr + if(!istype(user)) + return + if(user.incapacitated()) //Dead spessmen adjust no glasses. Resting/buckled ones do, though + return + + var/action_fluff = "You adjust \the [src]" + if(user.glasses == src) + if(!user.canUnEquip(src)) + to_chat(usr, "[src] is stuck to you!") + return + if(attack_hand(user)) //Remove the glasses for this action. Prevents logic-defying instances where glasses phase through your mask as it ascends/descends to another plane of existence. + action_fluff = "You remove \the [src] and adjust it" + + over_mask = !over_mask + to_chat(user, "[action_fluff] to be worn [over_mask ? "over" : "under"] a mask.") + +//Gloves +/obj/item/clothing/gloves + name = "gloves" + gender = PLURAL //Carn: for grammarically correct text-parsing + w_class = WEIGHT_CLASS_SMALL + icon = 'icons/obj/clothing/gloves.dmi' + siemens_coefficient = 0.50 + body_parts_covered = HANDS + slot_flags = SLOT_GLOVES + attack_verb = list("challenged") + var/transfer_prints = FALSE + var/pickpocket = 0 //Master pickpocket? + var/clipped = 0 + strip_delay = 20 + put_on_delay = 40 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/gloves.dmi', + "Drask" = 'icons/mob/species/drask/gloves.dmi' + ) + +// 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() + +/obj/item/clothing/gloves/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/wirecutters)) + if(!clipped) + playsound(src.loc, W.usesound, 100, 1) + user.visible_message("[user] snips the fingertips off [src].","You snip the fingertips off [src].") + clipped = 1 + name = "mangled [name]" + desc = "[desc] They have had the fingertips cut off of them." + update_icon() + else + to_chat(user, "[src] have already been clipped!") + return + else + return ..() + +/obj/item/clothing/under/proc/set_sensors(mob/user as mob) + var/mob/M = user + if(istype(M, /mob/dead/)) return + if(user.stat || user.restrained()) return + if(has_sensor >= 2) + to_chat(user, "The controls are locked.") + return 0 + if(has_sensor <= 0) + to_chat(user, "This suit does not have any sensors.") + return 0 + + var/list/modes = list("Off", "Binary sensors", "Vitals tracker", "Tracking beacon") + var/switchMode = input("Select a sensor mode:", "Suit Sensor Mode", modes[sensor_mode + 1]) in modes + if(get_dist(user, src) > 1) + to_chat(user, "You have moved too far away.") + return + sensor_mode = modes.Find(switchMode) - 1 + + if(src.loc == user) + switch(sensor_mode) + if(0) + to_chat(user, "You disable your suit's remote sensing equipment.") + if(1) + to_chat(user, "Your suit will now report whether you are live or dead.") + if(2) + to_chat(user, "Your suit will now report your vital lifesigns.") + if(3) + to_chat(user, "Your suit will now report your vital lifesigns as well as your coordinate position.") + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.w_uniform == src) + H.update_suit_sensors() + + else if(istype(src.loc, /mob)) + switch(sensor_mode) + if(0) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] disables [src.loc]'s remote sensing equipment.", 1) + if(1) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] turns [src.loc]'s remote sensors to binary.", 1) + if(2) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] sets [src.loc]'s sensors to track vitals.", 1) + if(3) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] sets [src.loc]'s sensors to maximum.", 1) + if(istype(src,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + if(H.w_uniform == src) + H.update_suit_sensors() + +/obj/item/clothing/under/verb/toggle() + set name = "Toggle Suit Sensors" + set category = "Object" + set src in usr + set_sensors(usr) + +//Head +/obj/item/clothing/head + name = "head" + icon = 'icons/obj/clothing/hats.dmi' + body_parts_covered = HEAD + slot_flags = SLOT_HEAD + var/blockTracking // Do we block AI tracking? + var/HUDType = null + + var/vision_flags = 0 + var/see_in_dark = 0 + var/lighting_alpha + + var/can_toggle = null + +//Mask +/obj/item/clothing/mask + name = "mask" + icon = 'icons/obj/clothing/masks.dmi' + body_parts_covered = HEAD + slot_flags = SLOT_MASK + var/mask_adjusted = 0 + var/adjusted_flags = null + strip_delay = 40 + put_on_delay = 40 + +//Proc that moves gas/breath masks out of the way +/obj/item/clothing/mask/proc/adjustmask(var/mob/user) + var/mob/living/carbon/human/H = usr //Used to check if the mask is on the head, to check if the hands are full, and to turn off internals if they were on when the mask was pushed out of the way. + if(user.incapacitated()) //This check allows you to adjust your masks while you're buckled into chairs or beds. + return + if(mask_adjusted) + icon_state = initial(icon_state) + gas_transfer_coefficient = initial(gas_transfer_coefficient) + permeability_coefficient = initial(permeability_coefficient) + to_chat(user, "You push \the [src] back into place.") + mask_adjusted = 0 + slot_flags = initial(slot_flags) + if(flags_inv != initial(flags_inv)) + if(initial(flags_inv) & HIDEFACE) //If the mask is one that hides the face and can be adjusted yet lost that trait when it was adjusted, make it hide the face again. + flags_inv |= HIDEFACE + if(flags != initial(flags)) + if(initial(flags) & AIRTIGHT) //If the mask is airtight and thus, one that you'd be able to run internals from yet can't because it was adjusted, make it airtight again. + flags |= AIRTIGHT + if(flags_cover != initial(flags_cover)) + if(initial(flags_cover) & MASKCOVERSMOUTH) //If the mask covers the mouth when it's down and can be adjusted yet lost that trait when it was adjusted, make it cover the mouth again. + flags_cover |= MASKCOVERSMOUTH + if(H.head == src && flags_inv == HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. + if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. + user.unEquip(src) + else //Otherwise, put it in an available hand, the active one preferentially. + src.loc = user + H.head = null + user.put_in_hands(src) + else + icon_state += "_up" + to_chat(user, "You push \the [src] out of the way.") + gas_transfer_coefficient = null + permeability_coefficient = null + mask_adjusted = 1 + if(adjusted_flags) + slot_flags = adjusted_flags + if(ishuman(user) && H.internal && !H.get_organ_slot("breathing_tube") && user.wear_mask == src) /*If the user was wearing the mask providing internals on their face at the time it was adjusted, turn off internals. + Otherwise, they adjusted it while it was in their hands or some such so we won't be needing to turn off internals.*/ + H.internal = null + H.update_action_buttons_icon() + if(flags_inv & HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. + flags_inv &= ~HIDEFACE /*Done after the above to avoid having to do a check for initial(src.flags_inv == HIDEFACE). + This reveals the user's face since the bandana will now be going on their head.*/ + if(flags_cover & MASKCOVERSMOUTH) //Mask won't cover the mouth any more since it's been pushed out of the way. Allows for CPRing with adjusted masks. + flags_cover &= ~MASKCOVERSMOUTH + if(flags & AIRTIGHT) //If the mask was airtight, it won't be anymore since you just pushed it off your face. + flags &= ~AIRTIGHT + if(user.wear_mask == src && initial(flags_inv) == HIDEFACE) //Means that you won't have to take off and put back on simple things like breath masks which, realistically, can just be pulled down off your face. + if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. + user.unEquip(src) + else //Otherwise, put it in an available hand, the active one preferentially. + src.loc = user + user.wear_mask = null + user.put_in_hands(src) + H.wear_mask_update(src, toggle_off = mask_adjusted) + usr.update_inv_wear_mask() + usr.update_inv_head() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +//Shoes +/obj/item/clothing/shoes + name = "shoes" + icon = 'icons/obj/clothing/shoes.dmi' + desc = "Comfortable-looking shoes." + gender = PLURAL //Carn: for grammatically correct text-parsing + var/chained = 0 + var/can_cut_open = 0 + var/cut_open = 0 + body_parts_covered = FEET + slot_flags = SLOT_FEET + + var/silence_steps = 0 + var/shoe_sound_footstep = 1 + var/shoe_sound = null + var/blood_state = BLOOD_STATE_NOT_BLOODY + var/list/bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) + + permeability_coefficient = 0.50 + slowdown = SHOES_SLOWDOWN + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/shoes.dmi', + "Drask" = 'icons/mob/species/drask/shoes.dmi' + ) + +/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/match) && src.loc == user) + var/obj/item/match/M = I + if(M.matchignite()) // Match isn't lit, but isn't burnt. + user.visible_message("[user] strikes a [M] on the bottom of [src], lighting it.","You strike the [M] on the bottom of [src] to light it.") + playsound(user.loc, 'sound/goonstation/misc/matchstick_light.ogg', 50, 1) + else + user.visible_message("[user] crushes the [M] into the bottom of [src], extinguishing it.","You crush the [M] into the bottom of [src], extinguishing it.") + M.dropped() + return + + if(istype(I, /obj/item/wirecutters)) + if(can_cut_open) + if(!cut_open) + playsound(src.loc, I.usesound, 100, 1) + user.visible_message("[user] cuts open the toes of [src].","You cut open the toes of [src].") + cut_open = 1 + icon_state = "[icon_state]_opentoe" + item_state = "[item_state]_opentoe" + name = "mangled [name]" + desc = "[desc] They have had their toes opened up." + update_icon() + else + to_chat(user, "[src] have already had [p_their()] toes cut open!") + return + else + return ..() + +/obj/item/clothing/shoes/proc/step_action(var/mob/living/carbon/human/H) //squeek squeek + SEND_SIGNAL(src, COMSIG_SHOES_STEP_ACTION) + if(shoe_sound) + var/turf/T = get_turf(H) + + if(!istype(H) || !istype(T)) + return 0 + + if(H.m_intent == MOVE_INTENT_RUN) + if(shoe_sound_footstep >= 2) + if(T.shoe_running_volume) + playsound(src, shoe_sound, T.shoe_running_volume, 1) + shoe_sound_footstep = 0 + else + shoe_sound_footstep++ + else if(T.shoe_walking_volume) + playsound(src, shoe_sound, T.shoe_walking_volume, 1) + + return 1 + +/obj/item/proc/negates_gravity() + return 0 + +//Suit +/obj/item/clothing/suit + icon = 'icons/obj/clothing/suits.dmi' + name = "suit" + var/fire_resist = T0C+100 + allowed = list(/obj/item/tank/emergency_oxygen) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + slot_flags = SLOT_OCLOTHING + var/blood_overlay_type = "suit" + var/suittoggled = FALSE + var/suit_adjusted = 0 + var/ignore_suitadjust = 1 + var/adjust_flavour = null + var/list/hide_tail_by_species = null + +//Proc that opens and closes jackets. +/obj/item/clothing/suit/proc/adjustsuit(var/mob/user) + if(!ignore_suitadjust) + if(!user.incapacitated()) + if(!(HULK in user.mutations)) + if(suit_adjusted) + var/flavour = "close" + icon_state = copytext(icon_state, 1, findtext(icon_state, "_open")) /*Trims the '_open' off the end of the icon state, thus avoiding a case where jackets that start open will + end up with a suffix of _open_open if adjusted twice, since their initial state is _open. */ + item_state = copytext(item_state, 1, findtext(item_state, "_open")) + if(adjust_flavour) + flavour = "[copytext(adjust_flavour, 3, length(adjust_flavour) + 1)] up" //Trims off the 'un' at the beginning of the word. unzip -> zip, unbutton->button. + to_chat(user, "You [flavour] \the [src].") + suit_adjusted = 0 //Suit is no longer adjusted. + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + else + var/flavour = "open" + icon_state += "_open" + item_state += "_open" + if(adjust_flavour) + flavour = "[adjust_flavour]" + to_chat(user, "You [flavour] \the [src].") + suit_adjusted = 1 //Suit's adjusted. + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + else + if(user.canUnEquip(src)) //Checks to see if the item can be unequipped. If so, lets shred. Otherwise, struggle and fail. + if(contents) //If the suit's got any storage capability... + for(var/obj/item/O in contents) //AVOIDING ITEM LOSS. Check through everything that's stored in the jacket and see if one of the items is a pocket. + if(istype(O, /obj/item/storage/internal)) //If it's a pocket... + if(O.contents) //Check to see if the pocket's got anything in it. + for(var/obj/item/I in O.contents) //Dump the pocket out onto the floor below the user. + user.unEquip(I,1) + + user.visible_message("[user] bellows, [pick("shredding", "ripping open", "tearing off")] [user.p_their()] jacket in a fit of rage!","You accidentally [pick("shred", "rend", "tear apart")] [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")]!") + user.unEquip(src) + qdel(src) //Now that the pockets have been emptied, we can safely destroy the jacket. + user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) + else + to_chat(user, "You yank and pull at \the [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")], however you are unable to change its state!")//Yep, that's all they get. Avoids having to snowflake in a cooldown. + + return + user.update_inv_wear_suit() + else + to_chat(user, "You attempt to button up the velcro on \the [src], before promptly realising how foolish you are.") + +/obj/item/clothing/suit/equipped(var/mob/living/carbon/human/user, var/slot) //Handle tail-hiding on a by-species basis. + ..() + if(ishuman(user) && hide_tail_by_species && slot == slot_wear_suit) + if(user.dna.species.name in hide_tail_by_species) + if(!(flags_inv & HIDETAIL)) //Hide the tail if the user's species is in the hide_tail_by_species list and the tail isn't already hidden. + flags_inv |= HIDETAIL + else + if(!(initial(flags_inv) & HIDETAIL) && (flags_inv & HIDETAIL)) //Otherwise, remove the HIDETAIL flag if it wasn't already in the flags_inv to start with. + flags_inv &= ~HIDETAIL + +/obj/item/clothing/suit/ui_action_click(mob/user) //This is what happens when you click the HUD action button to adjust your suit. + if(!ignore_suitadjust) + adjustsuit(user) + else + ..() //This is required in order to ensure that the UI buttons for items that have alternate functions tied to UI buttons still work. + +/obj/item/clothing/suit/proc/special_overlays() // Does it have special overlays when worn? + return FALSE + +//Spacesuit +//Note: Everything in modules/clothing/spacesuits should have the entire suit grouped together. +// Meaning the the suit is defined directly after the corrisponding helmet. Just like below! +/obj/item/clothing/head/helmet/space + name = "Space helmet" + icon_state = "space" + desc = "A special helmet designed for work in a hazardous, low-pressure environment." + flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + item_state = "s_helmet" + permeability_coefficient = 0.01 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + species_restricted = list("exclude","Wryn") + flash_protect = 2 + strip_delay = 50 + put_on_delay = 50 + resistance_flags = NONE + dog_fashion = null + + +/obj/item/clothing/suit/space + name = "Space suit" + desc = "A suit that protects against low pressure environments. Has a big 13 on the back." + icon_state = "space" + item_state = "s_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.02 + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/flashlight,/obj/item/tank) + slowdown = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + strip_delay = 80 + put_on_delay = 80 + resistance_flags = NONE + hide_tail_by_species = null + species_restricted = list("exclude","Wryn") + + +//Under clothing +/obj/item/clothing/under + icon = 'icons/obj/clothing/uniforms.dmi' + name = "under" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + permeability_coefficient = 0.90 + slot_flags = SLOT_ICLOTHING + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/uniform.dmi', + "Drask" = 'icons/mob/species/drask/uniform.dmi', + "Grey" = 'icons/mob/species/grey/uniform.dmi' + ) + + var/has_sensor = TRUE//For the crew computer 2 = unable to change mode + var/sensor_mode = SENSOR_OFF + var/random_sensor = TRUE + /* + 1 = Report living/dead + 2 = Report detailed damages + 3 = Report location + */ + var/list/accessories = list() + var/displays_id = 1 + var/rolled_down = 0 + var/basecolor + +/obj/item/clothing/under/rank/New() + if(random_sensor) + sensor_mode = pick(SENSOR_OFF, SENSOR_LIVING, SENSOR_VITALS, SENSOR_COORDS) + ..() + +/obj/item/clothing/under/Destroy() + QDEL_LIST(accessories) + return ..() + +/obj/item/clothing/under/proc/can_attach_accessory(obj/item/clothing/accessory/A) + if(istype(A)) + . = TRUE + else + return FALSE + + if(accessories.len) + for(var/obj/item/clothing/accessory/AC in accessories) + if((A.slot in list(ACCESSORY_SLOT_UTILITY, ACCESSORY_SLOT_ARMBAND)) && AC.slot == A.slot) + return FALSE + if(!A.allow_duplicates && AC.type == A.type) + return FALSE + +/obj/item/clothing/under/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/clothing/accessory)) + attach_accessory(I, user, TRUE) + + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + A.attackby(I, user, params) + return TRUE + + . = ..() + +/obj/item/clothing/under/proc/attach_accessory(obj/item/clothing/accessory/A, mob/user, unequip = FALSE) + if(can_attach_accessory(A)) + if(unequip && !user.unEquip(A)) // Make absolutely sure this accessory is removed from hands + return FALSE + + accessories += A + A.on_attached(src, user) + + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + H.update_inv_w_uniform() + + return TRUE + else + to_chat(user, "You cannot attach more accessories of this type to [src].") + + return FALSE + +/obj/item/clothing/under/examine(mob/user) + . = ..() + switch(sensor_mode) + if(0) + . += "Its sensors appear to be disabled." + if(1) + . += "Its binary life sensors appear to be enabled." + if(2) + . += "Its vital tracker appears to be enabled." + if(3) + . += "Its vital tracker and tracking beacon appear to be enabled." + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + . += "\A [A] is attached to it." + + +/obj/item/clothing/under/verb/rollsuit() + set name = "Roll Down Jumpsuit" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) return + if(usr.stat) return + + if(!usr.incapacitated()) + if(copytext(item_color,-2) != "_d") + basecolor = item_color + if((basecolor + "_d_s") in icon_states('icons/mob/uniform.dmi')) + item_color = item_color == "[basecolor]" ? "[basecolor]_d" : "[basecolor]" + usr.update_inv_w_uniform() + else + to_chat(usr, "You cannot roll down this uniform!") + else + to_chat(usr, "You cannot roll down the uniform!") + +/obj/item/clothing/under/verb/removetie() + set name = "Remove Accessory" + set category = "Object" + set src in usr + handle_accessories_removal() + +/obj/item/clothing/under/proc/handle_accessories_removal() + if(!isliving(usr)) + return + if(usr.incapacitated()) + return + if(!Adjacent(usr)) + return + if(!accessories.len) + return + var/obj/item/clothing/accessory/A + if(accessories.len > 1) + A = input("Select an accessory to remove from [src]") as null|anything in accessories + else + A = accessories[1] + remove_accessory(usr,A) + +/obj/item/clothing/under/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A) + if(!(A in accessories)) + return + if(!isliving(user)) + return + if(user.incapacitated()) + return + if(!Adjacent(user)) + return + A.on_removed(user) + accessories -= A + to_chat(user, "You remove [A] from [src].") + usr.update_inv_w_uniform() + +/obj/item/clothing/under/emp_act(severity) + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + A.emp_act(severity) + ..() + +/obj/item/clothing/under/AltClick() + handle_accessories_removal() + +/obj/item/clothing/obj_destruction(damage_flag) + if(damage_flag == "bomb" || damage_flag == "melee") + var/turf/T = get_turf(src) + spawn(1) //so the shred survives potential turf change from the explosion. + var/obj/effect/decal/cleanable/shreds/Shreds = new(T) + Shreds.desc = "The sad remains of what used to be [name]." + deconstruct(FALSE) + else + ..() diff --git a/code/modules/clothing/ears/ears.dm b/code/modules/clothing/ears/ears.dm index 2ba883a6f89..ea8f4198f50 100644 --- a/code/modules/clothing/ears/ears.dm +++ b/code/modules/clothing/ears/ears.dm @@ -25,4 +25,4 @@ var/datum/action/A = X A.UpdateButtonIcon() - user.update_inv_ears() \ No newline at end of file + user.update_inv_ears() diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index e80f461246e..1b9c87f56e5 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -1,525 +1,525 @@ -/obj/item/clothing/glasses/New() - . = ..() - if(prescription_upgradable && prescription) - // Pre-upgraded upgradable glasses - name = "prescription [name]" - -/obj/item/clothing/glasses/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(user.stat || user.restrained() || !ishuman(user)) - return ..() - var/mob/living/carbon/human/H = user - if(prescription_upgradable) - if(istype(O, /obj/item/clothing/glasses/regular)) - if(prescription) - to_chat(H, "You can't possibly imagine how adding more lenses would improve \the [name].") - return - H.unEquip(O) - O.loc = src // Store the glasses for later removal - to_chat(H, "You fit \the [name] with lenses from \the [O].") - prescription = 1 - name = "prescription [name]" - return - if(prescription && istype(O, /obj/item/screwdriver)) - var/obj/item/clothing/glasses/regular/G = locate() in src - if(!G) - G = new(get_turf(H)) - to_chat(H, "You salvage the prescription lenses from \the [name].") - prescription = 0 - name = initial(name) - H.put_in_hands(G) - return - return ..() - -/obj/item/clothing/glasses/visor_toggling() - ..() - if(visor_vars_to_toggle & VISOR_VISIONFLAGS) - vision_flags ^= initial(vision_flags) - if(visor_vars_to_toggle & VISOR_DARKNESSVIEW) - see_in_dark ^= initial(see_in_dark) - if(visor_vars_to_toggle & VISOR_INVISVIEW) - invis_view ^= initial(invis_view) - -/obj/item/clothing/glasses/weldingvisortoggle(mob/user) - . = ..() - if(. && user) - user.update_sight() - user.update_inv_glasses() - -/obj/item/clothing/glasses/meson - name = "Optical Meson Scanner" - desc = "Used for seeing walls, floors, and stuff through anything." - icon_state = "meson" - item_state = "glasses" - origin_tech = "magnets=1;engineering=2" - vision_flags = SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/meson/night - name = "Night Vision Optical Meson Scanner" - desc = "An Optical Meson Scanner fitted with an amplified visible light spectrum overlay, providing greater visual clarity in darkness." - icon_state = "nvgmeson" - item_state = "glasses" - origin_tech = "magnets=4;engineering=5;plasmatech=4" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/meson/prescription - prescription = 1 - -/obj/item/clothing/glasses/meson/gar - name = "gar mesons" - icon_state = "garm" - item_state = "garm" - desc = "Do the impossible, see the invisible!" - force = 10 - throwforce = 10 - throw_speed = 4 - attack_verb = list("sliced") - hitsound = 'sound/weapons/bladeslice.ogg' - sharp = 1 - -/obj/item/clothing/glasses/meson/cyber - name = "Eye Replacement Implant" - desc = "An implanted replacement for a left eye with meson vision capabilities." - icon_state = "cybereye-green" - item_state = "eyepatch" - flags = NODROP - flags_cover = null - prescription_upgradable = 0 - -/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" - origin_tech = "magnets=2;engineering=1" - prescription_upgradable = 0 - scan_reagents = 1 //You can see reagents while wearing science goggles - resistance_flags = ACID_PROOF - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - actions_types = list(/datum/action/item_action/toggle_research_scanner) - -/obj/item/clothing/glasses/science/item_action_slot_check(slot) - if(slot == slot_glasses) - return 1 - -/obj/item/clothing/glasses/science/night - name = "Night Vision Science Goggle" - desc = "Now you can science in darkness." - icon_state = "nvpurple" - item_state = "glasses" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - -/obj/item/clothing/glasses/janitor - name = "Janitorial Goggles" - desc = "These'll keep the soap out of your eyes." - icon_state = "purple" - item_state = "glasses" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi' - ) - -/obj/item/clothing/glasses/night - name = "Night Vision Goggles" - desc = "You can totally see in the dark now!" - icon_state = "night" - item_state = "glasses" - origin_tech = "materials=4;magnets=4;plasmatech=4;engineering=4" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/eyepatch - name = "eyepatch" - desc = "Yarr." - icon_state = "eyepatch" - item_state = "eyepatch" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/monocle - name = "monocle" - desc = "Such a dapper eyepiece!" - icon_state = "monocle" - item_state = "headset" // lol - prescription_upgradable = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/material - name = "Optical Material Scanner" - desc = "Very confusing glasses." - icon_state = "material" - item_state = "glasses" - origin_tech = "magnets=3;engineering=3" - vision_flags = SEE_OBJS - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/material/cyber - name = "Eye Replacement Implant" - desc = "An implanted replacement for a left eye with material vision capabilities." - icon_state = "cybereye-blue" - item_state = "eyepatch" - flags = NODROP - flags_cover = null - -/obj/item/clothing/glasses/material/lighting - name = "Neutron Goggles" - desc = "These odd glasses use a form of neutron-based imaging to completely negate the effects of light and darkness." - origin_tech = null - vision_flags = 0 - - flags = NODROP - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - -/obj/item/clothing/glasses/regular - name = "prescription glasses" - desc = "Made by Nerd. Co." - icon_state = "glasses" - item_state = "glasses" - prescription = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/regular/hipster - name = "prescription glasses" - desc = "Made by Uncool. Co." - icon_state = "hipster_glasses" - item_state = "hipster_glasses" - -/obj/item/clothing/glasses/threedglasses - desc = "A long time ago, people used these glasses to makes images from screens threedimensional." - name = "3D glasses" - icon_state = "3d" - item_state = "3d" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/gglasses - name = "Green Glasses" - desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." - icon_state = "gglasses" - item_state = "gglasses" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - prescription_upgradable = 1 - -/obj/item/clothing/glasses/sunglasses - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." - name = "sunglasses" - icon_state = "sun" - item_state = "sunglasses" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - prescription_upgradable = 1 - dog_fashion = /datum/dog_fashion/head - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/sunglasses_fake - desc = "Cheap, plastic sunglasses. They don't even have UV protection." - name = "cheap sunglasses" - icon_state = "sun" - item_state = "sunglasses" - see_in_dark = 0 - flash_protect = 0 - tint = 0 - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/sunglasses/noir - name = "noir sunglasses" - desc = "Somehow these seem even more out-of-date than normal sunglasses." - actions_types = list(/datum/action/item_action/noir) - -/obj/item/clothing/glasses/sunglasses/noir/attack_self(mob/user) - toggle_noir(user) - -/obj/item/clothing/glasses/sunglasses/noir/item_action_slot_check(slot) - if(slot == slot_glasses) - return 1 - -/obj/item/clothing/glasses/sunglasses/noir/proc/toggle_noir(mob/user) - color_view = color_view ? null : MATRIX_GREYSCALE //Toggles between null and grayscale, with null being the default option. - user.update_client_colour() - -/obj/item/clothing/glasses/sunglasses/yeah - name = "agreeable glasses" - desc = "H.C Limited edition." - var/punused = null - actions_types = list(/datum/action/item_action/YEEEAAAAAHHHHHHHHHHHHH) - -/obj/item/clothing/glasses/sunglasses/yeah/attack_self() - pun() - -/obj/item/clothing/glasses/sunglasses/yeah/proc/pun() - if(!punused)//one per round - punused = 1 - playsound(src.loc, 'sound/misc/yeah.ogg', 100, 0) - usr.visible_message("YEEEAAAAAHHHHHHHHHHHHH!!") - else - to_chat(usr, "The moment is gone.") - - -/obj/item/clothing/glasses/sunglasses/reagent - name = "sunscanners" - desc = "Strangely ancient technology used to help provide rudimentary eye color. Outfitted with apparatus to scan individual reagents." - scan_reagents = 1 - -/obj/item/clothing/glasses/virussunglasses - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." - name = "sunglasses" - icon_state = "sun" - item_state = "sunglasses" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/sunglasses/lasers - desc = "A peculiar set of sunglasses; they have various chips and other panels attached to the sides of the frames." - name = "high-tech sunglasses" - flags = NODROP - -/obj/item/clothing/glasses/sunglasses/lasers/equipped(mob/user, slot) //grant them laser eyes upon equipping it. - if(slot == slot_glasses) - user.mutations.Add(LASER) - user.regenerate_icons() - ..(user, slot) - -/obj/item/clothing/glasses/welding - name = "welding goggles" - desc = "Protects the eyes from welders, approved by the mad scientist association." - icon_state = "welding-g" - item_state = "welding-g" - actions_types = list(/datum/action/item_action/toggle) - flash_protect = 2 - tint = 2 - visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/welding/attack_self(mob/user) - weldingvisortoggle(user) - -/obj/item/clothing/glasses/welding/superior - name = "superior welding goggles" - desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." - icon_state = "rwelding-g" - item_state = "rwelding-g" - flash_protect = 2 - tint = 0 - -/obj/item/clothing/glasses/sunglasses/blindfold - name = "blindfold" - desc = "Covers the eyes, preventing sight." - icon_state = "blindfold" - item_state = "blindfold" - //vision_flags = BLIND - flash_protect = 2 - tint = 3 //to make them blind - prescription_upgradable = 0 - -/obj/item/clothing/glasses/sunglasses/prescription - prescription = 1 - -/obj/item/clothing/glasses/sunglasses/big - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." - icon_state = "bigsunglasses" - item_state = "bigsunglasses" - -/obj/item/clothing/glasses/thermal - name = "Optical Thermal Scanner" - desc = "Thermals in the shape of glasses." - icon_state = "thermal" - item_state = "glasses" - origin_tech = "magnets=3" - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - flash_protect = -1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/thermal/emp_act(severity) - if(istype(src.loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/M = src.loc - to_chat(M, "The Optical Thermal Scanner overloads and blinds you!") - if(M.glasses == src) - M.EyeBlind(3) - M.EyeBlurry(5) - if(!(M.disabilities & NEARSIGHTED)) - M.BecomeNearsighted() - spawn(100) - M.CureNearsighted() - ..() - -/obj/item/clothing/glasses/thermal/monocle - name = "Thermoncle" - desc = "A monocle thermal." - icon_state = "thermoncle" - flags_cover = null //doesn't protect eyes because it's a monocle, duh - -/obj/item/clothing/glasses/thermal/eyepatch - name = "Optical Thermal Eyepatch" - desc = "An eyepatch with built-in thermal optics" - icon_state = "eyepatch" - item_state = "eyepatch" - -/obj/item/clothing/glasses/thermal/jensen - name = "Optical Thermal Implants" - desc = "A set of implantable lenses designed to augment your vision" - icon_state = "thermalimplants" - item_state = "syringe_kit" - -/obj/item/clothing/glasses/thermal/cyber - name = "Eye Replacement Implant" - desc = "An implanted replacement for a left eye with thermal vision capabilities." - icon_state = "cybereye-red" - item_state = "eyepatch" - flags = NODROP - - -/obj/item/clothing/glasses/godeye - name = "eye of god" - desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes." - icon_state = "godeye" - item_state = "godeye" - vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS - see_in_dark = 8 - scan_reagents = 1 - flags = NODROP - flags_cover = null - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - resistance_flags = LAVA_PROOF | FIRE_PROOF - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/godeye/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, src) && W != src && W.loc == user) - if(W.icon_state == "godeye") - W.icon_state = "doublegodeye" - W.item_state = "doublegodeye" - W.desc = "A pair of strange eyes, said to have been torn from an omniscient creature that used to roam the wastes. There's no real reason to have two, but that isn't stopping you." - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.update_inv_wear_mask() - else - to_chat(user, "The eye winks at you and vanishes into the abyss, you feel really unlucky.") - qdel(src) - ..() - -/obj/item/clothing/glasses/tajblind - name = "embroidered veil" - desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes." - icon_state = "tajblind" - item_state = "tajblind" - flags_cover = GLASSESCOVERSEYES - actions_types = list(/datum/action/item_action/toggle) - up = 0 - tint = 0 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/tajblind/eng - name = "industrial veil" - icon_state = "tajblind_engi" - item_state = "tajblind_engi" - -/obj/item/clothing/glasses/tajblind/sci - name = "hi-tech veil" - icon_state = "tajblind_sci" - item_state = "tajblind_sci" - -/obj/item/clothing/glasses/tajblind/cargo - name = "khaki veil" - icon_state = "tajblind_cargo" - item_state = "tajblind_cargo" - -/obj/item/clothing/glasses/tajblind/attack_self() - toggle_veil() - -/obj/item/clothing/glasses/proc/toggle_veil() - if(usr.canmove && !usr.incapacitated()) - if(up) - up = !up - tint = initial(tint) - to_chat(usr, "You activate [src], allowing you to see.") - else - up = !up - tint = 3 - to_chat(usr, "You deactivate [src], obscuring your vision.") - var/mob/living/carbon/user = usr - user.update_tint() - user.update_inv_glasses() \ No newline at end of file +/obj/item/clothing/glasses/New() + . = ..() + if(prescription_upgradable && prescription) + // Pre-upgraded upgradable glasses + name = "prescription [name]" + +/obj/item/clothing/glasses/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(user.stat || user.restrained() || !ishuman(user)) + return ..() + var/mob/living/carbon/human/H = user + if(prescription_upgradable) + if(istype(O, /obj/item/clothing/glasses/regular)) + if(prescription) + to_chat(H, "You can't possibly imagine how adding more lenses would improve \the [name].") + return + H.unEquip(O) + O.loc = src // Store the glasses for later removal + to_chat(H, "You fit \the [name] with lenses from \the [O].") + prescription = 1 + name = "prescription [name]" + return + if(prescription && istype(O, /obj/item/screwdriver)) + var/obj/item/clothing/glasses/regular/G = locate() in src + if(!G) + G = new(get_turf(H)) + to_chat(H, "You salvage the prescription lenses from \the [name].") + prescription = 0 + name = initial(name) + H.put_in_hands(G) + return + return ..() + +/obj/item/clothing/glasses/visor_toggling() + ..() + if(visor_vars_to_toggle & VISOR_VISIONFLAGS) + vision_flags ^= initial(vision_flags) + if(visor_vars_to_toggle & VISOR_DARKNESSVIEW) + see_in_dark ^= initial(see_in_dark) + if(visor_vars_to_toggle & VISOR_INVISVIEW) + invis_view ^= initial(invis_view) + +/obj/item/clothing/glasses/weldingvisortoggle(mob/user) + . = ..() + if(. && user) + user.update_sight() + user.update_inv_glasses() + +/obj/item/clothing/glasses/meson + name = "Optical Meson Scanner" + desc = "Used for seeing walls, floors, and stuff through anything." + icon_state = "meson" + item_state = "glasses" + origin_tech = "magnets=1;engineering=2" + vision_flags = SEE_TURFS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/meson/night + name = "Night Vision Optical Meson Scanner" + desc = "An Optical Meson Scanner fitted with an amplified visible light spectrum overlay, providing greater visual clarity in darkness." + icon_state = "nvgmeson" + item_state = "glasses" + origin_tech = "magnets=4;engineering=5;plasmatech=4" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/meson/prescription + prescription = 1 + +/obj/item/clothing/glasses/meson/gar + name = "gar mesons" + icon_state = "garm" + item_state = "garm" + desc = "Do the impossible, see the invisible!" + force = 10 + throwforce = 10 + throw_speed = 4 + attack_verb = list("sliced") + hitsound = 'sound/weapons/bladeslice.ogg' + sharp = 1 + +/obj/item/clothing/glasses/meson/cyber + name = "Eye Replacement Implant" + desc = "An implanted replacement for a left eye with meson vision capabilities." + icon_state = "cybereye-green" + item_state = "eyepatch" + flags = NODROP + flags_cover = null + prescription_upgradable = 0 + +/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" + origin_tech = "magnets=2;engineering=1" + prescription_upgradable = 0 + scan_reagents = 1 //You can see reagents while wearing science goggles + resistance_flags = ACID_PROOF + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + actions_types = list(/datum/action/item_action/toggle_research_scanner) + +/obj/item/clothing/glasses/science/item_action_slot_check(slot) + if(slot == slot_glasses) + return 1 + +/obj/item/clothing/glasses/science/night + name = "Night Vision Science Goggle" + desc = "Now you can science in darkness." + icon_state = "nvpurple" + item_state = "glasses" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + +/obj/item/clothing/glasses/janitor + name = "Janitorial Goggles" + desc = "These'll keep the soap out of your eyes." + icon_state = "purple" + item_state = "glasses" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi' + ) + +/obj/item/clothing/glasses/night + name = "Night Vision Goggles" + desc = "You can totally see in the dark now!" + icon_state = "night" + item_state = "glasses" + origin_tech = "materials=4;magnets=4;plasmatech=4;engineering=4" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/eyepatch + name = "eyepatch" + desc = "Yarr." + icon_state = "eyepatch" + item_state = "eyepatch" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/monocle + name = "monocle" + desc = "Such a dapper eyepiece!" + icon_state = "monocle" + item_state = "headset" // lol + prescription_upgradable = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/material + name = "Optical Material Scanner" + desc = "Very confusing glasses." + icon_state = "material" + item_state = "glasses" + origin_tech = "magnets=3;engineering=3" + vision_flags = SEE_OBJS + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/material/cyber + name = "Eye Replacement Implant" + desc = "An implanted replacement for a left eye with material vision capabilities." + icon_state = "cybereye-blue" + item_state = "eyepatch" + flags = NODROP + flags_cover = null + +/obj/item/clothing/glasses/material/lighting + name = "Neutron Goggles" + desc = "These odd glasses use a form of neutron-based imaging to completely negate the effects of light and darkness." + origin_tech = null + vision_flags = 0 + + flags = NODROP + lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + +/obj/item/clothing/glasses/regular + name = "prescription glasses" + desc = "Made by Nerd. Co." + icon_state = "glasses" + item_state = "glasses" + prescription = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/regular/hipster + name = "prescription glasses" + desc = "Made by Uncool. Co." + icon_state = "hipster_glasses" + item_state = "hipster_glasses" + +/obj/item/clothing/glasses/threedglasses + desc = "A long time ago, people used these glasses to makes images from screens threedimensional." + name = "3D glasses" + icon_state = "3d" + item_state = "3d" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/gglasses + name = "Green Glasses" + desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." + icon_state = "gglasses" + item_state = "gglasses" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + prescription_upgradable = 1 + +/obj/item/clothing/glasses/sunglasses + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." + name = "sunglasses" + icon_state = "sun" + item_state = "sunglasses" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + prescription_upgradable = 1 + dog_fashion = /datum/dog_fashion/head + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/sunglasses_fake + desc = "Cheap, plastic sunglasses. They don't even have UV protection." + name = "cheap sunglasses" + icon_state = "sun" + item_state = "sunglasses" + see_in_dark = 0 + flash_protect = 0 + tint = 0 + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/sunglasses/noir + name = "noir sunglasses" + desc = "Somehow these seem even more out-of-date than normal sunglasses." + actions_types = list(/datum/action/item_action/noir) + +/obj/item/clothing/glasses/sunglasses/noir/attack_self(mob/user) + toggle_noir(user) + +/obj/item/clothing/glasses/sunglasses/noir/item_action_slot_check(slot) + if(slot == slot_glasses) + return 1 + +/obj/item/clothing/glasses/sunglasses/noir/proc/toggle_noir(mob/user) + color_view = color_view ? null : MATRIX_GREYSCALE //Toggles between null and grayscale, with null being the default option. + user.update_client_colour() + +/obj/item/clothing/glasses/sunglasses/yeah + name = "agreeable glasses" + desc = "H.C Limited edition." + var/punused = null + actions_types = list(/datum/action/item_action/YEEEAAAAAHHHHHHHHHHHHH) + +/obj/item/clothing/glasses/sunglasses/yeah/attack_self() + pun() + +/obj/item/clothing/glasses/sunglasses/yeah/proc/pun() + if(!punused)//one per round + punused = 1 + playsound(src.loc, 'sound/misc/yeah.ogg', 100, 0) + usr.visible_message("YEEEAAAAAHHHHHHHHHHHHH!!") + else + to_chat(usr, "The moment is gone.") + + +/obj/item/clothing/glasses/sunglasses/reagent + name = "sunscanners" + desc = "Strangely ancient technology used to help provide rudimentary eye color. Outfitted with apparatus to scan individual reagents." + scan_reagents = 1 + +/obj/item/clothing/glasses/virussunglasses + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." + name = "sunglasses" + icon_state = "sun" + item_state = "sunglasses" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/sunglasses/lasers + desc = "A peculiar set of sunglasses; they have various chips and other panels attached to the sides of the frames." + name = "high-tech sunglasses" + flags = NODROP + +/obj/item/clothing/glasses/sunglasses/lasers/equipped(mob/user, slot) //grant them laser eyes upon equipping it. + if(slot == slot_glasses) + user.mutations.Add(LASER) + user.regenerate_icons() + ..(user, slot) + +/obj/item/clothing/glasses/welding + name = "welding goggles" + desc = "Protects the eyes from welders, approved by the mad scientist association." + icon_state = "welding-g" + item_state = "welding-g" + actions_types = list(/datum/action/item_action/toggle) + flash_protect = 2 + tint = 2 + visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/welding/attack_self(mob/user) + weldingvisortoggle(user) + +/obj/item/clothing/glasses/welding/superior + name = "superior welding goggles" + desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." + icon_state = "rwelding-g" + item_state = "rwelding-g" + flash_protect = 2 + tint = 0 + +/obj/item/clothing/glasses/sunglasses/blindfold + name = "blindfold" + desc = "Covers the eyes, preventing sight." + icon_state = "blindfold" + item_state = "blindfold" + //vision_flags = BLIND + flash_protect = 2 + tint = 3 //to make them blind + prescription_upgradable = 0 + +/obj/item/clothing/glasses/sunglasses/prescription + prescription = 1 + +/obj/item/clothing/glasses/sunglasses/big + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." + icon_state = "bigsunglasses" + item_state = "bigsunglasses" + +/obj/item/clothing/glasses/thermal + name = "Optical Thermal Scanner" + desc = "Thermals in the shape of glasses." + icon_state = "thermal" + item_state = "glasses" + origin_tech = "magnets=3" + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + flash_protect = -1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/thermal/emp_act(severity) + if(istype(src.loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/M = src.loc + to_chat(M, "The Optical Thermal Scanner overloads and blinds you!") + if(M.glasses == src) + M.EyeBlind(3) + M.EyeBlurry(5) + if(!(M.disabilities & NEARSIGHTED)) + M.BecomeNearsighted() + spawn(100) + M.CureNearsighted() + ..() + +/obj/item/clothing/glasses/thermal/monocle + name = "Thermoncle" + desc = "A monocle thermal." + icon_state = "thermoncle" + flags_cover = null //doesn't protect eyes because it's a monocle, duh + +/obj/item/clothing/glasses/thermal/eyepatch + name = "Optical Thermal Eyepatch" + desc = "An eyepatch with built-in thermal optics" + icon_state = "eyepatch" + item_state = "eyepatch" + +/obj/item/clothing/glasses/thermal/jensen + name = "Optical Thermal Implants" + desc = "A set of implantable lenses designed to augment your vision" + icon_state = "thermalimplants" + item_state = "syringe_kit" + +/obj/item/clothing/glasses/thermal/cyber + name = "Eye Replacement Implant" + desc = "An implanted replacement for a left eye with thermal vision capabilities." + icon_state = "cybereye-red" + item_state = "eyepatch" + flags = NODROP + + +/obj/item/clothing/glasses/godeye + name = "eye of god" + desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes." + icon_state = "godeye" + item_state = "godeye" + vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS + see_in_dark = 8 + scan_reagents = 1 + flags = NODROP + flags_cover = null + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + resistance_flags = LAVA_PROOF | FIRE_PROOF + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/godeye/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, src) && W != src && W.loc == user) + if(W.icon_state == "godeye") + W.icon_state = "doublegodeye" + W.item_state = "doublegodeye" + W.desc = "A pair of strange eyes, said to have been torn from an omniscient creature that used to roam the wastes. There's no real reason to have two, but that isn't stopping you." + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.update_inv_wear_mask() + else + to_chat(user, "The eye winks at you and vanishes into the abyss, you feel really unlucky.") + qdel(src) + ..() + +/obj/item/clothing/glasses/tajblind + name = "embroidered veil" + desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes." + icon_state = "tajblind" + item_state = "tajblind" + flags_cover = GLASSESCOVERSEYES + actions_types = list(/datum/action/item_action/toggle) + up = 0 + tint = 0 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/tajblind/eng + name = "industrial veil" + icon_state = "tajblind_engi" + item_state = "tajblind_engi" + +/obj/item/clothing/glasses/tajblind/sci + name = "hi-tech veil" + icon_state = "tajblind_sci" + item_state = "tajblind_sci" + +/obj/item/clothing/glasses/tajblind/cargo + name = "khaki veil" + icon_state = "tajblind_cargo" + item_state = "tajblind_cargo" + +/obj/item/clothing/glasses/tajblind/attack_self() + toggle_veil() + +/obj/item/clothing/glasses/proc/toggle_veil() + if(usr.canmove && !usr.incapacitated()) + if(up) + up = !up + tint = initial(tint) + to_chat(usr, "You activate [src], allowing you to see.") + else + up = !up + tint = 3 + to_chat(usr, "You deactivate [src], obscuring your vision.") + var/mob/living/carbon/user = usr + user.update_tint() + user.update_inv_glasses() diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index 27cd78afad3..4d2255a09ab 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -1,198 +1,198 @@ -/obj/item/clothing/glasses/hud - name = "\improper HUD" - desc = "A heads-up display that provides important info in (almost) real time." - flags = null //doesn't protect eyes because it's a monocle, duh - origin_tech = "magnets=3;biotech=2" - var/HUDType = null //Hudtype is defined on glasses.dm - prescription_upgradable = 1 - var/list/icon/current = list() //the current hud icons - - -/obj/item/clothing/glasses/hud/equipped(mob/living/carbon/human/user, slot) - ..() - if(HUDType && slot == slot_glasses) - var/datum/atom_hud/H = huds[HUDType] - H.add_hud_to(user) - -/obj/item/clothing/glasses/hud/dropped(mob/living/carbon/human/user) - ..() - if(HUDType && istype(user) && user.glasses == src) - var/datum/atom_hud/H = huds[HUDType] - H.remove_hud_from(user) - -/obj/item/clothing/glasses/hud/emp_act(severity) - if(emagged == 0) - emagged = 1 - desc = desc + " The display flickers slightly." - -/obj/item/clothing/glasses/hud/health - name = "\improper Health Scanner HUD" - desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." - icon_state = "healthhud" - origin_tech = "magnets=3;biotech=2" - HUDType = DATA_HUD_MEDICAL_ADVANCED - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/health/night - name = "\improper Night Vision Health Scanner HUD" - desc = "An advanced medical head-up display that allows doctors to find patients in complete darkness." - icon_state = "healthhudnight" - item_state = "glasses" - origin_tech = "magnets=4;biotech=4;plasmatech=4;engineering=5" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/health/sunglasses - name = "medical HUDSunglasses" - desc = "Sunglasses with a medical HUD." - icon_state = "sunhudmed" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - -/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." - icon_state = "diagnostichud" - origin_tech = "magnets=2;engineering=2" - HUDType = DATA_HUD_DIAGNOSTIC - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/diagnostic/night - name = "Night Vision Diagnostic HUD" - desc = "A robotics diagnostic HUD fitted with a light amplifier." - icon_state = "diagnostichudnight" - item_state = "glasses" - origin_tech = "magnets=4;powerstorage=4;plasmatech=4;engineering=5" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/diagnostic/sunglasses - name = "diagnostic sunglasses" - desc = "Sunglasses with a diagnostic HUD." - icon_state = "sunhuddiag" - item_state = "glasses" - flash_protect = 1 - tint = 1 - -/obj/item/clothing/glasses/hud/security - name = "\improper 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" - origin_tech = "magnets=3;combat=2" - var/global/list/jobs[0] - HUDType = DATA_HUD_SECURITY_ADVANCED - var/read_only = FALSE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - - -/obj/item/clothing/glasses/hud/security/sunglasses/jensenshades - name = "augmented shades" - desc = "Polarized bioneural eyewear, designed to augment your vision." - icon_state = "jensenshades" - item_state = "jensenshades" - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - -/obj/item/clothing/glasses/hud/security/night - name = "\improper Night Vision Security HUD" - desc = "An advanced heads-up display which provides id data and vision in complete darkness." - icon_state = "securityhudnight" - origin_tech = "magnets=4;combat=4;plasmatech=4;engineering=5" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/security/sunglasses/read_only - read_only = TRUE - -/obj/item/clothing/glasses/hud/security/sunglasses - name = "HUDSunglasses" - desc = "Sunglasses with a HUD." - icon_state = "sunhud" - origin_tech = "magnets=3;combat=3;engineering=3" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - prescription_upgradable = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/security/sunglasses/prescription - prescription = 1 - -/obj/item/clothing/glasses/hud/hydroponic - name = "Hydroponic HUD" - desc = "A heads-up display capable of analyzing the health and status of plants growing in hydro trays and soil." - icon_state = "hydroponichud" - HUDType = DATA_HUD_HYDROPONIC - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/hydroponic/night - name = "Night Vision Hydroponic HUD" - desc = "A hydroponic HUD fitted with a light amplifier." - icon_state = "hydroponichudnight" - item_state = "glasses" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/security/tajblind - name = "sleek veil" - desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an in-built security HUD." - icon_state = "tajblind_sec" - item_state = "tajblind_sec" - flags_cover = GLASSESCOVERSEYES - actions_types = list(/datum/action/item_action/toggle) - up = 0 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/security/tajblind/attack_self() - toggle_veil() - -/obj/item/clothing/glasses/hud/health/tajblind - name = "lightweight veil" - desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an installed medical HUD." - icon_state = "tajblind_med" - item_state = "tajblind_med" - flags_cover = GLASSESCOVERSEYES - actions_types = list(/datum/action/item_action/toggle) - up = 0 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/health/tajblind/attack_self() - toggle_veil() +/obj/item/clothing/glasses/hud + name = "\improper HUD" + desc = "A heads-up display that provides important info in (almost) real time." + flags = null //doesn't protect eyes because it's a monocle, duh + origin_tech = "magnets=3;biotech=2" + var/HUDType = null //Hudtype is defined on glasses.dm + prescription_upgradable = 1 + var/list/icon/current = list() //the current hud icons + + +/obj/item/clothing/glasses/hud/equipped(mob/living/carbon/human/user, slot) + ..() + if(HUDType && slot == slot_glasses) + var/datum/atom_hud/H = GLOB.huds[HUDType] + H.add_hud_to(user) + +/obj/item/clothing/glasses/hud/dropped(mob/living/carbon/human/user) + ..() + if(HUDType && istype(user) && user.glasses == src) + var/datum/atom_hud/H = GLOB.huds[HUDType] + H.remove_hud_from(user) + +/obj/item/clothing/glasses/hud/emp_act(severity) + if(emagged == 0) + emagged = 1 + desc = desc + " The display flickers slightly." + +/obj/item/clothing/glasses/hud/health + name = "\improper Health Scanner HUD" + desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." + icon_state = "healthhud" + origin_tech = "magnets=3;biotech=2" + HUDType = DATA_HUD_MEDICAL_ADVANCED + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/health/night + name = "\improper Night Vision Health Scanner HUD" + desc = "An advanced medical head-up display that allows doctors to find patients in complete darkness." + icon_state = "healthhudnight" + item_state = "glasses" + origin_tech = "magnets=4;biotech=4;plasmatech=4;engineering=5" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/health/sunglasses + name = "medical HUDSunglasses" + desc = "Sunglasses with a medical HUD." + icon_state = "sunhudmed" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + +/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." + icon_state = "diagnostichud" + origin_tech = "magnets=2;engineering=2" + HUDType = DATA_HUD_DIAGNOSTIC + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/diagnostic/night + name = "Night Vision Diagnostic HUD" + desc = "A robotics diagnostic HUD fitted with a light amplifier." + icon_state = "diagnostichudnight" + item_state = "glasses" + origin_tech = "magnets=4;powerstorage=4;plasmatech=4;engineering=5" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/diagnostic/sunglasses + name = "diagnostic sunglasses" + desc = "Sunglasses with a diagnostic HUD." + icon_state = "sunhuddiag" + item_state = "glasses" + flash_protect = 1 + tint = 1 + +/obj/item/clothing/glasses/hud/security + name = "\improper 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" + origin_tech = "magnets=3;combat=2" + var/global/list/jobs[0] + HUDType = DATA_HUD_SECURITY_ADVANCED + var/read_only = FALSE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + + +/obj/item/clothing/glasses/hud/security/sunglasses/jensenshades + name = "augmented shades" + desc = "Polarized bioneural eyewear, designed to augment your vision." + icon_state = "jensenshades" + item_state = "jensenshades" + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + +/obj/item/clothing/glasses/hud/security/night + name = "\improper Night Vision Security HUD" + desc = "An advanced heads-up display which provides id data and vision in complete darkness." + icon_state = "securityhudnight" + origin_tech = "magnets=4;combat=4;plasmatech=4;engineering=5" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/security/sunglasses/read_only + read_only = TRUE + +/obj/item/clothing/glasses/hud/security/sunglasses + name = "HUDSunglasses" + desc = "Sunglasses with a HUD." + icon_state = "sunhud" + origin_tech = "magnets=3;combat=3;engineering=3" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + prescription_upgradable = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/security/sunglasses/prescription + prescription = 1 + +/obj/item/clothing/glasses/hud/hydroponic + name = "Hydroponic HUD" + desc = "A heads-up display capable of analyzing the health and status of plants growing in hydro trays and soil." + icon_state = "hydroponichud" + HUDType = DATA_HUD_HYDROPONIC + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/hydroponic/night + name = "Night Vision Hydroponic HUD" + desc = "A hydroponic HUD fitted with a light amplifier." + icon_state = "hydroponichudnight" + item_state = "glasses" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/security/tajblind + name = "sleek veil" + desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an in-built security HUD." + icon_state = "tajblind_sec" + item_state = "tajblind_sec" + flags_cover = GLASSESCOVERSEYES + actions_types = list(/datum/action/item_action/toggle) + up = 0 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/security/tajblind/attack_self() + toggle_veil() + +/obj/item/clothing/glasses/hud/health/tajblind + name = "lightweight veil" + desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an installed medical HUD." + icon_state = "tajblind_med" + item_state = "tajblind_med" + flags_cover = GLASSESCOVERSEYES + actions_types = list(/datum/action/item_action/toggle) + up = 0 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/health/tajblind/attack_self() + toggle_veil() diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index c31e1abf4b5..701984de055 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -1,19 +1,19 @@ -/obj/item/clothing/gloves/boxing - name = "boxing gloves" - desc = "Because you really needed another excuse to punch your crewmates." - icon_state = "boxing" - item_state = "boxing" - put_on_delay = 60 - species_exception = list(/datum/species/golem) // now you too can be a golem boxing champion - -/obj/item/clothing/gloves/boxing/green - icon_state = "boxinggreen" - item_state = "boxinggreen" - -/obj/item/clothing/gloves/boxing/blue - icon_state = "boxingblue" - item_state = "boxingblue" - -/obj/item/clothing/gloves/boxing/yellow - icon_state = "boxingyellow" - item_state = "boxingyellow" \ No newline at end of file +/obj/item/clothing/gloves/boxing + name = "boxing gloves" + desc = "Because you really needed another excuse to punch your crewmates." + icon_state = "boxing" + item_state = "boxing" + put_on_delay = 60 + species_exception = list(/datum/species/golem) // now you too can be a golem boxing champion + +/obj/item/clothing/gloves/boxing/green + icon_state = "boxinggreen" + item_state = "boxinggreen" + +/obj/item/clothing/gloves/boxing/blue + icon_state = "boxingblue" + item_state = "boxingblue" + +/obj/item/clothing/gloves/boxing/yellow + icon_state = "boxingyellow" + item_state = "boxingyellow" diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index cbf9d0695ce..ec4ac9bbf15 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -1,240 +1,240 @@ -/obj/item/clothing/gloves/color/yellow - desc = "These gloves will protect the wearer from electric shock." - name = "insulated gloves" - icon_state = "yellow" - item_state = "ygloves" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - item_color="yellow" - resistance_flags = NONE - -/obj/item/clothing/gloves/color/yellow/power - description_antag = "These are a pair of power gloves, and can be used to fire bolts of electricity while standing over powered power cables." - var/old_mclick_override - var/datum/middleClickOverride/power_gloves/mclick_override = new /datum/middleClickOverride/power_gloves - var/last_shocked = 0 - var/shock_delay = 40 - var/unlimited_power = FALSE // Does this really need explanation? - -/obj/item/clothing/gloves/color/yellow/power/equipped(mob/user, slot) - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if(slot == slot_gloves) - if(H.middleClickOverride) - old_mclick_override = H.middleClickOverride - H.middleClickOverride = mclick_override - if(!unlimited_power) - to_chat(H, "You feel electricity begin to build up in [src].") - else - to_chat(H, "You feel like you have UNLIMITED POWER!!") - -/obj/item/clothing/gloves/color/yellow/power/dropped(mob/user, slot) - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if(H.get_item_by_slot(slot_gloves) == src && H.middleClickOverride == mclick_override) - if(old_mclick_override) - H.middleClickOverride = old_mclick_override - old_mclick_override = null - else - H.middleClickOverride = null - -/obj/item/clothing/gloves/color/yellow/power/unlimited - name = "UNLIMITED POWER gloves" - desc = "These gloves possess UNLIMITED POWER." - shock_delay = 0 - unlimited_power = TRUE - -/obj/item/clothing/gloves/color/yellow/fake - desc = "These gloves will protect the wearer from electric shock. They don't feel like rubber..." - siemens_coefficient = 1 - -/obj/item/clothing/gloves/color/fyellow //Cheap Chinese Crap - desc = "These gloves are cheap copies of the coveted gloves, no way this can end badly." - name = "budget insulated gloves" - icon_state = "yellow" - item_state = "ygloves" - siemens_coefficient = 1 //Set to a default of 1, gets overridden in New() - permeability_coefficient = 0.05 - item_color="yellow" - resistance_flags = NONE - -/obj/item/clothing/gloves/color/fyellow/New() - ..() - siemens_coefficient = pick(0,0.5,0.5,0.5,0.5,0.75,1.5) - -/obj/item/clothing/gloves/color/fyellow/old - desc = "Old and worn out insulated gloves, hopefully they still work." - name = "worn out insulated gloves" - -/obj/item/clothing/gloves/color/fyellow/old/New() - ..() - siemens_coefficient = pick(0,0,0,0.5,0.5,0.5,0.75) - -/obj/item/clothing/gloves/color/black - desc = "These gloves are fire-resistant." - name = "black gloves" - icon_state = "black" - item_state = "bgloves" - item_color="brown" - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT - 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/thief - pickpocket = 1 - -/obj/item/clothing/gloves/color/black/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/wirecutters)) - if(can_be_cut && icon_state == initial(icon_state))//only if not dyed - var/confirm = alert("Do you want to cut off the gloves fingertips? Warning: It might destroy their functionality.","Cut tips?","Yes","No") - if(get_dist(user, src) > 1) - to_chat(user, "You have moved too far away.") - return - if(confirm == "Yes") - to_chat(user, "You snip the fingertips off of [src].") - playsound(user.loc, W.usesound, rand(10,50), 1) - var/obj/item/clothing/gloves/fingerless/F = new/obj/item/clothing/gloves/fingerless(user.loc) - if(pickpocket) - F.pickpocket = FALSE - qdel(src) - return - ..() - -/obj/item/clothing/gloves/color/orange - name = "orange gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "orange" - item_state = "orangegloves" - item_color="orange" - -/obj/item/clothing/gloves/color/red - name = "red gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "red" - item_state = "redgloves" - item_color = "red" - -/obj/item/clothing/gloves/color/red/insulated - name = "insulated gloves" - desc = "These gloves will protect the wearer from electric shock." - siemens_coefficient = 0 - permeability_coefficient = 0.05 - resistance_flags = NONE - -/obj/item/clothing/gloves/color/rainbow - name = "rainbow gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "rainbow" - item_state = "rainbowgloves" - item_color = "rainbow" - -/obj/item/clothing/gloves/color/rainbow/clown - item_color = "clown" - -/obj/item/clothing/gloves/color/blue - name = "blue gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "blue" - item_state = "bluegloves" - item_color="blue" - -/obj/item/clothing/gloves/color/purple - name = "purple gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "purple" - item_state = "purplegloves" - item_color="purple" - -/obj/item/clothing/gloves/color/green - name = "green gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "green" - item_state = "greengloves" - item_color="green" - -/obj/item/clothing/gloves/color/grey - name = "grey gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "gray" - item_state = "graygloves" - item_color="grey" - -/obj/item/clothing/gloves/color/grey/rd - item_color = "director" //Exists for washing machines. Is not different from gray gloves in any way. - -/obj/item/clothing/gloves/color/grey/hop - item_color = "hop" //Exists for washing machines. Is not different from gray gloves in any way. - -/obj/item/clothing/gloves/color/light_brown - name = "light brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "lightbrown" - item_state = "lightbrowngloves" - item_color="light brown" - -/obj/item/clothing/gloves/color/brown - name = "brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "brown" - item_state = "browngloves" - item_color="brown" - -/obj/item/clothing/gloves/color/brown/cargo - item_color = "cargo" //Exists for washing machines. Is not different from brown gloves in any way. - -/obj/item/clothing/gloves/color/latex - name = "latex gloves" - desc = "Cheap sterile gloves made from latex." - icon_state = "latex" - item_state = "lgloves" - siemens_coefficient = 0.30 - permeability_coefficient = 0.01 - item_color="white" - transfer_prints = TRUE - resistance_flags = NONE - -/obj/item/clothing/gloves/color/latex/nitrile - name = "nitrile gloves" - desc = "Pricy sterile gloves that are stronger than latex." - icon_state = "nitrile" - item_state = "nitrilegloves" - transfer_prints = FALSE - item_color = "medical" - -/obj/item/clothing/gloves/color/white - name = "white gloves" - desc = "These look pretty fancy." - icon_state = "white" - item_state = "wgloves" - item_color="mime" - -/obj/item/clothing/gloves/color/white/redcoat - item_color = "redcoat" //Exists for washing machines. Is not different from white gloves in any way. - - -/obj/item/clothing/gloves/color/captain - desc = "Regal blue gloves, with a nice gold trim. Swanky." - name = "captain's gloves" - icon_state = "captain" - item_state = "egloves" - item_color = "captain" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT - 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) \ No newline at end of file +/obj/item/clothing/gloves/color/yellow + desc = "These gloves will protect the wearer from electric shock." + name = "insulated gloves" + icon_state = "yellow" + item_state = "ygloves" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + item_color="yellow" + resistance_flags = NONE + +/obj/item/clothing/gloves/color/yellow/power + description_antag = "These are a pair of power gloves, and can be used to fire bolts of electricity while standing over powered power cables." + var/old_mclick_override + var/datum/middleClickOverride/power_gloves/mclick_override = new /datum/middleClickOverride/power_gloves + var/last_shocked = 0 + var/shock_delay = 40 + var/unlimited_power = FALSE // Does this really need explanation? + +/obj/item/clothing/gloves/color/yellow/power/equipped(mob/user, slot) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(slot == slot_gloves) + if(H.middleClickOverride) + old_mclick_override = H.middleClickOverride + H.middleClickOverride = mclick_override + if(!unlimited_power) + to_chat(H, "You feel electricity begin to build up in [src].") + else + to_chat(H, "You feel like you have UNLIMITED POWER!!") + +/obj/item/clothing/gloves/color/yellow/power/dropped(mob/user, slot) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(H.get_item_by_slot(slot_gloves) == src && H.middleClickOverride == mclick_override) + if(old_mclick_override) + H.middleClickOverride = old_mclick_override + old_mclick_override = null + else + H.middleClickOverride = null + +/obj/item/clothing/gloves/color/yellow/power/unlimited + name = "UNLIMITED POWER gloves" + desc = "These gloves possess UNLIMITED POWER." + shock_delay = 0 + unlimited_power = TRUE + +/obj/item/clothing/gloves/color/yellow/fake + desc = "These gloves will protect the wearer from electric shock. They don't feel like rubber..." + siemens_coefficient = 1 + +/obj/item/clothing/gloves/color/fyellow //Cheap Chinese Crap + desc = "These gloves are cheap copies of the coveted gloves, no way this can end badly." + name = "budget insulated gloves" + icon_state = "yellow" + item_state = "ygloves" + siemens_coefficient = 1 //Set to a default of 1, gets overridden in New() + permeability_coefficient = 0.05 + item_color="yellow" + resistance_flags = NONE + +/obj/item/clothing/gloves/color/fyellow/New() + ..() + siemens_coefficient = pick(0,0.5,0.5,0.5,0.5,0.75,1.5) + +/obj/item/clothing/gloves/color/fyellow/old + desc = "Old and worn out insulated gloves, hopefully they still work." + name = "worn out insulated gloves" + +/obj/item/clothing/gloves/color/fyellow/old/New() + ..() + siemens_coefficient = pick(0,0,0,0.5,0.5,0.5,0.75) + +/obj/item/clothing/gloves/color/black + desc = "These gloves are fire-resistant." + name = "black gloves" + icon_state = "black" + item_state = "bgloves" + item_color="brown" + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + 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/thief + pickpocket = 1 + +/obj/item/clothing/gloves/color/black/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/wirecutters)) + if(can_be_cut && icon_state == initial(icon_state))//only if not dyed + var/confirm = alert("Do you want to cut off the gloves fingertips? Warning: It might destroy their functionality.","Cut tips?","Yes","No") + if(get_dist(user, src) > 1) + to_chat(user, "You have moved too far away.") + return + if(confirm == "Yes") + to_chat(user, "You snip the fingertips off of [src].") + playsound(user.loc, W.usesound, rand(10,50), 1) + var/obj/item/clothing/gloves/fingerless/F = new/obj/item/clothing/gloves/fingerless(user.loc) + if(pickpocket) + F.pickpocket = FALSE + qdel(src) + return + ..() + +/obj/item/clothing/gloves/color/orange + name = "orange gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "orange" + item_state = "orangegloves" + item_color="orange" + +/obj/item/clothing/gloves/color/red + name = "red gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "red" + item_state = "redgloves" + item_color = "red" + +/obj/item/clothing/gloves/color/red/insulated + name = "insulated gloves" + desc = "These gloves will protect the wearer from electric shock." + siemens_coefficient = 0 + permeability_coefficient = 0.05 + resistance_flags = NONE + +/obj/item/clothing/gloves/color/rainbow + name = "rainbow gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "rainbow" + item_state = "rainbowgloves" + item_color = "rainbow" + +/obj/item/clothing/gloves/color/rainbow/clown + item_color = "clown" + +/obj/item/clothing/gloves/color/blue + name = "blue gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "blue" + item_state = "bluegloves" + item_color="blue" + +/obj/item/clothing/gloves/color/purple + name = "purple gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "purple" + item_state = "purplegloves" + item_color="purple" + +/obj/item/clothing/gloves/color/green + name = "green gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "green" + item_state = "greengloves" + item_color="green" + +/obj/item/clothing/gloves/color/grey + name = "grey gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "gray" + item_state = "graygloves" + item_color="grey" + +/obj/item/clothing/gloves/color/grey/rd + item_color = "director" //Exists for washing machines. Is not different from gray gloves in any way. + +/obj/item/clothing/gloves/color/grey/hop + item_color = "hop" //Exists for washing machines. Is not different from gray gloves in any way. + +/obj/item/clothing/gloves/color/light_brown + name = "light brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "lightbrown" + item_state = "lightbrowngloves" + item_color="light brown" + +/obj/item/clothing/gloves/color/brown + name = "brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "brown" + item_state = "browngloves" + item_color="brown" + +/obj/item/clothing/gloves/color/brown/cargo + item_color = "cargo" //Exists for washing machines. Is not different from brown gloves in any way. + +/obj/item/clothing/gloves/color/latex + name = "latex gloves" + desc = "Cheap sterile gloves made from latex." + icon_state = "latex" + item_state = "lgloves" + siemens_coefficient = 0.30 + permeability_coefficient = 0.01 + item_color="white" + transfer_prints = TRUE + resistance_flags = NONE + +/obj/item/clothing/gloves/color/latex/nitrile + name = "nitrile gloves" + desc = "Pricy sterile gloves that are stronger than latex." + icon_state = "nitrile" + item_state = "nitrilegloves" + transfer_prints = FALSE + item_color = "medical" + +/obj/item/clothing/gloves/color/white + name = "white gloves" + desc = "These look pretty fancy." + icon_state = "white" + item_state = "wgloves" + item_color="mime" + +/obj/item/clothing/gloves/color/white/redcoat + item_color = "redcoat" //Exists for washing machines. Is not different from white gloves in any way. + + +/obj/item/clothing/gloves/color/captain + desc = "Regal blue gloves, with a nice gold trim. Swanky." + name = "captain's gloves" + icon_state = "captain" + item_state = "egloves" + item_color = "captain" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + 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) diff --git a/code/modules/clothing/head/beanie.dm b/code/modules/clothing/head/beanie.dm index b912a07c212..ada61c41384 100644 --- a/code/modules/clothing/head/beanie.dm +++ b/code/modules/clothing/head/beanie.dm @@ -93,4 +93,4 @@ name = "rastacap" desc = "Perfect for tucking in those dreadlocks." icon_state = "beanierasta" - item_color = "beanierasta" \ No newline at end of file + item_color = "beanierasta" diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index cc5c3248b19..501ec562441 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -1,146 +1,146 @@ - -//Hat Station 13 - -/obj/item/clothing/head/collectable - name = "collectable hat" - desc = "A rare collectable hat." - -/obj/item/clothing/head/collectable/petehat - name = "ultra rare Pete's hat!" - desc = "It smells faintly of plasma" - icon_state = "petehat" - -/obj/item/clothing/head/collectable/slime - name = "collectable slime cap!" - desc = "It just latches right in place!" - icon_state = "slime" - -/obj/item/clothing/head/collectable/xenom - name = "collectable xenomorph helmet!" - desc = "Hiss hiss hiss!" - icon_state = "xenom" - -/obj/item/clothing/head/collectable/chef - name = "collectable chef's hat" - desc = "A rare Chef's Hat meant for hat collectors!" - icon_state = "chef" - item_state = "chef" - dog_fashion = /datum/dog_fashion/head/chef - -/obj/item/clothing/head/collectable/paper - name = "collectable paper hat" - desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." - icon_state = "paper" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/collectable/tophat - name = "collectable top hat" - desc = "A top hat worn by only the most prestigious hat collectors." - icon_state = "tophat" - item_state = "that" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/collectable/captain - name = "collectable captain's hat" - desc = "A Collectable Hat that'll make you look just like a real comdom!" - icon_state = "captain" - item_state = "caphat" - dog_fashion = /datum/dog_fashion/head/captain - -/obj/item/clothing/head/collectable/police - name = "collectable police officer's hat" - desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." - icon_state = "policehelm" - dog_fashion = /datum/dog_fashion/head/warden - -/obj/item/clothing/head/collectable/beret - name = "collectable beret" - desc = "A Collectable red Beret. It smells faintly of Garlic." - icon_state = "beret" - dog_fashion = /datum/dog_fashion/head/beret - -/obj/item/clothing/head/collectable/welding - name = "collectable welding helmet" - desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" - icon_state = "welding" - item_state = "welding" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi' - ) - -/obj/item/clothing/head/collectable/slime - name = "collectable slime hat" - desc = "Just like a real Brain Slug!" - icon_state = "headslime" - item_state = "headslime" - -/obj/item/clothing/head/collectable/flatcap - name = "collectable flat cap" - desc = "A Collectible farmer's Flat Cap!" - icon_state = "flat_cap" - item_state = "detective" - -/obj/item/clothing/head/collectable/pirate - name = "collectable pirate hat" - desc = "You'd make a great Dread Syndie Roberts!" - icon_state = "pirate" - item_state = "pirate" - dog_fashion = /datum/dog_fashion/head/pirate - -/obj/item/clothing/head/collectable/kitty - name = "collectable kitty ears" - desc = "The fur feels.....a bit too realistic." - icon_state = "kitty" - item_state = "kitty" - dog_fashion = /datum/dog_fashion/head/kitty - -/obj/item/clothing/head/collectable/rabbitears - name = "collectable rabbit ears" - desc = "Not as lucky as the feet!" - icon_state = "bunny" - item_state = "bunny" - dog_fashion = /datum/dog_fashion/head/rabbit - -/obj/item/clothing/head/collectable/wizard - name = "collectable wizard's hat" - desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." - icon_state = "wizard" - dog_fashion = /datum/dog_fashion/head/blue_wizard - -/obj/item/clothing/head/collectable/hardhat - name = "collectable hard hat" - desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" - icon_state = "hardhat0_yellow" - item_state = "hardhat0_yellow" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/collectable/HoS - name = "collectable HoS hat" - desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" - icon_state = "hoscap" - -/obj/item/clothing/head/collectable/HoP - name = "collectable HoP hat" - desc = "It's your turn to demand excessive paperwork, signatures, stamps, and hire more clowns! Papers, please!" - icon_state = "hopcap" - dog_fashion = /datum/dog_fashion/head/hop - -/obj/item/clothing/head/collectable/thunderdome - name = "collectable Thunderdome helmet" - desc = "Go Red! I mean Green! I mean Red! No Green!" - icon_state = "thunderdome" - item_state = "thunderdome" - -/obj/item/clothing/head/collectable/swat - name = "collectable SWAT helmet" - desc = "Now you can be in the Deathsquad too!" - icon_state = "swat" - item_state = "swat" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi' - ) + +//Hat Station 13 + +/obj/item/clothing/head/collectable + name = "collectable hat" + desc = "A rare collectable hat." + +/obj/item/clothing/head/collectable/petehat + name = "ultra rare Pete's hat!" + desc = "It smells faintly of plasma" + icon_state = "petehat" + +/obj/item/clothing/head/collectable/slime + name = "collectable slime cap!" + desc = "It just latches right in place!" + icon_state = "slime" + +/obj/item/clothing/head/collectable/xenom + name = "collectable xenomorph helmet!" + desc = "Hiss hiss hiss!" + icon_state = "xenom" + +/obj/item/clothing/head/collectable/chef + name = "collectable chef's hat" + desc = "A rare Chef's Hat meant for hat collectors!" + icon_state = "chef" + item_state = "chef" + dog_fashion = /datum/dog_fashion/head/chef + +/obj/item/clothing/head/collectable/paper + name = "collectable paper hat" + desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." + icon_state = "paper" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/collectable/tophat + name = "collectable top hat" + desc = "A top hat worn by only the most prestigious hat collectors." + icon_state = "tophat" + item_state = "that" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/collectable/captain + name = "collectable captain's hat" + desc = "A Collectable Hat that'll make you look just like a real comdom!" + icon_state = "captain" + item_state = "caphat" + dog_fashion = /datum/dog_fashion/head/captain + +/obj/item/clothing/head/collectable/police + name = "collectable police officer's hat" + desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." + icon_state = "policehelm" + dog_fashion = /datum/dog_fashion/head/warden + +/obj/item/clothing/head/collectable/beret + name = "collectable beret" + desc = "A Collectable red Beret. It smells faintly of Garlic." + icon_state = "beret" + dog_fashion = /datum/dog_fashion/head/beret + +/obj/item/clothing/head/collectable/welding + name = "collectable welding helmet" + desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" + icon_state = "welding" + item_state = "welding" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi' + ) + +/obj/item/clothing/head/collectable/slime + name = "collectable slime hat" + desc = "Just like a real Brain Slug!" + icon_state = "headslime" + item_state = "headslime" + +/obj/item/clothing/head/collectable/flatcap + name = "collectable flat cap" + desc = "A Collectible farmer's Flat Cap!" + icon_state = "flat_cap" + item_state = "detective" + +/obj/item/clothing/head/collectable/pirate + name = "collectable pirate hat" + desc = "You'd make a great Dread Syndie Roberts!" + icon_state = "pirate" + item_state = "pirate" + dog_fashion = /datum/dog_fashion/head/pirate + +/obj/item/clothing/head/collectable/kitty + name = "collectable kitty ears" + desc = "The fur feels.....a bit too realistic." + icon_state = "kitty" + item_state = "kitty" + dog_fashion = /datum/dog_fashion/head/kitty + +/obj/item/clothing/head/collectable/rabbitears + name = "collectable rabbit ears" + desc = "Not as lucky as the feet!" + icon_state = "bunny" + item_state = "bunny" + dog_fashion = /datum/dog_fashion/head/rabbit + +/obj/item/clothing/head/collectable/wizard + name = "collectable wizard's hat" + desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." + icon_state = "wizard" + dog_fashion = /datum/dog_fashion/head/blue_wizard + +/obj/item/clothing/head/collectable/hardhat + name = "collectable hard hat" + desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" + icon_state = "hardhat0_yellow" + item_state = "hardhat0_yellow" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/collectable/HoS + name = "collectable HoS hat" + desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" + icon_state = "hoscap" + +/obj/item/clothing/head/collectable/HoP + name = "collectable HoP hat" + desc = "It's your turn to demand excessive paperwork, signatures, stamps, and hire more clowns! Papers, please!" + icon_state = "hopcap" + dog_fashion = /datum/dog_fashion/head/hop + +/obj/item/clothing/head/collectable/thunderdome + name = "collectable Thunderdome helmet" + desc = "Go Red! I mean Green! I mean Red! No Green!" + icon_state = "thunderdome" + item_state = "thunderdome" + +/obj/item/clothing/head/collectable/swat + name = "collectable SWAT helmet" + desc = "Now you can be in the Deathsquad too!" + icon_state = "swat" + item_state = "swat" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi' + ) diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 18b92433f83..b5d4b94038f 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -101,4 +101,4 @@ dog_fashion = null sprite_sheets = list( "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) \ No newline at end of file + ) diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index d47e3c32365..2a9abf461c9 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -1,301 +1,301 @@ -/obj/item/clothing/head/helmet - name = "helmet" - desc = "Standard Security gear. Protects the head from impacts." - icon_state = "helmetmaterials" - flags = HEADBANGPROTECT - flags_cover = HEADCOVERSEYES - item_state = "helmetmaterials" - armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - flags_inv = HIDEEARS|HIDEEYES - cold_protection = HEAD - min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT - strip_delay = 60 - dog_fashion = /datum/dog_fashion/head/helmet - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Drask" = 'icons/mob/species/drask/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - -/obj/item/clothing/head/helmet/attack_self(mob/user) - if(can_toggle && !user.incapacitated()) - if(world.time > cooldown + toggle_cooldown) - cooldown = world.time - up = !up - flags ^= visor_flags - flags_inv ^= visor_flags_inv - icon_state = "[initial(icon_state)][up ? "up" : ""]" - to_chat(user, "[up ? alt_toggle_message : toggle_message] \the [src]") - - user.update_inv_head() - - if(active_sound) - while(up) - playsound(src.loc, "[active_sound]", 100, 0, 4) - sleep(15) - if(toggle_sound) - playsound(src.loc, "[toggle_sound]", 100, 0, 4) - - -/obj/item/clothing/head/helmet/visor - name = "visor helmet" - desc = "A helmet with a built-in visor. It doesn't seem to do anything, but it sure looks cool!" - icon_state = "helmetgoggles" - -/obj/item/clothing/head/helmet/thermal - name = "thermal visor helmet" - desc = "A helmet with a built-in thermal scanning visor." - icon_state = "helmetthermals" - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - -/obj/item/clothing/head/helmet/meson - name = "meson visor helmet" - desc = "A helmet with a built-in meson scanning visor." - icon_state = "helmetmesons" - vision_flags = SEE_TURFS - -/obj/item/clothing/head/helmet/material - name = "material visor helmet" - desc = "A helmet with a built-in material scanning visor." - icon_state = "helmetmaterials" - vision_flags = SEE_OBJS - -/obj/item/clothing/head/helmet/night - name = "night-vision helmet" - desc = "A helmet with a built-in pair of night vision goggles." - icon_state = "helmetNVG" - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - -/obj/item/clothing/head/helmet/alt - name = "bulletproof helmet" - desc = "A bulletproof helmet that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." - icon_state = "swat" - item_state = "swat-alt" - armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - dog_fashion = null - -/obj/item/clothing/head/helmet/riot - name = "riot helmet" - desc = "It's a helmet specifically designed to protect against close range attacks." - icon_state = "riot" - item_state = "helmet" - armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) - flags_inv = HIDEEARS - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - strip_delay = 80 - dog_fashion = null - -/obj/item/clothing/head/helmet/riot/knight - name = "medieval helmet" - desc = "A classic metal helmet." - icon_state = "knight_green" - item_state = "knight_green" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - dog_fashion = null - -/obj/item/clothing/head/helmet/justice - name = "helmet of justice" - desc = "WEEEEOOO. WEEEEEOOO. WEEEEOOOO." - icon_state = "justice" - toggle_message = "You turn off the lights on" - alt_toggle_message = "You turn on the lights on" - actions_types = list(/datum/action/item_action/toggle_helmet_light) - can_toggle = 1 - toggle_cooldown = 20 - active_sound = 'sound/items/weeoo1.ogg' - dog_fashion = null - -/obj/item/clothing/head/helmet/justice/escape - name = "alarm helmet" - desc = "WEEEEOOO. WEEEEEOOO. STOP THAT MONKEY. WEEEOOOO." - icon_state = "justice2" - toggle_message = "You turn off the light on" - alt_toggle_message = "You turn on the light on" - - -/obj/item/clothing/head/helmet/swat - name = "\improper SWAT helmet" - desc = "They're often used by highly trained Swat Members." - icon_state = "swat" - item_state = "swat" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 50, "acid" = 50) - flags = null - flags_inv = HIDEEARS|HIDEEYES - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT - strip_delay = 80 - dog_fashion = null - -/obj/item/clothing/head/helmet/swat/syndicate - name = "blood-red helmet" - desc = "An extremely robust, space-worthy helmet without a visor to allow for goggle usage underneath. Property of Gorlex Marauders." - icon_state = "helmetsyndi" - item_state = "helmet" - -/obj/item/clothing/head/helmet/thunderdome - name = "\improper Thunderdome helmet" - desc = "'Let the battle commence!'" - icon_state = "thunderdome" - flags = null - item_state = "thunderdome" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT - strip_delay = 80 - dog_fashion = null - -/obj/item/clothing/head/helmet/roman - name = "roman helmet" - desc = "An ancient helmet made of bronze and leather." - flags = null - armor = list("melee" = 25, "bullet" = 0, "laser" = 25, "energy" = 10, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) - resistance_flags = FIRE_PROOF - icon_state = "roman" - item_state = "roman" - strip_delay = 100 - dog_fashion = null - -/obj/item/clothing/head/helmet/roman/fake - desc = "An ancient helmet made of plastic and leather." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/clothing/head/helmet/roman/legionaire - name = "roman legionaire helmet" - desc = "An ancient helmet made of bronze and leather. Has a red crest on top of it." - icon_state = "roman_c" - item_state = "roman_c" - -/obj/item/clothing/head/helmet/roman/legionaire/fake - desc = "An ancient helmet made of plastic and leather. Has a red crest on top of it." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/clothing/head/helmet/gladiator - name = "gladiator helmet" - desc = "Ave, Imperator, morituri te salutant." - icon_state = "gladiator" - flags = BLOCKHAIR - item_state = "gladiator" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - toggle_message = "You attach the face shield to the" - alt_toggle_message = "You remove the face shield from the" - actions_types = list(/datum/action/item_action/toggle_helmet_mode) - can_toggle = 1 - toggle_cooldown = 20 - toggle_sound = 'sound/items/zippoclose.ogg' - dog_fashion = null - -obj/item/clothing/head/helmet/redtaghelm - name = "red laser tag helmet" - desc = "They have chosen their own end." - icon_state = "redtaghelm" - flags = null - item_state = "redtaghelm" - armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) - // Offer about the same protection as a hardhat. - flags_inv = HIDEEARS|HIDEEYES - dog_fashion = null - -obj/item/clothing/head/helmet/bluetaghelm - name = "blue laser tag helmet" - desc = "They'll need more men." - icon_state = "bluetaghelm" - flags = null - item_state = "bluetaghelm" - armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) - // Offer about the same protection as a hardhat. - flags_inv = HIDEEARS|HIDEEYES - dog_fashion = null - -obj/item/clothing/head/blob - name = "blob hat" - desc = "A collectible hat handed out at the latest Blob Family Reunion." - icon_state = "blobhat" - item_state = "blobhat" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi' - ) - -/obj/item/clothing/head/helmet/riot/knight/blue - icon_state = "knight_blue" - item_state = "knight_blue" - -/obj/item/clothing/head/helmet/riot/knight/yellow - icon_state = "knight_yellow" - item_state = "knight_yellow" - -/obj/item/clothing/head/helmet/riot/knight/red - icon_state = "knight_red" - item_state = "knight_red" - -/obj/item/clothing/head/helmet/riot/knight/templar - name = "crusader helmet" - desc = "Deus Vult." - icon_state = "knight_templar" - item_state = "knight_templar" - armor = list(melee = 20, bullet = 7, laser = 2, energy = 2, bomb = 2, bio = 2, rad = 0, fire = 80, acid = 80) - -/obj/item/clothing/head/helmet/skull - name = "skull helmet" - desc = "An intimidating tribal helmet, it doesn't look very comfortable." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - icon_state = "skull" - item_state = "skull" - strip_delay = 100 - -/obj/item/clothing/head/helmet/durathread - name = "durathread helmet" - desc = "A helmet made from durathread and leather." - icon_state = "durathread" - item_state = "durathread" - resistance_flags = FLAMMABLE - armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) - strip_delay = 60 - -//Commander -/obj/item/clothing/head/helmet/ert/command - name = "emergency response team commander helmet" - desc = "An in-atmosphere helmet worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." - icon_state = "erthelmet_cmd" - -//Security -/obj/item/clothing/head/helmet/ert/security - name = "emergency response team security helmet" - desc = "An in-atmosphere helmet worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." - icon_state = "erthelmet_sec" - -/obj/item/clothing/head/helmet/ert/security/paranormal - name = "paranormal emergency response team helmet" - desc = "An in-atmosphere helmet worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." - icon_state = "knight_templar" - item_state = "knight_templar" - -//Engineer -/obj/item/clothing/head/helmet/ert/engineer - name = "emergency response team engineer helmet" - desc = "An in-atmosphere helmet worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." - icon_state = "erthelmet_eng" - -//Medical -/obj/item/clothing/head/helmet/ert/medical - name = "emergency response team medical helmet" - desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "erthelmet_med" - -//Janitorial -/obj/item/clothing/head/helmet/ert/janitor - name = "emergency response team janitor helmet" - desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "erthelmet_jan" +/obj/item/clothing/head/helmet + name = "helmet" + desc = "Standard Security gear. Protects the head from impacts." + icon_state = "helmetmaterials" + flags = HEADBANGPROTECT + flags_cover = HEADCOVERSEYES + item_state = "helmetmaterials" + armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + flags_inv = HIDEEARS|HIDEEYES + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT + strip_delay = 60 + dog_fashion = /datum/dog_fashion/head/helmet + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Drask" = 'icons/mob/species/drask/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + +/obj/item/clothing/head/helmet/attack_self(mob/user) + if(can_toggle && !user.incapacitated()) + if(world.time > cooldown + toggle_cooldown) + cooldown = world.time + up = !up + flags ^= visor_flags + flags_inv ^= visor_flags_inv + icon_state = "[initial(icon_state)][up ? "up" : ""]" + to_chat(user, "[up ? alt_toggle_message : toggle_message] \the [src]") + + user.update_inv_head() + + if(active_sound) + while(up) + playsound(src.loc, "[active_sound]", 100, 0, 4) + sleep(15) + if(toggle_sound) + playsound(src.loc, "[toggle_sound]", 100, 0, 4) + + +/obj/item/clothing/head/helmet/visor + name = "visor helmet" + desc = "A helmet with a built-in visor. It doesn't seem to do anything, but it sure looks cool!" + icon_state = "helmetgoggles" + +/obj/item/clothing/head/helmet/thermal + name = "thermal visor helmet" + desc = "A helmet with a built-in thermal scanning visor." + icon_state = "helmetthermals" + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + +/obj/item/clothing/head/helmet/meson + name = "meson visor helmet" + desc = "A helmet with a built-in meson scanning visor." + icon_state = "helmetmesons" + vision_flags = SEE_TURFS + +/obj/item/clothing/head/helmet/material + name = "material visor helmet" + desc = "A helmet with a built-in material scanning visor." + icon_state = "helmetmaterials" + vision_flags = SEE_OBJS + +/obj/item/clothing/head/helmet/night + name = "night-vision helmet" + desc = "A helmet with a built-in pair of night vision goggles." + icon_state = "helmetNVG" + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + +/obj/item/clothing/head/helmet/alt + name = "bulletproof helmet" + desc = "A bulletproof helmet that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." + icon_state = "swat" + item_state = "swat-alt" + armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + dog_fashion = null + +/obj/item/clothing/head/helmet/riot + name = "riot helmet" + desc = "It's a helmet specifically designed to protect against close range attacks." + icon_state = "riot" + item_state = "helmet" + armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) + flags_inv = HIDEEARS + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + strip_delay = 80 + dog_fashion = null + +/obj/item/clothing/head/helmet/riot/knight + name = "medieval helmet" + desc = "A classic metal helmet." + icon_state = "knight_green" + item_state = "knight_green" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + dog_fashion = null + +/obj/item/clothing/head/helmet/justice + name = "helmet of justice" + desc = "WEEEEOOO. WEEEEEOOO. WEEEEOOOO." + icon_state = "justice" + toggle_message = "You turn off the lights on" + alt_toggle_message = "You turn on the lights on" + actions_types = list(/datum/action/item_action/toggle_helmet_light) + can_toggle = 1 + toggle_cooldown = 20 + active_sound = 'sound/items/weeoo1.ogg' + dog_fashion = null + +/obj/item/clothing/head/helmet/justice/escape + name = "alarm helmet" + desc = "WEEEEOOO. WEEEEEOOO. STOP THAT MONKEY. WEEEOOOO." + icon_state = "justice2" + toggle_message = "You turn off the light on" + alt_toggle_message = "You turn on the light on" + + +/obj/item/clothing/head/helmet/swat + name = "\improper SWAT helmet" + desc = "They're often used by highly trained Swat Members." + icon_state = "swat" + item_state = "swat" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 50, "acid" = 50) + flags = null + flags_inv = HIDEEARS|HIDEEYES + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + strip_delay = 80 + dog_fashion = null + +/obj/item/clothing/head/helmet/swat/syndicate + name = "blood-red helmet" + desc = "An extremely robust, space-worthy helmet without a visor to allow for goggle usage underneath. Property of Gorlex Marauders." + icon_state = "helmetsyndi" + item_state = "helmet" + +/obj/item/clothing/head/helmet/thunderdome + name = "\improper Thunderdome helmet" + desc = "'Let the battle commence!'" + icon_state = "thunderdome" + flags = null + item_state = "thunderdome" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + strip_delay = 80 + dog_fashion = null + +/obj/item/clothing/head/helmet/roman + name = "roman helmet" + desc = "An ancient helmet made of bronze and leather." + flags = null + armor = list("melee" = 25, "bullet" = 0, "laser" = 25, "energy" = 10, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + icon_state = "roman" + item_state = "roman" + strip_delay = 100 + dog_fashion = null + +/obj/item/clothing/head/helmet/roman/fake + desc = "An ancient helmet made of plastic and leather." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/clothing/head/helmet/roman/legionaire + name = "roman legionaire helmet" + desc = "An ancient helmet made of bronze and leather. Has a red crest on top of it." + icon_state = "roman_c" + item_state = "roman_c" + +/obj/item/clothing/head/helmet/roman/legionaire/fake + desc = "An ancient helmet made of plastic and leather. Has a red crest on top of it." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/clothing/head/helmet/gladiator + name = "gladiator helmet" + desc = "Ave, Imperator, morituri te salutant." + icon_state = "gladiator" + flags = BLOCKHAIR + item_state = "gladiator" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + toggle_message = "You attach the face shield to the" + alt_toggle_message = "You remove the face shield from the" + actions_types = list(/datum/action/item_action/toggle_helmet_mode) + can_toggle = 1 + toggle_cooldown = 20 + toggle_sound = 'sound/items/zippoclose.ogg' + dog_fashion = null + +obj/item/clothing/head/helmet/redtaghelm + name = "red laser tag helmet" + desc = "They have chosen their own end." + icon_state = "redtaghelm" + flags = null + item_state = "redtaghelm" + armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) + // Offer about the same protection as a hardhat. + flags_inv = HIDEEARS|HIDEEYES + dog_fashion = null + +obj/item/clothing/head/helmet/bluetaghelm + name = "blue laser tag helmet" + desc = "They'll need more men." + icon_state = "bluetaghelm" + flags = null + item_state = "bluetaghelm" + armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) + // Offer about the same protection as a hardhat. + flags_inv = HIDEEARS|HIDEEYES + dog_fashion = null + +obj/item/clothing/head/blob + name = "blob hat" + desc = "A collectible hat handed out at the latest Blob Family Reunion." + icon_state = "blobhat" + item_state = "blobhat" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi' + ) + +/obj/item/clothing/head/helmet/riot/knight/blue + icon_state = "knight_blue" + item_state = "knight_blue" + +/obj/item/clothing/head/helmet/riot/knight/yellow + icon_state = "knight_yellow" + item_state = "knight_yellow" + +/obj/item/clothing/head/helmet/riot/knight/red + icon_state = "knight_red" + item_state = "knight_red" + +/obj/item/clothing/head/helmet/riot/knight/templar + name = "crusader helmet" + desc = "Deus Vult." + icon_state = "knight_templar" + item_state = "knight_templar" + armor = list(melee = 20, bullet = 7, laser = 2, energy = 2, bomb = 2, bio = 2, rad = 0, fire = 80, acid = 80) + +/obj/item/clothing/head/helmet/skull + name = "skull helmet" + desc = "An intimidating tribal helmet, it doesn't look very comfortable." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + icon_state = "skull" + item_state = "skull" + strip_delay = 100 + +/obj/item/clothing/head/helmet/durathread + name = "durathread helmet" + desc = "A helmet made from durathread and leather." + icon_state = "durathread" + item_state = "durathread" + resistance_flags = FLAMMABLE + armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) + strip_delay = 60 + +//Commander +/obj/item/clothing/head/helmet/ert/command + name = "emergency response team commander helmet" + desc = "An in-atmosphere helmet worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." + icon_state = "erthelmet_cmd" + +//Security +/obj/item/clothing/head/helmet/ert/security + name = "emergency response team security helmet" + desc = "An in-atmosphere helmet worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." + icon_state = "erthelmet_sec" + +/obj/item/clothing/head/helmet/ert/security/paranormal + name = "paranormal emergency response team helmet" + desc = "An in-atmosphere helmet worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." + icon_state = "knight_templar" + item_state = "knight_templar" + +//Engineer +/obj/item/clothing/head/helmet/ert/engineer + name = "emergency response team engineer helmet" + desc = "An in-atmosphere helmet worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." + icon_state = "erthelmet_eng" + +//Medical +/obj/item/clothing/head/helmet/ert/medical + name = "emergency response team medical helmet" + desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "erthelmet_med" + +//Janitorial +/obj/item/clothing/head/helmet/ert/janitor + name = "emergency response team janitor helmet" + desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "erthelmet_jan" diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index e3ce5a62cd3..e2736054899 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -1,190 +1,190 @@ - -//Bartender -/obj/item/clothing/head/chefhat - name = "chef's hat" - desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." - icon_state = "chef" - item_state = "chef" - desc = "The commander in chef's head wear." - strip_delay = 10 - put_on_delay = 10 - dog_fashion = /datum/dog_fashion/head/chef - -//Captain -/obj/item/clothing/head/caphat - name = "captain's hat" - icon_state = "captain" - desc = "It's good being the king." - item_state = "caphat" - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - strip_delay = 60 - dog_fashion = /datum/dog_fashion/head/captain - -//Captain: no longer space-worthy -/obj/item/clothing/head/caphat/parade - name = "captain's parade cap" - desc = "Worn only by Captains with an abundance of class." - icon_state = "capcap" - dog_fashion = null - -//Head of Personnel -/obj/item/clothing/head/hopcap - name = "head of personnel's cap" - icon_state = "hopcap" - desc = "The symbol of true bureaucratic micromanagement." - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - dog_fashion = /datum/dog_fashion/head/hop - -//Nanotrasen Representative -/obj/item/clothing/head/ntrep - name = "Nanotrasen Representative hat" - desc = "A cap issued to the Nanotrasen Representative" - icon_state = "ntrep" - - -//Chaplain -/obj/item/clothing/head/hooded/chaplain_hood - name = "chaplain's hood" - desc = "It's hood that covers the head. It keeps you warm during the space winters." - icon_state = "chaplain_hood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -//Chaplain -/obj/item/clothing/head/hooded/nun_hood - name = "nun hood" - desc = "Maximum piety in this star system." - icon_state = "nun_hood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -//Chaplain -/obj/item/clothing/head/hooded/monk_hood - name = "monk hood" - desc = "Wooden board not included." - icon_state = "monk_hood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/head/witchhunter_hat - name = "witchhunter hat" - desc = "This hat saw much use back in the day." - icon_state = "witchhunterhat" - item_state = "witchhunterhat" - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/head/det_hat - name = "hat" - desc = "Someone who wears this will look very smart." - icon_state = "detective" - allowed = list(/obj/item/reagent_containers/food/snacks/candy/candy_corn, /obj/item/pen) - armor = list("melee" = 25, "bullet" = 5, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) - dog_fashion = /datum/dog_fashion/head/detective - -//Mime -/obj/item/clothing/head/beret - name = "beret" - desc = "A beret, an artists favorite headwear." - icon_state = "beret" - dog_fashion = /datum/dog_fashion/head/beret - -/obj/item/clothing/head/beret/durathread - name = "durathread beret" - desc = "A beret made from durathread, its resilient fibres provide some protection to the wearer." - icon_state = "beretdurathread" - item_color = null - armor = list("melee" = 15, "bullet" = 5, "laser" = 15, "energy" = 5, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 5) - -//Security -/obj/item/clothing/head/HoS - name = "head of security cap" - desc = "The robust standard-issue cap of the Head of Security. For showing the officers who's in charge." - icon_state = "hoscap" - armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60) - strip_delay = 80 - -/obj/item/clothing/head/HoS/beret - name = "head of security beret" - desc = "A robust beret for the Head of Security, for looking stylish while not sacrificing protection." - icon_state = "beret_hos_black" - -/obj/item/clothing/head/warden - name = "warden's police hat" - desc = "It's a special armored hat issued to the Warden of a security force. Protects the head from impacts." - icon_state = "policehelm" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 60) - strip_delay = 60 - dog_fashion = /datum/dog_fashion/head/warden - -/obj/item/clothing/head/officer - name = "officer's cap" - desc = "A red cap with an old-fashioned badge on the front for establishing that you are, in fact, the law." - icon_state = "customshelm" - item_state = "customshelm" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - -/obj/item/clothing/head/beret/sec - name = "security beret" - desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." - icon_state = "beret_officer" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - dog_fashion = null - -/obj/item/clothing/head/beret/sec/warden - name = "warden's beret" - desc = "A special beret with the Warden's insignia emblazoned on it. For wardens with class." - icon_state = "beret_warden" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) - -/obj/item/clothing/head/beret/eng - name = "engineering beret" - desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." - icon_state = "beret_engineering" - -/obj/item/clothing/head/beret/atmos - name = "atmospherics beret" - desc = "A beret for those who have shown immaculate proficienty in piping. Or plumbing." - icon_state = "beret_atmospherics" - -/obj/item/clothing/head/beret/ce - name = "chief engineer beret" - desc = "A white beret with the engineering insignia emblazoned on it. Its owner knows what they're doing. Probably." - icon_state = "beret_ce" - -/obj/item/clothing/head/beret/sci - name = "science beret" - desc = "A purple beret with the science insignia emblazoned on it. It has that authentic burning plasma smell." - icon_state = "beret_sci" - -//Medical -/obj/item/clothing/head/beret/med - name = "medical beret" - desc = "A white beret with a green cross finely threaded into it. It has that sterile smell about it." - icon_state = "beret_med" - -/obj/item/clothing/head/surgery - name = "surgical cap" - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." - icon_state = "surgcap_blue" - flags = BLOCKHEADHAIR - sprite_sheets = list( - "Drask" = 'icons/mob/species/drask/head.dmi' - ) - -/obj/item/clothing/head/surgery/purple - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." - icon_state = "surgcap_purple" - -/obj/item/clothing/head/surgery/blue - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." - icon_state = "surgcap_blue" - -/obj/item/clothing/head/surgery/green - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." - icon_state = "surgcap_green" - -/obj/item/clothing/head/surgery/black - desc = "A cap coroners wear during autopsies. Keeps their hair from falling into the cadavers. It is as dark than the coroner's humor." - icon_state = "surgcap_black" + +//Bartender +/obj/item/clothing/head/chefhat + name = "chef's hat" + desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." + icon_state = "chef" + item_state = "chef" + desc = "The commander in chef's head wear." + strip_delay = 10 + put_on_delay = 10 + dog_fashion = /datum/dog_fashion/head/chef + +//Captain +/obj/item/clothing/head/caphat + name = "captain's hat" + icon_state = "captain" + desc = "It's good being the king." + item_state = "caphat" + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 60 + dog_fashion = /datum/dog_fashion/head/captain + +//Captain: no longer space-worthy +/obj/item/clothing/head/caphat/parade + name = "captain's parade cap" + desc = "Worn only by Captains with an abundance of class." + icon_state = "capcap" + dog_fashion = null + +//Head of Personnel +/obj/item/clothing/head/hopcap + name = "head of personnel's cap" + icon_state = "hopcap" + desc = "The symbol of true bureaucratic micromanagement." + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + dog_fashion = /datum/dog_fashion/head/hop + +//Nanotrasen Representative +/obj/item/clothing/head/ntrep + name = "Nanotrasen Representative hat" + desc = "A cap issued to the Nanotrasen Representative" + icon_state = "ntrep" + + +//Chaplain +/obj/item/clothing/head/hooded/chaplain_hood + name = "chaplain's hood" + desc = "It's hood that covers the head. It keeps you warm during the space winters." + icon_state = "chaplain_hood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +//Chaplain +/obj/item/clothing/head/hooded/nun_hood + name = "nun hood" + desc = "Maximum piety in this star system." + icon_state = "nun_hood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +//Chaplain +/obj/item/clothing/head/hooded/monk_hood + name = "monk hood" + desc = "Wooden board not included." + icon_state = "monk_hood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/head/witchhunter_hat + name = "witchhunter hat" + desc = "This hat saw much use back in the day." + icon_state = "witchhunterhat" + item_state = "witchhunterhat" + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/head/det_hat + name = "hat" + desc = "Someone who wears this will look very smart." + icon_state = "detective" + allowed = list(/obj/item/reagent_containers/food/snacks/candy/candy_corn, /obj/item/pen) + armor = list("melee" = 25, "bullet" = 5, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) + dog_fashion = /datum/dog_fashion/head/detective + +//Mime +/obj/item/clothing/head/beret + name = "beret" + desc = "A beret, an artists favorite headwear." + icon_state = "beret" + dog_fashion = /datum/dog_fashion/head/beret + +/obj/item/clothing/head/beret/durathread + name = "durathread beret" + desc = "A beret made from durathread, its resilient fibres provide some protection to the wearer." + icon_state = "beretdurathread" + item_color = null + armor = list("melee" = 15, "bullet" = 5, "laser" = 15, "energy" = 5, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 5) + +//Security +/obj/item/clothing/head/HoS + name = "head of security cap" + desc = "The robust standard-issue cap of the Head of Security. For showing the officers who's in charge." + icon_state = "hoscap" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60) + strip_delay = 80 + +/obj/item/clothing/head/HoS/beret + name = "head of security beret" + desc = "A robust beret for the Head of Security, for looking stylish while not sacrificing protection." + icon_state = "beret_hos_black" + +/obj/item/clothing/head/warden + name = "warden's police hat" + desc = "It's a special armored hat issued to the Warden of a security force. Protects the head from impacts." + icon_state = "policehelm" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 60) + strip_delay = 60 + dog_fashion = /datum/dog_fashion/head/warden + +/obj/item/clothing/head/officer + name = "officer's cap" + desc = "A red cap with an old-fashioned badge on the front for establishing that you are, in fact, the law." + icon_state = "customshelm" + item_state = "customshelm" + armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/head/beret/sec + name = "security beret" + desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." + icon_state = "beret_officer" + armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + dog_fashion = null + +/obj/item/clothing/head/beret/sec/warden + name = "warden's beret" + desc = "A special beret with the Warden's insignia emblazoned on it. For wardens with class." + icon_state = "beret_warden" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) + +/obj/item/clothing/head/beret/eng + name = "engineering beret" + desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." + icon_state = "beret_engineering" + +/obj/item/clothing/head/beret/atmos + name = "atmospherics beret" + desc = "A beret for those who have shown immaculate proficienty in piping. Or plumbing." + icon_state = "beret_atmospherics" + +/obj/item/clothing/head/beret/ce + name = "chief engineer beret" + desc = "A white beret with the engineering insignia emblazoned on it. Its owner knows what they're doing. Probably." + icon_state = "beret_ce" + +/obj/item/clothing/head/beret/sci + name = "science beret" + desc = "A purple beret with the science insignia emblazoned on it. It has that authentic burning plasma smell." + icon_state = "beret_sci" + +//Medical +/obj/item/clothing/head/beret/med + name = "medical beret" + desc = "A white beret with a green cross finely threaded into it. It has that sterile smell about it." + icon_state = "beret_med" + +/obj/item/clothing/head/surgery + name = "surgical cap" + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." + icon_state = "surgcap_blue" + flags = BLOCKHEADHAIR + sprite_sheets = list( + "Drask" = 'icons/mob/species/drask/head.dmi' + ) + +/obj/item/clothing/head/surgery/purple + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." + icon_state = "surgcap_purple" + +/obj/item/clothing/head/surgery/blue + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." + icon_state = "surgcap_blue" + +/obj/item/clothing/head/surgery/green + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." + icon_state = "surgcap_green" + +/obj/item/clothing/head/surgery/black + desc = "A cap coroners wear during autopsies. Keeps their hair from falling into the cadavers. It is as dark than the coroner's humor." + icon_state = "surgcap_black" diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index ef49855dbec..cac07fe5b1d 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -1,493 +1,493 @@ - - -/obj/item/clothing/head/centhat - name = "\improper CentComm. hat" - icon_state = "centcom" - desc = "It's good to be emperor." - item_state = "centhat" - armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - strip_delay = 80 - -/obj/item/clothing/head/hairflower - name = "hair flower pin" - icon_state = "hairflower" - desc = "Smells nice." - item_state = "hairflower" - -/obj/item/clothing/head/powdered_wig - name = "powdered wig" - desc = "A powdered wig." - icon_state = "pwig" - item_state = "pwig" - -/obj/item/clothing/head/justice_wig - name = "Justice wig" - desc = "A fancy powdered wig given to arbitrators of the law. It looks itchy." - icon_state = "jwig" - item_state = "jwig" - -/obj/item/clothing/head/beret/blue - icon_state = "beret_blue" - -/obj/item/clothing/head/beret/black - icon_state = "beret_black" - -/obj/item/clothing/head/beret/purple_normal - icon_state = "beret_purple_normal" - -/obj/item/clothing/head/that - name = "top-hat" - desc = "It's an amish looking hat." - icon_state = "tophat" - item_state = "that" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/redcoat - name = "redcoat's hat" - icon_state = "redcoat" - desc = "'I guess it's a redhead.'" - -/obj/item/clothing/head/mailman - name = "mailman's hat" - icon_state = "mailman" - desc = "'Right-on-time' mail service head wear." - -/obj/item/clothing/head/plaguedoctorhat - name = "plague doctor's hat" - desc = "These were once used by Plague doctors. They're pretty much useless." - icon_state = "plaguedoctor" - permeability_coefficient = 0.01 - -/obj/item/clothing/head/hasturhood - name = "hastur's hood" - desc = "It's unspeakably stylish" - icon_state = "hasturhood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/head/nursehat - name = "nurse's hat" - desc = "It allows quick identification of trained medical personnel." - icon_state = "nursehat" - dog_fashion = /datum/dog_fashion/head/nurse - -/obj/item/clothing/head/syndicatefake - name = "black and red space-helmet replica" - icon_state = "syndicate-helm-black-red" - item_state = "syndicate-helm-black-red" - desc = "A plastic replica of a syndicate agent's space helmet, you'll look just like a real murderous syndicate agent in this! This is a toy, it is not made for use in space!" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - - -/obj/item/clothing/head/cueball - name = "cueball helmet" - desc = "A large, featureless white orb meant to be worn on your head. How do you even see out of this thing?" - icon_state = "cueball" - item_state = "cueball" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/snowman - name = "snowman head" - desc = "A ball of white styrofoam. So festive." - icon_state = "snowman_h" - item_state = "snowman_h" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/that - name = "sturdy top-hat" - desc = "It's an amish looking armored top hat." - icon_state = "tophat" - item_state = "that" - flags_inv = 0 - - -/obj/item/clothing/head/greenbandana - name = "green bandana" - desc = "It's a green bandana with some fine nanotech lining." - icon_state = "greenbandana" - item_state = "greenbandana" - flags_inv = 0 - -/obj/item/clothing/head/justice - name = "justice hat" - desc = "Fight for what's righteous!" - icon_state = "justicered" - item_state = "justicered" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - -/obj/item/clothing/head/justice/blue - icon_state = "justiceblue" - item_state = "justiceblue" - -/obj/item/clothing/head/justice/yellow - icon_state = "justiceyellow" - item_state = "justiceyellow" - -/obj/item/clothing/head/justice/green - icon_state = "justicegreen" - item_state = "justicegreen" - -/obj/item/clothing/head/justice/pink - icon_state = "justicepink" - item_state = "justicepink" - -/obj/item/clothing/head/rabbitears - name = "rabbit ears" - desc = "Wearing these makes you look useless, and only good for your sex appeal." - icon_state = "bunny" - dog_fashion = /datum/dog_fashion/head/rabbit - -/obj/item/clothing/head/flatcap - name = "flat cap" - desc = "A working man's cap." - icon_state = "flat_cap" - item_state = "detective" - -/obj/item/clothing/head/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - dog_fashion = /datum/dog_fashion/head/pirate - -/obj/item/clothing/head/hgpiratecap - name = "pirate hat" - desc = "Yarr." - icon_state = "hgpiratecap" - item_state = "hgpiratecap" - -/obj/item/clothing/head/bandana - name = "pirate bandana" - desc = "Yarr." - icon_state = "bandana" - item_state = "bandana" - -//stylish bs12 hats - -/obj/item/clothing/head/bowlerhat - name = "bowler hat" - icon_state = "bowler_hat" - item_state = "bowler_hat" - desc = "For that industrial age look." - -/obj/item/clothing/head/beaverhat - name = "beaver hat" - icon_state = "beaver_hat" - item_state = "beaver_hat" - desc = "Like a top hat, but made of beavers." - -/obj/item/clothing/head/boaterhat - name = "boater hat" - icon_state = "boater_hat" - item_state = "boater_hat" - desc = "Goes well with celery." - -/obj/item/clothing/head/cowboyhat - name = "cowboy hat" - icon_state = "cowboyhat" - item_state = "cowboyhat" - desc = "For the Rancher in us all." - -/obj/item/clothing/head/cowboyhat/tan - name = "tan cowboy hat" - icon_state = "cowboyhat_tan" - item_state = "cowboyhat_tan" - desc = "There's a new sheriff in town. Pass the whiskey." - -/obj/item/clothing/head/cowboyhat/black - name = "black cowboy hat" - icon_state = "cowboyhat_black" - item_state = "cowboyhat_black" - desc = "This station ain't big enough for the two ah' us." - -/obj/item/clothing/head/cowboyhat/white - name = "white cowboy hat" - icon_state = "cowboyhat_white" - item_state = "cowboyhat_white" - desc = "Authentic Marshall hair case. Now ya can protect this here homestead. Navy Model not included." - -/obj/item/clothing/head/cowboyhat/pink - name = "cowgirl hat" - icon_state = "cowboyhat_pink" - item_state = "cowboyhat_pink" - desc = "For those buckle bunnies wanta' become a real buckaroo." - -/obj/item/clothing/head/fedora - name = "fedora" - icon_state = "fedora" - item_state = "fedora" - desc = "A great hat ruined by being within fifty yards of you." - actions_types = list(/datum/action/item_action/tip_fedora) - -/obj/item/clothing/head/fedora/attack_self(mob/user) - tip_fedora(user) - -/obj/item/clothing/head/fedora/item_action_slot_check(slot) - if(slot == slot_head) - return 1 - -/obj/item/clothing/head/fedora/proc/tip_fedora(mob/user) - user.visible_message("[user] tips [user.p_their()] fedora.", "You tip your fedora") - - -/obj/item/clothing/head/fez - name = "fez" - icon_state = "fez" - item_state = "fez" - desc = "Put it on your monkey, make lots of cash money." - -//end bs12 hats - -/obj/item/clothing/head/witchwig - name = "witch costume wig" - desc = "Eeeee~heheheheheheh!" - icon_state = "witch" - item_state = "witch" - flags = BLOCKHAIR - -/obj/item/clothing/head/chicken - name = "chicken suit head" - desc = "Bkaw!" - icon_state = "chickenhead" - item_state = "chickensuit" - flags = BLOCKHAIR - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/corgi - name = "corgi suit head" - desc = "Woof!" - icon_state = "corgihead" - item_state = "chickensuit" - flags = BLOCKHAIR - -/obj/item/clothing/head/corgi/super_hero - name = "super-hero corgi suit head" - desc = "Woof! This one seems to pulse with a strange power" - flags = BLOCKHAIR | NODROP - -/obj/item/clothing/head/corgi/super_hero/en - name = "E-N suit head" - icon_state = "enhead" - -/obj/item/clothing/head/bearpelt - name = "bear pelt hat" - desc = "Fuzzy." - icon_state = "bearpelt" - item_state = "bearpelt" - flags = BLOCKHAIR - -/obj/item/clothing/head/xenos - name = "xenos helmet" - icon_state = "xenos" - item_state = "xenos_helm" - desc = "A helmet made out of chitinous alien hide." - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - -/obj/item/clothing/head/fedora - name = "fedora" - desc = "Someone wearing this definitely makes them cool" - icon_state = "fedora" - -/obj/item/clothing/head/fedora/whitefedora - name = "white fedora" - icon_state = "wfedora" - -/obj/item/clothing/head/fedora/brownfedora - name = "brown fedora" - icon_state = "bfedora" - -/obj/item/clothing/head/stalhelm - name = "Clown Stalhelm" - desc = "The typical clown soldier's helmet." - icon_state = "stalhelm" - item_state = "stalhelm" - flags = BLOCKHAIR - flags_inv = HIDEEARS - -/obj/item/clothing/head/panzer - name = "Clown HONKMech Cap" - desc = "The softcap worn by HONK Mech pilots." - icon_state = "panzercap" - item_state = "panzercap" - flags = BLOCKHAIR - -/obj/item/clothing/head/naziofficer - name = "Clown Officer Cap" - desc = "The peaked clown officer's cap, disturbingly similar to the warden's." - icon_state = "officercap" - item_state = "officercap" - flags = BLOCKHAIR - flags_inv = HIDEEARS - -/obj/item/clothing/head/beret/purple - name = "Pierson Family Beret" - desc = " A purple beret, with a small golden crescent moon sewn onto it." - icon_state = "beret_purple" - item_state = "purpleberet" - -/obj/item/clothing/head/beret/centcom/officer - name = "officers beret" - desc = "A black beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." - icon_state = "beret_centcom_officer" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - -/obj/item/clothing/head/beret/centcom/officer/navy - name = "navy blue officers beret" - desc = "A navy blue beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." - icon_state = "beret_centcom_officer_navy" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - -/obj/item/clothing/head/beret/centcom/captain - name = "captains beret" - desc = "A white beret adorned with the shield—a cobalt kite shield with an engraved sword—of the Nanotrasen security forces, worn only by those captaining a vessel of the Nanotrasen Navy." - icon_state = "beret_centcom_captain" - -/obj/item/clothing/head/sombrero - name = "sombrero" - icon_state = "sombrero" - item_state = "sombrero" - desc = "You can practically taste the fiesta." - dog_fashion = /datum/dog_fashion/head/sombrero - -/obj/item/clothing/head/sombrero/green - name = "green sombrero" - icon_state = "greensombrero" - item_state = "greensombrero" - desc = "As elegant as a dancing cactus." - dog_fashion = null - -/obj/item/clothing/head/sombrero/shamebrero - name = "shamebrero" - icon_state = "shamebrero" - item_state = "shamebrero" - desc = "Once it's on, it never comes off." - flags = NODROP - dog_fashion = null - -/obj/item/clothing/head/cone - desc = "This cone is trying to warn you of something!" - name = "warning cone" - icon = 'icons/obj/janitor.dmi' - icon_state = "cone" - item_state = "cone" - force = 1.0 - throwforce = 3.0 - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - attack_verb = list("warned", "cautioned", "smashed") - resistance_flags = NONE - -/obj/item/clothing/head/jester - name = "jester hat" - desc = "A hat with bells, to add some merryness to the suit." - icon_state = "jester_hat" - -/obj/item/clothing/head/rice_hat - name = "rice hat" - desc = "Welcome to the rice fields, motherfucker." - icon_state = "rice_hat" - -/obj/item/clothing/head/griffin - name = "griffon head" - desc = "Why not 'eagle head'? Who knows." - icon_state = "griffinhat" - item_state = "griffinhat" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - actions_types = list(/datum/action/item_action/caw) - -/obj/item/clothing/head/griffin/attack_self() - caw() - -/obj/item/clothing/head/griffin/proc/caw() - if(cooldown < world.time - 20) // A cooldown, to stop people being jerks - playsound(src.loc, 'sound/creatures/caw.ogg', 50, 1) - cooldown = world.time - - -/obj/item/clothing/head/lordadmiralhat - name = "Lord Admiral's Hat" - desc = "A hat suitable for any man of high and exalted rank." - icon_state = "lordadmiralhat" - item_state = "lordadmiralhat" - -/obj/item/clothing/head/human_head - name = "bloated human head" - desc = "A horribly bloated and mismatched human head." - icon_state = "lingspacehelmet" - item_state = "lingspacehelmet" - -/obj/item/clothing/head/papersack - name = "paper sack hat" - desc = "A paper sack with crude holes cut out for eyes. Useful for hiding one's identity or ugliness." - icon_state = "papersack" - flags = BLOCKHAIR - flags_inv = HIDEFACE|HIDEEARS - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/papersack/smiley - name = "paper sack hat" - desc = "A paper sack with crude holes cut out for eyes and a sketchy smile drawn on the front. Not creepy at all." - icon_state = "papersack_smile" - flags = BLOCKHAIR - flags_inv = HIDEFACE|HIDEEARS - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/crown - name = "crown" - desc = "A crown fit for a king, a petty king maybe." - icon_state = "crown" - armor = list("melee" = 15, "bullet" = 0, "laser" = 0,"energy" = 15, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) - resistance_flags = FIRE_PROOF - -/obj/item/clothing/head/crown/fancy - name = "magnificent crown" - desc = "A crown worn by only the highest emperors of the land." - icon_state = "fancycrown" - -/obj/item/clothing/head/zepelli - name = "chequered diamond hat" - desc = "Wearing this makes you feel like a real mozzarella cheeseball. " - icon_state = "zepelli" - item_state = "zepelli" - -/obj/item/clothing/head/cuban_hat - name = "rhumba hat" - desc = "Now just to find some maracas!" - icon_state = "cuban_hat" - item_state = "cuban_hat" \ No newline at end of file + + +/obj/item/clothing/head/centhat + name = "\improper CentComm. hat" + icon_state = "centcom" + desc = "It's good to be emperor." + item_state = "centhat" + armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 80 + +/obj/item/clothing/head/hairflower + name = "hair flower pin" + icon_state = "hairflower" + desc = "Smells nice." + item_state = "hairflower" + +/obj/item/clothing/head/powdered_wig + name = "powdered wig" + desc = "A powdered wig." + icon_state = "pwig" + item_state = "pwig" + +/obj/item/clothing/head/justice_wig + name = "Justice wig" + desc = "A fancy powdered wig given to arbitrators of the law. It looks itchy." + icon_state = "jwig" + item_state = "jwig" + +/obj/item/clothing/head/beret/blue + icon_state = "beret_blue" + +/obj/item/clothing/head/beret/black + icon_state = "beret_black" + +/obj/item/clothing/head/beret/purple_normal + icon_state = "beret_purple_normal" + +/obj/item/clothing/head/that + name = "top-hat" + desc = "It's an amish looking hat." + icon_state = "tophat" + item_state = "that" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/redcoat + name = "redcoat's hat" + icon_state = "redcoat" + desc = "'I guess it's a redhead.'" + +/obj/item/clothing/head/mailman + name = "mailman's hat" + icon_state = "mailman" + desc = "'Right-on-time' mail service head wear." + +/obj/item/clothing/head/plaguedoctorhat + name = "plague doctor's hat" + desc = "These were once used by Plague doctors. They're pretty much useless." + icon_state = "plaguedoctor" + permeability_coefficient = 0.01 + +/obj/item/clothing/head/hasturhood + name = "hastur's hood" + desc = "It's unspeakably stylish" + icon_state = "hasturhood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/head/nursehat + name = "nurse's hat" + desc = "It allows quick identification of trained medical personnel." + icon_state = "nursehat" + dog_fashion = /datum/dog_fashion/head/nurse + +/obj/item/clothing/head/syndicatefake + name = "black and red space-helmet replica" + icon_state = "syndicate-helm-black-red" + item_state = "syndicate-helm-black-red" + desc = "A plastic replica of a syndicate agent's space helmet, you'll look just like a real murderous syndicate agent in this! This is a toy, it is not made for use in space!" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + + +/obj/item/clothing/head/cueball + name = "cueball helmet" + desc = "A large, featureless white orb meant to be worn on your head. How do you even see out of this thing?" + icon_state = "cueball" + item_state = "cueball" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/snowman + name = "snowman head" + desc = "A ball of white styrofoam. So festive." + icon_state = "snowman_h" + item_state = "snowman_h" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/that + name = "sturdy top-hat" + desc = "It's an amish looking armored top hat." + icon_state = "tophat" + item_state = "that" + flags_inv = 0 + + +/obj/item/clothing/head/greenbandana + name = "green bandana" + desc = "It's a green bandana with some fine nanotech lining." + icon_state = "greenbandana" + item_state = "greenbandana" + flags_inv = 0 + +/obj/item/clothing/head/justice + name = "justice hat" + desc = "Fight for what's righteous!" + icon_state = "justicered" + item_state = "justicered" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + +/obj/item/clothing/head/justice/blue + icon_state = "justiceblue" + item_state = "justiceblue" + +/obj/item/clothing/head/justice/yellow + icon_state = "justiceyellow" + item_state = "justiceyellow" + +/obj/item/clothing/head/justice/green + icon_state = "justicegreen" + item_state = "justicegreen" + +/obj/item/clothing/head/justice/pink + icon_state = "justicepink" + item_state = "justicepink" + +/obj/item/clothing/head/rabbitears + name = "rabbit ears" + desc = "Wearing these makes you look useless, and only good for your sex appeal." + icon_state = "bunny" + dog_fashion = /datum/dog_fashion/head/rabbit + +/obj/item/clothing/head/flatcap + name = "flat cap" + desc = "A working man's cap." + icon_state = "flat_cap" + item_state = "detective" + +/obj/item/clothing/head/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + dog_fashion = /datum/dog_fashion/head/pirate + +/obj/item/clothing/head/hgpiratecap + name = "pirate hat" + desc = "Yarr." + icon_state = "hgpiratecap" + item_state = "hgpiratecap" + +/obj/item/clothing/head/bandana + name = "pirate bandana" + desc = "Yarr." + icon_state = "bandana" + item_state = "bandana" + +//stylish bs12 hats + +/obj/item/clothing/head/bowlerhat + name = "bowler hat" + icon_state = "bowler_hat" + item_state = "bowler_hat" + desc = "For that industrial age look." + +/obj/item/clothing/head/beaverhat + name = "beaver hat" + icon_state = "beaver_hat" + item_state = "beaver_hat" + desc = "Like a top hat, but made of beavers." + +/obj/item/clothing/head/boaterhat + name = "boater hat" + icon_state = "boater_hat" + item_state = "boater_hat" + desc = "Goes well with celery." + +/obj/item/clothing/head/cowboyhat + name = "cowboy hat" + icon_state = "cowboyhat" + item_state = "cowboyhat" + desc = "For the Rancher in us all." + +/obj/item/clothing/head/cowboyhat/tan + name = "tan cowboy hat" + icon_state = "cowboyhat_tan" + item_state = "cowboyhat_tan" + desc = "There's a new sheriff in town. Pass the whiskey." + +/obj/item/clothing/head/cowboyhat/black + name = "black cowboy hat" + icon_state = "cowboyhat_black" + item_state = "cowboyhat_black" + desc = "This station ain't big enough for the two ah' us." + +/obj/item/clothing/head/cowboyhat/white + name = "white cowboy hat" + icon_state = "cowboyhat_white" + item_state = "cowboyhat_white" + desc = "Authentic Marshall hair case. Now ya can protect this here homestead. Navy Model not included." + +/obj/item/clothing/head/cowboyhat/pink + name = "cowgirl hat" + icon_state = "cowboyhat_pink" + item_state = "cowboyhat_pink" + desc = "For those buckle bunnies wanta' become a real buckaroo." + +/obj/item/clothing/head/fedora + name = "fedora" + icon_state = "fedora" + item_state = "fedora" + desc = "A great hat ruined by being within fifty yards of you." + actions_types = list(/datum/action/item_action/tip_fedora) + +/obj/item/clothing/head/fedora/attack_self(mob/user) + tip_fedora(user) + +/obj/item/clothing/head/fedora/item_action_slot_check(slot) + if(slot == slot_head) + return 1 + +/obj/item/clothing/head/fedora/proc/tip_fedora(mob/user) + user.visible_message("[user] tips [user.p_their()] fedora.", "You tip your fedora") + + +/obj/item/clothing/head/fez + name = "fez" + icon_state = "fez" + item_state = "fez" + desc = "Put it on your monkey, make lots of cash money." + +//end bs12 hats + +/obj/item/clothing/head/witchwig + name = "witch costume wig" + desc = "Eeeee~heheheheheheh!" + icon_state = "witch" + item_state = "witch" + flags = BLOCKHAIR + +/obj/item/clothing/head/chicken + name = "chicken suit head" + desc = "Bkaw!" + icon_state = "chickenhead" + item_state = "chickensuit" + flags = BLOCKHAIR + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/corgi + name = "corgi suit head" + desc = "Woof!" + icon_state = "corgihead" + item_state = "chickensuit" + flags = BLOCKHAIR + +/obj/item/clothing/head/corgi/super_hero + name = "super-hero corgi suit head" + desc = "Woof! This one seems to pulse with a strange power" + flags = BLOCKHAIR | NODROP + +/obj/item/clothing/head/corgi/super_hero/en + name = "E-N suit head" + icon_state = "enhead" + +/obj/item/clothing/head/bearpelt + name = "bear pelt hat" + desc = "Fuzzy." + icon_state = "bearpelt" + item_state = "bearpelt" + flags = BLOCKHAIR + +/obj/item/clothing/head/xenos + name = "xenos helmet" + icon_state = "xenos" + item_state = "xenos_helm" + desc = "A helmet made out of chitinous alien hide." + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + +/obj/item/clothing/head/fedora + name = "fedora" + desc = "Someone wearing this definitely makes them cool" + icon_state = "fedora" + +/obj/item/clothing/head/fedora/whitefedora + name = "white fedora" + icon_state = "wfedora" + +/obj/item/clothing/head/fedora/brownfedora + name = "brown fedora" + icon_state = "bfedora" + +/obj/item/clothing/head/stalhelm + name = "Clown Stalhelm" + desc = "The typical clown soldier's helmet." + icon_state = "stalhelm" + item_state = "stalhelm" + flags = BLOCKHAIR + flags_inv = HIDEEARS + +/obj/item/clothing/head/panzer + name = "Clown HONKMech Cap" + desc = "The softcap worn by HONK Mech pilots." + icon_state = "panzercap" + item_state = "panzercap" + flags = BLOCKHAIR + +/obj/item/clothing/head/naziofficer + name = "Clown Officer Cap" + desc = "The peaked clown officer's cap, disturbingly similar to the warden's." + icon_state = "officercap" + item_state = "officercap" + flags = BLOCKHAIR + flags_inv = HIDEEARS + +/obj/item/clothing/head/beret/purple + name = "Pierson Family Beret" + desc = " A purple beret, with a small golden crescent moon sewn onto it." + icon_state = "beret_purple" + item_state = "purpleberet" + +/obj/item/clothing/head/beret/centcom/officer + name = "officers beret" + desc = "A black beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." + icon_state = "beret_centcom_officer" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/head/beret/centcom/officer/navy + name = "navy blue officers beret" + desc = "A navy blue beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." + icon_state = "beret_centcom_officer_navy" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/head/beret/centcom/captain + name = "captains beret" + desc = "A white beret adorned with the shield—a cobalt kite shield with an engraved sword—of the Nanotrasen security forces, worn only by those captaining a vessel of the Nanotrasen Navy." + icon_state = "beret_centcom_captain" + +/obj/item/clothing/head/sombrero + name = "sombrero" + icon_state = "sombrero" + item_state = "sombrero" + desc = "You can practically taste the fiesta." + dog_fashion = /datum/dog_fashion/head/sombrero + +/obj/item/clothing/head/sombrero/green + name = "green sombrero" + icon_state = "greensombrero" + item_state = "greensombrero" + desc = "As elegant as a dancing cactus." + dog_fashion = null + +/obj/item/clothing/head/sombrero/shamebrero + name = "shamebrero" + icon_state = "shamebrero" + item_state = "shamebrero" + desc = "Once it's on, it never comes off." + flags = NODROP + dog_fashion = null + +/obj/item/clothing/head/cone + desc = "This cone is trying to warn you of something!" + name = "warning cone" + icon = 'icons/obj/janitor.dmi' + icon_state = "cone" + item_state = "cone" + force = 1.0 + throwforce = 3.0 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("warned", "cautioned", "smashed") + resistance_flags = NONE + +/obj/item/clothing/head/jester + name = "jester hat" + desc = "A hat with bells, to add some merryness to the suit." + icon_state = "jester_hat" + +/obj/item/clothing/head/rice_hat + name = "rice hat" + desc = "Welcome to the rice fields, motherfucker." + icon_state = "rice_hat" + +/obj/item/clothing/head/griffin + name = "griffon head" + desc = "Why not 'eagle head'? Who knows." + icon_state = "griffinhat" + item_state = "griffinhat" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + actions_types = list(/datum/action/item_action/caw) + +/obj/item/clothing/head/griffin/attack_self() + caw() + +/obj/item/clothing/head/griffin/proc/caw() + if(cooldown < world.time - 20) // A cooldown, to stop people being jerks + playsound(src.loc, 'sound/creatures/caw.ogg', 50, 1) + cooldown = world.time + + +/obj/item/clothing/head/lordadmiralhat + name = "Lord Admiral's Hat" + desc = "A hat suitable for any man of high and exalted rank." + icon_state = "lordadmiralhat" + item_state = "lordadmiralhat" + +/obj/item/clothing/head/human_head + name = "bloated human head" + desc = "A horribly bloated and mismatched human head." + icon_state = "lingspacehelmet" + item_state = "lingspacehelmet" + +/obj/item/clothing/head/papersack + name = "paper sack hat" + desc = "A paper sack with crude holes cut out for eyes. Useful for hiding one's identity or ugliness." + icon_state = "papersack" + flags = BLOCKHAIR + flags_inv = HIDEFACE|HIDEEARS + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/papersack/smiley + name = "paper sack hat" + desc = "A paper sack with crude holes cut out for eyes and a sketchy smile drawn on the front. Not creepy at all." + icon_state = "papersack_smile" + flags = BLOCKHAIR + flags_inv = HIDEFACE|HIDEEARS + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/crown + name = "crown" + desc = "A crown fit for a king, a petty king maybe." + icon_state = "crown" + armor = list("melee" = 15, "bullet" = 0, "laser" = 0,"energy" = 15, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + +/obj/item/clothing/head/crown/fancy + name = "magnificent crown" + desc = "A crown worn by only the highest emperors of the land." + icon_state = "fancycrown" + +/obj/item/clothing/head/zepelli + name = "chequered diamond hat" + desc = "Wearing this makes you feel like a real mozzarella cheeseball. " + icon_state = "zepelli" + item_state = "zepelli" + +/obj/item/clothing/head/cuban_hat + name = "rhumba hat" + desc = "Now just to find some maracas!" + icon_state = "cuban_hat" + item_state = "cuban_hat" diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index d939dadffba..5f8ae17d061 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -1,273 +1,273 @@ -/* - * Contents: - * Welding mask - * Cakehat - * Ushanka - * Pumpkin head - * Kitty ears - * Cardborg Disguise - * Head Mirror - */ - -/* - * Welding mask - */ -/obj/item/clothing/head/welding - name = "welding helmet" - desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." - icon_state = "welding" - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - item_state = "welding" - materials = list(MAT_METAL=1750, MAT_GLASS=400) - flash_protect = 2 - tint = 2 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 60) - flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - actions_types = list(/datum/action/item_action/toggle) - visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - resistance_flags = FIRE_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - -/obj/item/clothing/head/welding/attack_self(mob/user) - weldingvisortoggle(user) - -/obj/item/clothing/head/welding/flamedecal - name = "flame decal welding helmet" - desc = "A welding helmet adorned with flame decals, and several cryptic slogans of varying degrees of legibility." - icon_state = "welding_redflame" - -/obj/item/clothing/head/welding/flamedecal/blue - name = "blue flame decal welding helmet" - desc = "A welding helmet with blue flame decals on it." - icon_state = "welding_blueflame" - -/obj/item/clothing/head/welding/white - name = "white decal welding helmet" - desc = "A white welding helmet with a character written across it." - icon_state = "welding_white" - -/obj/item/clothing/head/welding/attack_self() - toggle() - -/obj/item/clothing/head/welding/proc/toggle() - if(up) - up = !up - flags_cover |= (HEADCOVERSEYES | HEADCOVERSMOUTH) - flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = initial(icon_state) - to_chat(usr, "You flip the [src] down to protect your eyes.") - flash_protect = 2 - tint = 2 - else - up = !up - flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) - flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = "[initial(icon_state)]up" - to_chat(usr, "You push the [src] up out of your face.") - flash_protect = 0 - tint = 0 - var/mob/living/carbon/user = usr - user.update_tint() - user.update_inv_head() //so our mob-overlays update - - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - - - -/* - * Cakehat - */ -/obj/item/clothing/head/cakehat - name = "cake-hat" - desc = "It's tasty looking!" - icon_state = "cake0" - flags_cover = HEADCOVERSEYES - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - var/onfire = 0.0 - var/status = 0 - var/fire_resist = T0C+1300 //this is the max temp it can stand before you start to cook. although it might not burn away, you take damage - var/processing = 0 //I dont think this is used anywhere. - -/obj/item/clothing/head/cakehat/process() - if(!onfire) - STOP_PROCESSING(SSobj, src) - return - - var/turf/location = src.loc - if(istype(location, /mob/)) - var/mob/living/carbon/human/M = location - if(M.l_hand == src || M.r_hand == src || M.head == src) - location = M.loc - - if(istype(location, /turf)) - location.hotspot_expose(700, 1) - -/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) - if(status > 1) return - src.onfire = !( src.onfire ) - if(src.onfire) - src.force = 3 - src.damtype = "fire" - src.icon_state = "cake1" - START_PROCESSING(SSobj, src) - else - src.force = null - src.damtype = "brute" - src.icon_state = "cake0" - return - - -/* - * Ushanka - */ -/obj/item/clothing/head/ushanka - name = "ushanka" - desc = "Perfect for winter in Siberia, da?" - icon_state = "ushankadown" - item_state = "ushankadown" - flags_inv = HIDEEARS - cold_protection = HEAD - min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT - dog_fashion = /datum/dog_fashion/head/ushanka - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) - if(src.icon_state == "ushankadown") - src.icon_state = "ushankaup" - src.item_state = "ushankaup" - to_chat(user, "You raise the ear flaps on the ushanka.") - else - src.icon_state = "ushankadown" - src.item_state = "ushankadown" - to_chat(user, "You lower the ear flaps on the ushanka.") - -/* - * Pumpkin head - */ -/obj/item/clothing/head/hardhat/pumpkinhead - name = "carved pumpkin" - desc = "A jack o' lantern! Believed to ward off evil spirits." - icon_state = "hardhat0_pumpkin"//Could stand to be renamed - item_state = "hardhat0_pumpkin" - item_color = "pumpkin" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - brightness_on = 2 //luminosity when on - - -/obj/item/clothing/head/hardhat/reindeer - name = "novelty reindeer hat" - desc = "Some fake antlers and a very fake red nose." - icon_state = "hardhat0_reindeer" - item_state = "hardhat0_reindeer" - item_color = "reindeer" - flags_inv = 0 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - brightness_on = 1 //luminosity when on - dog_fashion = /datum/dog_fashion/head/reindeer - - -/* - * Kitty ears - */ -/obj/item/clothing/head/kitty - name = "kitty ears" - desc = "A pair of kitty ears. Meow!" - icon_state = "kitty" - var/icon/mob - dog_fashion = /datum/dog_fashion/head/kitty - -/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) - if(!istype(user)) return - var/obj/item/organ/external/head/head_organ = user.get_organ("head") - - mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kitty") - mob.Blend(head_organ.hair_colour, ICON_ADD) - - var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kittyinner") - mob.Blend(earbit, ICON_OVERLAY) - - icon_override = mob - -/obj/item/clothing/head/kitty/equipped(var/mob/M, slot) - . = ..() - if(ishuman(M) && slot == slot_head) - update_icon(M) - - -/obj/item/clothing/head/kitty/mouse - name = "mouse ears" - desc = "A pair of mouse ears. Squeak!" - icon_state = "mousey" - -/obj/item/clothing/head/kitty/mouse/update_icon(var/mob/living/carbon/human/user) - if(!istype(user)) return - var/obj/item/organ/external/head/head_organ = user.get_organ("head") - mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mousey") - mob.Blend(head_organ.hair_colour, ICON_ADD) - - var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mouseyinner") - mob.Blend(earbit, ICON_OVERLAY) - - icon_override = mob - -/obj/item/clothing/head/cardborg - name = "cardborg helmet" - desc = "A helmet made out of a box." - icon_state = "cardborg_h" - item_state = "cardborg_h" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - species_disguise = "High-tech robot" - dog_fashion = /datum/dog_fashion/head/cardborg - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - - -/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot) - ..() - if(ishuman(user) && slot == slot_head) - var/mob/living/carbon/human/H = user - if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg)) - var/obj/item/clothing/suit/cardborg/CB = H.wear_suit - CB.disguise(user, src) - -/obj/item/clothing/head/cardborg/dropped(mob/living/user) - ..() - user.remove_alt_appearance("standard_borg_disguise") - -/* - * Head Mirror - */ -/obj/item/clothing/head/headmirror - name = "head mirror" - desc = "A band of rubber with a very reflective looking mirror attached to the front of it. One of the early signs of medical budget cuts." - icon_state = "head_mirror" - item_state = "head_mirror" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Drask" = 'icons/mob/species/drask/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - +/* + * Contents: + * Welding mask + * Cakehat + * Ushanka + * Pumpkin head + * Kitty ears + * Cardborg Disguise + * Head Mirror + */ + +/* + * Welding mask + */ +/obj/item/clothing/head/welding + name = "welding helmet" + desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." + icon_state = "welding" + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + item_state = "welding" + materials = list(MAT_METAL=1750, MAT_GLASS=400) + flash_protect = 2 + tint = 2 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 60) + flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + actions_types = list(/datum/action/item_action/toggle) + visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + resistance_flags = FIRE_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + +/obj/item/clothing/head/welding/attack_self(mob/user) + weldingvisortoggle(user) + +/obj/item/clothing/head/welding/flamedecal + name = "flame decal welding helmet" + desc = "A welding helmet adorned with flame decals, and several cryptic slogans of varying degrees of legibility." + icon_state = "welding_redflame" + +/obj/item/clothing/head/welding/flamedecal/blue + name = "blue flame decal welding helmet" + desc = "A welding helmet with blue flame decals on it." + icon_state = "welding_blueflame" + +/obj/item/clothing/head/welding/white + name = "white decal welding helmet" + desc = "A white welding helmet with a character written across it." + icon_state = "welding_white" + +/obj/item/clothing/head/welding/attack_self() + toggle() + +/obj/item/clothing/head/welding/proc/toggle() + if(up) + up = !up + flags_cover |= (HEADCOVERSEYES | HEADCOVERSMOUTH) + flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = initial(icon_state) + to_chat(usr, "You flip the [src] down to protect your eyes.") + flash_protect = 2 + tint = 2 + else + up = !up + flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) + flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = "[initial(icon_state)]up" + to_chat(usr, "You push the [src] up out of your face.") + flash_protect = 0 + tint = 0 + var/mob/living/carbon/user = usr + user.update_tint() + user.update_inv_head() //so our mob-overlays update + + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + + + +/* + * Cakehat + */ +/obj/item/clothing/head/cakehat + name = "cake-hat" + desc = "It's tasty looking!" + icon_state = "cake0" + flags_cover = HEADCOVERSEYES + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + var/onfire = 0.0 + var/status = 0 + var/fire_resist = T0C+1300 //this is the max temp it can stand before you start to cook. although it might not burn away, you take damage + var/processing = 0 //I dont think this is used anywhere. + +/obj/item/clothing/head/cakehat/process() + if(!onfire) + STOP_PROCESSING(SSobj, src) + return + + var/turf/location = src.loc + if(istype(location, /mob/)) + var/mob/living/carbon/human/M = location + if(M.l_hand == src || M.r_hand == src || M.head == src) + location = M.loc + + if(istype(location, /turf)) + location.hotspot_expose(700, 1) + +/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) + if(status > 1) return + src.onfire = !( src.onfire ) + if(src.onfire) + src.force = 3 + src.damtype = "fire" + src.icon_state = "cake1" + START_PROCESSING(SSobj, src) + else + src.force = null + src.damtype = "brute" + src.icon_state = "cake0" + return + + +/* + * Ushanka + */ +/obj/item/clothing/head/ushanka + name = "ushanka" + desc = "Perfect for winter in Siberia, da?" + icon_state = "ushankadown" + item_state = "ushankadown" + flags_inv = HIDEEARS + cold_protection = HEAD + min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT + dog_fashion = /datum/dog_fashion/head/ushanka + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) + if(src.icon_state == "ushankadown") + src.icon_state = "ushankaup" + src.item_state = "ushankaup" + to_chat(user, "You raise the ear flaps on the ushanka.") + else + src.icon_state = "ushankadown" + src.item_state = "ushankadown" + to_chat(user, "You lower the ear flaps on the ushanka.") + +/* + * Pumpkin head + */ +/obj/item/clothing/head/hardhat/pumpkinhead + name = "carved pumpkin" + desc = "A jack o' lantern! Believed to ward off evil spirits." + icon_state = "hardhat0_pumpkin"//Could stand to be renamed + item_state = "hardhat0_pumpkin" + item_color = "pumpkin" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + brightness_on = 2 //luminosity when on + + +/obj/item/clothing/head/hardhat/reindeer + name = "novelty reindeer hat" + desc = "Some fake antlers and a very fake red nose." + icon_state = "hardhat0_reindeer" + item_state = "hardhat0_reindeer" + item_color = "reindeer" + flags_inv = 0 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + brightness_on = 1 //luminosity when on + dog_fashion = /datum/dog_fashion/head/reindeer + + +/* + * Kitty ears + */ +/obj/item/clothing/head/kitty + name = "kitty ears" + desc = "A pair of kitty ears. Meow!" + icon_state = "kitty" + var/icon/mob + dog_fashion = /datum/dog_fashion/head/kitty + +/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) + if(!istype(user)) return + var/obj/item/organ/external/head/head_organ = user.get_organ("head") + + mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kitty") + mob.Blend(head_organ.hair_colour, ICON_ADD) + + var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kittyinner") + mob.Blend(earbit, ICON_OVERLAY) + + icon_override = mob + +/obj/item/clothing/head/kitty/equipped(var/mob/M, slot) + . = ..() + if(ishuman(M) && slot == slot_head) + update_icon(M) + + +/obj/item/clothing/head/kitty/mouse + name = "mouse ears" + desc = "A pair of mouse ears. Squeak!" + icon_state = "mousey" + +/obj/item/clothing/head/kitty/mouse/update_icon(var/mob/living/carbon/human/user) + if(!istype(user)) return + var/obj/item/organ/external/head/head_organ = user.get_organ("head") + mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mousey") + mob.Blend(head_organ.hair_colour, ICON_ADD) + + var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mouseyinner") + mob.Blend(earbit, ICON_OVERLAY) + + icon_override = mob + +/obj/item/clothing/head/cardborg + name = "cardborg helmet" + desc = "A helmet made out of a box." + icon_state = "cardborg_h" + item_state = "cardborg_h" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + species_disguise = "High-tech robot" + dog_fashion = /datum/dog_fashion/head/cardborg + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + + +/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot) + ..() + if(ishuman(user) && slot == slot_head) + var/mob/living/carbon/human/H = user + if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg)) + var/obj/item/clothing/suit/cardborg/CB = H.wear_suit + CB.disguise(user, src) + +/obj/item/clothing/head/cardborg/dropped(mob/living/user) + ..() + user.remove_alt_appearance("standard_borg_disguise") + +/* + * Head Mirror + */ +/obj/item/clothing/head/headmirror + name = "head mirror" + desc = "A band of rubber with a very reflective looking mirror attached to the front of it. One of the early signs of medical budget cuts." + icon_state = "head_mirror" + item_state = "head_mirror" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Drask" = 'icons/mob/species/drask/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index b111672e86a..d34584ff425 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -1,132 +1,132 @@ -/obj/item/clothing/head/soft - name = "cargo cap" - desc = "It's a baseball hat in a tasteless yellow colour." - icon_state = "cargosoft" - item_state = "helmet" - item_color = "cargo" - var/flipped = 0 - actions_types = list(/datum/action/item_action/flip_cap) - dog_fashion = /datum/dog_fashion/head/cargo_tech - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi' - ) - -/obj/item/clothing/head/soft/dropped() - icon_state = "[item_color]soft" - flipped = 0 - ..() - -/obj/item/clothing/head/soft/attack_self(mob/user) - flip(user) - -/obj/item/clothing/head/soft/proc/flip(mob/user) - flipped = !flipped - if(flipped) - icon_state = "[item_color]soft_flipped" - to_chat(usr, "You flip the hat backwards.") - else - icon_state = "[item_color]soft" - to_chat(user, "You flip the hat back in normal position.") - user.update_inv_head() //so our mob-overlays update - - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/head/soft/red - name = "red cap" - desc = "It's a baseball hat in a tasteless red colour." - icon_state = "redsoft" - item_color = "red" - dog_fashion = null - -/obj/item/clothing/head/soft/blue - name = "blue cap" - desc = "It's a baseball hat in a tasteless blue colour." - icon_state = "bluesoft" - item_color = "blue" - dog_fashion = null - -/obj/item/clothing/head/soft/green - name = "green cap" - desc = "It's a baseball hat in a tasteless green colour." - icon_state = "greensoft" - item_color = "green" - dog_fashion = null - -/obj/item/clothing/head/soft/yellow - name = "yellow cap" - desc = "It's a baseball hat in a tasteless yellow colour." - icon_state = "yellowsoft" - item_color = "yellow" - dog_fashion = null - -/obj/item/clothing/head/soft/grey - name = "grey cap" - desc = "It's a baseball hat in a tasteful grey colour." - icon_state = "greysoft" - item_color = "grey" - dog_fashion = null - -/obj/item/clothing/head/soft/orange - name = "orange cap" - desc = "It's a baseball hat in a tasteless orange colour." - icon_state = "orangesoft" - item_color = "orange" - -/obj/item/clothing/head/soft/mime - name = "white cap" - desc = "It's a baseball hat in a tasteless white colour." - icon_state = "mimesoft" - item_color = "mime" - dog_fashion = null - -/obj/item/clothing/head/soft/purple - name = "purple cap" - desc = "It's a baseball hat in a tasteless purple colour." - icon_state = "purplesoft" - item_color = "purple" - dog_fashion = null - -/obj/item/clothing/head/soft/black - name = "black cap" - desc = "It's a baseball hat in a tasteless black colour." - icon_state = "blacksoft" - item_color = "black" - dog_fashion = null - -/obj/item/clothing/head/soft/rainbow - name = "rainbow cap" - desc = "It's a baseball hat in a bright rainbow of colors." - icon_state = "rainbowsoft" - item_color = "rainbow" - dog_fashion = null - -/obj/item/clothing/head/soft/sec - name = "security cap" - desc = "It's baseball hat in tasteful red colour." - icon_state = "secsoft" - item_color = "sec" - armor = list("melee" = 30, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - dog_fashion = null - -/obj/item/clothing/head/soft/sec/corp - name = "corporate security cap" - desc = "It's baseball hat in corpotate colours." - icon_state = "corpsoft" - item_color = "corp" - -/obj/item/clothing/head/soft/solgov - name = "Sol Federation marine cap" - desc = "A soft cap worn by marines of the Sol Federation." - icon_state = "solgovsoft" - item_color = "solgov" - dog_fashion = null - -/obj/item/clothing/head/soft/solgov/command - name = "Sol Federation Lieutenant's cap" - desc = "A soft cap worn by marines of the Sol Federation. The insignia signifies the wearer bears the rank of a Lieutenant." - icon_state = "solgovcsoft" - item_color = "solgovc" - dog_fashion = null \ No newline at end of file +/obj/item/clothing/head/soft + name = "cargo cap" + desc = "It's a baseball hat in a tasteless yellow colour." + icon_state = "cargosoft" + item_state = "helmet" + item_color = "cargo" + var/flipped = 0 + actions_types = list(/datum/action/item_action/flip_cap) + dog_fashion = /datum/dog_fashion/head/cargo_tech + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi' + ) + +/obj/item/clothing/head/soft/dropped() + icon_state = "[item_color]soft" + flipped = 0 + ..() + +/obj/item/clothing/head/soft/attack_self(mob/user) + flip(user) + +/obj/item/clothing/head/soft/proc/flip(mob/user) + flipped = !flipped + if(flipped) + icon_state = "[item_color]soft_flipped" + to_chat(usr, "You flip the hat backwards.") + else + icon_state = "[item_color]soft" + to_chat(user, "You flip the hat back in normal position.") + user.update_inv_head() //so our mob-overlays update + + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/head/soft/red + name = "red cap" + desc = "It's a baseball hat in a tasteless red colour." + icon_state = "redsoft" + item_color = "red" + dog_fashion = null + +/obj/item/clothing/head/soft/blue + name = "blue cap" + desc = "It's a baseball hat in a tasteless blue colour." + icon_state = "bluesoft" + item_color = "blue" + dog_fashion = null + +/obj/item/clothing/head/soft/green + name = "green cap" + desc = "It's a baseball hat in a tasteless green colour." + icon_state = "greensoft" + item_color = "green" + dog_fashion = null + +/obj/item/clothing/head/soft/yellow + name = "yellow cap" + desc = "It's a baseball hat in a tasteless yellow colour." + icon_state = "yellowsoft" + item_color = "yellow" + dog_fashion = null + +/obj/item/clothing/head/soft/grey + name = "grey cap" + desc = "It's a baseball hat in a tasteful grey colour." + icon_state = "greysoft" + item_color = "grey" + dog_fashion = null + +/obj/item/clothing/head/soft/orange + name = "orange cap" + desc = "It's a baseball hat in a tasteless orange colour." + icon_state = "orangesoft" + item_color = "orange" + +/obj/item/clothing/head/soft/mime + name = "white cap" + desc = "It's a baseball hat in a tasteless white colour." + icon_state = "mimesoft" + item_color = "mime" + dog_fashion = null + +/obj/item/clothing/head/soft/purple + name = "purple cap" + desc = "It's a baseball hat in a tasteless purple colour." + icon_state = "purplesoft" + item_color = "purple" + dog_fashion = null + +/obj/item/clothing/head/soft/black + name = "black cap" + desc = "It's a baseball hat in a tasteless black colour." + icon_state = "blacksoft" + item_color = "black" + dog_fashion = null + +/obj/item/clothing/head/soft/rainbow + name = "rainbow cap" + desc = "It's a baseball hat in a bright rainbow of colors." + icon_state = "rainbowsoft" + item_color = "rainbow" + dog_fashion = null + +/obj/item/clothing/head/soft/sec + name = "security cap" + desc = "It's baseball hat in tasteful red colour." + icon_state = "secsoft" + item_color = "sec" + armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + dog_fashion = null + +/obj/item/clothing/head/soft/sec/corp + name = "corporate security cap" + desc = "It's baseball hat in corpotate colours." + icon_state = "corpsoft" + item_color = "corp" + +/obj/item/clothing/head/soft/solgov + name = "Sol Federation marine cap" + desc = "A soft cap worn by marines of the Sol Federation." + icon_state = "solgovsoft" + item_color = "solgov" + dog_fashion = null + +/obj/item/clothing/head/soft/solgov/command + name = "Sol Federation Lieutenant's cap" + desc = "A soft cap worn by marines of the Sol Federation. The insignia signifies the wearer bears the rank of a Lieutenant." + icon_state = "solgovcsoft" + item_color = "solgovc" + dog_fashion = null diff --git a/code/modules/clothing/masks/boxing.dm b/code/modules/clothing/masks/boxing.dm index 78034a3c0c8..14afa81cb58 100644 --- a/code/modules/clothing/masks/boxing.dm +++ b/code/modules/clothing/masks/boxing.dm @@ -1,52 +1,52 @@ -/obj/item/clothing/mask/balaclava - name = "balaclava" - desc = "LOADSAMONEY" - icon_state = "balaclava" - item_state = "balaclava" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - actions_types = list(/datum/action/item_action/adjust) - adjusted_flags = SLOT_HEAD - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/balaclava/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/luchador - name = "Luchador Mask" - desc = "Worn by robust fighters, flying high to defeat their foes!" - icon_state = "luchag" - item_state = "luchag" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/luchador/tecnicos - name = "Tecnicos Mask" - desc = "Worn by robust fighters who uphold justice and fight honorably." - icon_state = "luchador" - item_state = "luchador" - -/obj/item/clothing/mask/luchador/rudos - name = "Rudos Mask" - desc = "Worn by robust fighters who are willing to do anything to win." - icon_state = "luchar" - item_state = "luchar" +/obj/item/clothing/mask/balaclava + name = "balaclava" + desc = "LOADSAMONEY" + icon_state = "balaclava" + item_state = "balaclava" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + actions_types = list(/datum/action/item_action/adjust) + adjusted_flags = SLOT_HEAD + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/balaclava/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/luchador + name = "Luchador Mask" + desc = "Worn by robust fighters, flying high to defeat their foes!" + icon_state = "luchag" + item_state = "luchag" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/luchador/tecnicos + name = "Tecnicos Mask" + desc = "Worn by robust fighters who uphold justice and fight honorably." + icon_state = "luchador" + item_state = "luchador" + +/obj/item/clothing/mask/luchador/rudos + name = "Rudos Mask" + desc = "Worn by robust fighters who are willing to do anything to win." + icon_state = "luchar" + item_state = "luchar" diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm index c37fad48906..032062461e7 100644 --- a/code/modules/clothing/masks/breath.dm +++ b/code/modules/clothing/masks/breath.dm @@ -1,54 +1,54 @@ -/obj/item/clothing/mask/breath - desc = "A close-fitting mask that can be connected to an air supply." - name = "breath mask" - icon_state = "breath" - item_state = "breath" - flags = AIRTIGHT - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.10 - permeability_coefficient = 0.50 - actions_types = list(/datum/action/item_action/adjust) - resistance_flags = NONE - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Vox Armalis" = 'icons/mob/species/armalis/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi', - "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' - ) - -/obj/item/clothing/mask/breath/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/breath/AltClick(mob/user) - ..() - if( (!in_range(src, user)) || user.stat || user.restrained() ) - return - adjustmask(user) - -/obj/item/clothing/mask/breath/medical - desc = "A close-fitting sterile mask that can be connected to an air supply." - name = "medical mask" - icon_state = "medical" - item_state = "medical" - permeability_coefficient = 0.01 - put_on_delay = 10 - -/obj/item/clothing/mask/breath/vox - desc = "A weirdly-shaped breath mask." - name = "vox breath mask" - icon_state = "voxmask" - item_state = "voxmask" - permeability_coefficient = 0.01 - species_restricted = list("Vox", "Vox Armalis") //These should fit the "Mega Vox" just fine. - actions_types = list() - -/obj/item/clothing/mask/breath/vox/attack_self(var/mob/user) - return - -/obj/item/clothing/mask/breath/vox/AltClick(mob/user) - return +/obj/item/clothing/mask/breath + desc = "A close-fitting mask that can be connected to an air supply." + name = "breath mask" + icon_state = "breath" + item_state = "breath" + flags = AIRTIGHT + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.10 + permeability_coefficient = 0.50 + actions_types = list(/datum/action/item_action/adjust) + resistance_flags = NONE + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Vox Armalis" = 'icons/mob/species/armalis/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi', + "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' + ) + +/obj/item/clothing/mask/breath/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/breath/AltClick(mob/user) + ..() + if( (!in_range(src, user)) || user.stat || user.restrained() ) + return + adjustmask(user) + +/obj/item/clothing/mask/breath/medical + desc = "A close-fitting sterile mask that can be connected to an air supply." + name = "medical mask" + icon_state = "medical" + item_state = "medical" + permeability_coefficient = 0.01 + put_on_delay = 10 + +/obj/item/clothing/mask/breath/vox + desc = "A weirdly-shaped breath mask." + name = "vox breath mask" + icon_state = "voxmask" + item_state = "voxmask" + permeability_coefficient = 0.01 + species_restricted = list("Vox", "Vox Armalis") //These should fit the "Mega Vox" just fine. + actions_types = list() + +/obj/item/clothing/mask/breath/vox/attack_self(var/mob/user) + return + +/obj/item/clothing/mask/breath/vox/AltClick(mob/user) + return diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index b6e3164a44d..b02bfc537bd 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -1,365 +1,371 @@ -/obj/item/clothing/mask/gas - name = "gas mask" - desc = "A face-covering mask that can be connected to an air supply." - icon_state = "gas_alt" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES - w_class = WEIGHT_CLASS_NORMAL - item_state = "gas_alt" - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - resistance_flags = NONE - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' - ) - -// **** Welding gas mask **** - -/obj/item/clothing/mask/gas/welding - name = "welding mask" - desc = "A gas mask with built in welding goggles and face shield. Looks like a skull, clearly designed by a nerd." - icon_state = "weldingmask" - item_state = "weldingmask" - materials = list(MAT_METAL=4000, MAT_GLASS=2000) - flash_protect = 2 - tint = 2 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 55) - origin_tech = "materials=2;engineering=3" - actions_types = list(/datum/action/item_action/toggle) - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = MASKCOVERSEYES - visor_flags_inv = HIDEEYES - resistance_flags = FIRE_PROOF - -/obj/item/clothing/mask/gas/welding/attack_self(mob/user) - weldingvisortoggle(user) - -/obj/item/clothing/mask/gas/explorer - name = "explorer gas mask" - desc = "A military-grade gas mask that can be connected to an air supply." - icon_state = "gas_mining" - 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) - resistance_flags = FIRE_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi' - ) - -/obj/item/clothing/mask/gas/explorer/attack_self(mob/user) - adjustmask(user) - -/obj/item/clothing/mask/gas/explorer/adjustmask(user) - ..() - w_class = mask_adjusted ? WEIGHT_CLASS_SMALL : WEIGHT_CLASS_NORMAL - -/obj/item/clothing/mask/gas/explorer/folded/Initialize() - . = ..() - adjustmask() - -//Bane gas mask -/obj/item/clothing/mask/banemask - name = "bane mask" - desc = "Only when the station is in flames, do you have my permission to robust." - icon_state = "bane_mask" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES - w_class = WEIGHT_CLASS_NORMAL - item_state = "bane_mask" - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - - -//Plague Dr suit can be found in clothing/suits/bio.dm -/obj/item/clothing/mask/gas/plaguedoctor - name = "plague doctor mask" - desc = "A modernised version of the classic design, this mask will not only filter out toxins but it can also be connected to an air supply." - icon_state = "plaguedoctor" - item_state = "gas_mask" - armor = list("melee" = 0, "bullet" = 0, "laser" = 2, "energy" = 2, "bomb" = 0, "bio" = 75, "rad" = 0, "fire" = 0, "acid" = 0) - -/obj/item/clothing/mask/gas/swat - name = "\improper SWAT mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - -/obj/item/clothing/mask/gas/syndicate - name = "syndicate mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - strip_delay = 60 - -/obj/item/clothing/mask/gas/clown_hat - name = "clown wig and mask" - desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask." - icon_state = "clown" - item_state = "clown_hat" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR - resistance_flags = FLAMMABLE - dog_fashion = /datum/dog_fashion/head/clown - -/obj/item/clothing/mask/gas/clown_hat/attack_self(mob/user) - - var/mob/M = usr - var/list/options = list() - options["True Form"] = "clown" - options["The Feminist"] = "sexyclown" - options["The Madman"] = "joker" - options["The Rainbow Color"] ="rainbow" - - var/choice = input(M,"To what form do you wish to Morph this mask?","Morph Mask") in options - - if(src && choice && !M.stat && in_range(M,src)) - icon_state = options[choice] - to_chat(M, "Your Clown Mask has now morphed into [choice], all praise the Honk Mother!") - return 1 - -/obj/item/clothing/mask/gas/clown_hat/sexy - name = "sexy-clown wig and mask" - desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." - icon_state = "sexyclown" - item_state = "sexyclown" - -/obj/item/clothing/mask/gas/clownwiz - name = "wizard clown wig and mask" - desc = "Some pranksters are truly magical." - icon_state = "wizzclown" - item_state = "wizzclown" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR - flags_inv = HIDEEARS | HIDEEYES - magical = TRUE - -/obj/item/clothing/mask/gas/clown_hat/nodrop - flags = NODROP - -/obj/item/clothing/mask/gas/mime - name = "mime mask" - desc = "The traditional mime's mask. It has an eerie facial posture." - icon_state = "mime" - item_state = "mime" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/mime/nodrop - flags = NODROP - -/obj/item/clothing/mask/gas/monkeymask - name = "monkey mask" - desc = "A mask used when acting as a monkey." - icon_state = "monkeymask" - item_state = "monkeymask" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/sexymime - name = "sexy mime mask" - desc = "A traditional female mime's mask." - icon_state = "sexymime" - item_state = "sexymime" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/cyborg - name = "cyborg visor" - desc = "Beep boop" - icon_state = "death" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/owl_mask - name = "owl mask" - desc = "Twoooo!" - icon_state = "owl" - resistance_flags = FLAMMABLE - actions_types = list(/datum/action/item_action/hoot) - -/obj/item/clothing/mask/gas/owl_mask/super_hero - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | NODROP - -/obj/item/clothing/mask/gas/owl_mask/attack_self() - hoot() - -/obj/item/clothing/mask/gas/owl_mask/proc/hoot() - if(cooldown < world.time - 35) // A cooldown, to stop people being jerks - playsound(src.loc, 'sound/creatures/hoot.ogg', 50, 1) - cooldown = world.time - -// ******************************************************************** - -// **** Security gas mask **** - -/obj/item/clothing/mask/gas/sechailer - name = "security gas mask" - desc = "A standard issue Security gas mask with integrated 'Compli-o-nator 3000' device, plays over a dozen pre-recorded compliance phrases designed to get scumbags to stand still whilst you taze them. Do not tamper with the device." - icon_state = "sechailer" - item_state = "sechailer" - var/phrase = 1 - var/aggressiveness = 1 - var/safety = 1 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase) - var/phrase_list = list( - - "halt" = "HALT! HALT! HALT! HALT!", - "bobby" = "Stop in the name of the Law.", - "compliance" = "Compliance is in your best interest.", - "justice" = "Prepare for justice!", - "running" = "Running will only increase your sentence.", - "dontmove" = "Don't move, Creep!", - "floor" = "Down on the floor, Creep!", - "robocop" = "Dead or alive you're coming with me.", - "god" = "God made today for the crooks we could not catch yesterday.", - "freeze" = "Freeze, Scum Bag!", - "imperial" = "Stop right there, criminal scum!", - "bash" = "Stop or I'll bash you.", - "harry" = "Go ahead, make my day.", - "asshole" = "Stop breaking the law, asshole.", - "stfu" = "You have the right to shut the fuck up", - "shutup" = "Shut up crime!", - "super" = "Face the wrath of the golden bolt.", - "dredd" = "I am, the LAW!" - ) -/obj/item/clothing/mask/gas/sechailer/hos - name = "\improper HOS SWAT mask" - desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a tan stripe." - icon_state = "hosmask" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/warden - name = "\improper Warden SWAT mask" - desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a blue stripe." - icon_state = "wardenmask" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - - -/obj/item/clothing/mask/gas/sechailer/swat - name = "\improper SWAT mask" - desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000." - icon_state = "officermask" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/blue - name = "\improper blue SWAT mask" - desc = "A neon blue swat mask, used for demoralizing Greytide in the wild." - icon_state = "blue_sechailer" - item_state = "blue_sechailer" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/cyborg - name = "security hailer" - desc = "A set of recognizable pre-recorded messages for cyborgs to use when apprehending criminals." - icon = 'icons/obj/device.dmi' - icon_state = "taperecorder_idle" - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype) - if(actiontype == /datum/action/item_action/halt) - halt() - else if(actiontype == /datum/action/item_action/adjust) - adjustmask(user) - else if(actiontype == /datum/action/item_action/selectphrase) - var/key = phrase_list[phrase] - var/message = phrase_list[key] - - if (!safety) - to_chat(user, "You set the restrictor to: FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT.") - return - - switch(aggressiveness) - if(1) - phrase = (phrase < 6) ? (phrase + 1) : 1 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - if(2) - phrase = (phrase < 11 && phrase >= 7) ? (phrase + 1) : 7 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - if(3) - phrase = (phrase < 18 && phrase >= 12 ) ? (phrase + 1) : 12 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - if(4) - phrase = (phrase < 18 && phrase >= 1 ) ? (phrase + 1) : 1 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - else - to_chat(user, "It's broken.") - -/obj/item/clothing/mask/gas/sechailer/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/screwdriver)) - switch(aggressiveness) - if(1) - to_chat(user, "You set the aggressiveness restrictor to the second position.") - aggressiveness = 2 - phrase = 7 - if(2) - to_chat(user, "You set the aggressiveness restrictor to the third position.") - aggressiveness = 3 - phrase = 13 - if(3) - to_chat(user, "You set the aggressiveness restrictor to the fourth position.") - aggressiveness = 4 - phrase = 1 - if(4) - to_chat(user, "You set the aggressiveness restrictor to the first position.") - aggressiveness = 1 - phrase = 1 - if(5) - to_chat(user, "You adjust the restrictor but nothing happens, probably because its broken.") - else if(istype(W, /obj/item/wirecutters)) - if(aggressiveness != 5) - to_chat(user, "You broke it!") - aggressiveness = 5 - else - ..() - -/obj/item/clothing/mask/gas/sechailer/attack_self() - halt() - -/obj/item/clothing/mask/gas/sechailer/emag_act(mob/user as mob) - if(safety) - safety = 0 - to_chat(user, "You silently fry [src]'s vocal circuit with the cryptographic sequencer.") - else - return - -/obj/item/clothing/mask/gas/sechailer/proc/halt() - var/key = phrase_list[phrase] - var/message = phrase_list[key] - - - if(cooldown < world.time - 35) // A cooldown, to stop people being jerks - if(!safety) - message = "FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT." - usr.visible_message("[usr]'s Compli-o-Nator: [message]") - playsound(src.loc, 'sound/voice/binsult.ogg', 100, 0, 4) - cooldown = world.time - return - - usr.visible_message("[usr]'s Compli-o-Nator: [message]") - playsound(src.loc, "sound/voice/complionator/[key].ogg", 100, 0, 4) - cooldown = world.time - - - -// ******************************************************************** +/obj/item/clothing/mask/gas + name = "gas mask" + desc = "A face-covering mask that can be connected to an air supply." + icon_state = "gas_alt" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES + w_class = WEIGHT_CLASS_NORMAL + item_state = "gas_alt" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + resistance_flags = NONE + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' + ) + +// **** Welding gas mask **** + +/obj/item/clothing/mask/gas/welding + name = "welding mask" + desc = "A gas mask with built in welding goggles and face shield. Looks like a skull, clearly designed by a nerd." + icon_state = "weldingmask" + item_state = "weldingmask" + materials = list(MAT_METAL=4000, MAT_GLASS=2000) + flash_protect = 2 + tint = 2 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 55) + origin_tech = "materials=2;engineering=3" + actions_types = list(/datum/action/item_action/toggle) + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = MASKCOVERSEYES + visor_flags_inv = HIDEEYES + resistance_flags = FIRE_PROOF + +/obj/item/clothing/mask/gas/welding/attack_self(mob/user) + weldingvisortoggle(user) + +/obj/item/clothing/mask/gas/explorer + name = "explorer gas mask" + desc = "A military-grade gas mask that can be connected to an air supply." + icon_state = "gas_mining" + 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) + resistance_flags = FIRE_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi' + ) + +/obj/item/clothing/mask/gas/explorer/attack_self(mob/user) + adjustmask(user) + +/obj/item/clothing/mask/gas/explorer/adjustmask(user) + ..() + w_class = mask_adjusted ? WEIGHT_CLASS_SMALL : WEIGHT_CLASS_NORMAL + +/obj/item/clothing/mask/gas/explorer/folded/Initialize() + . = ..() + adjustmask() + +//Bane gas mask +/obj/item/clothing/mask/banemask + name = "bane mask" + desc = "Only when the station is in flames, do you have my permission to robust." + icon_state = "bane_mask" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES + w_class = WEIGHT_CLASS_NORMAL + item_state = "bane_mask" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + + +//Plague Dr suit can be found in clothing/suits/bio.dm +/obj/item/clothing/mask/gas/plaguedoctor + name = "plague doctor mask" + desc = "A modernised version of the classic design, this mask will not only filter out toxins but it can also be connected to an air supply." + icon_state = "plaguedoctor" + item_state = "gas_mask" + armor = list("melee" = 0, "bullet" = 0, "laser" = 2, "energy" = 2, "bomb" = 0, "bio" = 75, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/mask/gas/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + +/obj/item/clothing/mask/gas/syndicate + name = "syndicate mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + strip_delay = 60 + +/obj/item/clothing/mask/gas/clown_hat + name = "clown wig and mask" + desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask." + icon_state = "clown" + item_state = "clown_hat" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR + resistance_flags = FLAMMABLE + dog_fashion = /datum/dog_fashion/head/clown + +/obj/item/clothing/mask/gas/clown_hat/attack_self(mob/user) + + var/mob/M = usr + var/list/options = list() + options["True Form"] = "clown" + options["The Feminist"] = "sexyclown" + options["The Madman"] = "joker" + options["The Rainbow Color"] ="rainbow" + + var/choice = input(M,"To what form do you wish to Morph this mask?","Morph Mask") in options + + if(src && choice && !M.stat && in_range(M,src)) + icon_state = options[choice] + to_chat(M, "Your Clown Mask has now morphed into [choice], all praise the Honk Mother!") + return 1 + +/obj/item/clothing/mask/gas/clown_hat/sexy + name = "sexy-clown wig and mask" + desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." + icon_state = "sexyclown" + item_state = "sexyclown" + +/obj/item/clothing/mask/gas/clownwiz + name = "wizard clown wig and mask" + desc = "Some pranksters are truly magical." + icon_state = "wizzclown" + item_state = "wizzclown" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR + flags_inv = HIDEEARS | HIDEEYES + magical = TRUE + +/obj/item/clothing/mask/gas/clown_hat/nodrop + flags = NODROP + +/obj/item/clothing/mask/gas/mime + name = "mime mask" + desc = "The traditional mime's mask. It has an eerie facial posture." + icon_state = "mime" + item_state = "mime" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/mime/wizard + name = "magical mime mask" + desc = "A mime mask glowing with power. Its eyes gaze deep into your soul." + flags_inv = HIDEEARS | HIDEEYES + magical = TRUE + +/obj/item/clothing/mask/gas/mime/nodrop + flags = NODROP + +/obj/item/clothing/mask/gas/monkeymask + name = "monkey mask" + desc = "A mask used when acting as a monkey." + icon_state = "monkeymask" + item_state = "monkeymask" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/sexymime + name = "sexy mime mask" + desc = "A traditional female mime's mask." + icon_state = "sexymime" + item_state = "sexymime" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/cyborg + name = "cyborg visor" + desc = "Beep boop" + icon_state = "death" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/owl_mask + name = "owl mask" + desc = "Twoooo!" + icon_state = "owl" + resistance_flags = FLAMMABLE + actions_types = list(/datum/action/item_action/hoot) + +/obj/item/clothing/mask/gas/owl_mask/super_hero + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | NODROP + +/obj/item/clothing/mask/gas/owl_mask/attack_self() + hoot() + +/obj/item/clothing/mask/gas/owl_mask/proc/hoot() + if(cooldown < world.time - 35) // A cooldown, to stop people being jerks + playsound(src.loc, 'sound/creatures/hoot.ogg', 50, 1) + cooldown = world.time + +// ******************************************************************** + +// **** Security gas mask **** + +/obj/item/clothing/mask/gas/sechailer + name = "security gas mask" + desc = "A standard issue Security gas mask with integrated 'Compli-o-nator 3000' device, plays over a dozen pre-recorded compliance phrases designed to get scumbags to stand still whilst you taze them. Do not tamper with the device." + icon_state = "sechailer" + item_state = "sechailer" + var/phrase = 1 + var/aggressiveness = 1 + var/safety = 1 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase) + var/phrase_list = list( + + "halt" = "HALT! HALT! HALT! HALT!", + "bobby" = "Stop in the name of the Law.", + "compliance" = "Compliance is in your best interest.", + "justice" = "Prepare for justice!", + "running" = "Running will only increase your sentence.", + "dontmove" = "Don't move, Creep!", + "floor" = "Down on the floor, Creep!", + "robocop" = "Dead or alive you're coming with me.", + "god" = "God made today for the crooks we could not catch yesterday.", + "freeze" = "Freeze, Scum Bag!", + "imperial" = "Stop right there, criminal scum!", + "bash" = "Stop or I'll bash you.", + "harry" = "Go ahead, make my day.", + "asshole" = "Stop breaking the law, asshole.", + "stfu" = "You have the right to shut the fuck up", + "shutup" = "Shut up crime!", + "super" = "Face the wrath of the golden bolt.", + "dredd" = "I am, the LAW!" + ) +/obj/item/clothing/mask/gas/sechailer/hos + name = "\improper HOS SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a tan stripe." + icon_state = "hosmask" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/warden + name = "\improper Warden SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a blue stripe." + icon_state = "wardenmask" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + + +/obj/item/clothing/mask/gas/sechailer/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000." + icon_state = "officermask" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/blue + name = "\improper blue SWAT mask" + desc = "A neon blue swat mask, used for demoralizing Greytide in the wild." + icon_state = "blue_sechailer" + item_state = "blue_sechailer" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/cyborg + name = "security hailer" + desc = "A set of recognizable pre-recorded messages for cyborgs to use when apprehending criminals." + icon = 'icons/obj/device.dmi' + icon_state = "taperecorder_idle" + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype) + if(actiontype == /datum/action/item_action/halt) + halt() + else if(actiontype == /datum/action/item_action/adjust) + adjustmask(user) + else if(actiontype == /datum/action/item_action/selectphrase) + var/key = phrase_list[phrase] + var/message = phrase_list[key] + + if (!safety) + to_chat(user, "You set the restrictor to: FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT.") + return + + switch(aggressiveness) + if(1) + phrase = (phrase < 6) ? (phrase + 1) : 1 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + if(2) + phrase = (phrase < 11 && phrase >= 7) ? (phrase + 1) : 7 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + if(3) + phrase = (phrase < 18 && phrase >= 12 ) ? (phrase + 1) : 12 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + if(4) + phrase = (phrase < 18 && phrase >= 1 ) ? (phrase + 1) : 1 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + else + to_chat(user, "It's broken.") + +/obj/item/clothing/mask/gas/sechailer/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/screwdriver)) + switch(aggressiveness) + if(1) + to_chat(user, "You set the aggressiveness restrictor to the second position.") + aggressiveness = 2 + phrase = 7 + if(2) + to_chat(user, "You set the aggressiveness restrictor to the third position.") + aggressiveness = 3 + phrase = 13 + if(3) + to_chat(user, "You set the aggressiveness restrictor to the fourth position.") + aggressiveness = 4 + phrase = 1 + if(4) + to_chat(user, "You set the aggressiveness restrictor to the first position.") + aggressiveness = 1 + phrase = 1 + if(5) + to_chat(user, "You adjust the restrictor but nothing happens, probably because its broken.") + else if(istype(W, /obj/item/wirecutters)) + if(aggressiveness != 5) + to_chat(user, "You broke it!") + aggressiveness = 5 + else + ..() + +/obj/item/clothing/mask/gas/sechailer/attack_self() + halt() + +/obj/item/clothing/mask/gas/sechailer/emag_act(mob/user as mob) + if(safety) + safety = 0 + to_chat(user, "You silently fry [src]'s vocal circuit with the cryptographic sequencer.") + else + return + +/obj/item/clothing/mask/gas/sechailer/proc/halt() + var/key = phrase_list[phrase] + var/message = phrase_list[key] + + + if(cooldown < world.time - 35) // A cooldown, to stop people being jerks + if(!safety) + message = "FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT." + usr.visible_message("[usr]'s Compli-o-Nator: [message]") + playsound(src.loc, 'sound/voice/binsult.ogg', 100, 0, 4) + cooldown = world.time + return + + usr.visible_message("[usr]'s Compli-o-Nator: [message]") + playsound(src.loc, "sound/voice/complionator/[key].ogg", 100, 0, 4) + cooldown = world.time + + + +// ******************************************************************** diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 867a50cb70f..5cc891b56af 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -1,523 +1,523 @@ -/obj/item/clothing/mask/muzzle - name = "muzzle" - desc = "To stop that awful noise." - icon_state = "muzzle" - item_state = "muzzle" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - put_on_delay = 20 - var/resist_time = 0 //deciseconds of how long you need to gnaw to get rid of the gag, 0 to make it impossible to remove - var/mute = MUZZLE_MUTE_ALL - var/security_lock = FALSE // Requires brig access to remove 0 - Remove as normal - var/locked = FALSE //Indicates if a mask is locked, should always start as 0. - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi' - ) - -// Clumsy folks can't take the mask off themselves. -/obj/item/clothing/mask/muzzle/attack_hand(mob/user as mob) - if(user.wear_mask == src && !user.IsAdvancedToolUser()) - return 0 - else if(security_lock && locked) - if(do_unlock(user)) - visible_message("[user] unlocks [user.p_their()] [src.name].", \ - "[user] unlocks [user.p_their()] [src.name].") - ..() - return 1 - -/obj/item/clothing/mask/muzzle/proc/do_break() - if(security_lock) - security_lock = FALSE - locked = FALSE - flags &= ~NODROP - desc += " This one appears to be broken." - return TRUE - else - return FALSE - -/obj/item/clothing/mask/muzzle/proc/do_unlock(mob/living/carbon/human/user) - if(istype(user.get_inactive_hand(), /obj/item/card/emag)) - to_chat(user, "The lock vibrates as the card forces its locking system open.") - do_break() - return TRUE - else if(access_brig in user.get_access()) - to_chat(user, "The muzzle unlocks with a click.") - locked = FALSE - flags &= ~NODROP - return TRUE - - to_chat(user, "You must be wearing a security ID card or have one in your inactive hand to remove the muzzle.") - return FALSE - -/obj/item/clothing/mask/muzzle/proc/do_lock(mob/living/carbon/human/user) - if(security_lock) - locked = TRUE - flags |= NODROP - return TRUE - return FALSE - -/obj/item/clothing/mask/muzzle/Topic(href, href_list) - ..() - if(href_list["locked"]) - var/mob/living/carbon/wearer = locate(href_list["locked"]) - var/success = 0 - if(ishuman(usr)) - visible_message("[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].", \ - "[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].") - if(do_mob(usr, wearer, POCKET_STRIP_DELAY)) - if(locked) - success = do_unlock(usr) - else - success = do_lock(usr) - if(success) - visible_message("[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].", \ - "[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].") - if(usr.machine == wearer && in_range(src, usr)) - wearer.show_inv(usr) - else - to_chat(usr, "You lack the ability to manipulate the lock.") - - -/obj/item/clothing/mask/muzzle/tapegag - name = "tape gag" - desc = "MHPMHHH!" - icon_state = "tapegag" - item_state = null - w_class = WEIGHT_CLASS_TINY - resist_time = 150 - mute = MUZZLE_MUTE_MUFFLE - flags = DROPDEL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi' - ) - -/obj/item/clothing/mask/muzzle/tapegag/dropped(mob/user) - var/obj/item/trash/tapetrash/TT = new - transfer_fingerprints_to(TT) - user.transfer_fingerprints_to(TT) - user.put_in_active_hand(TT) - playsound(src, 'sound/items/poster_ripped.ogg', 40, 1) - user.emote("scream") - ..() - -/obj/item/clothing/mask/muzzle/safety - name = "safety muzzle" - desc = "A muzzle designed to prevent biting." - icon_state = "muzzle_secure" - item_state = "muzzle_secure" - resist_time = 0 - mute = MUZZLE_MUTE_NONE - security_lock = TRUE - locked = FALSE - materials = list(MAT_METAL=500, MAT_GLASS=50) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/muzzle/safety/shock - name = "shock muzzle" - desc = "A muzzle designed to prevent biting. This one is fitted with a behavior correction system." - var/obj/item/assembly/trigger = null - origin_tech = "materials=1;engineering=1" - materials = list(MAT_METAL=500, MAT_GLASS=50) - -/obj/item/clothing/mask/muzzle/safety/shock/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/assembly/signaler) || istype(W, /obj/item/assembly/voice)) - if(istype(trigger, /obj/item/assembly/signaler) || istype(trigger, /obj/item/assembly/voice)) - to_chat(user, "Something is already attached to [src].") - return FALSE - if(!user.drop_item()) - to_chat(user, "You are unable to insert [W] into [src].") - return FALSE - trigger = W - trigger.forceMove(src) - trigger.master = src - trigger.holder = src - to_chat(user, "You attach the [W] to [src].") - return TRUE - else if(istype(W, /obj/item/assembly)) - to_chat(user, "That won't fit in [src]. Perhaps a signaler or voice analyzer would?") - return FALSE - - return ..() - -/obj/item/clothing/mask/muzzle/safety/shock/screwdriver_act(mob/user, obj/item/I) - if(!trigger) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - to_chat(user, "You remove [trigger] from [src].") - trigger.forceMove(get_turf(user)) - trigger.master = null - trigger.holder = null - trigger = null - -/obj/item/clothing/mask/muzzle/safety/shock/proc/can_shock(obj/item/clothing/C) - if(istype(C)) - if(isliving(C.loc)) - return C.loc - else if(isliving(loc)) - return loc - return FALSE - -/obj/item/clothing/mask/muzzle/safety/shock/proc/process_activation(var/obj/D, var/normal = 1, var/special = 1) - visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - var/mob/M = can_shock(loc) - if(M) - to_chat(M, "You feel a sharp shock!") - do_sparks(3, 1, M) - - M.Weaken(5) - M.Stuttering(1) - M.Jitter(20) - return - -/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM as mob|obj) - if(trigger) - trigger.HasProximity(AM) - - -/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, list/message_pieces) - if(trigger) - trigger.hear_talk(M, message_pieces) - -/obj/item/clothing/mask/muzzle/safety/shock/hear_message(mob/living/M as mob, msg) - if(trigger) - trigger.hear_message(M, msg) - - - -/obj/item/clothing/mask/surgical - name = "sterile mask" - desc = "A sterile mask designed to help prevent the spread of diseases." - icon_state = "sterile" - item_state = "sterile" - w_class = WEIGHT_CLASS_TINY - flags_cover = MASKCOVERSMOUTH - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.01 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 25, "rad" = 0, "fire" = 0, "acid" = 0) - actions_types = list(/datum/action/item_action/adjust) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - - -/obj/item/clothing/mask/surgical/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/fakemoustache - name = "completely real moustache" - desc = "moustache is totally real." - icon_state = "fake-moustache" - flags_inv = HIDEFACE - actions_types = list(/datum/action/item_action/pontificate) - dog_fashion = /datum/dog_fashion/head/not_ian - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/fakemoustache/attack_self(mob/user) - pontificate(user) - -/obj/item/clothing/mask/fakemoustache/item_action_slot_check(slot) - if(slot == slot_wear_mask) - return 1 - -/obj/item/clothing/mask/fakemoustache/proc/pontificate(mob/user) - user.visible_message("\ [user] twirls [user.p_their()] moustache and laughs [pick("fiendishly","maniacally","diabolically","evilly")]!") - -//scarves (fit in in mask slot) - -/obj/item/clothing/mask/bluescarf - name = "blue neck scarf" - desc = "A blue neck scarf." - icon_state = "blueneckscarf" - item_state = "blueneckscarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - - -/obj/item/clothing/mask/redscarf - name = "red scarf" - desc = "A red and white checkered neck scarf." - icon_state = "redwhite_scarf" - item_state = "redwhite_scarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/greenscarf - name = "green scarf" - desc = "A green neck scarf." - icon_state = "green_scarf" - item_state = "green_scarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/ninjascarf - name = "ninja scarf" - desc = "A stealthy, dark scarf." - icon_state = "ninja_scarf" - item_state = "ninja_scarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - - -/obj/item/clothing/mask/pig - name = "pig mask" - desc = "A rubber pig mask." - icon_state = "pig" - item_state = "pig" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - - -/obj/item/clothing/mask/horsehead - name = "horse head mask" - desc = "A mask made of soft vinyl and latex, representing the head of a horse." - icon_state = "horsehead" - item_state = "horsehead" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - var/voicechange = 0 - var/temporaryname = " the Horse" - var/originalname = "" - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/horsehead/equipped(mob/user, slot) - if(flags & NODROP) //cursed masks only - originalname = user.real_name - if(!user.real_name || user.real_name == "Unknown") - user.real_name = "A Horse With No Name" //it felt good to be out of the rain - else - user.real_name = "[user.name][temporaryname]" - ..() - -/obj/item/clothing/mask/horsehead/dropped() //this really shouldn't happen, but call it extreme caution - if(flags & NODROP) - goodbye_horses(loc) - ..() - -/obj/item/clothing/mask/horsehead/Destroy() - if(flags & NODROP) - goodbye_horses(loc) - return ..() - -/obj/item/clothing/mask/horsehead/proc/goodbye_horses(mob/user) //I'm flying over you - if(!ismob(user)) - return - if(user.real_name == "[originalname][temporaryname]" || user.real_name == "A Horse With No Name") //if it's somehow changed while the mask is on it doesn't revert - user.real_name = originalname - -/obj/item/clothing/mask/face - flags_inv = HIDEFACE - flags_cover = MASKCOVERSMOUTH - -/obj/item/clothing/mask/face/rat - name = "rat mask" - desc = "A mask made of soft vinyl and latex, representing the head of a rat." - icon_state = "rat" - item_state = "rat" - -/obj/item/clothing/mask/face/fox - name = "fox mask" - desc = "A mask made of soft vinyl and latex, representing the head of a fox." - icon_state = "fox" - item_state = "fox" - -/obj/item/clothing/mask/face/bee - name = "bee mask" - desc = "A mask made of soft vinyl and latex, representing the head of a bee." - icon_state = "bee" - item_state = "bee" - -/obj/item/clothing/mask/face/bear - name = "bear mask" - desc = "A mask made of soft vinyl and latex, representing the head of a bear." - icon_state = "bear" - item_state = "bear" - -/obj/item/clothing/mask/face/bat - name = "bat mask" - desc = "A mask made of soft vinyl and latex, representing the head of a bat." - icon_state = "bat" - item_state = "bat" - -/obj/item/clothing/mask/face/raven - name = "raven mask" - desc = "A mask made of soft vinyl and latex, representing the head of a raven." - icon_state = "raven" - item_state = "raven" - -/obj/item/clothing/mask/face/jackal - name = "jackal mask" - desc = "A mask made of soft vinyl and latex, representing the head of a jackal." - icon_state = "jackal" - item_state = "jackal" - -/obj/item/clothing/mask/face/tribal - name = "tribal mask" - desc = "A mask carved out of wood, detailed carefully by hand." - icon_state = "bumba" - item_state = "bumba" - -/obj/item/clothing/mask/fawkes - name = "Guy Fawkes mask" - desc = "A mask designed to help you remember a specific date." - icon_state = "fawkes" - item_state = "fawkes" - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - -/obj/item/clothing/mask/gas/clown_hat/pennywise - name = "Pennywise Mask" - desc = "It's the eater of worlds, and of children." - icon_state = "pennywise_mask" - item_state = "pennywise_mask" - - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR - -// Bandanas -/obj/item/clothing/mask/bandana - name = "bandana" - desc = "A colorful bandana." - flags_inv = HIDEFACE - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_MASK - adjusted_flags = SLOT_HEAD - icon_state = "bandbotany" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - actions_types = list(/datum/action/item_action/adjust) - -/obj/item/clothing/mask/bandana/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/bandana/red - name = "red bandana" - icon_state = "bandred" - item_color = "red" - desc = "It's a red bandana." - -/obj/item/clothing/mask/bandana/blue - name = "blue bandana" - icon_state = "bandblue" - item_color = "blue" - desc = "It's a blue bandana." - -/obj/item/clothing/mask/bandana/gold - name = "gold bandana" - icon_state = "bandgold" - item_color = "yellow" - desc = "It's a gold bandana." - -/obj/item/clothing/mask/bandana/green - name = "green bandana" - icon_state = "bandgreen" - item_color = "green" - desc = "It's a green bandana." - -/obj/item/clothing/mask/bandana/orange - name = "orange bandana" - icon_state = "bandorange" - item_color = "orange" - desc = "It's an orange bandana." - -/obj/item/clothing/mask/bandana/purple - name = "purple bandana" - icon_state = "bandpurple" - item_color = "purple" - desc = "It's a purple bandana." - -/obj/item/clothing/mask/bandana/botany - name = "botany bandana" - desc = "It's a green bandana with some fine nanotech lining." - icon_state = "bandbotany" - -/obj/item/clothing/mask/bandana/skull - name = "skull bandana" - desc = "It's a black bandana with a skull pattern." - icon_state = "bandskull" - -/obj/item/clothing/mask/bandana/black - name = "black bandana" - icon_state = "bandblack" - item_color = "black" - desc = "It's a black bandana." - -/obj/item/clothing/mask/bandana/durathread - name = "durathread bandana" - desc = "A bandana made from durathread, you wish it would provide some protection to its wearer, but it's far too thin..." - icon_state = "banddurathread" - -/obj/item/clothing/mask/cursedclown - name = "cursed clown mask" - desc = "This is a very, very odd looking mask." - icon = 'icons/goonstation/objects/clothing/mask.dmi' - icon_state = "cursedclown" - item_state = "cclown_hat" - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - icon_override = 'icons/goonstation/mob/clothing/mask.dmi' - lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - flags = NODROP | AIRTIGHT - flags_cover = MASKCOVERSMOUTH - -/obj/item/clothing/mask/cursedclown/equipped(mob/user, slot) - ..() - var/mob/living/carbon/human/H = user - if(istype(H) && slot == slot_wear_mask) - to_chat(H, "[src] grips your face!") - if(H.mind && H.mind.assigned_role != "Cluwne") - H.makeCluwne() - -/obj/item/clothing/mask/cursedclown/suicide_act(mob/user) - user.visible_message("[user] gazes into the eyes of [src]. [src] gazes back!") - spawn(10) - if(user) - user.gib() - return OBLITERATION +/obj/item/clothing/mask/muzzle + name = "muzzle" + desc = "To stop that awful noise." + icon_state = "muzzle" + item_state = "muzzle" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + put_on_delay = 20 + var/resist_time = 0 //deciseconds of how long you need to gnaw to get rid of the gag, 0 to make it impossible to remove + var/mute = MUZZLE_MUTE_ALL + var/security_lock = FALSE // Requires brig access to remove 0 - Remove as normal + var/locked = FALSE //Indicates if a mask is locked, should always start as 0. + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi' + ) + +// Clumsy folks can't take the mask off themselves. +/obj/item/clothing/mask/muzzle/attack_hand(mob/user as mob) + if(user.wear_mask == src && !user.IsAdvancedToolUser()) + return 0 + else if(security_lock && locked) + if(do_unlock(user)) + visible_message("[user] unlocks [user.p_their()] [src.name].", \ + "[user] unlocks [user.p_their()] [src.name].") + ..() + return 1 + +/obj/item/clothing/mask/muzzle/proc/do_break() + if(security_lock) + security_lock = FALSE + locked = FALSE + flags &= ~NODROP + desc += " This one appears to be broken." + return TRUE + else + return FALSE + +/obj/item/clothing/mask/muzzle/proc/do_unlock(mob/living/carbon/human/user) + if(istype(user.get_inactive_hand(), /obj/item/card/emag)) + to_chat(user, "The lock vibrates as the card forces its locking system open.") + do_break() + return TRUE + else if(ACCESS_BRIG in user.get_access()) + to_chat(user, "The muzzle unlocks with a click.") + locked = FALSE + flags &= ~NODROP + return TRUE + + to_chat(user, "You must be wearing a security ID card or have one in your inactive hand to remove the muzzle.") + return FALSE + +/obj/item/clothing/mask/muzzle/proc/do_lock(mob/living/carbon/human/user) + if(security_lock) + locked = TRUE + flags |= NODROP + return TRUE + return FALSE + +/obj/item/clothing/mask/muzzle/Topic(href, href_list) + ..() + if(href_list["locked"]) + var/mob/living/carbon/wearer = locate(href_list["locked"]) + var/success = 0 + if(ishuman(usr)) + visible_message("[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].", \ + "[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].") + if(do_mob(usr, wearer, POCKET_STRIP_DELAY)) + if(locked) + success = do_unlock(usr) + else + success = do_lock(usr) + if(success) + visible_message("[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].", \ + "[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].") + if(usr.machine == wearer && in_range(src, usr)) + wearer.show_inv(usr) + else + to_chat(usr, "You lack the ability to manipulate the lock.") + + +/obj/item/clothing/mask/muzzle/tapegag + name = "tape gag" + desc = "MHPMHHH!" + icon_state = "tapegag" + item_state = null + w_class = WEIGHT_CLASS_TINY + resist_time = 150 + mute = MUZZLE_MUTE_MUFFLE + flags = DROPDEL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi' + ) + +/obj/item/clothing/mask/muzzle/tapegag/dropped(mob/user) + var/obj/item/trash/tapetrash/TT = new + transfer_fingerprints_to(TT) + user.transfer_fingerprints_to(TT) + user.put_in_active_hand(TT) + playsound(src, 'sound/items/poster_ripped.ogg', 40, 1) + user.emote("scream") + ..() + +/obj/item/clothing/mask/muzzle/safety + name = "safety muzzle" + desc = "A muzzle designed to prevent biting." + icon_state = "muzzle_secure" + item_state = "muzzle_secure" + resist_time = 0 + mute = MUZZLE_MUTE_NONE + security_lock = TRUE + locked = FALSE + materials = list(MAT_METAL=500, MAT_GLASS=50) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/muzzle/safety/shock + name = "shock muzzle" + desc = "A muzzle designed to prevent biting. This one is fitted with a behavior correction system." + var/obj/item/assembly/trigger = null + origin_tech = "materials=1;engineering=1" + materials = list(MAT_METAL=500, MAT_GLASS=50) + +/obj/item/clothing/mask/muzzle/safety/shock/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/assembly/signaler) || istype(W, /obj/item/assembly/voice)) + if(istype(trigger, /obj/item/assembly/signaler) || istype(trigger, /obj/item/assembly/voice)) + to_chat(user, "Something is already attached to [src].") + return FALSE + if(!user.drop_item()) + to_chat(user, "You are unable to insert [W] into [src].") + return FALSE + trigger = W + trigger.forceMove(src) + trigger.master = src + trigger.holder = src + to_chat(user, "You attach the [W] to [src].") + return TRUE + else if(istype(W, /obj/item/assembly)) + to_chat(user, "That won't fit in [src]. Perhaps a signaler or voice analyzer would?") + return FALSE + + return ..() + +/obj/item/clothing/mask/muzzle/safety/shock/screwdriver_act(mob/user, obj/item/I) + if(!trigger) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You remove [trigger] from [src].") + trigger.forceMove(get_turf(user)) + trigger.master = null + trigger.holder = null + trigger = null + +/obj/item/clothing/mask/muzzle/safety/shock/proc/can_shock(obj/item/clothing/C) + if(istype(C)) + if(isliving(C.loc)) + return C.loc + else if(isliving(loc)) + return loc + return FALSE + +/obj/item/clothing/mask/muzzle/safety/shock/proc/process_activation(var/obj/D, var/normal = 1, var/special = 1) + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + var/mob/M = can_shock(loc) + if(M) + to_chat(M, "You feel a sharp shock!") + do_sparks(3, 1, M) + + M.Weaken(5) + M.Stuttering(1) + M.Jitter(20) + return + +/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM as mob|obj) + if(trigger) + trigger.HasProximity(AM) + + +/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, list/message_pieces) + if(trigger) + trigger.hear_talk(M, message_pieces) + +/obj/item/clothing/mask/muzzle/safety/shock/hear_message(mob/living/M as mob, msg) + if(trigger) + trigger.hear_message(M, msg) + + + +/obj/item/clothing/mask/surgical + name = "sterile mask" + desc = "A sterile mask designed to help prevent the spread of diseases." + icon_state = "sterile" + item_state = "sterile" + w_class = WEIGHT_CLASS_TINY + flags_cover = MASKCOVERSMOUTH + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.01 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 25, "rad" = 0, "fire" = 0, "acid" = 0) + actions_types = list(/datum/action/item_action/adjust) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + + +/obj/item/clothing/mask/surgical/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/fakemoustache + name = "completely real moustache" + desc = "moustache is totally real." + icon_state = "fake-moustache" + flags_inv = HIDEFACE + actions_types = list(/datum/action/item_action/pontificate) + dog_fashion = /datum/dog_fashion/head/not_ian + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/fakemoustache/attack_self(mob/user) + pontificate(user) + +/obj/item/clothing/mask/fakemoustache/item_action_slot_check(slot) + if(slot == slot_wear_mask) + return 1 + +/obj/item/clothing/mask/fakemoustache/proc/pontificate(mob/user) + user.visible_message("\ [user] twirls [user.p_their()] moustache and laughs [pick("fiendishly","maniacally","diabolically","evilly")]!") + +//scarves (fit in in mask slot) + +/obj/item/clothing/mask/bluescarf + name = "blue neck scarf" + desc = "A blue neck scarf." + icon_state = "blueneckscarf" + item_state = "blueneckscarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + + +/obj/item/clothing/mask/redscarf + name = "red scarf" + desc = "A red and white checkered neck scarf." + icon_state = "redwhite_scarf" + item_state = "redwhite_scarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/greenscarf + name = "green scarf" + desc = "A green neck scarf." + icon_state = "green_scarf" + item_state = "green_scarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/ninjascarf + name = "ninja scarf" + desc = "A stealthy, dark scarf." + icon_state = "ninja_scarf" + item_state = "ninja_scarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + + +/obj/item/clothing/mask/pig + name = "pig mask" + desc = "A rubber pig mask." + icon_state = "pig" + item_state = "pig" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + + +/obj/item/clothing/mask/horsehead + name = "horse head mask" + desc = "A mask made of soft vinyl and latex, representing the head of a horse." + icon_state = "horsehead" + item_state = "horsehead" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + var/voicechange = 0 + var/temporaryname = " the Horse" + var/originalname = "" + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/horsehead/equipped(mob/user, slot) + if(flags & NODROP) //cursed masks only + originalname = user.real_name + if(!user.real_name || user.real_name == "Unknown") + user.real_name = "A Horse With No Name" //it felt good to be out of the rain + else + user.real_name = "[user.name][temporaryname]" + ..() + +/obj/item/clothing/mask/horsehead/dropped() //this really shouldn't happen, but call it extreme caution + if(flags & NODROP) + goodbye_horses(loc) + ..() + +/obj/item/clothing/mask/horsehead/Destroy() + if(flags & NODROP) + goodbye_horses(loc) + return ..() + +/obj/item/clothing/mask/horsehead/proc/goodbye_horses(mob/user) //I'm flying over you + if(!ismob(user)) + return + if(user.real_name == "[originalname][temporaryname]" || user.real_name == "A Horse With No Name") //if it's somehow changed while the mask is on it doesn't revert + user.real_name = originalname + +/obj/item/clothing/mask/face + flags_inv = HIDEFACE + flags_cover = MASKCOVERSMOUTH + +/obj/item/clothing/mask/face/rat + name = "rat mask" + desc = "A mask made of soft vinyl and latex, representing the head of a rat." + icon_state = "rat" + item_state = "rat" + +/obj/item/clothing/mask/face/fox + name = "fox mask" + desc = "A mask made of soft vinyl and latex, representing the head of a fox." + icon_state = "fox" + item_state = "fox" + +/obj/item/clothing/mask/face/bee + name = "bee mask" + desc = "A mask made of soft vinyl and latex, representing the head of a bee." + icon_state = "bee" + item_state = "bee" + +/obj/item/clothing/mask/face/bear + name = "bear mask" + desc = "A mask made of soft vinyl and latex, representing the head of a bear." + icon_state = "bear" + item_state = "bear" + +/obj/item/clothing/mask/face/bat + name = "bat mask" + desc = "A mask made of soft vinyl and latex, representing the head of a bat." + icon_state = "bat" + item_state = "bat" + +/obj/item/clothing/mask/face/raven + name = "raven mask" + desc = "A mask made of soft vinyl and latex, representing the head of a raven." + icon_state = "raven" + item_state = "raven" + +/obj/item/clothing/mask/face/jackal + name = "jackal mask" + desc = "A mask made of soft vinyl and latex, representing the head of a jackal." + icon_state = "jackal" + item_state = "jackal" + +/obj/item/clothing/mask/face/tribal + name = "tribal mask" + desc = "A mask carved out of wood, detailed carefully by hand." + icon_state = "bumba" + item_state = "bumba" + +/obj/item/clothing/mask/fawkes + name = "Guy Fawkes mask" + desc = "A mask designed to help you remember a specific date." + icon_state = "fawkes" + item_state = "fawkes" + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + +/obj/item/clothing/mask/gas/clown_hat/pennywise + name = "Pennywise Mask" + desc = "It's the eater of worlds, and of children." + icon_state = "pennywise_mask" + item_state = "pennywise_mask" + + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR + +// Bandanas +/obj/item/clothing/mask/bandana + name = "bandana" + desc = "A colorful bandana." + flags_inv = HIDEFACE + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_MASK + adjusted_flags = SLOT_HEAD + icon_state = "bandbotany" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + actions_types = list(/datum/action/item_action/adjust) + +/obj/item/clothing/mask/bandana/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/bandana/red + name = "red bandana" + icon_state = "bandred" + item_color = "red" + desc = "It's a red bandana." + +/obj/item/clothing/mask/bandana/blue + name = "blue bandana" + icon_state = "bandblue" + item_color = "blue" + desc = "It's a blue bandana." + +/obj/item/clothing/mask/bandana/gold + name = "gold bandana" + icon_state = "bandgold" + item_color = "yellow" + desc = "It's a gold bandana." + +/obj/item/clothing/mask/bandana/green + name = "green bandana" + icon_state = "bandgreen" + item_color = "green" + desc = "It's a green bandana." + +/obj/item/clothing/mask/bandana/orange + name = "orange bandana" + icon_state = "bandorange" + item_color = "orange" + desc = "It's an orange bandana." + +/obj/item/clothing/mask/bandana/purple + name = "purple bandana" + icon_state = "bandpurple" + item_color = "purple" + desc = "It's a purple bandana." + +/obj/item/clothing/mask/bandana/botany + name = "botany bandana" + desc = "It's a green bandana with some fine nanotech lining." + icon_state = "bandbotany" + +/obj/item/clothing/mask/bandana/skull + name = "skull bandana" + desc = "It's a black bandana with a skull pattern." + icon_state = "bandskull" + +/obj/item/clothing/mask/bandana/black + name = "black bandana" + icon_state = "bandblack" + item_color = "black" + desc = "It's a black bandana." + +/obj/item/clothing/mask/bandana/durathread + name = "durathread bandana" + desc = "A bandana made from durathread, you wish it would provide some protection to its wearer, but it's far too thin..." + icon_state = "banddurathread" + +/obj/item/clothing/mask/cursedclown + name = "cursed clown mask" + desc = "This is a very, very odd looking mask." + icon = 'icons/goonstation/objects/clothing/mask.dmi' + icon_state = "cursedclown" + item_state = "cclown_hat" + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + icon_override = 'icons/goonstation/mob/clothing/mask.dmi' + lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' + flags = NODROP | AIRTIGHT + flags_cover = MASKCOVERSMOUTH + +/obj/item/clothing/mask/cursedclown/equipped(mob/user, slot) + ..() + var/mob/living/carbon/human/H = user + if(istype(H) && slot == slot_wear_mask) + to_chat(H, "[src] grips your face!") + if(H.mind && H.mind.assigned_role != "Cluwne") + H.makeCluwne() + +/obj/item/clothing/mask/cursedclown/suicide_act(mob/user) + user.visible_message("[user] gazes into the eyes of [src]. [src] gazes back!") + spawn(10) + if(user) + user.gib() + return OBLITERATION diff --git a/code/modules/clothing/patreon/glasses.dm b/code/modules/clothing/patreon/glasses.dm index f52d54f7df2..08d5d6b0af8 100644 --- a/code/modules/clothing/patreon/glasses.dm +++ b/code/modules/clothing/patreon/glasses.dm @@ -18,4 +18,4 @@ name = "Spiky Green-tinted Shades" desc = "Fight the power!" icon_state = "garm" - item_state = "garm" \ No newline at end of file + item_state = "garm" diff --git a/code/modules/clothing/patreon/hats.dm b/code/modules/clothing/patreon/hats.dm index abee0cb9466..8b769ba4150 100644 --- a/code/modules/clothing/patreon/hats.dm +++ b/code/modules/clothing/patreon/hats.dm @@ -60,4 +60,4 @@ sprite_sheets = list( "Vox" = 'icons/mob/species/vox/head.dmi' - ) \ No newline at end of file + ) diff --git a/code/modules/clothing/shoes/colour.dm b/code/modules/clothing/shoes/colour.dm index f864d3bf8f6..0bf54e8692f 100644 --- a/code/modules/clothing/shoes/colour.dm +++ b/code/modules/clothing/shoes/colour.dm @@ -1,106 +1,106 @@ -/obj/item/clothing/shoes/black - name = "black shoes" - icon_state = "black" - item_color = "black" - desc = "A pair of black shoes." - - cold_protection = FEET - min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT - heat_protection = FEET - max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - - redcoat - item_color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way. - -/obj/item/clothing/shoes/black/greytide - flags = NODROP - -/obj/item/clothing/shoes/brown - name = "brown shoes" - desc = "A pair of brown shoes." - icon_state = "brown" - - captain - item_color = "captain" //Exists for washing machines. Is not different from brown shoes in any way. - hop - item_color = "hop" //Exists for washing machines. Is not different from brown shoes in any way. - ce - item_color = "chief" //Exists for washing machines. Is not different from brown shoes in any way. - rd - item_color = "director" //Exists for washing machines. Is not different from brown shoes in any way. - cmo - item_color = "medical" //Exists for washing machines. Is not different from brown shoes in any way. - cmo - item_color = "cargo" //Exists for washing machines. Is not different from brown shoes in any way. - -/obj/item/clothing/shoes/blue - name = "blue shoes" - icon_state = "blue" - item_color = "blue" - -/obj/item/clothing/shoes/green - name = "green shoes" - icon_state = "green" - item_color = "green" - -/obj/item/clothing/shoes/yellow - name = "yellow shoes" - icon_state = "yellow" - item_color = "yellow" - -/obj/item/clothing/shoes/purple - name = "purple shoes" - icon_state = "purple" - item_color = "purple" - -/obj/item/clothing/shoes/brown - name = "brown shoes" - icon_state = "brown" - item_color = "brown" - -/obj/item/clothing/shoes/red - name = "red shoes" - desc = "Stylish red shoes." - icon_state = "red" - item_color = "red" - -/obj/item/clothing/shoes/white - name = "white shoes" - icon_state = "white" - permeability_coefficient = 0.01 - item_color = "white" - -/obj/item/clothing/shoes/leather - name = "leather shoes" - desc = "A sturdy pair of leather shoes." - icon_state = "leather" - item_color = "leather" - -/obj/item/clothing/shoes/rainbow - name = "rainbow shoes" - desc = "Very gay shoes." - icon_state = "rain_bow" - item_color = "rainbow" - -/obj/item/clothing/shoes/orange - name = "orange shoes" - icon_state = "orange" - item_color = "orange" - -/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) - if(src.chained) - src.chained = null - src.slowdown = SHOES_SLOWDOWN - new /obj/item/restraints/handcuffs( user.loc ) - src.icon_state = "orange" - return - -/obj/item/clothing/shoes/orange/attackby(obj/H, loc, params) - ..() - if(istype(H, /obj/item/restraints/handcuffs) && !chained && !(H.flags & NODROP)) - if(src.icon_state != "orange") return - qdel(H) - src.chained = 1 - src.slowdown = 15 - src.icon_state = "orange1" - return +/obj/item/clothing/shoes/black + name = "black shoes" + icon_state = "black" + item_color = "black" + desc = "A pair of black shoes." + + cold_protection = FEET + min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT + heat_protection = FEET + max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT + + redcoat + item_color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way. + +/obj/item/clothing/shoes/black/greytide + flags = NODROP + +/obj/item/clothing/shoes/brown + name = "brown shoes" + desc = "A pair of brown shoes." + icon_state = "brown" + + captain + item_color = "captain" //Exists for washing machines. Is not different from brown shoes in any way. + hop + item_color = "hop" //Exists for washing machines. Is not different from brown shoes in any way. + ce + item_color = "chief" //Exists for washing machines. Is not different from brown shoes in any way. + rd + item_color = "director" //Exists for washing machines. Is not different from brown shoes in any way. + cmo + item_color = "medical" //Exists for washing machines. Is not different from brown shoes in any way. + cmo + item_color = "cargo" //Exists for washing machines. Is not different from brown shoes in any way. + +/obj/item/clothing/shoes/blue + name = "blue shoes" + icon_state = "blue" + item_color = "blue" + +/obj/item/clothing/shoes/green + name = "green shoes" + icon_state = "green" + item_color = "green" + +/obj/item/clothing/shoes/yellow + name = "yellow shoes" + icon_state = "yellow" + item_color = "yellow" + +/obj/item/clothing/shoes/purple + name = "purple shoes" + icon_state = "purple" + item_color = "purple" + +/obj/item/clothing/shoes/brown + name = "brown shoes" + icon_state = "brown" + item_color = "brown" + +/obj/item/clothing/shoes/red + name = "red shoes" + desc = "Stylish red shoes." + icon_state = "red" + item_color = "red" + +/obj/item/clothing/shoes/white + name = "white shoes" + icon_state = "white" + permeability_coefficient = 0.01 + item_color = "white" + +/obj/item/clothing/shoes/leather + name = "leather shoes" + desc = "A sturdy pair of leather shoes." + icon_state = "leather" + item_color = "leather" + +/obj/item/clothing/shoes/rainbow + name = "rainbow shoes" + desc = "Very gay shoes." + icon_state = "rain_bow" + item_color = "rainbow" + +/obj/item/clothing/shoes/orange + name = "orange shoes" + icon_state = "orange" + item_color = "orange" + +/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) + if(src.chained) + src.chained = null + src.slowdown = SHOES_SLOWDOWN + new /obj/item/restraints/handcuffs( user.loc ) + src.icon_state = "orange" + return + +/obj/item/clothing/shoes/orange/attackby(obj/H, loc, params) + ..() + if(istype(H, /obj/item/restraints/handcuffs) && !chained && !(H.flags & NODROP)) + if(src.icon_state != "orange") return + qdel(H) + src.chained = 1 + src.slowdown = 15 + src.icon_state = "orange1" + return diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index 7b0baef536f..916cb8e89d4 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -1,118 +1,118 @@ -/obj/item/clothing/shoes/magboots - desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle." - name = "magboots" - icon_state = "magboots0" - origin_tech = "materials=3;magnets=4;engineering=4" - var/magboot_state = "magboots" - var/magpulse = 0 - var/slowdown_active = 2 - var/slowdown_passive = SHOES_SLOWDOWN - var/magpulse_name = "mag-pulse traction system" - actions_types = list(/datum/action/item_action/toggle) - strip_delay = 70 - put_on_delay = 70 - resistance_flags = FIRE_PROOF - -/obj/item/clothing/shoes/magboots/attack_self(mob/user) - if(magpulse) - flags &= ~NOSLIP - slowdown = slowdown_passive - else - flags |= NOSLIP - slowdown = slowdown_active - magpulse = !magpulse - icon_state = "[magboot_state][magpulse]" - to_chat(user, "You [magpulse ? "enable" : "disable"] the [magpulse_name].") - user.update_inv_shoes() //so our mob-overlays update - user.update_gravity(user.mob_has_gravity()) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/shoes/magboots/negates_gravity() - return flags & NOSLIP - -/obj/item/clothing/shoes/magboots/examine(mob/user) - . = ..() - . += "Its [magpulse_name] appears to be [magpulse ? "enabled" : "disabled"]." - - -/obj/item/clothing/shoes/magboots/advance - desc = "Advanced magnetic boots that have a lighter magnetic pull, placing less burden on the wearer." - name = "advanced magboots" - icon_state = "advmag0" - magboot_state = "advmag" - slowdown_active = SHOES_SLOWDOWN - origin_tech = null - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/shoes/magboots/syndie - desc = "Reverse-engineered magnetic boots that have a heavy magnetic pull. Property of Gorlex Marauders." - name = "blood-red magboots" - icon_state = "syndiemag0" - magboot_state = "syndiemag" - origin_tech = "magnets=4;syndicate=2" - -obj/item/clothing/shoes/magboots/syndie/advance //For the Syndicate Strike Team - desc = "Reverse-engineered magboots that appear to be based on an advanced model, as they have a lighter magnetic pull. Property of Gorlex Marauders." - name = "advanced blood-red magboots" - slowdown_active = SHOES_SLOWDOWN - -/obj/item/clothing/shoes/magboots/clown - desc = "The prankster's standard-issue clowning shoes. Damn they're huge! There's a red light on the side." - name = "clown shoes" - icon_state = "clownmag0" - magboot_state = "clownmag" - item_state = "clown_shoes" - slowdown = SHOES_SLOWDOWN+1 - slowdown_active = SHOES_SLOWDOWN+1 - slowdown_passive = SHOES_SLOWDOWN+1 - magpulse_name = "honk-powered traction system" - item_color = "clown" - silence_steps = 1 - shoe_sound = "clownstep" - origin_tech = "magnets=4;syndicate=2" - var/enabled_waddle = TRUE - var/datum/component/waddle - -/obj/item/clothing/shoes/magboots/clown/equipped(mob/user, slot) - . = ..() - if(slot == slot_shoes && enabled_waddle) - waddle = user.AddComponent(/datum/component/waddling) - -/obj/item/clothing/shoes/magboots/clown/dropped(mob/user) - . = ..() - QDEL_NULL(waddle) - -/obj/item/clothing/shoes/magboots/clown/CtrlClick(mob/living/user) - if(!isliving(user)) - return - if(user.get_active_hand() != src) - to_chat(user, "You must hold [src] in your hand to do this.") - return - if(!enabled_waddle) - to_chat(user, "You switch off the waddle dampeners!") - enabled_waddle = TRUE - else - to_chat(user, "You switch on the waddle dampeners!") - enabled_waddle = FALSE - -/obj/item/clothing/shoes/magboots/wizard //bundled with the wiz hardsuit - name = "boots of gripping" - desc = "These magical boots, once activated, will stay gripped to any surface without slowing you down." - icon_state = "wizmag0" - magboot_state = "wizmag" - slowdown_active = SHOES_SLOWDOWN //wiz hardsuit already slows you down, no need to double it - magpulse_name = "gripping ability" - magical = TRUE - -/obj/item/clothing/shoes/magboots/wizard/attack_self(mob/user) - if(user) - if(user.mind in SSticker.mode.wizards) - if(magpulse) //faint blue light when shoes are turned on gives a reason to turn them off when not needed in maint - set_light(0) - else - set_light(2, 1, LIGHT_COLOR_LIGHTBLUE) - ..() - else - to_chat(user, "You poke the gem on [src]. Nothing happens.") \ No newline at end of file +/obj/item/clothing/shoes/magboots + desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle." + name = "magboots" + icon_state = "magboots0" + origin_tech = "materials=3;magnets=4;engineering=4" + var/magboot_state = "magboots" + var/magpulse = 0 + var/slowdown_active = 2 + var/slowdown_passive = SHOES_SLOWDOWN + var/magpulse_name = "mag-pulse traction system" + actions_types = list(/datum/action/item_action/toggle) + strip_delay = 70 + put_on_delay = 70 + resistance_flags = FIRE_PROOF + +/obj/item/clothing/shoes/magboots/attack_self(mob/user) + if(magpulse) + flags &= ~NOSLIP + slowdown = slowdown_passive + else + flags |= NOSLIP + slowdown = slowdown_active + magpulse = !magpulse + icon_state = "[magboot_state][magpulse]" + to_chat(user, "You [magpulse ? "enable" : "disable"] the [magpulse_name].") + user.update_inv_shoes() //so our mob-overlays update + user.update_gravity(user.mob_has_gravity()) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/shoes/magboots/negates_gravity() + return flags & NOSLIP + +/obj/item/clothing/shoes/magboots/examine(mob/user) + . = ..() + . += "Its [magpulse_name] appears to be [magpulse ? "enabled" : "disabled"]." + + +/obj/item/clothing/shoes/magboots/advance + desc = "Advanced magnetic boots that have a lighter magnetic pull, placing less burden on the wearer." + name = "advanced magboots" + icon_state = "advmag0" + magboot_state = "advmag" + slowdown_active = SHOES_SLOWDOWN + origin_tech = null + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/shoes/magboots/syndie + desc = "Reverse-engineered magnetic boots that have a heavy magnetic pull. Property of Gorlex Marauders." + name = "blood-red magboots" + icon_state = "syndiemag0" + magboot_state = "syndiemag" + origin_tech = "magnets=4;syndicate=2" + +obj/item/clothing/shoes/magboots/syndie/advance //For the Syndicate Strike Team + desc = "Reverse-engineered magboots that appear to be based on an advanced model, as they have a lighter magnetic pull. Property of Gorlex Marauders." + name = "advanced blood-red magboots" + slowdown_active = SHOES_SLOWDOWN + +/obj/item/clothing/shoes/magboots/clown + desc = "The prankster's standard-issue clowning shoes. Damn they're huge! There's a red light on the side." + name = "clown shoes" + icon_state = "clownmag0" + magboot_state = "clownmag" + item_state = "clown_shoes" + slowdown = SHOES_SLOWDOWN+1 + slowdown_active = SHOES_SLOWDOWN+1 + slowdown_passive = SHOES_SLOWDOWN+1 + magpulse_name = "honk-powered traction system" + item_color = "clown" + silence_steps = 1 + shoe_sound = "clownstep" + origin_tech = "magnets=4;syndicate=2" + var/enabled_waddle = TRUE + var/datum/component/waddle + +/obj/item/clothing/shoes/magboots/clown/equipped(mob/user, slot) + . = ..() + if(slot == slot_shoes && enabled_waddle) + waddle = user.AddComponent(/datum/component/waddling) + +/obj/item/clothing/shoes/magboots/clown/dropped(mob/user) + . = ..() + QDEL_NULL(waddle) + +/obj/item/clothing/shoes/magboots/clown/CtrlClick(mob/living/user) + if(!isliving(user)) + return + if(user.get_active_hand() != src) + to_chat(user, "You must hold [src] in your hand to do this.") + return + if(!enabled_waddle) + to_chat(user, "You switch off the waddle dampeners!") + enabled_waddle = TRUE + else + to_chat(user, "You switch on the waddle dampeners!") + enabled_waddle = FALSE + +/obj/item/clothing/shoes/magboots/wizard //bundled with the wiz hardsuit + name = "boots of gripping" + desc = "These magical boots, once activated, will stay gripped to any surface without slowing you down." + icon_state = "wizmag0" + magboot_state = "wizmag" + slowdown_active = SHOES_SLOWDOWN //wiz hardsuit already slows you down, no need to double it + magpulse_name = "gripping ability" + magical = TRUE + +/obj/item/clothing/shoes/magboots/wizard/attack_self(mob/user) + if(user) + if(user.mind in SSticker.mode.wizards) + if(magpulse) //faint blue light when shoes are turned on gives a reason to turn them off when not needed in maint + set_light(0) + else + set_light(2, 1, LIGHT_COLOR_LIGHTBLUE) + ..() + else + to_chat(user, "You poke the gem on [src]. Nothing happens.") diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 9c7d8bac080..6858355f876 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -1,346 +1,346 @@ -/obj/item/clothing/shoes/mime - name = "mime shoes" - icon_state = "mime" - item_color = "mime" - -/obj/item/clothing/shoes/combat //basic syndicate combat boots for nuke ops and mob corpses - name = "combat boots" - desc = "High speed, low drag combat boots." - can_cut_open = 1 - icon_state = "jackboots" - item_state = "jackboots" - armor = list("melee" = 25, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 10, "rad" = 0, "fire" = 70, "acid" = 50) - strip_delay = 70 - resistance_flags = NONE - -/obj/item/clothing/shoes/combat/swat //overpowered boots for death squads - name = "\improper SWAT shoes" - desc = "High speed, no drag combat boots." - permeability_coefficient = 0.01 - armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 90, "acid" = 50) - flags = NOSLIP - -/obj/item/clothing/shoes/sandal - desc = "A pair of rather plain, wooden sandals." - name = "sandals" - icon_state = "wizard" - strip_delay = 50 - put_on_delay = 50 - magical = TRUE - -/obj/item/clothing/shoes/sandal/marisa - desc = "A pair of magic, black shoes." - name = "magic shoes" - icon_state = "black" - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/shoes/sandal/magic - name = "magical sandals" - desc = "A pair of sandals imbued with magic." - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/shoes/galoshes - desc = "A pair of yellow rubber boots, designed to prevent slipping on wet surfaces." - name = "galoshes" - icon_state = "galoshes" - permeability_coefficient = 0.05 - flags = NOSLIP - slowdown = SHOES_SLOWDOWN+1 - strip_delay = 50 - put_on_delay = 50 - resistance_flags = NONE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 75) - -/obj/item/clothing/shoes/galoshes/dry - name = "absorbent galoshes" - desc = "A pair of purple rubber boots, designed to prevent slipping on wet surfaces while also drying them." - icon_state = "galoshes_dry" - -/obj/item/clothing/shoes/galoshes/dry/step_action() - var/turf/simulated/t_loc = get_turf(src) - if(istype(t_loc) && t_loc.wet) - t_loc.MakeDry(TURF_WET_WATER) - -/obj/item/clothing/shoes/clown_shoes - desc = "The prankster's standard-issue clowning shoes. Damn they're huge! Ctrl-click to toggle the waddle dampeners!" - name = "clown shoes" - icon_state = "clown" - item_state = "clown_shoes" - slowdown = SHOES_SLOWDOWN+1 - item_color = "clown" - var/footstep = 1 //used for squeeks whilst walking - shoe_sound = "clownstep" - var/enabled_waddle = TRUE - var/datum/component/waddle - -/obj/item/clothing/shoes/clown_shoes/equipped(mob/user, slot) - . = ..() - if(slot == slot_shoes && enabled_waddle) - waddle = user.AddComponent(/datum/component/waddling) - -/obj/item/clothing/shoes/clown_shoes/dropped(mob/user) - . = ..() - QDEL_NULL(waddle) - -/obj/item/clothing/shoes/clown_shoes/CtrlClick(mob/living/user) - if(!isliving(user)) - return - if(user.get_active_hand() != src) - to_chat(user, "You must hold [src] in your hand to do this.") - return - if(!enabled_waddle) - to_chat(user, "You switch off the waddle dampeners!") - enabled_waddle = TRUE - else - to_chat(user, "You switch on the waddle dampeners!") - enabled_waddle = FALSE - -/obj/item/clothing/shoes/clown_shoes/nodrop - flags = NODROP - -/obj/item/clothing/shoes/clown_shoes/magical - name = "magical clown shoes" - desc = "Standard-issue shoes of the wizarding class clown. Damn they're huge! And powerful! Somehow." - magical = TRUE - -/obj/item/clothing/shoes/jackboots - name = "jackboots" - desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time." - can_cut_open = 1 - icon_state = "jackboots" - item_state = "jackboots" - item_color = "hosred" - strip_delay = 50 - put_on_delay = 50 - resistance_flags = NONE - var/footstep = 1 - shoe_sound = "jackboot" - -/obj/item/clothing/shoes/jackboots/jacksandals - name = "jacksandals" - desc = "Nanotrasen-issue Security combat sandals for combat scenarios. They're jacksandals, however that works." - can_cut_open = 0 - icon_state = "jacksandal" - item_color = "jacksandal" - -/obj/item/clothing/shoes/workboots - name = "work boots" - desc = "Thick-soled boots for industrial work environments." - can_cut_open = 1 - icon_state = "workboots" - -/obj/item/clothing/shoes/workboots/mining - name = "mining boots" - desc = "Steel-toed mining boots for mining in hazardous environments. Very good at keeping toes uncrushed." - icon_state = "explorer" - resistance_flags = FIRE_PROOF - -/obj/item/clothing/shoes/winterboots - name = "winter boots" - desc = "Boots lined with 'synthetic' animal fur." - can_cut_open = 1 - icon_state = "winterboots" - cold_protection = FEET|LEGS - min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT - heat_protection = FEET|LEGS - max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - -/obj/item/clothing/shoes/cult - name = "boots" - desc = "A pair of boots worn by the followers of Nar-Sie." - icon_state = "cult" - item_state = "cult" - item_color = "cult" - - cold_protection = FEET - min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT - heat_protection = FEET - max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - -/obj/item/clothing/shoes/cyborg - name = "cyborg boots" - desc = "Shoes for a cyborg costume" - icon_state = "boots" - -/obj/item/clothing/shoes/slippers - name = "bunny slippers" - desc = "Fluffy!" - icon_state = "slippers" - item_state = "slippers" - -/obj/item/clothing/shoes/slippers_worn - name = "worn bunny slippers" - desc = "Fluffy..." - icon_state = "slippers_worn" - item_state = "slippers_worn" - -/obj/item/clothing/shoes/laceup - name = "laceup shoes" - desc = "The height of fashion, and they're pre-polished!" - icon_state = "laceups" - put_on_delay = 50 - -/obj/item/clothing/shoes/roman - name = "roman sandals" - desc = "Sandals with buckled leather straps on it." - icon_state = "roman" - item_state = "roman" - strip_delay = 100 - put_on_delay = 100 - -/obj/item/clothing/shoes/centcom - name = "dress shoes" - desc = "They appear impeccably polished." - icon_state = "laceups" - -/obj/item/clothing/shoes/griffin - name = "griffon boots" - desc = "A pair of costume boots fashioned after bird talons." - icon_state = "griffinboots" - item_state = "griffinboots" - - -/obj/item/clothing/shoes/fluff/noble_boot - name = "noble boots" - desc = "The boots are economically designed to balance function and comfort, so that you can step on peasants without having to worry about blisters. The leather also resists unwanted blood stains." - icon_state = "noble_boot" - item_color = "noble_boot" - item_state = "noble_boot" - -/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stack/tape_roll) && !silence_steps) - var/obj/item/stack/tape_roll/TR = I - if((!silence_steps || shoe_sound) && TR.use(4)) - silence_steps = TRUE - shoe_sound = null - to_chat(user, "You tape the soles of [src] to silence your footsteps.") - else - return ..() - -/obj/item/clothing/shoes/sandal/white - name = "White Sandals" - desc = "Medical sandals that nerds wear." - icon_state = "medsandal" - item_color = "medsandal" - -/obj/item/clothing/shoes/sandal/fancy - name = "Fancy Sandals" - desc = "FANCY!!." - icon_state = "fancysandal" - item_color = "fancysandal" - -/obj/item/clothing/shoes/cursedclown - name = "cursed clown shoes" - desc = "Moldering clown flip flops. They're neon green for some reason." - icon = 'icons/goonstation/objects/clothing/feet.dmi' - icon_state = "cursedclown" - item_state = "cclown_shoes" - icon_override = 'icons/goonstation/mob/clothing/feet.dmi' - lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - flags = NODROP - shoe_sound = "clownstep" - -/obj/item/clothing/shoes/singery - name = "yellow performer's boots" - desc = "These boots were made for dancing." - icon_state = "ysing" - put_on_delay = 50 - -/obj/item/clothing/shoes/singerb - name = "blue performer's boots" - desc = "These boots were made for dancing." - icon_state = "bsing" - put_on_delay = 50 - -/obj/item/clothing/shoes/cowboy - name = "cowboy boots" - desc = "A pair a' brown boots." - icon_state = "cowboy_brown" - item_color = "cowboy_brown" - -/obj/item/clothing/shoes/cowboy/black - name = "black cowboy boots" - desc = "A pair a' black rustlers' boots" - icon_state = "cowboy_black" - item_color = "cowboy_black" - -/obj/item/clothing/shoes/cowboy/white - name = "white cowboy boots" - desc = "For the rancher in us all." - icon_state = "cowboy_white" - item_color = "cowboy_white" - -/obj/item/clothing/shoes/cowboy/fancy - name = "bilton wrangler boots" - desc = "A pair of authentic haute couture boots from Japanifornia. You doubt they have ever been close to cattle." - icon_state = "cowboy_fancy" - item_color = "cowboy_fancy" - -/obj/item/clothing/shoes/cowboy/pink - name = "pink cowgirl boots" - desc = "For a Rustlin' tustlin' cowgirl." - icon_state = "cowboyboots_pink" - item_color = "cowboyboots_pink" - -/obj/item/clothing/shoes/cowboy/lizard - name = "lizard skin boots" - desc = "You can hear a faint hissing from inside the boots; you hope it is just a mournful ghost." - icon_state = "lizardboots_green" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 0) //lizards like to stay warm - -/obj/item/clothing/shoes/cowboy/lizardmasterwork - name = "\improper Hugs-The-Feet lizard skin boots" - desc = "A pair of masterfully crafted lizard skin boots. Finally a good application for the station's most bothersome inhabitants." - icon_state = "lizardboots_blue" - -/obj/effect/spawner/lootdrop/lizardboots - name = "random lizard boot quality" - desc = "Which ever gets picked, the lizard race loses" - icon = 'icons/obj/clothing/shoes.dmi' - icon_state = "lizardboots_green" - loot = list( - /obj/item/clothing/shoes/cowboy/lizard = 7, - /obj/item/clothing/shoes/cowboy/lizardmasterwork = 1) - -/obj/item/clothing/shoes/footwraps - name = "cloth footwraps" - desc = "A roll of treated canvas used for wrapping claws or paws." - icon_state = "clothwrap" - item_state = "clothwrap" - force = 0 - silence_steps = TRUE - w_class = WEIGHT_CLASS_SMALL - -/obj/item/clothing/shoes/bhop - name = "jump boots" - desc = "A specialized pair of combat boots with a built-in propulsion system for rapid foward movement." - icon_state = "jetboots" - item_state = "jetboots" - item_color = "hosred" - resistance_flags = FIRE_PROOF - actions_types = list(/datum/action/item_action/bhop) - permeability_coefficient = 0.05 - can_cut_open = FALSE - var/jumpdistance = 5 //-1 from to see the actual distance, e.g 4 goes over 3 tiles - var/jumpspeed = 3 - var/recharging_rate = 60 //default 6 seconds between each dash - var/recharging_time = 0 //time until next dash - -/obj/item/clothing/shoes/bhop/ui_action_click(mob/user, action) - if(!isliving(user)) - return - - if(recharging_time > world.time) - to_chat(user, "The boot's internal propulsion needs to recharge still!") - return - - var/atom/target = get_edge_target_turf(user, user.dir) //gets the user's direction - - if (user.throw_at(target, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE)) - playsound(src, 'sound/effects/stealthoff.ogg', 50, 1, 1) - user.visible_message("[usr] dashes forward into the air!") - recharging_time = world.time + recharging_rate - else - to_chat(user, "Something prevents you from dashing forward!") \ No newline at end of file +/obj/item/clothing/shoes/mime + name = "mime shoes" + icon_state = "mime" + item_color = "mime" + +/obj/item/clothing/shoes/combat //basic syndicate combat boots for nuke ops and mob corpses + name = "combat boots" + desc = "High speed, low drag combat boots." + can_cut_open = 1 + icon_state = "jackboots" + item_state = "jackboots" + armor = list("melee" = 25, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 10, "rad" = 0, "fire" = 70, "acid" = 50) + strip_delay = 70 + resistance_flags = NONE + +/obj/item/clothing/shoes/combat/swat //overpowered boots for death squads + name = "\improper SWAT shoes" + desc = "High speed, no drag combat boots." + permeability_coefficient = 0.01 + armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 90, "acid" = 50) + flags = NOSLIP + +/obj/item/clothing/shoes/sandal + desc = "A pair of rather plain, wooden sandals." + name = "sandals" + icon_state = "wizard" + strip_delay = 50 + put_on_delay = 50 + magical = TRUE + +/obj/item/clothing/shoes/sandal/marisa + desc = "A pair of magic, black shoes." + name = "magic shoes" + icon_state = "black" + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/shoes/sandal/magic + name = "magical sandals" + desc = "A pair of sandals imbued with magic." + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/shoes/galoshes + desc = "A pair of yellow rubber boots, designed to prevent slipping on wet surfaces." + name = "galoshes" + icon_state = "galoshes" + permeability_coefficient = 0.05 + flags = NOSLIP + slowdown = SHOES_SLOWDOWN+1 + strip_delay = 50 + put_on_delay = 50 + resistance_flags = NONE + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 75) + +/obj/item/clothing/shoes/galoshes/dry + name = "absorbent galoshes" + desc = "A pair of purple rubber boots, designed to prevent slipping on wet surfaces while also drying them." + icon_state = "galoshes_dry" + +/obj/item/clothing/shoes/galoshes/dry/step_action() + var/turf/simulated/t_loc = get_turf(src) + if(istype(t_loc) && t_loc.wet) + t_loc.MakeDry(TURF_WET_WATER) + +/obj/item/clothing/shoes/clown_shoes + desc = "The prankster's standard-issue clowning shoes. Damn they're huge! Ctrl-click to toggle the waddle dampeners!" + name = "clown shoes" + icon_state = "clown" + item_state = "clown_shoes" + slowdown = SHOES_SLOWDOWN+1 + item_color = "clown" + var/footstep = 1 //used for squeeks whilst walking + shoe_sound = "clownstep" + var/enabled_waddle = TRUE + var/datum/component/waddle + +/obj/item/clothing/shoes/clown_shoes/equipped(mob/user, slot) + . = ..() + if(slot == slot_shoes && enabled_waddle) + waddle = user.AddComponent(/datum/component/waddling) + +/obj/item/clothing/shoes/clown_shoes/dropped(mob/user) + . = ..() + QDEL_NULL(waddle) + +/obj/item/clothing/shoes/clown_shoes/CtrlClick(mob/living/user) + if(!isliving(user)) + return + if(user.get_active_hand() != src) + to_chat(user, "You must hold [src] in your hand to do this.") + return + if(!enabled_waddle) + to_chat(user, "You switch off the waddle dampeners!") + enabled_waddle = TRUE + else + to_chat(user, "You switch on the waddle dampeners!") + enabled_waddle = FALSE + +/obj/item/clothing/shoes/clown_shoes/nodrop + flags = NODROP + +/obj/item/clothing/shoes/clown_shoes/magical + name = "magical clown shoes" + desc = "Standard-issue shoes of the wizarding class clown. Damn they're huge! And powerful! Somehow." + magical = TRUE + +/obj/item/clothing/shoes/jackboots + name = "jackboots" + desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time." + can_cut_open = 1 + icon_state = "jackboots" + item_state = "jackboots" + item_color = "hosred" + strip_delay = 50 + put_on_delay = 50 + resistance_flags = NONE + var/footstep = 1 + shoe_sound = "jackboot" + +/obj/item/clothing/shoes/jackboots/jacksandals + name = "jacksandals" + desc = "Nanotrasen-issue Security combat sandals for combat scenarios. They're jacksandals, however that works." + can_cut_open = 0 + icon_state = "jacksandal" + item_color = "jacksandal" + +/obj/item/clothing/shoes/workboots + name = "work boots" + desc = "Thick-soled boots for industrial work environments." + can_cut_open = 1 + icon_state = "workboots" + +/obj/item/clothing/shoes/workboots/mining + name = "mining boots" + desc = "Steel-toed mining boots for mining in hazardous environments. Very good at keeping toes uncrushed." + icon_state = "explorer" + resistance_flags = FIRE_PROOF + +/obj/item/clothing/shoes/winterboots + name = "winter boots" + desc = "Boots lined with 'synthetic' animal fur." + can_cut_open = 1 + icon_state = "winterboots" + cold_protection = FEET|LEGS + min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT + heat_protection = FEET|LEGS + max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT + +/obj/item/clothing/shoes/cult + name = "boots" + desc = "A pair of boots worn by the followers of Nar-Sie." + icon_state = "cult" + item_state = "cult" + item_color = "cult" + + cold_protection = FEET + min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT + heat_protection = FEET + max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT + +/obj/item/clothing/shoes/cyborg + name = "cyborg boots" + desc = "Shoes for a cyborg costume" + icon_state = "boots" + +/obj/item/clothing/shoes/slippers + name = "bunny slippers" + desc = "Fluffy!" + icon_state = "slippers" + item_state = "slippers" + +/obj/item/clothing/shoes/slippers_worn + name = "worn bunny slippers" + desc = "Fluffy..." + icon_state = "slippers_worn" + item_state = "slippers_worn" + +/obj/item/clothing/shoes/laceup + name = "laceup shoes" + desc = "The height of fashion, and they're pre-polished!" + icon_state = "laceups" + put_on_delay = 50 + +/obj/item/clothing/shoes/roman + name = "roman sandals" + desc = "Sandals with buckled leather straps on it." + icon_state = "roman" + item_state = "roman" + strip_delay = 100 + put_on_delay = 100 + +/obj/item/clothing/shoes/centcom + name = "dress shoes" + desc = "They appear impeccably polished." + icon_state = "laceups" + +/obj/item/clothing/shoes/griffin + name = "griffon boots" + desc = "A pair of costume boots fashioned after bird talons." + icon_state = "griffinboots" + item_state = "griffinboots" + + +/obj/item/clothing/shoes/fluff/noble_boot + name = "noble boots" + desc = "The boots are economically designed to balance function and comfort, so that you can step on peasants without having to worry about blisters. The leather also resists unwanted blood stains." + icon_state = "noble_boot" + item_color = "noble_boot" + item_state = "noble_boot" + +/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stack/tape_roll) && !silence_steps) + var/obj/item/stack/tape_roll/TR = I + if((!silence_steps || shoe_sound) && TR.use(4)) + silence_steps = TRUE + shoe_sound = null + to_chat(user, "You tape the soles of [src] to silence your footsteps.") + else + return ..() + +/obj/item/clothing/shoes/sandal/white + name = "White Sandals" + desc = "Medical sandals that nerds wear." + icon_state = "medsandal" + item_color = "medsandal" + +/obj/item/clothing/shoes/sandal/fancy + name = "Fancy Sandals" + desc = "FANCY!!." + icon_state = "fancysandal" + item_color = "fancysandal" + +/obj/item/clothing/shoes/cursedclown + name = "cursed clown shoes" + desc = "Moldering clown flip flops. They're neon green for some reason." + icon = 'icons/goonstation/objects/clothing/feet.dmi' + icon_state = "cursedclown" + item_state = "cclown_shoes" + icon_override = 'icons/goonstation/mob/clothing/feet.dmi' + lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + flags = NODROP + shoe_sound = "clownstep" + +/obj/item/clothing/shoes/singery + name = "yellow performer's boots" + desc = "These boots were made for dancing." + icon_state = "ysing" + put_on_delay = 50 + +/obj/item/clothing/shoes/singerb + name = "blue performer's boots" + desc = "These boots were made for dancing." + icon_state = "bsing" + put_on_delay = 50 + +/obj/item/clothing/shoes/cowboy + name = "cowboy boots" + desc = "A pair a' brown boots." + icon_state = "cowboy_brown" + item_color = "cowboy_brown" + +/obj/item/clothing/shoes/cowboy/black + name = "black cowboy boots" + desc = "A pair a' black rustlers' boots" + icon_state = "cowboy_black" + item_color = "cowboy_black" + +/obj/item/clothing/shoes/cowboy/white + name = "white cowboy boots" + desc = "For the rancher in us all." + icon_state = "cowboy_white" + item_color = "cowboy_white" + +/obj/item/clothing/shoes/cowboy/fancy + name = "bilton wrangler boots" + desc = "A pair of authentic haute couture boots from Japanifornia. You doubt they have ever been close to cattle." + icon_state = "cowboy_fancy" + item_color = "cowboy_fancy" + +/obj/item/clothing/shoes/cowboy/pink + name = "pink cowgirl boots" + desc = "For a Rustlin' tustlin' cowgirl." + icon_state = "cowboyboots_pink" + item_color = "cowboyboots_pink" + +/obj/item/clothing/shoes/cowboy/lizard + name = "lizard skin boots" + desc = "You can hear a faint hissing from inside the boots; you hope it is just a mournful ghost." + icon_state = "lizardboots_green" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 0) //lizards like to stay warm + +/obj/item/clothing/shoes/cowboy/lizardmasterwork + name = "\improper Hugs-The-Feet lizard skin boots" + desc = "A pair of masterfully crafted lizard skin boots. Finally a good application for the station's most bothersome inhabitants." + icon_state = "lizardboots_blue" + +/obj/effect/spawner/lootdrop/lizardboots + name = "random lizard boot quality" + desc = "Which ever gets picked, the lizard race loses" + icon = 'icons/obj/clothing/shoes.dmi' + icon_state = "lizardboots_green" + loot = list( + /obj/item/clothing/shoes/cowboy/lizard = 7, + /obj/item/clothing/shoes/cowboy/lizardmasterwork = 1) + +/obj/item/clothing/shoes/footwraps + name = "cloth footwraps" + desc = "A roll of treated canvas used for wrapping claws or paws." + icon_state = "clothwrap" + item_state = "clothwrap" + force = 0 + silence_steps = TRUE + w_class = WEIGHT_CLASS_SMALL + +/obj/item/clothing/shoes/bhop + name = "jump boots" + desc = "A specialized pair of combat boots with a built-in propulsion system for rapid foward movement." + icon_state = "jetboots" + item_state = "jetboots" + item_color = "hosred" + resistance_flags = FIRE_PROOF + actions_types = list(/datum/action/item_action/bhop) + permeability_coefficient = 0.05 + can_cut_open = FALSE + var/jumpdistance = 5 //-1 from to see the actual distance, e.g 4 goes over 3 tiles + var/jumpspeed = 3 + var/recharging_rate = 60 //default 6 seconds between each dash + var/recharging_time = 0 //time until next dash + +/obj/item/clothing/shoes/bhop/ui_action_click(mob/user, action) + if(!isliving(user)) + return + + if(recharging_time > world.time) + to_chat(user, "The boot's internal propulsion needs to recharge still!") + return + + var/atom/target = get_edge_target_turf(user, user.dir) //gets the user's direction + + if (user.throw_at(target, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE)) + playsound(src, 'sound/effects/stealthoff.ogg', 50, 1, 1) + user.visible_message("[usr] dashes forward into the air!") + recharging_time = world.time + recharging_rate + else + to_chat(user, "Something prevents you from dashing forward!") diff --git a/code/modules/clothing/spacesuits/breaches.dm b/code/modules/clothing/spacesuits/breaches.dm index 483c122fd80..899cb8e8d46 100644 --- a/code/modules/clothing/spacesuits/breaches.dm +++ b/code/modules/clothing/spacesuits/breaches.dm @@ -23,23 +23,23 @@ ..() base_name = "[name]" -//Some simple descriptors for breaches. Global because lazy, TODO: work out a better way to do this. +//Some simple descriptors for breaches. Global because lazy, TODO: work out a better way to do this. | 6 years late, but atleast they are proper globals now -var/global/list/breach_brute_descriptors = list( +GLOBAL_LIST_INIT(breach_brute_descriptors, list( "tiny puncture", "ragged tear", "large split", "huge tear", "gaping wound" - ) + )) -var/global/list/breach_burn_descriptors = list( +GLOBAL_LIST_INIT(breach_burn_descriptors, list( "small burn", "melted patch", "sizable burn", "large scorched area", "huge scorched area" - ) + )) /datum/breach/proc/update_descriptor() @@ -47,9 +47,9 @@ var/global/list/breach_burn_descriptors = list( class = max(1,min(class,5)) //Apply the correct descriptor. if(damtype == BURN) - descriptor = breach_burn_descriptors[class] + descriptor = GLOB.breach_burn_descriptors[class] else if(damtype == BRUTE) - descriptor = breach_brute_descriptors[class] + descriptor = GLOB.breach_brute_descriptors[class] //Repair a certain amount of brute or burn damage to the suit. /obj/item/clothing/suit/space/proc/repair_breaches(var/damtype, var/amount, var/mob/user) diff --git a/code/modules/clothing/spacesuits/chronosuit.dm b/code/modules/clothing/spacesuits/chronosuit.dm index 4f6468fb9b7..94773ceaa31 100644 --- a/code/modules/clothing/spacesuits/chronosuit.dm +++ b/code/modules/clothing/spacesuits/chronosuit.dm @@ -95,7 +95,7 @@ phaseanim.loc = to_turf sleep(7) if(holder) - if(user && user in holder.contents) + if(user && (user in holder.contents)) user.loc = to_turf if(user.client) if(camera) diff --git a/code/modules/clothing/spacesuits/ert.dm b/code/modules/clothing/spacesuits/ert.dm index a97752a29db..c412bb40f7d 100644 --- a/code/modules/clothing/spacesuits/ert.dm +++ b/code/modules/clothing/spacesuits/ert.dm @@ -22,7 +22,7 @@ else camera = new /obj/machinery/camera(src) camera.network = list("ERT") - cameranet.removeCamera(camera) + GLOB.cameranet.removeCamera(camera) camera.c_tag = user.name to_chat(user, "User scanned as [camera.c_tag]. Camera activated.") @@ -246,4 +246,4 @@ icon_state = "hardsuit-berserker" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/berserker armor = list(melee = 65, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 100, rad = 100, fire = 80, acid = 80) - slowdown = 0 \ No newline at end of file + slowdown = 0 diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index f9366e12778..5a01c9f6f55 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -1,590 +1,590 @@ -//Baseline hardsuits -/obj/item/clothing/head/helmet/space/hardsuit - name = "hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-pressure environment." - icon_state = "hardsuit0-engineering" - item_state = "eng_helm" - max_integrity = 300 - armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) - var/basestate = "hardsuit" - allowed = list(/obj/item/flashlight) - var/brightness_on = 4 //luminosity when on - var/on = FALSE - var/obj/item/clothing/suit/space/hardsuit/suit - item_color = "engineering" //Determines used sprites: hardsuit[on]-[color] and hardsuit[on]-[color]2 (lying down sprite) - actions_types = list(/datum/action/item_action/toggle_helmet_light) - - //Species-specific stuff. - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Skrell" = 'icons/mob/species/skrell/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Drask" = 'icons/mob/species/drask/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - sprite_sheets_obj = list( - "Unathi" = 'icons/obj/clothing/species/unathi/hats.dmi', - "Tajaran" = 'icons/obj/clothing/species/tajaran/hats.dmi', - "Skrell" = 'icons/obj/clothing/species/skrell/hats.dmi', - "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' - ) - -/obj/item/clothing/head/helmet/space/hardsuit/attack_self(mob/user) - toggle_light(user) - -/obj/item/clothing/head/helmet/space/hardsuit/proc/toggle_light(mob/user) - on = !on - icon_state = "[basestate][on]-[item_color]" - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_head() - - if(on) - set_light(brightness_on) - else - set_light(0) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/head/helmet/space/hardsuit/extinguish_light() - if(on) - toggle_light() - visible_message("[src]'s light fades and turns off.") - -/obj/item/clothing/head/helmet/space/hardsuit/dropped(mob/user) - ..() - if(suit) - suit.RemoveHelmet() - -/obj/item/clothing/head/helmet/space/hardsuit/item_action_slot_check(slot) - if(slot == slot_head) - return 1 - -/obj/item/clothing/head/helmet/space/hardsuit/equipped(mob/user, slot) - ..() - if(slot != slot_head) - if(suit) - suit.RemoveHelmet() - else - qdel(src) - -/obj/item/clothing/head/helmet/space/hardsuit/proc/display_visor_message(var/msg) - var/mob/wearer = loc - if(msg && ishuman(wearer)) - wearer.show_message("[msg]", 1) - -/obj/item/clothing/head/helmet/space/hardsuit/emp_act(severity) - ..() - display_visor_message("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!") - -/obj/item/clothing/suit/space/hardsuit - name = "hardsuit" - desc = "A special space suit for environments that might pose hazards beyond just the vacuum of space. Provides more protection than a standard space suit." - icon_state = "hardsuit-engineering" - item_state = "eng_hardsuit" - max_integrity = 300 - armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd) - siemens_coefficient = 0 - var/obj/item/clothing/head/helmet/space/hardsuit/helmet - actions_types = list(/datum/action/item_action/toggle_helmet) - var/helmettype = /obj/item/clothing/head/helmet/space/hardsuit - var/obj/item/tank/jetpack/suit/jetpack = null - - hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran") - species_restricted = list("exclude", "Wryn") - sprite_sheets = list( - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Skrell" = 'icons/mob/species/skrell/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Drask" = 'icons/mob/species/drask/suit.dmi' - ) - sprite_sheets_obj = list( - "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', - "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', - "Skrell" = 'icons/obj/clothing/species/skrell/suits.dmi', - "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' - ) - -/obj/item/clothing/suit/space/hardsuit/New() - if(jetpack && ispath(jetpack)) - jetpack = new jetpack(src) - ..() - -/obj/item/clothing/suit/space/hardsuit/attack_self(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - ..() - -/obj/item/clothing/suit/space/hardsuit/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/tank/jetpack/suit)) - if(jetpack) - to_chat(user, "[src] already has a jetpack installed.") - return - if(src == user.get_item_by_slot(slot_wear_suit)) //Make sure the player is not wearing the suit before applying the upgrade. - to_chat(user, "You cannot install the upgrade to [src] while wearing it.") - return - - if(user.unEquip(I)) - I.forceMove(src) - jetpack = I - to_chat(user, "You successfully install the jetpack into [src].") - return - return ..() - -/obj/item/clothing/suit/space/hardsuit/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!jetpack) - to_chat(user, "[src] has no jetpack installed.") - return - if(src == user.get_item_by_slot(slot_wear_suit)) - to_chat(user, "You cannot remove the jetpack from [src] while wearing it.") - return - jetpack.turn_off(user) - jetpack.forceMove(drop_location()) - jetpack = null - to_chat(user, "You successfully remove the jetpack from [src].") - -/obj/item/clothing/suit/space/hardsuit/equipped(mob/user, slot) - ..() - if(jetpack) - if(slot == slot_wear_suit) - for(var/X in jetpack.actions) - var/datum/action/A = X - A.Grant(user) - -/obj/item/clothing/suit/space/hardsuit/dropped(mob/user) - ..() - if(jetpack) - for(var/X in jetpack.actions) - var/datum/action/A = X - A.Remove(user) - -/obj/item/clothing/suit/space/hardsuit/item_action_slot_check(slot) - if(slot == slot_wear_suit) //we only give the mob the ability to toggle the helmet if he's wearing the hardsuit. - return 1 - -//Engineering hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/engine - name = "engineering hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." - icon_state = "hardsuit0-engineering" - item_state = "eng_helm" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) - resistance_flags = FIRE_PROOF - item_color = "engineering" - -/obj/item/clothing/suit/space/hardsuit/engine - name = "engineering hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has radiation shielding." - icon_state = "hardsuit-engineering" - item_state = "eng_hardsuit" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine - dog_fashion = /datum/dog_fashion/back/hardsuit - resistance_flags = FIRE_PROOF - -//Atmospherics -/obj/item/clothing/head/helmet/space/hardsuit/engine/atmos - name = "atmospherics hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has thermal shielding." - icon_state = "hardsuit0-atmos" - item_state = "atmos_helm" - item_color = "atmos" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) - heat_protection = HEAD //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - -/obj/item/clothing/suit/space/hardsuit/engine/atmos - name = "atmospherics hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has thermal shielding." - icon_state = "hardsuit-atmos" - item_state = "atmos_hardsuit" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/atmos - dog_fashion = null - -//Chief Engineer's hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/engine/elite - name = "advanced hardsuit helmet" - desc = "An advanced helmet designed for work in a hazardous, low pressure environment. Shines with a high polish." - icon_state = "hardsuit0-white" - item_state = "ce_helm" - item_color = "white" - armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90) - heat_protection = HEAD //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - -/obj/item/clothing/suit/space/hardsuit/engine/elite - icon_state = "hardsuit-white" - name = "advanced hardsuit" - desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish." - item_state = "ce_hardsuit" - armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90) - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite - jetpack = /obj/item/tank/jetpack/suit - dog_fashion = null - -//Mining hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/mining - name = "mining hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Has reinforced plating." - icon_state = "hardsuit0-mining" - item_state = "mining_helm" - item_color = "mining" - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF - heat_protection = HEAD - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) - brightness_on = 7 - -/obj/item/clothing/suit/space/hardsuit/mining - icon_state = "hardsuit-mining" - name = "mining hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating." - item_state = "mining_hardsuit" - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/storage/bag/ore, /obj/item/pickaxe, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/mining - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - -//Syndicate hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/syndi - name = "blood-red hardsuit helmet" - desc = "A dual-mode advanced helmet designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." - alt_desc = "A dual-mode advanced helmet designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." - icon_state = "hardsuit1-syndi" - item_state = "syndie_helm" - item_color = "syndi" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) - on = 1 - var/obj/item/clothing/suit/space/hardsuit/syndi/linkedsuit = null - actions_types = list(/datum/action/item_action/toggle_helmet_mode) - visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDETAIL - visor_flags = STOPSPRESSUREDMAGE - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/update_icon() - icon_state = "hardsuit[on]-[item_color]" - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/New() - ..() - if(istype(loc, /obj/item/clothing/suit/space/hardsuit/syndi)) - linkedsuit = loc - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/attack_self(mob/user) //Toggle Helmet - if(!isturf(user.loc)) - to_chat(user, "You cannot toggle your helmet while in this [user.loc]!" ) - return - on = !on - if(on) - to_chat(user, "You switch your hardsuit to EVA mode, sacrificing speed for space protection.") - name = initial(name) - desc = initial(desc) - set_light(brightness_on) - flags |= visor_flags - flags_cover |= HEADCOVERSEYES | HEADCOVERSMOUTH - flags_inv |= visor_flags_inv - cold_protection |= HEAD - else - to_chat(user, "You switch your hardsuit to combat mode and can now run at full speed.") - name += " (combat)" - desc = alt_desc - set_light(0) - flags &= ~visor_flags - flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) - flags_inv &= ~visor_flags_inv - cold_protection &= ~HEAD - update_icon() - playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1) - toggle_hardsuit_mode(user) - user.update_inv_head() - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.head_update(src, forced = 1) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/proc/toggle_hardsuit_mode(mob/user) //Helmet Toggles Suit Mode - if(linkedsuit) - if(on) - linkedsuit.name = initial(linkedsuit.name) - linkedsuit.desc = initial(linkedsuit.desc) - linkedsuit.slowdown = 1 - linkedsuit.flags |= STOPSPRESSUREDMAGE - linkedsuit.cold_protection |= UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - else - linkedsuit.name += " (combat)" - linkedsuit.desc = linkedsuit.alt_desc - linkedsuit.slowdown = 0 - linkedsuit.flags &= ~STOPSPRESSUREDMAGE - linkedsuit.cold_protection &= ~(UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS) - - linkedsuit.update_icon() - user.update_inv_wear_suit() - user.update_inv_w_uniform() - -/obj/item/clothing/suit/space/hardsuit/syndi - name = "blood-red hardsuit" - desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." - alt_desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." - icon_state = "hardsuit1-syndi" - item_state = "syndie_hardsuit" - item_color = "syndi" - w_class = WEIGHT_CLASS_NORMAL - var/on = 1 - actions_types = list(/datum/action/item_action/toggle_hardsuit_mode) - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) - allowed = list(/obj/item/gun, /obj/item/ammo_box,/obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/energy/sword, /obj/item/restraints/handcuffs, /obj/item/tank) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi - jetpack = /obj/item/tank/jetpack/suit - -/obj/item/clothing/suit/space/hardsuit/syndi/update_icon() - icon_state = "hardsuit[on]-[item_color]" - -//Elite Syndie suit -/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite - name = "elite syndicate hardsuit helmet" - desc = "An elite version of the syndicate helmet, with improved armour and fire shielding. It is in travel mode. Property of Gorlex Marauders." - icon_state = "hardsuit0-syndielite" - item_color = "syndielite" - armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) - heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/suit/space/hardsuit/syndi/elite - name = "elite syndicate hardsuit" - desc = "An elite version of the syndicate hardsuit, with improved armour and fire shielding. It is in travel mode." - icon_state = "hardsuit0-syndielite" - item_color = "syndielite" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite - armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - -//Strike team hardsuits -/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst - armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) //Almost as good as DS gear, but unlike DS can switch to combat for mobility - icon_state = "hardsuit0-sst" - item_color = "sst" - -/obj/item/clothing/suit/space/hardsuit/syndi/elite/sst - armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) - icon_state = "hardsuit0-sst" - item_color = "sst" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst - -/obj/item/clothing/suit/space/hardsuit/syndi/freedom - name = "eagle suit" - desc = "An advanced, light suit, fabricated from a mixture of synthetic feathers and space-resistant material. A gun holster appears to be integrated into the suit." - icon_state = "freedom" - item_state = "freedom" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom - sprite_sheets = null - -/obj/item/clothing/suit/space/hardsuit/syndi/freedom/update_icon() - return - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom - name = "eagle helmet" - desc = "An advanced, space-proof helmet. It appears to be modeled after an old-world eagle." - icon_state = "griffinhat" - item_state = "griffinhat" - sprite_sheets = null - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom/update_icon() - return - -//Medical hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/medical - name = "medical hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort, but does not protect the eyes from intense light." - icon_state = "hardsuit0-medical" - item_state = "medical_helm" - item_color = "medical" - flash_protect = 0 - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - scan_reagents = 1 //Generally worn by the CMO, so they'd get utility off of seeing reagents - -/obj/item/clothing/suit/space/hardsuit/medical - icon_state = "hardsuit-medical" - name = "medical hardsuit" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort." - item_state = "medical_hardsuit" - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/firstaid,/obj/item/healthanalyzer,/obj/item/stack/medical,/obj/item/rad_laser) - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/medical - slowdown = 0.5 - - //Security -/obj/item/clothing/head/helmet/space/hardsuit/security - name = "security hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." - icon_state = "hardsuit0-sec" - item_state = "sec_helm" - item_color = "sec" - armor = list("melee" = 35, "bullet" = 15, "laser" = 30,"energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) - -/obj/item/clothing/suit/space/hardsuit/security - icon_state = "hardsuit-sec" - name = "security hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has an additional layer of armor." - item_state = "sec_hardsuit" - armor = list("melee" = 35, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) - allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/restraints/handcuffs) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security - -/obj/item/clothing/head/helmet/space/hardsuit/security/hos - name = "head of security's hardsuit helmet" - desc = "A special bulky helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." - icon_state = "hardsuit0-hos" - item_color = "hos" - armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) - -/obj/item/clothing/suit/space/hardsuit/security/hos - name = "head of security's hardsuit" - desc = "A special bulky suit that protects against hazardous, low pressure environments. Has an additional layer of armor." - icon_state = "hardsuit-hos" - armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security/hos - jetpack = /obj/item/tank/jetpack/suit - - -//Singuloth armor -/obj/item/clothing/head/helmet/space/hardsuit/singuloth - name = "singuloth knight's helmet" - desc = "This is an adamantium helmet from the chapter of the Singuloth Knights. It shines with a holy aura." - icon_state = "hardsuit0-singuloth" - item_state = "singuloth_helm" - item_color = "singuloth" - armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) - sprite_sheets = null - -/obj/item/clothing/suit/space/hardsuit/singuloth - icon_state = "hardsuit-singuloth" - name = "singuloth knight's armor" - desc = "This is a ceremonial armor from the chapter of the Singuloth Knights. It's made of pure forged adamantium." - item_state = "singuloth_hardsuit" - flags = STOPSPRESSUREDMAGE - armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/singuloth - sprite_sheets = null - - -/////////////SHIELDED////////////////////////////////// - -/obj/item/clothing/suit/space/hardsuit/shielded - name = "shielded hardsuit" - desc = "A hardsuit with built in energy shielding. Will rapidly recharge when not under fire." - icon_state = "hardsuit-hos" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded - allowed = list(/obj/item/flashlight,/obj/item/tank, /obj/item/gun,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs) - armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - var/current_charges = 3 - var/max_charges = 3 //How many charges total the shielding has - var/recharge_delay = 200 //How long after we've been shot before we can start recharging. 20 seconds here - var/recharge_cooldown = 0 //Time since we've last been shot - var/recharge_rate = 1 //How quickly the shield recharges once it starts charging - var/shield_state = "shield-old" - var/shield_on = "shield-old" - -/obj/item/clothing/suit/space/hardsuit/shielded/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - recharge_cooldown = world.time + recharge_delay - if(current_charges > 0) - do_sparks(2, 1, src) - owner.visible_message("[owner]'s shields deflect [attack_text] in a shower of sparks!") - current_charges-- - if(recharge_rate) - START_PROCESSING(SSobj, src) - if(current_charges <= 0) - owner.visible_message("[owner]'s shield overloads!") - shield_state = "broken" - owner.update_inv_wear_suit() - return 1 - return 0 - - - -/obj/item/clothing/suit/space/hardsuit/shielded/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/clothing/suit/space/hardsuit/shielded/process() - if(world.time > recharge_cooldown && current_charges < max_charges) - current_charges = Clamp((current_charges + recharge_rate), 0, max_charges) - playsound(loc, 'sound/magic/charge.ogg', 50, TRUE) - if(current_charges == max_charges) - playsound(loc, 'sound/machines/ding.ogg', 50, TRUE) - STOP_PROCESSING(SSobj, src) - shield_state = "[shield_on]" - if(ishuman(loc)) - var/mob/living/carbon/human/C = loc - C.update_inv_wear_suit() - -/obj/item/clothing/suit/space/hardsuit/shielded/special_overlays() - return mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01) - -/obj/item/clothing/head/helmet/space/hardsuit/shielded - resistance_flags = FIRE_PROOF | ACID_PROOF - - -//////Syndicate Version - -/obj/item/clothing/suit/space/hardsuit/shielded/syndi - name = "blood-red hardsuit" - desc = "An advanced hardsuit with built in energy shielding." - icon_state = "hardsuit1-syndi" - item_state = "syndie_hardsuit" - item_color = "syndi" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) - slowdown = 0 - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi - jetpack = /obj/item/tank/jetpack/suit - -/obj/item/clothing/suit/space/hardsuit/shielded/syndi/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(shield_state == "broken") - to_chat(user, "You can't interface with the hardsuit's software if the shield's broken!") - return - - if(shield_state == "shield-red") - shield_state = "shield-old" - shield_on = "shield-old" - to_chat(user, "You roll back the hardsuit's software, changing the shield's color!") - - else - shield_state = "shield-red" - shield_on = "shield-red" - to_chat(user, "You update the hardsuit's hardware, changing back the shield's color to red.") - user.update_inv_wear_suit() - -/obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi - name = "blood-red hardsuit helmet" - desc = "An advanced hardsuit helmet with built in energy shielding." - icon_state = "hardsuit1-syndi" - item_state = "syndie_helm" - item_color = "syndi" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) +//Baseline hardsuits +/obj/item/clothing/head/helmet/space/hardsuit + name = "hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment." + icon_state = "hardsuit0-engineering" + item_state = "eng_helm" + max_integrity = 300 + armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) + var/basestate = "hardsuit" + allowed = list(/obj/item/flashlight) + var/brightness_on = 4 //luminosity when on + var/on = FALSE + var/obj/item/clothing/suit/space/hardsuit/suit + item_color = "engineering" //Determines used sprites: hardsuit[on]-[color] and hardsuit[on]-[color]2 (lying down sprite) + actions_types = list(/datum/action/item_action/toggle_helmet_light) + + //Species-specific stuff. + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Skrell" = 'icons/mob/species/skrell/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Drask" = 'icons/mob/species/drask/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + sprite_sheets_obj = list( + "Unathi" = 'icons/obj/clothing/species/unathi/hats.dmi', + "Tajaran" = 'icons/obj/clothing/species/tajaran/hats.dmi', + "Skrell" = 'icons/obj/clothing/species/skrell/hats.dmi', + "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' + ) + +/obj/item/clothing/head/helmet/space/hardsuit/attack_self(mob/user) + toggle_light(user) + +/obj/item/clothing/head/helmet/space/hardsuit/proc/toggle_light(mob/user) + on = !on + icon_state = "[basestate][on]-[item_color]" + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_head() + + if(on) + set_light(brightness_on) + else + set_light(0) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/head/helmet/space/hardsuit/extinguish_light() + if(on) + toggle_light() + visible_message("[src]'s light fades and turns off.") + +/obj/item/clothing/head/helmet/space/hardsuit/dropped(mob/user) + ..() + if(suit) + suit.RemoveHelmet() + +/obj/item/clothing/head/helmet/space/hardsuit/item_action_slot_check(slot) + if(slot == slot_head) + return 1 + +/obj/item/clothing/head/helmet/space/hardsuit/equipped(mob/user, slot) + ..() + if(slot != slot_head) + if(suit) + suit.RemoveHelmet() + else + qdel(src) + +/obj/item/clothing/head/helmet/space/hardsuit/proc/display_visor_message(var/msg) + var/mob/wearer = loc + if(msg && ishuman(wearer)) + wearer.show_message("[msg]", 1) + +/obj/item/clothing/head/helmet/space/hardsuit/emp_act(severity) + ..() + display_visor_message("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!") + +/obj/item/clothing/suit/space/hardsuit + name = "hardsuit" + desc = "A special space suit for environments that might pose hazards beyond just the vacuum of space. Provides more protection than a standard space suit." + icon_state = "hardsuit-engineering" + item_state = "eng_hardsuit" + max_integrity = 300 + armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd) + siemens_coefficient = 0 + var/obj/item/clothing/head/helmet/space/hardsuit/helmet + actions_types = list(/datum/action/item_action/toggle_helmet) + var/helmettype = /obj/item/clothing/head/helmet/space/hardsuit + var/obj/item/tank/jetpack/suit/jetpack = null + + hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran") + species_restricted = list("exclude", "Wryn") + sprite_sheets = list( + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Skrell" = 'icons/mob/species/skrell/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Drask" = 'icons/mob/species/drask/suit.dmi' + ) + sprite_sheets_obj = list( + "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', + "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', + "Skrell" = 'icons/obj/clothing/species/skrell/suits.dmi', + "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' + ) + +/obj/item/clothing/suit/space/hardsuit/New() + if(jetpack && ispath(jetpack)) + jetpack = new jetpack(src) + ..() + +/obj/item/clothing/suit/space/hardsuit/attack_self(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + ..() + +/obj/item/clothing/suit/space/hardsuit/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/tank/jetpack/suit)) + if(jetpack) + to_chat(user, "[src] already has a jetpack installed.") + return + if(src == user.get_item_by_slot(slot_wear_suit)) //Make sure the player is not wearing the suit before applying the upgrade. + to_chat(user, "You cannot install the upgrade to [src] while wearing it.") + return + + if(user.unEquip(I)) + I.forceMove(src) + jetpack = I + to_chat(user, "You successfully install the jetpack into [src].") + return + return ..() + +/obj/item/clothing/suit/space/hardsuit/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!jetpack) + to_chat(user, "[src] has no jetpack installed.") + return + if(src == user.get_item_by_slot(slot_wear_suit)) + to_chat(user, "You cannot remove the jetpack from [src] while wearing it.") + return + jetpack.turn_off(user) + jetpack.forceMove(drop_location()) + jetpack = null + to_chat(user, "You successfully remove the jetpack from [src].") + +/obj/item/clothing/suit/space/hardsuit/equipped(mob/user, slot) + ..() + if(jetpack) + if(slot == slot_wear_suit) + for(var/X in jetpack.actions) + var/datum/action/A = X + A.Grant(user) + +/obj/item/clothing/suit/space/hardsuit/dropped(mob/user) + ..() + if(jetpack) + for(var/X in jetpack.actions) + var/datum/action/A = X + A.Remove(user) + +/obj/item/clothing/suit/space/hardsuit/item_action_slot_check(slot) + if(slot == slot_wear_suit) //we only give the mob the ability to toggle the helmet if he's wearing the hardsuit. + return 1 + +//Engineering hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/engine + name = "engineering hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." + icon_state = "hardsuit0-engineering" + item_state = "eng_helm" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) + resistance_flags = FIRE_PROOF + item_color = "engineering" + +/obj/item/clothing/suit/space/hardsuit/engine + name = "engineering hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has radiation shielding." + icon_state = "hardsuit-engineering" + item_state = "eng_hardsuit" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine + dog_fashion = /datum/dog_fashion/back/hardsuit + resistance_flags = FIRE_PROOF + +//Atmospherics +/obj/item/clothing/head/helmet/space/hardsuit/engine/atmos + name = "atmospherics hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has thermal shielding." + icon_state = "hardsuit0-atmos" + item_state = "atmos_helm" + item_color = "atmos" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) + heat_protection = HEAD //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + +/obj/item/clothing/suit/space/hardsuit/engine/atmos + name = "atmospherics hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has thermal shielding." + icon_state = "hardsuit-atmos" + item_state = "atmos_hardsuit" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/atmos + dog_fashion = null + +//Chief Engineer's hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/engine/elite + name = "advanced hardsuit helmet" + desc = "An advanced helmet designed for work in a hazardous, low pressure environment. Shines with a high polish." + icon_state = "hardsuit0-white" + item_state = "ce_helm" + item_color = "white" + armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90) + heat_protection = HEAD //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + +/obj/item/clothing/suit/space/hardsuit/engine/elite + icon_state = "hardsuit-white" + name = "advanced hardsuit" + desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish." + item_state = "ce_hardsuit" + armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite + jetpack = /obj/item/tank/jetpack/suit + dog_fashion = null + +//Mining hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/mining + name = "mining hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Has reinforced plating." + icon_state = "hardsuit0-mining" + item_state = "mining_helm" + item_color = "mining" + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF + heat_protection = HEAD + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) + brightness_on = 7 + +/obj/item/clothing/suit/space/hardsuit/mining + icon_state = "hardsuit-mining" + name = "mining hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating." + item_state = "mining_hardsuit" + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/storage/bag/ore, /obj/item/pickaxe, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/mining + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + +//Syndicate hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/syndi + name = "blood-red hardsuit helmet" + desc = "A dual-mode advanced helmet designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." + alt_desc = "A dual-mode advanced helmet designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." + icon_state = "hardsuit1-syndi" + item_state = "syndie_helm" + item_color = "syndi" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) + on = 1 + var/obj/item/clothing/suit/space/hardsuit/syndi/linkedsuit = null + actions_types = list(/datum/action/item_action/toggle_helmet_mode) + visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDETAIL + visor_flags = STOPSPRESSUREDMAGE + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/update_icon() + icon_state = "hardsuit[on]-[item_color]" + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/New() + ..() + if(istype(loc, /obj/item/clothing/suit/space/hardsuit/syndi)) + linkedsuit = loc + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/attack_self(mob/user) //Toggle Helmet + if(!isturf(user.loc)) + to_chat(user, "You cannot toggle your helmet while in this [user.loc]!" ) + return + on = !on + if(on) + to_chat(user, "You switch your hardsuit to EVA mode, sacrificing speed for space protection.") + name = initial(name) + desc = initial(desc) + set_light(brightness_on) + flags |= visor_flags + flags_cover |= HEADCOVERSEYES | HEADCOVERSMOUTH + flags_inv |= visor_flags_inv + cold_protection |= HEAD + else + to_chat(user, "You switch your hardsuit to combat mode and can now run at full speed.") + name += " (combat)" + desc = alt_desc + set_light(0) + flags &= ~visor_flags + flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) + flags_inv &= ~visor_flags_inv + cold_protection &= ~HEAD + update_icon() + playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1) + toggle_hardsuit_mode(user) + user.update_inv_head() + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.head_update(src, forced = 1) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/proc/toggle_hardsuit_mode(mob/user) //Helmet Toggles Suit Mode + if(linkedsuit) + if(on) + linkedsuit.name = initial(linkedsuit.name) + linkedsuit.desc = initial(linkedsuit.desc) + linkedsuit.slowdown = 1 + linkedsuit.flags |= STOPSPRESSUREDMAGE + linkedsuit.cold_protection |= UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + else + linkedsuit.name += " (combat)" + linkedsuit.desc = linkedsuit.alt_desc + linkedsuit.slowdown = 0 + linkedsuit.flags &= ~STOPSPRESSUREDMAGE + linkedsuit.cold_protection &= ~(UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS) + + linkedsuit.update_icon() + user.update_inv_wear_suit() + user.update_inv_w_uniform() + +/obj/item/clothing/suit/space/hardsuit/syndi + name = "blood-red hardsuit" + desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." + alt_desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." + icon_state = "hardsuit1-syndi" + item_state = "syndie_hardsuit" + item_color = "syndi" + w_class = WEIGHT_CLASS_NORMAL + var/on = 1 + actions_types = list(/datum/action/item_action/toggle_hardsuit_mode) + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) + allowed = list(/obj/item/gun, /obj/item/ammo_box,/obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/energy/sword, /obj/item/restraints/handcuffs, /obj/item/tank) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi + jetpack = /obj/item/tank/jetpack/suit + +/obj/item/clothing/suit/space/hardsuit/syndi/update_icon() + icon_state = "hardsuit[on]-[item_color]" + +//Elite Syndie suit +/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite + name = "elite syndicate hardsuit helmet" + desc = "An elite version of the syndicate helmet, with improved armour and fire shielding. It is in travel mode. Property of Gorlex Marauders." + icon_state = "hardsuit0-syndielite" + item_color = "syndielite" + armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) + heat_protection = HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/suit/space/hardsuit/syndi/elite + name = "elite syndicate hardsuit" + desc = "An elite version of the syndicate hardsuit, with improved armour and fire shielding. It is in travel mode." + icon_state = "hardsuit0-syndielite" + item_color = "syndielite" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite + armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + +//Strike team hardsuits +/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst + armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) //Almost as good as DS gear, but unlike DS can switch to combat for mobility + icon_state = "hardsuit0-sst" + item_color = "sst" + +/obj/item/clothing/suit/space/hardsuit/syndi/elite/sst + armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) + icon_state = "hardsuit0-sst" + item_color = "sst" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst + +/obj/item/clothing/suit/space/hardsuit/syndi/freedom + name = "eagle suit" + desc = "An advanced, light suit, fabricated from a mixture of synthetic feathers and space-resistant material. A gun holster appears to be integrated into the suit." + icon_state = "freedom" + item_state = "freedom" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom + sprite_sheets = null + +/obj/item/clothing/suit/space/hardsuit/syndi/freedom/update_icon() + return + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom + name = "eagle helmet" + desc = "An advanced, space-proof helmet. It appears to be modeled after an old-world eagle." + icon_state = "griffinhat" + item_state = "griffinhat" + sprite_sheets = null + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom/update_icon() + return + +//Medical hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/medical + name = "medical hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort, but does not protect the eyes from intense light." + icon_state = "hardsuit0-medical" + item_state = "medical_helm" + item_color = "medical" + flash_protect = 0 + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + scan_reagents = 1 //Generally worn by the CMO, so they'd get utility off of seeing reagents + +/obj/item/clothing/suit/space/hardsuit/medical + icon_state = "hardsuit-medical" + name = "medical hardsuit" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort." + item_state = "medical_hardsuit" + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/firstaid,/obj/item/healthanalyzer,/obj/item/stack/medical,/obj/item/rad_laser) + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/medical + slowdown = 0.5 + + //Security +/obj/item/clothing/head/helmet/space/hardsuit/security + name = "security hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." + icon_state = "hardsuit0-sec" + item_state = "sec_helm" + item_color = "sec" + armor = list("melee" = 35, "bullet" = 15, "laser" = 30,"energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) + +/obj/item/clothing/suit/space/hardsuit/security + icon_state = "hardsuit-sec" + name = "security hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has an additional layer of armor." + item_state = "sec_hardsuit" + armor = list("melee" = 35, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) + allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/restraints/handcuffs) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security + +/obj/item/clothing/head/helmet/space/hardsuit/security/hos + name = "head of security's hardsuit helmet" + desc = "A special bulky helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." + icon_state = "hardsuit0-hos" + item_color = "hos" + armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) + +/obj/item/clothing/suit/space/hardsuit/security/hos + name = "head of security's hardsuit" + desc = "A special bulky suit that protects against hazardous, low pressure environments. Has an additional layer of armor." + icon_state = "hardsuit-hos" + armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security/hos + jetpack = /obj/item/tank/jetpack/suit + + +//Singuloth armor +/obj/item/clothing/head/helmet/space/hardsuit/singuloth + name = "singuloth knight's helmet" + desc = "This is an adamantium helmet from the chapter of the Singuloth Knights. It shines with a holy aura." + icon_state = "hardsuit0-singuloth" + item_state = "singuloth_helm" + item_color = "singuloth" + armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) + sprite_sheets = null + +/obj/item/clothing/suit/space/hardsuit/singuloth + icon_state = "hardsuit-singuloth" + name = "singuloth knight's armor" + desc = "This is a ceremonial armor from the chapter of the Singuloth Knights. It's made of pure forged adamantium." + item_state = "singuloth_hardsuit" + flags = STOPSPRESSUREDMAGE + armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/singuloth + sprite_sheets = null + + +/////////////SHIELDED////////////////////////////////// + +/obj/item/clothing/suit/space/hardsuit/shielded + name = "shielded hardsuit" + desc = "A hardsuit with built in energy shielding. Will rapidly recharge when not under fire." + icon_state = "hardsuit-hos" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded + allowed = list(/obj/item/flashlight,/obj/item/tank, /obj/item/gun,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs) + armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + var/current_charges = 3 + var/max_charges = 3 //How many charges total the shielding has + var/recharge_delay = 200 //How long after we've been shot before we can start recharging. 20 seconds here + var/recharge_cooldown = 0 //Time since we've last been shot + var/recharge_rate = 1 //How quickly the shield recharges once it starts charging + var/shield_state = "shield-old" + var/shield_on = "shield-old" + +/obj/item/clothing/suit/space/hardsuit/shielded/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + recharge_cooldown = world.time + recharge_delay + if(current_charges > 0) + do_sparks(2, 1, src) + owner.visible_message("[owner]'s shields deflect [attack_text] in a shower of sparks!") + current_charges-- + if(recharge_rate) + START_PROCESSING(SSobj, src) + if(current_charges <= 0) + owner.visible_message("[owner]'s shield overloads!") + shield_state = "broken" + owner.update_inv_wear_suit() + return 1 + return 0 + + + +/obj/item/clothing/suit/space/hardsuit/shielded/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/clothing/suit/space/hardsuit/shielded/process() + if(world.time > recharge_cooldown && current_charges < max_charges) + current_charges = Clamp((current_charges + recharge_rate), 0, max_charges) + playsound(loc, 'sound/magic/charge.ogg', 50, TRUE) + if(current_charges == max_charges) + playsound(loc, 'sound/machines/ding.ogg', 50, TRUE) + STOP_PROCESSING(SSobj, src) + shield_state = "[shield_on]" + if(ishuman(loc)) + var/mob/living/carbon/human/C = loc + C.update_inv_wear_suit() + +/obj/item/clothing/suit/space/hardsuit/shielded/special_overlays() + return mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01) + +/obj/item/clothing/head/helmet/space/hardsuit/shielded + resistance_flags = FIRE_PROOF | ACID_PROOF + + +//////Syndicate Version + +/obj/item/clothing/suit/space/hardsuit/shielded/syndi + name = "blood-red hardsuit" + desc = "An advanced hardsuit with built in energy shielding." + icon_state = "hardsuit1-syndi" + item_state = "syndie_hardsuit" + item_color = "syndi" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) + slowdown = 0 + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi + jetpack = /obj/item/tank/jetpack/suit + +/obj/item/clothing/suit/space/hardsuit/shielded/syndi/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(shield_state == "broken") + to_chat(user, "You can't interface with the hardsuit's software if the shield's broken!") + return + + if(shield_state == "shield-red") + shield_state = "shield-old" + shield_on = "shield-old" + to_chat(user, "You roll back the hardsuit's software, changing the shield's color!") + + else + shield_state = "shield-red" + shield_on = "shield-red" + to_chat(user, "You update the hardsuit's hardware, changing back the shield's color to red.") + user.update_inv_wear_suit() + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi + name = "blood-red hardsuit helmet" + desc = "An advanced hardsuit helmet with built in energy shielding." + icon_state = "hardsuit1-syndi" + item_state = "syndie_helm" + item_color = "syndi" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index f516627e182..158aea81f7f 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -1,287 +1,286 @@ - //Captain's space suit, not hardsuits because no flashlight! -/obj/item/clothing/head/helmet/space/capspace - name = "captain's space helmet" - icon_state = "capspace" - item_state = "capspacehelmet" - desc = "A special helmet designed for only the most fashionable of military figureheads." - flags_inv = HIDEFACE - permeability_coefficient = 0.01 - armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - -/obj/item/clothing/head/helmet/space/capspace/equipped(mob/living/carbon/human/user, slot) - if(ishuman(user) && slot == slot_head) - if(isvox(user)) - if(flags & BLOCKHAIR) - flags &= ~BLOCKHAIR - else - if((initial(flags) & BLOCKHAIR) && !(flags & BLOCKHAIR)) - flags |= BLOCKHAIR - -/obj/item/clothing/suit/space/captain - name = "captain's space suit" - desc = "A bulky, heavy-duty piece of exclusive Nanotrasen armor. YOU are in charge!" - icon_state = "caparmor" - item_state = "capspacesuit" - w_class = WEIGHT_CLASS_BULKY - allowed = list(/obj/item/tank, /obj/item/flashlight,/obj/item/gun/energy, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton,/obj/item/restraints/handcuffs) - armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - - //Deathsquad space suit, not hardsuits because no flashlight! -/obj/item/clothing/head/helmet/space/deathsquad - name = "deathsquad helmet" - desc = "That's not red paint. That's real blood." - icon_state = "deathsquad" - item_state = "deathsquad" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - see_in_dark = 8 - HUDType = MEDHUD - strip_delay = 130 - -/obj/item/clothing/suit/space/deathsquad - name = "deathsquad suit" - desc = "A heavily armored, advanced space suit that protects against most forms of damage." - icon_state = "deathsquad" - item_state = "swat_suit" - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - strip_delay = 130 - dog_fashion = /datum/dog_fashion/back/deathsquad - - //NEW SWAT suit -/obj/item/clothing/suit/space/swat - name = "SWAT armor" - desc = "Space-proof tactical SWAT armor." - icon_state = "heavy" - item_state = "swat_suit" - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 100, "acid" = 100) - strip_delay = 120 - resistance_flags = FIRE_PROOF | ACID_PROOF - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/head/helmet/space/deathsquad/beret - name = "officer's beret" - desc = "An armored beret commonly used by special operations officers." - icon_state = "beret_officer" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - -/obj/item/clothing/suit/space/deathsquad/officer - name = "officer jacket" - desc = "An armored jacket used in special operations." - icon_state = "detective" - item_state = "det_suit" - blood_overlay_type = "coat" - flags_inv = 0 - slowdown = 0 - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - w_class = WEIGHT_CLASS_NORMAL - -//Space santa outfit suit -/obj/item/clothing/head/helmet/space/santahat - name = "Santa's hat" - desc = "Ho ho ho. Merrry X-mas!" - icon_state = "santahat" - - sprite_sheets = list( - "Grey" = 'icons/mob/species/Grey/head.dmi', - "Drask" = 'icons/mob/species/Drask/helmet.dmi' - ) - flags = BLOCKHAIR | STOPSPRESSUREDMAGE - flags_cover = HEADCOVERSEYES - dog_fashion = /datum/dog_fashion/head/santa - -/obj/item/clothing/head/helmet/space/santahat/attack_self(mob/user as mob) - if(src.icon_state == "santahat") - src.icon_state = "santahat_beard" - src.item_state = "santahat_beard" - to_chat(user, "Santa's beard expands out from the hat!") - else - src.icon_state = "santahat" - src.item_state = "santahat" - to_chat(user, "The beard slinks back into the hat...") - -/obj/item/clothing/suit/space/santa - name = "Santa's suit" - desc = "Festive!" - icon_state = "santa" - item_state = "santa" - slowdown = 0 - flags = STOPSPRESSUREDMAGE - flags_size = ONESIZEFITSALL - allowed = list(/obj/item) //for stuffing extra special presents - -//Space pirate outfit -/obj/item/clothing/head/helmet/space/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) - flags = BLOCKHAIR | STOPSPRESSUREDMAGE - flags_cover = HEADCOVERSEYES - strip_delay = 40 - put_on_delay = 20 - -/obj/item/clothing/suit/space/pirate - name = "pirate coat" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank) - slowdown = 0 - armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) - strip_delay = 40 - put_on_delay = 20 - -//Paramedic EVA suit -/obj/item/clothing/head/helmet/space/eva/paramedic - name = "Paramedic EVA helmet" - desc = "A brand new paramedic EVA helmet. It seems to mold to your head shape. Used for retrieving bodies in space." - icon_state = "paramedic-eva-helmet" - item_state = "paramedic-eva-helmet" - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi', - "Skrell" = 'icons/mob/species/skrell/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Drask" = 'icons/mob/species/drask/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - ) - sprite_sheets_obj = list( - "Vox" = 'icons/obj/clothing/species/vox/hats.dmi' - ) - -/obj/item/clothing/suit/space/eva/paramedic - name = "Paramedic EVA suit" - icon_state = "paramedic-eva" - item_state = "paramedic-eva" - desc = "A brand new paramedic EVA suit. The nitrile seems a bit too thin to be space proof. Used for retrieving bodies in space." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Skrell" = 'icons/mob/species/skrell/suit.dmi', - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Drask" = 'icons/mob/species/drask/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - ) - sprite_sheets_obj = list( - "Vox" = 'icons/obj/clothing/species/vox/suits.dmi' - ) - -/obj/item/clothing/suit/space/eva - name = "EVA suit" - icon_state = "spacenew" - item_state = "s_suit" - desc = "A lightweight space suit with the basic ability to protect the wearer from the vacuum of space during emergencies." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - ) - sprite_sheets_obj = list( - "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', - "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', - "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' - ) - -/obj/item/clothing/head/helmet/space/eva - name = "EVA helmet" - icon_state = "spacenew" - item_state = "s_helmet" - desc = "A lightweight space helmet with the basic ability to protect the wearer from the vacuum of space during emergencies." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - flash_protect = 0 - species_restricted = list("exclude", "Wryn") - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - sprite_sheets_obj = list( - "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' - ) - -//Mime's Hardsuit -/obj/item/clothing/head/helmet/space/eva/mime - name = "mime eva helmet" -// icon = 'spaceciv.dmi' - desc = ". . ." - icon_state = "spacemimehelmet" - item_state = "spacemimehelmet" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi') - sprite_sheets_obj = null - -/obj/item/clothing/suit/space/eva/mime - name = "mime eva suit" -// icon = 'spaceciv.dmi' - desc = ". . ." - icon_state = "spacemime_suit" - item_state = "spacemime_items" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi') - sprite_sheets_obj = null - -/obj/item/clothing/head/helmet/space/eva/clown - name = "clown eva helmet" -// icon = 'spaceciv.dmi' - desc = "An EVA helmet specifically designed for the clown. SPESSHONK!" - icon_state = "clownhelmet" - item_state = "clownhelmet" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi') - sprite_sheets_obj = null - -/obj/item/clothing/suit/space/eva/clown - name = "clown eva suit" -// icon = 'spaceciv.dmi' - desc = "An EVA suit specifically designed for the clown. SPESSHONK!" - icon_state = "spaceclown_suit" - item_state = "spaceclown_items" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi') - sprite_sheets_obj = null + //Captain's space suit, not hardsuits because no flashlight! +/obj/item/clothing/head/helmet/space/capspace + name = "captain's space helmet" + icon_state = "capspace" + item_state = "capspacehelmet" + desc = "A special helmet designed for only the most fashionable of military figureheads." + flags_inv = HIDEFACE + permeability_coefficient = 0.01 + armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + +/obj/item/clothing/head/helmet/space/capspace/equipped(mob/living/carbon/human/user, slot) + if(ishuman(user) && slot == slot_head) + if(isvox(user)) + if(flags & BLOCKHAIR) + flags &= ~BLOCKHAIR + else + if((initial(flags) & BLOCKHAIR) && !(flags & BLOCKHAIR)) + flags |= BLOCKHAIR + +/obj/item/clothing/suit/space/captain + name = "captain's space suit" + desc = "A bulky, heavy-duty piece of exclusive Nanotrasen armor. YOU are in charge!" + icon_state = "caparmor" + item_state = "capspacesuit" + w_class = WEIGHT_CLASS_BULKY + allowed = list(/obj/item/tank, /obj/item/flashlight,/obj/item/gun/energy, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton,/obj/item/restraints/handcuffs) + armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + + //Deathsquad space suit, not hardsuits because no flashlight! +/obj/item/clothing/head/helmet/space/deathsquad + name = "deathsquad helmet" + desc = "That's not red paint. That's real blood." + icon_state = "deathsquad" + item_state = "deathsquad" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + see_in_dark = 8 + HUDType = MEDHUD + strip_delay = 130 + +/obj/item/clothing/suit/space/deathsquad + name = "deathsquad suit" + desc = "A heavily armored, advanced space suit that protects against most forms of damage." + icon_state = "deathsquad" + item_state = "swat_suit" + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + strip_delay = 130 + dog_fashion = /datum/dog_fashion/back/deathsquad + + //NEW SWAT suit +/obj/item/clothing/suit/space/swat + name = "SWAT armor" + desc = "Space-proof tactical SWAT armor." + icon_state = "heavy" + item_state = "swat_suit" + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 100, "acid" = 100) + strip_delay = 120 + resistance_flags = FIRE_PROOF | ACID_PROOF + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/head/helmet/space/deathsquad/beret + name = "officer's beret" + desc = "An armored beret commonly used by special operations officers." + icon_state = "beret_officer" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + +/obj/item/clothing/suit/space/deathsquad/officer + name = "officer jacket" + desc = "An armored jacket used in special operations." + icon_state = "detective" + item_state = "det_suit" + blood_overlay_type = "coat" + flags_inv = 0 + slowdown = 0 + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + w_class = WEIGHT_CLASS_NORMAL + +//Space santa outfit suit +/obj/item/clothing/head/helmet/space/santahat + name = "Santa's hat" + desc = "Ho ho ho. Merrry X-mas!" + icon_state = "santahat" + + sprite_sheets = list( + "Grey" = 'icons/mob/species/Grey/head.dmi', + "Drask" = 'icons/mob/species/Drask/helmet.dmi' + ) + flags = BLOCKHAIR | STOPSPRESSUREDMAGE + flags_cover = HEADCOVERSEYES + dog_fashion = /datum/dog_fashion/head/santa + +/obj/item/clothing/head/helmet/space/santahat/attack_self(mob/user as mob) + if(src.icon_state == "santahat") + src.icon_state = "santahat_beard" + src.item_state = "santahat_beard" + to_chat(user, "Santa's beard expands out from the hat!") + else + src.icon_state = "santahat" + src.item_state = "santahat" + to_chat(user, "The beard slinks back into the hat...") + +/obj/item/clothing/suit/space/santa + name = "Santa's suit" + desc = "Festive!" + icon_state = "santa" + item_state = "santa" + slowdown = 0 + flags = STOPSPRESSUREDMAGE + allowed = list(/obj/item) //for stuffing extra special presents + +//Space pirate outfit +/obj/item/clothing/head/helmet/space/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) + flags = BLOCKHAIR | STOPSPRESSUREDMAGE + flags_cover = HEADCOVERSEYES + strip_delay = 40 + put_on_delay = 20 + +/obj/item/clothing/suit/space/pirate + name = "pirate coat" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank) + slowdown = 0 + armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) + strip_delay = 40 + put_on_delay = 20 + +//Paramedic EVA suit +/obj/item/clothing/head/helmet/space/eva/paramedic + name = "Paramedic EVA helmet" + desc = "A brand new paramedic EVA helmet. It seems to mold to your head shape. Used for retrieving bodies in space." + icon_state = "paramedic-eva-helmet" + item_state = "paramedic-eva-helmet" + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi', + "Skrell" = 'icons/mob/species/skrell/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Drask" = 'icons/mob/species/drask/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + ) + sprite_sheets_obj = list( + "Vox" = 'icons/obj/clothing/species/vox/hats.dmi' + ) + +/obj/item/clothing/suit/space/eva/paramedic + name = "Paramedic EVA suit" + icon_state = "paramedic-eva" + item_state = "paramedic-eva" + desc = "A brand new paramedic EVA suit. The nitrile seems a bit too thin to be space proof. Used for retrieving bodies in space." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Skrell" = 'icons/mob/species/skrell/suit.dmi', + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Drask" = 'icons/mob/species/drask/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + ) + sprite_sheets_obj = list( + "Vox" = 'icons/obj/clothing/species/vox/suits.dmi' + ) + +/obj/item/clothing/suit/space/eva + name = "EVA suit" + icon_state = "spacenew" + item_state = "s_suit" + desc = "A lightweight space suit with the basic ability to protect the wearer from the vacuum of space during emergencies." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + ) + sprite_sheets_obj = list( + "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', + "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', + "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' + ) + +/obj/item/clothing/head/helmet/space/eva + name = "EVA helmet" + icon_state = "spacenew" + item_state = "s_helmet" + desc = "A lightweight space helmet with the basic ability to protect the wearer from the vacuum of space during emergencies." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + flash_protect = 0 + species_restricted = list("exclude", "Wryn") + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + sprite_sheets_obj = list( + "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' + ) + +//Mime's Hardsuit +/obj/item/clothing/head/helmet/space/eva/mime + name = "mime eva helmet" +// icon = 'spaceciv.dmi' + desc = ". . ." + icon_state = "spacemimehelmet" + item_state = "spacemimehelmet" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi') + sprite_sheets_obj = null + +/obj/item/clothing/suit/space/eva/mime + name = "mime eva suit" +// icon = 'spaceciv.dmi' + desc = ". . ." + icon_state = "spacemime_suit" + item_state = "spacemime_items" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi') + sprite_sheets_obj = null + +/obj/item/clothing/head/helmet/space/eva/clown + name = "clown eva helmet" +// icon = 'spaceciv.dmi' + desc = "An EVA helmet specifically designed for the clown. SPESSHONK!" + icon_state = "clownhelmet" + item_state = "clownhelmet" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi') + sprite_sheets_obj = null + +/obj/item/clothing/suit/space/eva/clown + name = "clown eva suit" +// icon = 'spaceciv.dmi' + desc = "An EVA suit specifically designed for the clown. SPESSHONK!" + icon_state = "spaceclown_suit" + item_state = "spaceclown_items" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi') + sprite_sheets_obj = null diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index bd35abb19a6..49d3fd5fb53 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -68,7 +68,7 @@ on = !on icon_state = "[initial(icon_state)][on ? "-light":""]" item_state = icon_state - + var/mob/living/carbon/human/H = user if(istype(H)) H.update_inv_head() @@ -93,14 +93,14 @@ /obj/item/clothing/head/helmet/space/plasmaman/security name = "security plasma envirosuit helmet" - desc = "A plasmaman containment helmet designed for security officers, protecting them from being flashed and burning alive, along-side other undesirables." + desc = "A plasmaman containment helmet designed for security officers, protecting them from being flashed and burning alive, alongside other undesirables." icon_state = "security_envirohelm" item_state = "security_envirohelm" armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 0, "fire" = 100, "acid" = 75) /obj/item/clothing/head/helmet/space/plasmaman/security/warden name = "warden's plasma envirosuit helmet" - desc = "A plasmaman containment helmet designed for the warden, a pair of white stripes being added to differeciate them from other members of security." + desc = "A plasmaman containment helmet designed for the warden, a pair of white stripes being added to differentiate them from other members of security." icon_state = "warden_envirohelm" item_state = "warden_envirohelm" @@ -111,14 +111,14 @@ item_state = "hos_envirohelm" /obj/item/clothing/head/helmet/space/plasmaman/medical - name = "medical's plasma envirosuit helmet" - desc = "An envriohelmet designed for plasmaman medical doctors, having two stripes down it's length to denote as much" + name = "medical plasma envirosuit helmet" + desc = "An envirohelmet designed for plasmaman medical doctors, having two stripes down its length to denote as much." icon_state = "doctor_envirohelm" item_state = "doctor_envirohelm" /obj/item/clothing/head/helmet/space/plasmaman/cmo name = "chief medical officer's plasma envirosuit helmet" - desc = "An envriohelmet designed for plasmaman employed as the cheif medical officer." + desc = "An envirohelmet designed for plasmamen employed as the chief medical officer." icon_state = "cmo_envirohelm" item_state = "cmo_envirohelm" @@ -136,7 +136,7 @@ /obj/item/clothing/head/helmet/space/plasmaman/chemist name = "chemistry plasma envirosuit helmet" - desc = "A plasmaman envirosuit designed for chemists, two orange stripes going down it's face." + desc = "A plasmaman envirohelmet designed for chemists, two orange stripes going down its face." icon_state = "chemist_envirohelm" item_state = "chemist_envirohelm" @@ -167,7 +167,7 @@ /obj/item/clothing/head/helmet/space/plasmaman/engineering/ce name = "chief engineer's plasma envirosuit helmet" - desc = "A space-worthy helmet specially designed for chief engineer employed plasmamen." + desc = "A space-worthy helmet specially designed for plasmamen employed as the chief engineer." icon_state = "ce_envirohelm" item_state = "ce_envirohelm" @@ -179,13 +179,13 @@ /obj/item/clothing/head/helmet/space/plasmaman/cargo name = "cargo plasma envirosuit helmet" - desc = "An plasmaman envirohelmet designed for cargo techs and quartermasters." + desc = "A plasmaman envirohelmet designed for cargo techs and quartermasters." icon_state = "cargo_envirohelm" item_state = "cargo_envirohelm" /obj/item/clothing/head/helmet/space/plasmaman/mining name = "mining plasma envirosuit helmet" - desc = "A khaki helmet given to plasmamen miners operating on lavaland." + desc = "A khaki helmet given to plasmaman miners operating on Lavaland." icon_state = "explorer_envirohelm" item_state = "explorer_envirohelm" visor_icon = "explorer_envisor" @@ -210,7 +210,7 @@ /obj/item/clothing/head/helmet/space/plasmaman/librarian name = "librarian's plasma envirosuit helmet" - desc = "A slight modification on a tradiational voidsuit helmet, this helmet was Nano-Trasen's first solution to the *logistical problems* that come with employing plasmamen. Despite their limitations, these helmets still see use by historian and old-styled plasmamen alike." + desc = "A slight modification on a traditional voidsuit helmet, this helmet was Nanotrasen's first solution to the *logistical problems* that come with employing plasmamen. Despite their limitations, these helmets still see use by historian and old-styled plasmamen alike." icon_state = "prototype_envirohelm" item_state = "prototype_envirohelm" actions_types = list(/datum/action/item_action/toggle_welding_screen/plasmaman) @@ -218,7 +218,7 @@ /obj/item/clothing/head/helmet/space/plasmaman/botany name = "botany plasma envirosuit helmet" - desc = "A green and blue envirohelmet designating it's wearer as a botanist. While not specially designed for it, it would protect against minor planet-related injuries." + desc = "A green and blue envirohelmet designating its wearer as a botanist. While not specially designed for it, it would protect against minor plant-related injuries." icon_state = "botany_envirohelm" item_state = "botany_envirohelm" @@ -230,14 +230,14 @@ /obj/item/clothing/head/helmet/space/plasmaman/mime name = "mime envirosuit helmet" - desc = "The make-up is painted on, it's a miracle it doesn't chip. It's not very colourful." + desc = "The makeup is painted on, it's a miracle it doesn't chip. It's not very colourful." icon_state = "mime_envirohelm" item_state = "mime_envirohelm" visor_icon = "mime_envisor" /obj/item/clothing/head/helmet/space/plasmaman/clown name = "clown envirosuit helmet" - desc = "The make-up is painted on, it's a miracle it doesn't chip. 'HONK!'" + desc = "The makeup is painted on, it's a miracle it doesn't chip. 'HONK!'" icon_state = "clown_envirohelm" item_state = "clown_envirohelm" visor_icon = "clown_envisor" @@ -258,4 +258,15 @@ name = "blueshield envirosuit helmet" desc = "A plasmaman envirohelm designed for the blueshield." icon_state = "bs_envirohelm" - item_state = "bs_envirohelm" \ No newline at end of file + item_state = "bs_envirohelm" + +/obj/item/clothing/head/helmet/space/plasmaman/wizard + name = "wizard plasma envirosuit helmet" + desc = "A magical plasmaman containment helmet designed to spread chaos in safety and comfort." + icon_state = "wizard_envirohelm" + item_state = "wizard_envirohelm" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm index 74af696e76e..749e12e8053 100644 --- a/code/modules/clothing/spacesuits/rig/modules/computer.dm +++ b/code/modules/clothing/spacesuits/rig/modules/computer.dm @@ -23,7 +23,7 @@ to_chat(usr, "Your module is not installed in a hardsuit.") return - module.holder.ui_interact(usr, nano_state = contained_state) + module.holder.ui_interact(usr, state = GLOB.contained_state) /obj/item/rig_module/ai_container @@ -139,7 +139,7 @@ if(!target) if(ai_card) if(istype(ai_card,/obj/item/aicard)) - ai_card.ui_interact(H, state = deep_inventory_state) + ai_card.ui_interact(H, state = GLOB.deep_inventory_state) else eject_ai(H) update_verb_holder() diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm index 9fc960cdbce..33ae007ceee 100644 --- a/code/modules/clothing/spacesuits/rig/modules/vision.dm +++ b/code/modules/clothing/spacesuits/rig/modules/vision.dm @@ -188,4 +188,4 @@ if(!vision) vision = vision_datum processed_vision += vision_datum - vision_modes = processed_vision \ No newline at end of file + vision_modes = processed_vision diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 3167e68ca6f..3608ee85164 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -522,7 +522,7 @@ cell.use(cost*10) return 1 -/obj/item/rig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state) +/obj/item/rig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) if(!user) return @@ -532,7 +532,7 @@ ui.open() ui.set_auto_update(1) -/obj/item/rig/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state) +/obj/item/rig/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) var/data[0] data["primarysystem"] = null @@ -982,7 +982,7 @@ return 0 if(malfunctioning) - direction = pick(cardinal) + direction = pick(GLOB.cardinal) // Inside an object, tell it we moved. if(isobj(wearer.loc) || ismob(wearer.loc)) diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm index 4e1782d2384..4a71f6c67bc 100644 --- a/code/modules/clothing/spacesuits/rig/rig_verbs.dm +++ b/code/modules/clothing/spacesuits/rig/rig_verbs.dm @@ -332,4 +332,4 @@ return to_chat(usr, "You attempt to engage the [module.interface_name].") - module.engage() \ No newline at end of file + module.engage() diff --git a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm b/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm index 3ff5a19b468..5485e1fe882 100644 --- a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm +++ b/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm @@ -8,7 +8,7 @@ helm_type = /obj/item/clothing/head/helmet/space/new_rig/ert - req_access = list(access_cent_specops) + req_access = list(ACCESS_CENT_SPECOPS) armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 50) allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd, /obj/item/crowbar, \ @@ -78,4 +78,4 @@ /obj/item/rig_module/device/plasmacutter, // /obj/item/rig_module/device/rcd, /obj/item/rig_module/datajack - ) \ No newline at end of file + ) diff --git a/code/modules/clothing/spacesuits/rig/suits/light.dm b/code/modules/clothing/spacesuits/rig/suits/light.dm index 2d1933dad60..d76691daf57 100644 --- a/code/modules/clothing/spacesuits/rig/suits/light.dm +++ b/code/modules/clothing/spacesuits/rig/suits/light.dm @@ -35,7 +35,7 @@ desc = "An advanced powered armour suit with many cyberwarfare enhancements. Comes with built-in insulated gloves for safely tampering with electronics." icon_state = "hacker_rig" - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) airtight = 0 seal_delay = 5 //not being vaccum-proof has an upside I guess @@ -82,7 +82,7 @@ chest_type = /obj/item/clothing/suit/space/new_rig/light/ninja glove_type = /obj/item/clothing/gloves/rig/light/ninja - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) initial_modules = list( /obj/item/rig_module/teleporter, @@ -112,7 +112,7 @@ desc = "A highly advanced and expensive suit designed for covert operations." icon_state = "ninja_rig" //supposed to be "stealth_rig", but as it currently only has a semi-copied ninja rig sprite, we can just use them directly. - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) initial_modules = list( /obj/item/rig_module/stealth_field, diff --git a/code/modules/clothing/spacesuits/rig/suits/station.dm b/code/modules/clothing/spacesuits/rig/suits/station.dm index 0954a6c0f62..c4bbd059fae 100644 --- a/code/modules/clothing/spacesuits/rig/suits/station.dm +++ b/code/modules/clothing/spacesuits/rig/suits/station.dm @@ -32,7 +32,7 @@ /obj/item/rig/internalaffairs/equipped - req_access = list(access_lawyer) + req_access = list(ACCESS_LAWYER) initial_modules = list( /obj/item/rig_module/ai_container, @@ -125,7 +125,7 @@ /obj/item/rig/ce/equipped - req_access = list(access_ce) + req_access = list(ACCESS_CE) initial_modules = list( /obj/item/rig_module/ai_container, @@ -163,7 +163,7 @@ /obj/item/rig/hazmat/equipped - req_access = list(access_rd) + req_access = list(ACCESS_RD) initial_modules = list( /obj/item/rig_module/ai_container, diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm index a1043eb2643..8c65ed02553 100644 --- a/code/modules/clothing/spacesuits/syndi.dm +++ b/code/modules/clothing/spacesuits/syndi.dm @@ -1,188 +1,188 @@ -//Regular syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate - name = "red space helmet" - desc = "Top secret Spess Helmet." - icon_state = "syndicate" - item_state = "syndicate" - desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi') - - -/obj/item/clothing/suit/space/syndicate - name = "red space suit" - icon_state = "syndicate" - item_state = "space_suit_syndicate" - desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi') - - -//Green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green - name = "Green Space Helmet" - icon_state = "syndicate-helm-green" - item_state = "syndicate-helm-green" - -/obj/item/clothing/suit/space/syndicate/green - name = "Green Space Suit" - icon_state = "syndicate-green" - item_state = "syndicate-green" - - -//Dark green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green/dark - name = "Dark Green Space Helmet" - icon_state = "syndicate-helm-green-dark" - item_state = "syndicate-helm-green-dark" - -/obj/item/clothing/suit/space/syndicate/green/dark - name = "Dark Green Space Suit" - icon_state = "syndicate-green-dark" - item_state = "syndicate-green-dark" - - -//Orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/orange - name = "Orange Space Helmet" - icon_state = "syndicate-helm-orange" - item_state = "syndicate-helm-orange" - -/obj/item/clothing/suit/space/syndicate/orange - name = "Orange Space Suit" - icon_state = "syndicate-orange" - item_state = "syndicate-orange" - - -//Blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/blue - name = "Blue Space Helmet" - icon_state = "syndicate-helm-blue" - item_state = "syndicate-helm-blue" - -/obj/item/clothing/suit/space/syndicate/blue - name = "Blue Space Suit" - icon_state = "syndicate-blue" - item_state = "syndicate-blue" - - -//Black syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black - name = "Black Space Helmet" - icon_state = "syndicate-helm-black" - item_state = "syndicate-helm-black" - -obj/item/clothing/head/helmet/space/syndicate/black/strike - name = "Syndicate Strike Team commando helmet" - desc = "A heavily armored black helmet that is only given to high-ranking Syndicate operatives." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - -/obj/item/clothing/suit/space/syndicate/black - name = "Black Space Suit" - icon_state = "syndicate-black" - item_state = "syndicate-black" - -obj/item/clothing/suit/space/syndicate/black/strike - name = "Syndicate Strike Team commando space suit" - desc = "A heavily armored, black space suit that is only given to high-ranking Syndicate operatives." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - -//Black-green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/green - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-green" - item_state = "syndicate-helm-black-green" - -/obj/item/clothing/suit/space/syndicate/black/green - name = "Black and Green Space Suit" - icon_state = "syndicate-black-green" - item_state = "syndicate-black-green" - - -//Black-blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/blue - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-blue" - item_state = "syndicate-helm-black-blue" - -/obj/item/clothing/suit/space/syndicate/black/blue - name = "Black and Blue Space Suit" - icon_state = "syndicate-black-blue" - item_state = "syndicate-black-blue" - - -//Black medical syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/med - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-med" - item_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black/med - name = "Green Space Suit" - icon_state = "syndicate-black-med" - item_state = "syndicate-black" - - -//Black-orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/orange - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-orange" - item_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black/orange - name = "Black and Orange Space Suit" - icon_state = "syndicate-black-orange" - item_state = "syndicate-black" - - -//Black-red syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/red - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-red" - item_state = "syndicate-helm-black-red" - -obj/item/clothing/head/helmet/space/syndicate/black/red/strike - name = "Syndicate Strike Team leader helmet" - desc = "A heavily armored, black and red space helmet that is only given to elite Syndicate operatives, it looks particularly menacing." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - -/obj/item/clothing/suit/space/syndicate/black/red - name = "Black and Red Space Suit" - icon_state = "syndicate-black-red" - item_state = "syndicate-black-red" - -obj/item/clothing/suit/space/syndicate/black/red/strike - name = "Syndicate Strike Team leader space suit" - desc = "A heavily armored, black and red space suit that is only given to elite Syndicate operatives, it looks particularly menacing." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - - -//Black with yellow/red engineering syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/engie - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-engie" - item_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black/engie - name = "Black Engineering Space Suit" - icon_state = "syndicate-black-engie" - item_state = "syndicate-black" +//Regular syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate + name = "red space helmet" + desc = "Top secret Spess Helmet." + icon_state = "syndicate" + item_state = "syndicate" + desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi') + + +/obj/item/clothing/suit/space/syndicate + name = "red space suit" + icon_state = "syndicate" + item_state = "space_suit_syndicate" + desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi') + + +//Green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green + name = "Green Space Helmet" + icon_state = "syndicate-helm-green" + item_state = "syndicate-helm-green" + +/obj/item/clothing/suit/space/syndicate/green + name = "Green Space Suit" + icon_state = "syndicate-green" + item_state = "syndicate-green" + + +//Dark green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green/dark + name = "Dark Green Space Helmet" + icon_state = "syndicate-helm-green-dark" + item_state = "syndicate-helm-green-dark" + +/obj/item/clothing/suit/space/syndicate/green/dark + name = "Dark Green Space Suit" + icon_state = "syndicate-green-dark" + item_state = "syndicate-green-dark" + + +//Orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/orange + name = "Orange Space Helmet" + icon_state = "syndicate-helm-orange" + item_state = "syndicate-helm-orange" + +/obj/item/clothing/suit/space/syndicate/orange + name = "Orange Space Suit" + icon_state = "syndicate-orange" + item_state = "syndicate-orange" + + +//Blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/blue + name = "Blue Space Helmet" + icon_state = "syndicate-helm-blue" + item_state = "syndicate-helm-blue" + +/obj/item/clothing/suit/space/syndicate/blue + name = "Blue Space Suit" + icon_state = "syndicate-blue" + item_state = "syndicate-blue" + + +//Black syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black + name = "Black Space Helmet" + icon_state = "syndicate-helm-black" + item_state = "syndicate-helm-black" + +obj/item/clothing/head/helmet/space/syndicate/black/strike + name = "Syndicate Strike Team commando helmet" + desc = "A heavily armored black helmet that is only given to high-ranking Syndicate operatives." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + +/obj/item/clothing/suit/space/syndicate/black + name = "Black Space Suit" + icon_state = "syndicate-black" + item_state = "syndicate-black" + +obj/item/clothing/suit/space/syndicate/black/strike + name = "Syndicate Strike Team commando space suit" + desc = "A heavily armored, black space suit that is only given to high-ranking Syndicate operatives." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + +//Black-green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/green + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-green" + item_state = "syndicate-helm-black-green" + +/obj/item/clothing/suit/space/syndicate/black/green + name = "Black and Green Space Suit" + icon_state = "syndicate-black-green" + item_state = "syndicate-black-green" + + +//Black-blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/blue + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-blue" + item_state = "syndicate-helm-black-blue" + +/obj/item/clothing/suit/space/syndicate/black/blue + name = "Black and Blue Space Suit" + icon_state = "syndicate-black-blue" + item_state = "syndicate-black-blue" + + +//Black medical syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/med + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-med" + item_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black/med + name = "Green Space Suit" + icon_state = "syndicate-black-med" + item_state = "syndicate-black" + + +//Black-orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/orange + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-orange" + item_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black/orange + name = "Black and Orange Space Suit" + icon_state = "syndicate-black-orange" + item_state = "syndicate-black" + + +//Black-red syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/red + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-red" + item_state = "syndicate-helm-black-red" + +obj/item/clothing/head/helmet/space/syndicate/black/red/strike + name = "Syndicate Strike Team leader helmet" + desc = "A heavily armored, black and red space helmet that is only given to elite Syndicate operatives, it looks particularly menacing." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + +/obj/item/clothing/suit/space/syndicate/black/red + name = "Black and Red Space Suit" + icon_state = "syndicate-black-red" + item_state = "syndicate-black-red" + +obj/item/clothing/suit/space/syndicate/black/red/strike + name = "Syndicate Strike Team leader space suit" + desc = "A heavily armored, black and red space suit that is only given to elite Syndicate operatives, it looks particularly menacing." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + + +//Black with yellow/red engineering syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/engie + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-engie" + item_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black/engie + name = "Black Engineering Space Suit" + icon_state = "syndicate-black-engie" + item_state = "syndicate-black" diff --git a/code/modules/clothing/spacesuits/void.dm b/code/modules/clothing/spacesuits/void.dm index 0f97be88e10..04c5edf20ec 100644 --- a/code/modules/clothing/spacesuits/void.dm +++ b/code/modules/clothing/spacesuits/void.dm @@ -1,124 +1,124 @@ - -//Voidsuits -/obj/item/clothing/head/helmet/space/nasavoid - name = "NASA Void Helmet" - desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance." - icon_state = "void-red" - item_state = "void" - flags_inv = HIDEMASK|HIDEEARS - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi') - -/obj/item/clothing/suit/space/nasavoid - name = "NASA Void Suit" - icon_state = "void-red" - item_state = "void" - desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance." - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/multitool) - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi') - -//Colors!!! -/obj/item/clothing/head/helmet/space/nasavoid/green - icon_state = "void-green" - -/obj/item/clothing/suit/space/nasavoid/green - icon_state = "void-green" - -/obj/item/clothing/head/helmet/space/nasavoid/ntblue - icon_state = "void-ntblue" - -/obj/item/clothing/suit/space/nasavoid/ntblue - icon_state = "void-ntblue" - -/obj/item/clothing/head/helmet/space/nasavoid/purple - icon_state = "void-purple" - -/obj/item/clothing/suit/space/nasavoid/purple - icon_state = "void-purple" - -/obj/item/clothing/head/helmet/space/nasavoid/yellow - icon_state = "void-yellow" - -/obj/item/clothing/suit/space/nasavoid/yellow - icon_state = "void-yellow" - -/obj/item/clothing/head/helmet/space/nasavoid/ltblue - icon_state = "void-light_blue" - -/obj/item/clothing/suit/space/nasavoid/ltblue - icon_state = "void-light_blue" - - -//Captian's Suit, like the other captian's suit, but looks better, at the cost of armor -/obj/item/clothing/head/helmet/space/nasavoid/captain - name = "Fancy Retro Void Helmet" - icon_state = "void-captian" - desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance. This one is fit for a captain." - -/obj/item/clothing/suit/space/nasavoid/captain - name = "Fancy NASA Void Suit" - icon_state = "void-captian" - desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance. This one is fit for a captain." - -//Syndi's suit, on par with a blood red softsuit - -/obj/item/clothing/head/helmet/space/nasavoid/syndi - name = "Blood Red Retro Void Helmet" - icon_state = "void-syndi" - desc = "A high tech, NASA Centcom branch designed space suit helmet. This one looks rather suspicious." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - -/obj/item/clothing/suit/space/nasavoid/syndi - name = "Blood Red NASA Void Suit" - icon_state = "void-syndi" - desc = "A high tech, NASA Centcom branch designed space suit. This one looks rather suspicious." - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - -//random spawner - -/obj/effect/nasavoidsuitspawner - name = "NASA Void Suit Spawner" - icon = 'icons/obj/clothing/suits.dmi' - icon_state = "void-red" - desc = "You shouldn't see this, a spawner for NASA Void Suits." - var/suits = list("red", "green", "ntblue", "purple", "yellow", "ltblue") - -/obj/effect/nasavoidsuitspawner/New() - var/obj/item/clothing/head/helmet/space/nasavoid/H - var/obj/item/clothing/suit/space/nasavoid/S - switch(pick(suits)) - if("red") - H = new /obj/item/clothing/head/helmet/space/nasavoid - S = new /obj/item/clothing/suit/space/nasavoid - if("green") - H = new /obj/item/clothing/head/helmet/space/nasavoid/green - S = new /obj/item/clothing/suit/space/nasavoid/green - if("ntblue") - H = new /obj/item/clothing/head/helmet/space/nasavoid/ntblue - S = new /obj/item/clothing/suit/space/nasavoid/ntblue - if("purple") - H = new /obj/item/clothing/head/helmet/space/nasavoid/purple - S = new /obj/item/clothing/suit/space/nasavoid/purple - if("yellow") - H = new /obj/item/clothing/head/helmet/space/nasavoid/yellow - S = new /obj/item/clothing/suit/space/nasavoid/yellow - if("ltblue") - H = new /obj/item/clothing/head/helmet/space/nasavoid/ltblue - S = new /obj/item/clothing/suit/space/nasavoid/ltblue - var/turf/T = get_turf(src) - if(H) - H.forceMove(T) - if(S) - S.forceMove(T) - qdel(src) \ No newline at end of file + +//Voidsuits +/obj/item/clothing/head/helmet/space/nasavoid + name = "NASA Void Helmet" + desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance." + icon_state = "void-red" + item_state = "void" + flags_inv = HIDEMASK|HIDEEARS + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi') + +/obj/item/clothing/suit/space/nasavoid + name = "NASA Void Suit" + icon_state = "void-red" + item_state = "void" + desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance." + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/multitool) + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi') + +//Colors!!! +/obj/item/clothing/head/helmet/space/nasavoid/green + icon_state = "void-green" + +/obj/item/clothing/suit/space/nasavoid/green + icon_state = "void-green" + +/obj/item/clothing/head/helmet/space/nasavoid/ntblue + icon_state = "void-ntblue" + +/obj/item/clothing/suit/space/nasavoid/ntblue + icon_state = "void-ntblue" + +/obj/item/clothing/head/helmet/space/nasavoid/purple + icon_state = "void-purple" + +/obj/item/clothing/suit/space/nasavoid/purple + icon_state = "void-purple" + +/obj/item/clothing/head/helmet/space/nasavoid/yellow + icon_state = "void-yellow" + +/obj/item/clothing/suit/space/nasavoid/yellow + icon_state = "void-yellow" + +/obj/item/clothing/head/helmet/space/nasavoid/ltblue + icon_state = "void-light_blue" + +/obj/item/clothing/suit/space/nasavoid/ltblue + icon_state = "void-light_blue" + + +//Captian's Suit, like the other captian's suit, but looks better, at the cost of armor +/obj/item/clothing/head/helmet/space/nasavoid/captain + name = "Fancy Retro Void Helmet" + icon_state = "void-captian" + desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance. This one is fit for a captain." + +/obj/item/clothing/suit/space/nasavoid/captain + name = "Fancy NASA Void Suit" + icon_state = "void-captian" + desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance. This one is fit for a captain." + +//Syndi's suit, on par with a blood red softsuit + +/obj/item/clothing/head/helmet/space/nasavoid/syndi + name = "Blood Red Retro Void Helmet" + icon_state = "void-syndi" + desc = "A high tech, NASA Centcom branch designed space suit helmet. This one looks rather suspicious." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + +/obj/item/clothing/suit/space/nasavoid/syndi + name = "Blood Red NASA Void Suit" + icon_state = "void-syndi" + desc = "A high tech, NASA Centcom branch designed space suit. This one looks rather suspicious." + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + +//random spawner + +/obj/effect/nasavoidsuitspawner + name = "NASA Void Suit Spawner" + icon = 'icons/obj/clothing/suits.dmi' + icon_state = "void-red" + desc = "You shouldn't see this, a spawner for NASA Void Suits." + var/suits = list("red", "green", "ntblue", "purple", "yellow", "ltblue") + +/obj/effect/nasavoidsuitspawner/New() + var/obj/item/clothing/head/helmet/space/nasavoid/H + var/obj/item/clothing/suit/space/nasavoid/S + switch(pick(suits)) + if("red") + H = new /obj/item/clothing/head/helmet/space/nasavoid + S = new /obj/item/clothing/suit/space/nasavoid + if("green") + H = new /obj/item/clothing/head/helmet/space/nasavoid/green + S = new /obj/item/clothing/suit/space/nasavoid/green + if("ntblue") + H = new /obj/item/clothing/head/helmet/space/nasavoid/ntblue + S = new /obj/item/clothing/suit/space/nasavoid/ntblue + if("purple") + H = new /obj/item/clothing/head/helmet/space/nasavoid/purple + S = new /obj/item/clothing/suit/space/nasavoid/purple + if("yellow") + H = new /obj/item/clothing/head/helmet/space/nasavoid/yellow + S = new /obj/item/clothing/suit/space/nasavoid/yellow + if("ltblue") + H = new /obj/item/clothing/head/helmet/space/nasavoid/ltblue + S = new /obj/item/clothing/suit/space/nasavoid/ltblue + var/turf/T = get_turf(src) + if(H) + H.forceMove(T) + if(S) + S.forceMove(T) + qdel(src) diff --git a/code/modules/clothing/suits/alien.dm b/code/modules/clothing/suits/alien.dm index 0f82a9eae7e..1fde5cd6f84 100644 --- a/code/modules/clothing/suits/alien.dm +++ b/code/modules/clothing/suits/alien.dm @@ -16,4 +16,4 @@ desc = "A rather grisly selection of cured hides and skin, sewn together to form a ragged mantle." icon_state = "mantle-unathi" item_state = "mantle-unathi" - body_parts_covered = UPPER_TORSO \ No newline at end of file + body_parts_covered = UPPER_TORSO diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 037e967f5ee..6c4ace8c993 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -1,544 +1,541 @@ -/obj/item/clothing/suit/armor - allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic,/obj/item/kitchen/knife/combat) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 40 - max_integrity = 250 - resistance_flags = NONE - armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/armor/vest - name = "armor" - desc = "An armored vest that protects against some damage." - icon_state = "armor" - item_state = "armor" - blood_overlay_type = "armor" - flags_size = ONESIZEFITSALL - dog_fashion = /datum/dog_fashion/back - -/obj/item/clothing/suit/armor/vest/jacket - name = "military jacket" - desc = "An old military jacket, it has armoring." - icon_state = "militaryjacket" - item_state = "militaryjacket" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/suit/armor/vest/combat - name = "combat vest" - desc = "An armored vest that protects against some damage." - icon_state = "armor-combat" - item_state = "bulletproof" - blood_overlay_type = "armor" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/suit/armor/vest/security - name = "security armor" - desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." - icon_state = "armor" - item_state = "armor" - var/obj/item/clothing/accessory/holobadge/attached_badge - -/obj/item/clothing/suit/armor/vest/security/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/clothing/accessory/holobadge)) - if(user.unEquip(W)) - add_fingerprint(user) - W.forceMove(src) - attached_badge = W - - var/datum/action/A = new /datum/action/item_action/remove_badge(src) - A.Grant(user) - icon_state = "armorsec" - user.update_inv_wear_suit() - desc = "An armored vest that protects against some damage. This one has [attached_badge] attached to it." - to_chat(user, "You attach [attached_badge] to [src].") - return - ..() - -/obj/item/clothing/suit/armor/vest/security/attack_self(mob/user as mob) - if(attached_badge) - add_fingerprint(user) - user.put_in_hands(attached_badge) - - for(var/X in actions) - var/datum/action/A = X - A.Remove(user) - - icon_state = "armor" - user.update_inv_wear_suit() - desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." - to_chat(user, "You remove [attached_badge] from [src].") - - attached_badge = null - - return - - ..() - -/obj/item/clothing/suit/armor/vest/blueshield - name = "blueshield security armor" - desc = "An armored vest with the badge of a Blueshield Lieutenant." - icon_state = "blueshield" - item_state = "blueshield" - -/obj/item/clothing/suit/armor/vest/bloody - name = "bloodied security armor" - desc = "A vest drenched in the blood of Greytide. It has seen better days." - icon_state = "bloody_armor" - item_state = "bloody_armor" - sprite_sheets = null - -/obj/item/clothing/suit/armor/secjacket - name = "security jacket" - desc = "A sturdy black jacket with reinforced fabric. Bears insignia of NT corporate security." - icon_state = "secjacket_open" - item_state = "hos" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - armor = list(melee = 15, bullet = 10, laser = 15, energy = 5, bomb = 15, bio = 0, rad = 0, fire = 30, acid = 30) - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/openclose) - adjust_flavour = "unzip" - -/obj/item/clothing/suit/armor/hos - name = "armored coat" - desc = "A trench coat enhanced with a special alloy for some protection and style." - icon_state = "hos" - item_state = "hos" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90) - flags_inv = HIDEJUMPSUIT - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - strip_delay = 80 - -/obj/item/clothing/suit/armor/hos/alt - name = "armored trenchoat" - desc = "A trenchcoat enhanced with a special lightweight kevlar. The epitome of tactical plainclothes." - icon_state = "hostrench_open" - item_state = "hostrench_open" - flags_inv = 0 - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/openclose) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/armor/hos/jensen - name = "armored trenchcoat" - desc = "A trenchcoat augmented with a special alloy for some protection and style." - icon_state = "jensencoat" - item_state = "jensencoat" - flags_inv = 0 - sprite_sheets = null - -/obj/item/clothing/suit/armor/vest/warden - name = "Warden's armored jacket" - desc = "An armored jacket with silver rank pips and livery." - icon_state = "warden_jacket" - item_state = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|HANDS - heat_protection = UPPER_TORSO|LOWER_TORSO|HANDS - strip_delay = 70 - resistance_flags = FLAMMABLE - dog_fashion = null - -/obj/item/clothing/suit/armor/vest/warden/alt - name = "warden's jacket" - desc = "A navy-blue armored jacket with blue shoulder designations and '/Warden/' stitched into one of the chest pockets." - icon_state = "warden_jacket_alt" - -/obj/item/clothing/suit/armor/vest/capcarapace - name = "captain's carapace" - desc = "An armored vest reinforced with ceramic plates and pauldrons to provide additional protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest, although it does chafe your nipples." - icon_state = "capcarapace" - item_state = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90) - dog_fashion = null - resistance_flags = FIRE_PROOF - -/obj/item/clothing/suit/armor/vest/capcarapace/alt - name = "captain's parade jacket" - desc = "For when an armored vest isn't fashionable enough." - icon_state = "capformal" - item_state = "capspacesuit" - -/obj/item/clothing/suit/armor/riot - name = "Riot Suit" - desc = "A suit of armor with heavy padding to protect against melee attacks. Looks like it might impair movement." - icon_state = "riot" - item_state = "swat_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) - flags_inv = HIDEJUMPSUIT - strip_delay = 80 - put_on_delay = 60 - hide_tail_by_species = list("Vox") - -/obj/item/clothing/suit/armor/riot/knight - name = "plate armour" - desc = "A classic suit of plate armour, highly effective at stopping melee attacks." - icon_state = "knight_green" - item_state = "knight_green" - slowdown = 1 - -/obj/item/clothing/suit/armor/riot/knight/yellow - icon_state = "knight_yellow" - item_state = "knight_yellow" - -/obj/item/clothing/suit/armor/riot/knight/blue - icon_state = "knight_blue" - item_state = "knight_blue" - -/obj/item/clothing/suit/armor/riot/knight/red - icon_state = "knight_red" - item_state = "knight_red" - -/obj/item/clothing/suit/armor/riot/knight/templar - name = "crusader armour" - desc = "God wills it!" - icon_state = "knight_templar" - item_state = "knight_templar" - allowed = list(/obj/item/nullrod/claymore) - armor = list(melee = 25, bullet = 5, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) - -/obj/item/clothing/suit/armor/vest/durathread - name = "durathread vest" - desc = "A vest made of durathread with strips of leather acting as trauma plates." - icon_state = "durathread" - item_state = "durathread" - strip_delay = 60 - max_integrity = 200 - resistance_flags = FLAMMABLE - armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) - -/obj/item/clothing/suit/armor/bulletproof - name = "Bulletproof Vest" - desc = "A bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." - icon_state = "bulletproof" - item_state = "armor" - blood_overlay_type = "armor" - armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - strip_delay = 70 - put_on_delay = 50 - -/obj/item/clothing/suit/armor/laserproof - name = "Ablative Armor Vest" - desc = "A vest that excels in protecting the wearer against energy projectiles." - icon_state = "armor_reflec" - item_state = "armor_reflec" - blood_overlay_type = "armor" - armor = list("melee" = 10, "bullet" = 10, "laser" = 60, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/hit_reflect_chance = 40 - -/obj/item/clothing/suit/armor/laserproof/IsReflect(var/def_zone) - if(!(def_zone in list("chest", "groin"))) //If not shot where ablative is covering you, you don't get the reflection bonus! - return 0 - if(prob(hit_reflect_chance)) - return 1 - -/obj/item/clothing/suit/armor/vest/det_suit - name = "armor" - desc = "An armored vest with a detective's badge on it." - icon_state = "detective-armor" - item_state = "armor" - blood_overlay_type = "armor" - flags_size = ONESIZEFITSALL - allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/reagent_containers/spray/pepper,/obj/item/flashlight,/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/storage/fancy/cigarettes,/obj/item/lighter,/obj/item/detective_scanner,/obj/item/taperecorder) - resistance_flags = FLAMMABLE - dog_fashion = null - -//Reactive armor -/obj/item/clothing/suit/armor/reactive - name = "reactive armor" - desc = "Doesn't seem to do much for some reason." - var/active = 0 - icon_state = "reactiveoff" - item_state = "reactiveoff" - blood_overlay_type = "armor" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - actions_types = list(/datum/action/item_action/toggle) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - hit_reaction_chance = 50 - -/obj/item/clothing/suit/armor/reactive/attack_self(mob/user) - active = !(active) - if(active) - to_chat(user, "[src] is now active.") - icon_state = "reactive" - item_state = "reactive" - else - to_chat(user, "[src] is now inactive.") - icon_state = "reactiveoff" - item_state = "reactiveoff" - add_fingerprint(user) - user.update_inv_wear_suit() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/suit/armor/reactive/emp_act(severity) - active = 0 - icon_state = "reactiveoff" - item_state = "reactiveoff" - if(istype(loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/C = loc - C.update_inv_wear_suit() - ..() - -//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) -/obj/item/clothing/suit/armor/reactive/teleport - name = "reactive teleport armor" - desc = "Someone seperated our Research Director from his own head!" - var/tele_range = 6 - -/obj/item/clothing/suit/armor/reactive/teleport/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(!active) - return 0 - if(prob(hit_reaction_chance)) - var/mob/living/carbon/human/H = owner - owner.visible_message("The reactive teleport system flings [H] clear of [attack_text]!") - var/list/turfs = new/list() - for(var/turf/T in orange(tele_range, H)) - if(istype(T, /turf/space)) - continue - if(T.density) - continue - if(T.x>world.maxx-tele_range || T.xworld.maxy-tele_range || T.yThe [src] blocks the [attack_text], sending out jets of flame!") - for(var/mob/living/carbon/C in range(6, owner)) - if(C != owner) - C.fire_stacks += 8 - C.IgniteMob() - owner.fire_stacks = -20 - return 1 - return 0 - - -/obj/item/clothing/suit/armor/reactive/stealth - name = "reactive stealth armor" - -/obj/item/clothing/suit/armor/reactive/stealth/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(!active) - return 0 - if(prob(hit_reaction_chance)) - var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc) - E.Copy_Parent(owner, 50) - E.GiveTarget(owner) //so it starts running right away - E.Goto(owner, E.move_to_delay, E.minimum_distance) - owner.alpha = 0 - owner.visible_message("[owner] is hit by [attack_text] in the chest!") //We pretend to be hit, since blocking it would stop the message otherwise - spawn(40) - owner.alpha = initial(owner.alpha) - return 1 - -/obj/item/clothing/suit/armor/reactive/tesla - name = "reactive tesla armor" - -/obj/item/clothing/suit/armor/reactive/tesla/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(!active) - return 0 - if(prob(hit_reaction_chance)) - owner.visible_message("The [src] blocks the [attack_text], sending out arcs of lightning!") - for(var/mob/living/M in view(6, owner)) - if(M == owner) - continue - owner.Beam(M,icon_state="lightning[rand(1, 12)]",icon='icons/effects/effects.dmi',time=5) - M.adjustFireLoss(25) - playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1) - return 1 - -//All of the armor below is mostly unused - - -/obj/item/clothing/suit/armor/centcomm - name = "Cent. Com. armor" - desc = "A suit that protects against some damage." - icon_state = "centcom" - item_state = "centcom" - w_class = WEIGHT_CLASS_BULKY - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank/emergency_oxygen) - flags = THICKMATERIAL - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - sprite_sheets = null - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - -/obj/item/clothing/suit/armor/heavy - name = "heavy armor" - desc = "A heavily armored suit that protects against moderate damage." - icon_state = "heavy" - item_state = "swat_suit" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.90 - flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 3 - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - hide_tail_by_species = list("Vox") - -/obj/item/clothing/suit/armor/tdome - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - flags = THICKMATERIAL - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - hide_tail_by_species = list("Vox") - -/obj/item/clothing/suit/armor/tdome/red - name = "Red Thunderdome Armor" - desc = "Armor worn by the red Thunderdome team." - icon_state = "tdred" - item_state = "tdred" - -/obj/item/clothing/suit/armor/tdome/green - name = "Green Thunderdome Armor" - desc = "Armor worn by the green Thunderdome team." - icon_state = "tdgreen" - item_state = "tdgreen" - -//Non-hardsuit ERT armor. -/obj/item/clothing/suit/armor/vest/ert - name = "emergency response team armor" - desc = "A set of armor worn by members of the Nanotrasen Emergency Response Team." - icon_state = "ertarmor_cmd" - item_state = "armor" - armor = list(melee = 30, bullet = 30, laser = 30, energy = 30, bomb = 20, bio = 0, rad = 0, fire = 50, acid = 50) - -//Commander -/obj/item/clothing/suit/armor/vest/ert/command - name = "emergency response team commander armor" - desc = "A set of armor worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." - -//Security -/obj/item/clothing/suit/armor/vest/ert/security - name = "emergency response team security armor" - desc = "A set of armor worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." - icon_state = "ertarmor_sec" - - -/obj/item/clothing/suit/armor/vest/ert/security/paranormal - name = "emergency response team paranormal armor" - desc = "A set of armor worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." - icon_state = "knight_templar" - item_state = "knight_templar" - -//Engineer -/obj/item/clothing/suit/armor/vest/ert/engineer - name = "emergency response team engineer armor" - desc = "A set of armor worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." - icon_state = "ertarmor_eng" - -//Medical -/obj/item/clothing/suit/armor/vest/ert/medical - name = "emergency response team medical armor" - desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "ertarmor_med" - -//Janitorial -/obj/item/clothing/suit/armor/vest/ert/janitor - name = "emergency response team janitor armor" - desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "ertarmor_jan" - -//same defense as basic sec armor -/obj/item/clothing/suit/storage/lawyer/blackjacket/armored - desc = "A snappy dress jacket, reinforced with a layer of armor protecting the torso." - allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/gun/projectile/revolver, /obj/item/gun/projectile/automatic/pistol) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 40, acid = 40) - -//LAVALAND! - -/obj/item/clothing/suit/hooded/drake - name = "drake armour" - icon_state = "dragon" - item_state = "dragon" - desc = "A suit of armour fashioned from the remains of an ash drake." - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/twohanded/spear) - armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) - hoodtype = /obj/item/clothing/head/hooded/drake - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/head/hooded/drake - name = "drake helmet" - icon_state = "dragon" - item_state = "dragon" - desc = "The skull of a dragon." - armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) - heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/suit/hooded/goliath - name = "goliath cloak" - icon_state = "goliath_cloak" - item_state = "goliath_cloak" - desc = "A staunch, practical cape made out of numerous monster materials, it is coveted amongst exiles & hermits." - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/pickaxe, /obj/item/twohanded/spear, /obj/item/organ/internal/regenerative_core/legion, /obj/item/kitchen/knife/combat/survival) - armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) //a fair alternative to bone armor, requiring alternative materials and gaining a suit slot - hoodtype = /obj/item/clothing/head/hooded/goliath - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/head/hooded/goliath - name = "goliath cloak hood" - icon_state = "golhood" - item_state = "golhood" - desc = "A protective & concealing hood." - armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/suit/armor/bone - name = "bone armor" - desc = "A tribal armor plate, crafted from animal bone." - icon_state = "bonearmor" - item_state = "bonearmor" - blood_overlay_type = "armor" - armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS \ No newline at end of file +/obj/item/clothing/suit/armor + allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic,/obj/item/kitchen/knife/combat) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 40 + max_integrity = 250 + resistance_flags = NONE + armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/armor/vest + name = "armor" + desc = "An armored vest that protects against some damage." + icon_state = "armor" + item_state = "armor" + blood_overlay_type = "armor" + dog_fashion = /datum/dog_fashion/back + +/obj/item/clothing/suit/armor/vest/jacket + name = "military jacket" + desc = "An old military jacket, it has armoring." + icon_state = "militaryjacket" + item_state = "militaryjacket" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/suit/armor/vest/combat + name = "combat vest" + desc = "An armored vest that protects against some damage." + icon_state = "armor-combat" + item_state = "bulletproof" + blood_overlay_type = "armor" + +/obj/item/clothing/suit/armor/vest/security + name = "security armor" + desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." + icon_state = "armor" + item_state = "armor" + var/obj/item/clothing/accessory/holobadge/attached_badge + +/obj/item/clothing/suit/armor/vest/security/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/clothing/accessory/holobadge)) + if(user.unEquip(W)) + add_fingerprint(user) + W.forceMove(src) + attached_badge = W + + var/datum/action/A = new /datum/action/item_action/remove_badge(src) + A.Grant(user) + icon_state = "armorsec" + user.update_inv_wear_suit() + desc = "An armored vest that protects against some damage. This one has [attached_badge] attached to it." + to_chat(user, "You attach [attached_badge] to [src].") + return + ..() + +/obj/item/clothing/suit/armor/vest/security/attack_self(mob/user as mob) + if(attached_badge) + add_fingerprint(user) + user.put_in_hands(attached_badge) + + for(var/X in actions) + var/datum/action/A = X + A.Remove(user) + + icon_state = "armor" + user.update_inv_wear_suit() + desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." + to_chat(user, "You remove [attached_badge] from [src].") + + attached_badge = null + + return + + ..() + +/obj/item/clothing/suit/armor/vest/blueshield + name = "blueshield security armor" + desc = "An armored vest with the badge of a Blueshield Lieutenant." + icon_state = "blueshield" + item_state = "blueshield" + +/obj/item/clothing/suit/armor/vest/bloody + name = "bloodied security armor" + desc = "A vest drenched in the blood of Greytide. It has seen better days." + icon_state = "bloody_armor" + item_state = "bloody_armor" + sprite_sheets = null + +/obj/item/clothing/suit/armor/secjacket + name = "security jacket" + desc = "A sturdy black jacket with reinforced fabric. Bears insignia of NT corporate security." + icon_state = "secjacket_open" + item_state = "hos" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + armor = list(melee = 15, bullet = 10, laser = 15, energy = 5, bomb = 15, bio = 0, rad = 0, fire = 30, acid = 30) + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/openclose) + adjust_flavour = "unzip" + +/obj/item/clothing/suit/armor/hos + name = "armored coat" + desc = "A trench coat enhanced with a special alloy for some protection and style." + icon_state = "hos" + item_state = "hos" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90) + flags_inv = HIDEJUMPSUIT + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + strip_delay = 80 + +/obj/item/clothing/suit/armor/hos/alt + name = "armored trenchoat" + desc = "A trenchcoat enhanced with a special lightweight kevlar. The epitome of tactical plainclothes." + icon_state = "hostrench_open" + item_state = "hostrench_open" + flags_inv = 0 + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/openclose) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/armor/hos/jensen + name = "armored trenchcoat" + desc = "A trenchcoat augmented with a special alloy for some protection and style." + icon_state = "jensencoat" + item_state = "jensencoat" + flags_inv = 0 + sprite_sheets = null + +/obj/item/clothing/suit/armor/vest/warden + name = "Warden's armored jacket" + desc = "An armored jacket with silver rank pips and livery." + icon_state = "warden_jacket" + item_state = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|HANDS + heat_protection = UPPER_TORSO|LOWER_TORSO|HANDS + strip_delay = 70 + resistance_flags = FLAMMABLE + dog_fashion = null + +/obj/item/clothing/suit/armor/vest/warden/alt + name = "warden's jacket" + desc = "A navy-blue armored jacket with blue shoulder designations and '/Warden/' stitched into one of the chest pockets." + icon_state = "warden_jacket_alt" + +/obj/item/clothing/suit/armor/vest/capcarapace + name = "captain's carapace" + desc = "An armored vest reinforced with ceramic plates and pauldrons to provide additional protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest, although it does chafe your nipples." + icon_state = "capcarapace" + item_state = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90) + dog_fashion = null + resistance_flags = FIRE_PROOF + +/obj/item/clothing/suit/armor/vest/capcarapace/alt + name = "captain's parade jacket" + desc = "For when an armored vest isn't fashionable enough." + icon_state = "capformal" + item_state = "capspacesuit" + +/obj/item/clothing/suit/armor/riot + name = "Riot Suit" + desc = "A suit of armor with heavy padding to protect against melee attacks. Looks like it might impair movement." + icon_state = "riot" + item_state = "swat_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) + flags_inv = HIDEJUMPSUIT + strip_delay = 80 + put_on_delay = 60 + hide_tail_by_species = list("Vox") + +/obj/item/clothing/suit/armor/riot/knight + name = "plate armour" + desc = "A classic suit of plate armour, highly effective at stopping melee attacks." + icon_state = "knight_green" + item_state = "knight_green" + slowdown = 1 + +/obj/item/clothing/suit/armor/riot/knight/yellow + icon_state = "knight_yellow" + item_state = "knight_yellow" + +/obj/item/clothing/suit/armor/riot/knight/blue + icon_state = "knight_blue" + item_state = "knight_blue" + +/obj/item/clothing/suit/armor/riot/knight/red + icon_state = "knight_red" + item_state = "knight_red" + +/obj/item/clothing/suit/armor/riot/knight/templar + name = "crusader armour" + desc = "God wills it!" + icon_state = "knight_templar" + item_state = "knight_templar" + allowed = list(/obj/item/nullrod/claymore) + armor = list(melee = 25, bullet = 5, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) + +/obj/item/clothing/suit/armor/vest/durathread + name = "durathread vest" + desc = "A vest made of durathread with strips of leather acting as trauma plates." + icon_state = "durathread" + item_state = "durathread" + strip_delay = 60 + max_integrity = 200 + resistance_flags = FLAMMABLE + armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) + +/obj/item/clothing/suit/armor/bulletproof + name = "Bulletproof Vest" + desc = "A bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." + icon_state = "bulletproof" + item_state = "armor" + blood_overlay_type = "armor" + armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 70 + put_on_delay = 50 + +/obj/item/clothing/suit/armor/laserproof + name = "Ablative Armor Vest" + desc = "A vest that excels in protecting the wearer against energy projectiles." + icon_state = "armor_reflec" + item_state = "armor_reflec" + blood_overlay_type = "armor" + armor = list("melee" = 10, "bullet" = 10, "laser" = 60, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/hit_reflect_chance = 40 + +/obj/item/clothing/suit/armor/laserproof/IsReflect(var/def_zone) + if(!(def_zone in list("chest", "groin"))) //If not shot where ablative is covering you, you don't get the reflection bonus! + return 0 + if(prob(hit_reflect_chance)) + return 1 + +/obj/item/clothing/suit/armor/vest/det_suit + name = "armor" + desc = "An armored vest with a detective's badge on it." + icon_state = "detective-armor" + item_state = "armor" + blood_overlay_type = "armor" + allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/reagent_containers/spray/pepper,/obj/item/flashlight,/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/storage/fancy/cigarettes,/obj/item/lighter,/obj/item/detective_scanner,/obj/item/taperecorder) + resistance_flags = FLAMMABLE + dog_fashion = null + +//Reactive armor +/obj/item/clothing/suit/armor/reactive + name = "reactive armor" + desc = "Doesn't seem to do much for some reason." + var/active = 0 + icon_state = "reactiveoff" + item_state = "reactiveoff" + blood_overlay_type = "armor" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + actions_types = list(/datum/action/item_action/toggle) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + hit_reaction_chance = 50 + +/obj/item/clothing/suit/armor/reactive/attack_self(mob/user) + active = !(active) + if(active) + to_chat(user, "[src] is now active.") + icon_state = "reactive" + item_state = "reactive" + else + to_chat(user, "[src] is now inactive.") + icon_state = "reactiveoff" + item_state = "reactiveoff" + add_fingerprint(user) + user.update_inv_wear_suit() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/suit/armor/reactive/emp_act(severity) + active = 0 + icon_state = "reactiveoff" + item_state = "reactiveoff" + if(istype(loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/C = loc + C.update_inv_wear_suit() + ..() + +//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) +/obj/item/clothing/suit/armor/reactive/teleport + name = "reactive teleport armor" + desc = "Someone seperated our Research Director from his own head!" + var/tele_range = 6 + +/obj/item/clothing/suit/armor/reactive/teleport/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(!active) + return 0 + if(prob(hit_reaction_chance)) + var/mob/living/carbon/human/H = owner + owner.visible_message("The reactive teleport system flings [H] clear of [attack_text]!") + var/list/turfs = new/list() + for(var/turf/T in orange(tele_range, H)) + if(istype(T, /turf/space)) + continue + if(T.density) + continue + if(T.x>world.maxx-tele_range || T.xworld.maxy-tele_range || T.yThe [src] blocks the [attack_text], sending out jets of flame!") + for(var/mob/living/carbon/C in range(6, owner)) + if(C != owner) + C.fire_stacks += 8 + C.IgniteMob() + owner.fire_stacks = -20 + return 1 + return 0 + + +/obj/item/clothing/suit/armor/reactive/stealth + name = "reactive stealth armor" + +/obj/item/clothing/suit/armor/reactive/stealth/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(!active) + return 0 + if(prob(hit_reaction_chance)) + var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc) + E.Copy_Parent(owner, 50) + E.GiveTarget(owner) //so it starts running right away + E.Goto(owner, E.move_to_delay, E.minimum_distance) + owner.alpha = 0 + owner.visible_message("[owner] is hit by [attack_text] in the chest!") //We pretend to be hit, since blocking it would stop the message otherwise + spawn(40) + owner.alpha = initial(owner.alpha) + return 1 + +/obj/item/clothing/suit/armor/reactive/tesla + name = "reactive tesla armor" + +/obj/item/clothing/suit/armor/reactive/tesla/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(!active) + return 0 + if(prob(hit_reaction_chance)) + owner.visible_message("The [src] blocks the [attack_text], sending out arcs of lightning!") + for(var/mob/living/M in view(6, owner)) + if(M == owner) + continue + owner.Beam(M,icon_state="lightning[rand(1, 12)]",icon='icons/effects/effects.dmi',time=5) + M.adjustFireLoss(25) + playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1) + return 1 + +//All of the armor below is mostly unused + + +/obj/item/clothing/suit/armor/centcomm + name = "Cent. Com. armor" + desc = "A suit that protects against some damage." + icon_state = "centcom" + item_state = "centcom" + w_class = WEIGHT_CLASS_BULKY + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank/emergency_oxygen) + flags = THICKMATERIAL + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + sprite_sheets = null + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + +/obj/item/clothing/suit/armor/heavy + name = "heavy armor" + desc = "A heavily armored suit that protects against moderate damage." + icon_state = "heavy" + item_state = "swat_suit" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.90 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 3 + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + hide_tail_by_species = list("Vox") + +/obj/item/clothing/suit/armor/tdome + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + flags = THICKMATERIAL + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + hide_tail_by_species = list("Vox") + +/obj/item/clothing/suit/armor/tdome/red + name = "Red Thunderdome Armor" + desc = "Armor worn by the red Thunderdome team." + icon_state = "tdred" + item_state = "tdred" + +/obj/item/clothing/suit/armor/tdome/green + name = "Green Thunderdome Armor" + desc = "Armor worn by the green Thunderdome team." + icon_state = "tdgreen" + item_state = "tdgreen" + +//Non-hardsuit ERT armor. +/obj/item/clothing/suit/armor/vest/ert + name = "emergency response team armor" + desc = "A set of armor worn by members of the Nanotrasen Emergency Response Team." + icon_state = "ertarmor_cmd" + item_state = "armor" + armor = list(melee = 30, bullet = 30, laser = 30, energy = 30, bomb = 20, bio = 0, rad = 0, fire = 50, acid = 50) + +//Commander +/obj/item/clothing/suit/armor/vest/ert/command + name = "emergency response team commander armor" + desc = "A set of armor worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." + +//Security +/obj/item/clothing/suit/armor/vest/ert/security + name = "emergency response team security armor" + desc = "A set of armor worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." + icon_state = "ertarmor_sec" + + +/obj/item/clothing/suit/armor/vest/ert/security/paranormal + name = "emergency response team paranormal armor" + desc = "A set of armor worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." + icon_state = "knight_templar" + item_state = "knight_templar" + +//Engineer +/obj/item/clothing/suit/armor/vest/ert/engineer + name = "emergency response team engineer armor" + desc = "A set of armor worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." + icon_state = "ertarmor_eng" + +//Medical +/obj/item/clothing/suit/armor/vest/ert/medical + name = "emergency response team medical armor" + desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "ertarmor_med" + +//Janitorial +/obj/item/clothing/suit/armor/vest/ert/janitor + name = "emergency response team janitor armor" + desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "ertarmor_jan" + +//same defense as basic sec armor +/obj/item/clothing/suit/storage/lawyer/blackjacket/armored + desc = "A snappy dress jacket, reinforced with a layer of armor protecting the torso." + allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/gun/projectile/revolver, /obj/item/gun/projectile/automatic/pistol) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 40, acid = 40) + +//LAVALAND! + +/obj/item/clothing/suit/hooded/drake + name = "drake armour" + icon_state = "dragon" + item_state = "dragon" + desc = "A suit of armour fashioned from the remains of an ash drake." + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/twohanded/spear) + armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) + hoodtype = /obj/item/clothing/head/hooded/drake + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/head/hooded/drake + name = "drake helmet" + icon_state = "dragon" + item_state = "dragon" + desc = "The skull of a dragon." + armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) + heat_protection = HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/suit/hooded/goliath + name = "goliath cloak" + icon_state = "goliath_cloak" + item_state = "goliath_cloak" + desc = "A staunch, practical cape made out of numerous monster materials, it is coveted amongst exiles & hermits." + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/pickaxe, /obj/item/twohanded/spear, /obj/item/organ/internal/regenerative_core/legion, /obj/item/kitchen/knife/combat/survival) + armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) //a fair alternative to bone armor, requiring alternative materials and gaining a suit slot + hoodtype = /obj/item/clothing/head/hooded/goliath + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/head/hooded/goliath + name = "goliath cloak hood" + icon_state = "golhood" + item_state = "golhood" + desc = "A protective & concealing hood." + armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/suit/armor/bone + name = "bone armor" + desc = "A tribal armor plate, crafted from animal bone." + icon_state = "bonearmor" + item_state = "bonearmor" + blood_overlay_type = "armor" + armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm index 6a2d582d355..eff7a9448ac 100644 --- a/code/modules/clothing/suits/bio.dm +++ b/code/modules/clothing/suits/bio.dm @@ -1,111 +1,110 @@ -//Biosuit complete with shoes (in the item sprite) -/obj/item/clothing/head/bio_hood - name = "bio hood" - icon_state = "bio" - desc = "A hood that protects the head and face from biological comtaminants." - permeability_coefficient = 0.01 - flags = BLOCKHAIR | THICKMATERIAL - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - resistance_flags = ACID_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/head.dmi', - "Tajaran" = 'icons/mob/species/tajaran/head.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/suit/bio_suit - name = "bio suit" - desc = "A suit that protects against biological contamination." - icon_state = "bio" - item_state = "bio_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - flags = THICKMATERIAL - flags_size = ONESIZEFITSALL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 1 - allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/pen,/obj/item/flashlight/pen) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - strip_delay = 70 - put_on_delay = 70 - resistance_flags = ACID_PROOF - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - - -//Standard biosuit, orange stripe -/obj/item/clothing/head/bio_hood/general - icon_state = "bio_general" - -/obj/item/clothing/suit/bio_suit/general - icon_state = "bio_general" - - -//Virology biosuit, green stripe -/obj/item/clothing/head/bio_hood/virology - icon_state = "bio_virology" - -/obj/item/clothing/suit/bio_suit/virology - icon_state = "bio_virology" - - -//Security biosuit, grey with red stripe across the chest -/obj/item/clothing/head/bio_hood/security - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - icon_state = "bio_security" - -/obj/item/clothing/suit/bio_suit/security - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - icon_state = "bio_security" - - -//Janitor's biosuit, grey with purple arms -/obj/item/clothing/head/bio_hood/janitor - icon_state = "bio_janitor" - -/obj/item/clothing/suit/bio_suit/janitor - icon_state = "bio_janitor" - - -//Scientist's biosuit, white with a pink-ish hue -/obj/item/clothing/head/bio_hood/scientist - icon_state = "bio_scientist" - -/obj/item/clothing/suit/bio_suit/scientist - icon_state = "bio_scientist" - - -//CMO's biosuit, blue stripe -/obj/item/clothing/suit/bio_suit/cmo - icon_state = "bio_cmo" - -/obj/item/clothing/head/bio_hood/cmo - icon_state = "bio_cmo" - - -//Plague Dr mask can be found in clothing/masks/gasmask.dm -/obj/item/clothing/suit/bio_suit/plaguedoctorsuit - name = "Plague doctor suit" - desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." - icon_state = "plaguedoctor" - item_state = "bio_suit" - strip_delay = 40 - put_on_delay = 20 - - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - ) - hide_tail_by_species = list("Unathi, Tajaran, Vulpkanin") \ No newline at end of file +//Biosuit complete with shoes (in the item sprite) +/obj/item/clothing/head/bio_hood + name = "bio hood" + icon_state = "bio" + desc = "A hood that protects the head and face from biological comtaminants." + permeability_coefficient = 0.01 + flags = BLOCKHAIR | THICKMATERIAL + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + resistance_flags = ACID_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/head.dmi', + "Tajaran" = 'icons/mob/species/tajaran/head.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/suit/bio_suit + name = "bio suit" + desc = "A suit that protects against biological contamination." + icon_state = "bio" + item_state = "bio_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 1 + allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/pen,/obj/item/flashlight/pen) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + strip_delay = 70 + put_on_delay = 70 + resistance_flags = ACID_PROOF + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + + +//Standard biosuit, orange stripe +/obj/item/clothing/head/bio_hood/general + icon_state = "bio_general" + +/obj/item/clothing/suit/bio_suit/general + icon_state = "bio_general" + + +//Virology biosuit, green stripe +/obj/item/clothing/head/bio_hood/virology + icon_state = "bio_virology" + +/obj/item/clothing/suit/bio_suit/virology + icon_state = "bio_virology" + + +//Security biosuit, grey with red stripe across the chest +/obj/item/clothing/head/bio_hood/security + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + icon_state = "bio_security" + +/obj/item/clothing/suit/bio_suit/security + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + icon_state = "bio_security" + + +//Janitor's biosuit, grey with purple arms +/obj/item/clothing/head/bio_hood/janitor + icon_state = "bio_janitor" + +/obj/item/clothing/suit/bio_suit/janitor + icon_state = "bio_janitor" + + +//Scientist's biosuit, white with a pink-ish hue +/obj/item/clothing/head/bio_hood/scientist + icon_state = "bio_scientist" + +/obj/item/clothing/suit/bio_suit/scientist + icon_state = "bio_scientist" + + +//CMO's biosuit, blue stripe +/obj/item/clothing/suit/bio_suit/cmo + icon_state = "bio_cmo" + +/obj/item/clothing/head/bio_hood/cmo + icon_state = "bio_cmo" + + +//Plague Dr mask can be found in clothing/masks/gasmask.dm +/obj/item/clothing/suit/bio_suit/plaguedoctorsuit + name = "Plague doctor suit" + desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." + icon_state = "plaguedoctor" + item_state = "bio_suit" + strip_delay = 40 + put_on_delay = 20 + + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + ) + hide_tail_by_species = list("Unathi, Tajaran, Vulpkanin") diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index dde11f1fcee..a7bd3fb2627 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -1,385 +1,382 @@ -/* - * Job related - */ -//Paramedic -/obj/item/clothing/suit/storage/paramedic - name = "paramedic vest" - desc = "A hazard vest used in the recovery of bodies." - icon_state = "paramedic-vest" - item_state = "paramedic-vest" - allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ - /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 10, fire = 50, acid = 50) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Brig Physician -/obj/item/clothing/suit/storage/brigdoc - name = "brig physician vest" - desc = "A vest often worn by doctors caring for inmates." - icon_state = "brigphysician-vest" - item_state = "brigphysician-vest" - allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ - /obj/item/healthanalyzer, /obj/item/flashlight, \ - /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) - armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 50, acid = 50) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Botanist -/obj/item/clothing/suit/apron - name = "apron" - desc = "A basic blue apron." - icon_state = "apron" - item_state = "apron" - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - allowed = list(/obj/item/reagent_containers/spray/plantbgone,/obj/item/plant_analyzer,/obj/item/seeds,/obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/cultivator,/obj/item/reagent_containers/spray/pestspray,/obj/item/hatchet,/obj/item/storage/bag/plants) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Captain -/obj/item/clothing/suit/captunic - name = "captain's parade tunic" - desc = "Worn by a Captain to show their class." - icon_state = "captunic" - item_state = "bio_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEJUMPSUIT - flags_size = ONESIZEFITSALL - allowed = list(/obj/item/disk, /obj/item/stamp, /obj/item/reagent_containers/food/drinks/flask, /obj/item/melee, /obj/item/storage/lockbox/medal, /obj/item/flash, /obj/item/storage/box/matches, /obj/item/lighter, /obj/item/clothing/mask/cigarette, /obj/item/storage/fancy/cigarettes, /obj/item/tank/emergency_oxygen) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/captunic/capjacket - name = "captain's uniform jacket" - desc = "A less formal jacket for everyday captain use." - icon_state = "capjacket" - item_state = "bio_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEJUMPSUIT - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/mantle/armor/captain - name = "captain's cloak" - desc = "An armor-plated piece of fashion for the ruling elite. Protect your upper half in style." - icon_state = "capmantle" - item_state = "capmantle" - armor = list(melee = 50, bullet = 40, laser = 50, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) - -//Chaplain -/obj/item/clothing/suit/hooded/chaplain_hoodie - name = "chaplain hoodie" - desc = "This suit says to you 'hush'!" - icon_state = "chaplain_hoodie" - item_state = "chaplain_hoodie" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - hoodtype = /obj/item/clothing/head/hooded/chaplain_hood - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Chaplain -/obj/item/clothing/suit/hooded/nun - name = "nun robe" - desc = "Maximum piety in this star system." - icon_state = "nun" - item_state = "nun" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS - flags_inv = HIDESHOES|HIDEJUMPSUIT - hoodtype = /obj/item/clothing/head/hooded/nun_hood - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Chaplain -/obj/item/clothing/suit/hooded/monk - name = "monk robe" - desc = "Wooden board not included." - icon_state = "monkrobe" - item_state = "monkrobe" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS - flags_inv = HIDESHOES|HIDEJUMPSUIT - hoodtype = /obj/item/clothing/head/hooded/monk_hood - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - -/obj/item/clothing/suit/witchhunter - name = "witchhunter garb" - desc = "Dosen't weigh the same a a duck." - icon_state = "witchhunter" - item_state = "witchhunter" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - - - //Chef -/obj/item/clothing/suit/toggle/chef - name = "chef's apron" - - -//Chef -/obj/item/clothing/suit/chef - name = "chef's apron" - desc = "An apron used by a high class chef." - icon_state = "chef" - item_state = "chef" - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - allowed = list (/obj/item/kitchen/knife) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Chef -/obj/item/clothing/suit/chef/classic - name = "classic chef's apron" - desc = "A basic, dull, white chef's apron." - icon_state = "apronchef" - item_state = "apronchef" - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - allowed = list(/obj/item/kitchen/knife) - -//Chief Engineer -/obj/item/clothing/suit/mantle/chief_engineer - name = "chief engineer's mantle" - desc = "A slick, authoritative cloak designed for the Chief Engineer." - icon_state = "cemantle" - item_state = "cemantle" - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd) - -//Chief Medical Officer -/obj/item/clothing/suit/mantle/labcoat/chief_medical_officer - name = "chief medical officer's mantle" - desc = "An absorbent, clean cover found on the shoulders of the Chief Medical Officer." - icon_state = "cmomantle" - item_state = "cmomantle" - -//Detective -/obj/item/clothing/suit/storage/det_suit - name = "coat" - desc = "An 18th-century multi-purpose trenchcoat. Someone who wears this means serious business." - icon_state = "detective" - item_state = "det_suit" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/reagent_containers/spray/pepper, /obj/item/flashlight, /obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/detective_scanner, /obj/item/taperecorder) - armor = list("melee" = 25, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45) - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_size = ONESIZEFITSALL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Forensics -/obj/item/clothing/suit/storage/det_suit/forensics - name = "jacket" - desc = "A forensics technician jacket." - item_state = "det_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/suit/storage/det_suit/forensics/red - name = "red jacket" - desc = "A red forensics technician jacket." - icon_state = "forensics_red" - -/obj/item/clothing/suit/storage/det_suit/forensics/blue - name = "blue jacket" - desc = "A blue forensics technician jacket." - icon_state = "forensics_blue" - -//Blueshield -/obj/item/clothing/suit/storage/blueshield - name = "blueshield coat" - desc = "NT deluxe ripoff. You finally have your own coat." - icon_state = "blueshieldcoat" - item_state = "blueshieldcoat" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic) - armor = list(melee = 25, bullet = 10, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 45) - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_size = ONESIZEFITSALL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Engineering -/obj/item/clothing/suit/storage/hazardvest - name = "hazard vest" - desc = "A high-visibility vest used in work zones." - icon_state = "hazard" - item_state = "hazard" - blood_overlay_type = "armor" - allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen) - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Lawyer -/obj/item/clothing/suit/storage/lawyer - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/storage/lawyer/blackjacket - name = "black suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_black_open" - item_state = "suitjacket_black_open" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/storage/lawyer/bluejacket - name = "blue suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_blue_open" - item_state = "suitjacket_blue_open" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/storage/lawyer/purpjacket - name = "purple suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_purp" - item_state = "suitjacket_purp" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - -//Head of Security -/obj/item/clothing/suit/mantle/armor - name = "armored shawl" - desc = "A reinforced shawl, worn by the Head of Security. Do you dare take up their mantle?" - icon_state = "hosmantle" - item_state = "hosmantle" - allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic, /obj/item/kitchen/knife/combat) - armor = list(melee = 30, bullet = 30, laser = 30, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO|ARMS - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 40 - resistance_flags = NONE - -//Head of Personnel -/obj/item/clothing/suit/mantle/armor/head_of_personnel - name = "head of personnel's shawl" - desc = "An armored shawl for the head of personnel. It's remarkably well kept." - icon_state = "hopmantle" - item_state = "hopmantle" - armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) - -//Internal Affairs -/obj/item/clothing/suit/storage/internalaffairs - name = "\improper Internal Affairs jacket" - desc = "A smooth black jacket." - icon_state = "ia_jacket_open" - item_state = "ia_jacket_open" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/storage/ntrep - name = "\improper Nanotrasen Representative jacket" - desc = "A fancy black jacket; standard issue to Nanotrasen Representatives." - icon_state = "ntrep" - item_state = "ntrep" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Medical -/obj/item/clothing/suit/storage/fr_jacket - name = "first responder jacket" - desc = "A high-visibility jacket worn by medical first responders." - icon_state = "fr_jacket_open" - item_state = "fr_jacket_open" - blood_overlay_type = "armor" - allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ - /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Mime -/obj/item/clothing/suit/suspenders - name = "suspenders" - desc = "They suspend the illusion of the mime's play." - icon = 'icons/obj/clothing/belts.dmi' - icon_state = "suspenders" - blood_overlay_type = "armor" //it's the less thing that I can put here - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/suspenders/nodrop - flags = NODROP - -// Surgeon -/obj/item/clothing/suit/apron/surgical - name = "surgical apron" - desc = "A sterile blue surgical apron." - icon_state = "surgical" - item_state = "surgical" - allowed = list(/obj/item/scalpel, /obj/item/surgical_drapes, /obj/item/cautery, /obj/item/hemostat, /obj/item/retractor) - -//Research Director -/obj/item/clothing/suit/mantle/labcoat - name = "research director's mantle" - desc = "A tweed mantle, worn by the Research Director. Smells like science." - icon_state = "rdmantle" - item_state = "rdmantle" - allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/food/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/rad_laser) - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 50, rad = 0, fire = 50, acid = 50) +/* + * Job related + */ +//Paramedic +/obj/item/clothing/suit/storage/paramedic + name = "paramedic vest" + desc = "A hazard vest used in the recovery of bodies." + icon_state = "paramedic-vest" + item_state = "paramedic-vest" + allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator, /obj/item/reagent_containers/syringe, + /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 10, fire = 50, acid = 50) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Brig Physician +/obj/item/clothing/suit/storage/brigdoc + name = "brig physician vest" + desc = "A vest often worn by doctors caring for inmates." + icon_state = "brigphysician-vest" + item_state = "brigphysician-vest" + allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator, /obj/item/reagent_containers/syringe, + /obj/item/healthanalyzer, /obj/item/flashlight, \ + /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) + armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 50, acid = 50) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Botanist +/obj/item/clothing/suit/apron + name = "apron" + desc = "A basic blue apron." + icon_state = "apron" + item_state = "apron" + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + allowed = list(/obj/item/reagent_containers/spray/plantbgone,/obj/item/plant_analyzer,/obj/item/seeds,/obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/cultivator,/obj/item/reagent_containers/spray/pestspray,/obj/item/hatchet,/obj/item/storage/bag/plants) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Captain +/obj/item/clothing/suit/captunic + name = "captain's parade tunic" + desc = "Worn by a Captain to show their class." + icon_state = "captunic" + item_state = "bio_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT + allowed = list(/obj/item/disk, /obj/item/stamp, /obj/item/reagent_containers/food/drinks/flask, /obj/item/melee, /obj/item/storage/lockbox/medal, /obj/item/flash, /obj/item/storage/box/matches, /obj/item/lighter, /obj/item/clothing/mask/cigarette, /obj/item/storage/fancy/cigarettes, /obj/item/tank/emergency_oxygen) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/captunic/capjacket + name = "captain's uniform jacket" + desc = "A less formal jacket for everyday captain use." + icon_state = "capjacket" + item_state = "bio_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/mantle/armor/captain + name = "captain's cloak" + desc = "An armor-plated piece of fashion for the ruling elite. Protect your upper half in style." + icon_state = "capmantle" + item_state = "capmantle" + armor = list(melee = 50, bullet = 40, laser = 50, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) + +//Chaplain +/obj/item/clothing/suit/hooded/chaplain_hoodie + name = "chaplain hoodie" + desc = "This suit says to you 'hush'!" + icon_state = "chaplain_hoodie" + item_state = "chaplain_hoodie" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + hoodtype = /obj/item/clothing/head/hooded/chaplain_hood + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Chaplain +/obj/item/clothing/suit/hooded/nun + name = "nun robe" + desc = "Maximum piety in this star system." + icon_state = "nun" + item_state = "nun" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS + flags_inv = HIDESHOES|HIDEJUMPSUIT + hoodtype = /obj/item/clothing/head/hooded/nun_hood + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Chaplain +/obj/item/clothing/suit/hooded/monk + name = "monk robe" + desc = "Wooden board not included." + icon_state = "monkrobe" + item_state = "monkrobe" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS + flags_inv = HIDESHOES|HIDEJUMPSUIT + hoodtype = /obj/item/clothing/head/hooded/monk_hood + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + +/obj/item/clothing/suit/witchhunter + name = "witchhunter garb" + desc = "Dosen't weigh the same a a duck." + icon_state = "witchhunter" + item_state = "witchhunter" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + + + //Chef +/obj/item/clothing/suit/toggle/chef + name = "chef's apron" + + +//Chef +/obj/item/clothing/suit/chef + name = "chef's apron" + desc = "An apron used by a high class chef." + icon_state = "chef" + item_state = "chef" + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + allowed = list (/obj/item/kitchen/knife) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Chef +/obj/item/clothing/suit/chef/classic + name = "classic chef's apron" + desc = "A basic, dull, white chef's apron." + icon_state = "apronchef" + item_state = "apronchef" + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + allowed = list(/obj/item/kitchen/knife) + +//Chief Engineer +/obj/item/clothing/suit/mantle/chief_engineer + name = "chief engineer's mantle" + desc = "A slick, authoritative cloak designed for the Chief Engineer." + icon_state = "cemantle" + item_state = "cemantle" + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd) + +//Chief Medical Officer +/obj/item/clothing/suit/mantle/labcoat/chief_medical_officer + name = "chief medical officer's mantle" + desc = "An absorbent, clean cover found on the shoulders of the Chief Medical Officer." + icon_state = "cmomantle" + item_state = "cmomantle" + +//Detective +/obj/item/clothing/suit/storage/det_suit + name = "coat" + desc = "An 18th-century multi-purpose trenchcoat. Someone who wears this means serious business." + icon_state = "detective" + item_state = "det_suit" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/reagent_containers/spray/pepper, /obj/item/flashlight, /obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/detective_scanner, /obj/item/taperecorder) + armor = list("melee" = 25, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45) + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Forensics +/obj/item/clothing/suit/storage/det_suit/forensics + name = "jacket" + desc = "A forensics technician jacket." + item_state = "det_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/suit/storage/det_suit/forensics/red + name = "red jacket" + desc = "A red forensics technician jacket." + icon_state = "forensics_red" + +/obj/item/clothing/suit/storage/det_suit/forensics/blue + name = "blue jacket" + desc = "A blue forensics technician jacket." + icon_state = "forensics_blue" + +//Blueshield +/obj/item/clothing/suit/storage/blueshield + name = "blueshield coat" + desc = "NT deluxe ripoff. You finally have your own coat." + icon_state = "blueshieldcoat" + item_state = "blueshieldcoat" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic) + armor = list(melee = 25, bullet = 10, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 45) + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Engineering +/obj/item/clothing/suit/storage/hazardvest + name = "hazard vest" + desc = "A high-visibility vest used in work zones." + icon_state = "hazard" + item_state = "hazard" + blood_overlay_type = "armor" + allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen) + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Lawyer +/obj/item/clothing/suit/storage/lawyer + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/storage/lawyer/blackjacket + name = "black suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_black_open" + item_state = "suitjacket_black_open" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/storage/lawyer/bluejacket + name = "blue suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_blue_open" + item_state = "suitjacket_blue_open" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/storage/lawyer/purpjacket + name = "purple suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_purp" + item_state = "suitjacket_purp" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + +//Head of Security +/obj/item/clothing/suit/mantle/armor + name = "armored shawl" + desc = "A reinforced shawl, worn by the Head of Security. Do you dare take up their mantle?" + icon_state = "hosmantle" + item_state = "hosmantle" + allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic, /obj/item/kitchen/knife/combat) + armor = list(melee = 30, bullet = 30, laser = 30, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO|ARMS + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 40 + resistance_flags = NONE + +//Head of Personnel +/obj/item/clothing/suit/mantle/armor/head_of_personnel + name = "head of personnel's shawl" + desc = "An armored shawl for the head of personnel. It's remarkably well kept." + icon_state = "hopmantle" + item_state = "hopmantle" + armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) + +//Internal Affairs +/obj/item/clothing/suit/storage/internalaffairs + name = "\improper Internal Affairs jacket" + desc = "A smooth black jacket." + icon_state = "ia_jacket_open" + item_state = "ia_jacket_open" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/storage/ntrep + name = "\improper Nanotrasen Representative jacket" + desc = "A fancy black jacket; standard issue to Nanotrasen Representatives." + icon_state = "ntrep" + item_state = "ntrep" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Medical +/obj/item/clothing/suit/storage/fr_jacket + name = "first responder jacket" + desc = "A high-visibility jacket worn by medical first responders." + icon_state = "fr_jacket_open" + item_state = "fr_jacket_open" + blood_overlay_type = "armor" + allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator, /obj/item/reagent_containers/syringe, + /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Mime +/obj/item/clothing/suit/suspenders + name = "suspenders" + desc = "They suspend the illusion of the mime's play." + icon = 'icons/obj/clothing/belts.dmi' + icon_state = "suspenders" + blood_overlay_type = "armor" //it's the less thing that I can put here + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/suspenders/nodrop + flags = NODROP + +// Surgeon +/obj/item/clothing/suit/apron/surgical + name = "surgical apron" + desc = "A sterile blue surgical apron." + icon_state = "surgical" + item_state = "surgical" + allowed = list(/obj/item/scalpel, /obj/item/surgical_drapes, /obj/item/cautery, /obj/item/hemostat, /obj/item/retractor) + +//Research Director +/obj/item/clothing/suit/mantle/labcoat + name = "research director's mantle" + desc = "A tweed mantle, worn by the Research Director. Smells like science." + icon_state = "rdmantle" + item_state = "rdmantle" + allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/food/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/rad_laser) + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 50, rad = 0, fire = 50, acid = 50) diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm index 36157df8bc8..5281a32676e 100644 --- a/code/modules/clothing/suits/labcoat.dm +++ b/code/modules/clothing/suits/labcoat.dm @@ -1,65 +1,65 @@ -/obj/item/clothing/suit/storage/labcoat - name = "labcoat" - desc = "A suit that protects against minor chemical spills." - icon_state = "labcoat_open" - item_state = "labcoat_open" - ignore_suitadjust = 0 - suit_adjusted = 1 - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - allowed = list(/obj/item/analyzer,/obj/item/stack/medical,/obj/item/dnainjector,/obj/item/reagent_containers/dropper,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/hypospray,/obj/item/healthanalyzer,/obj/item/flashlight/pen,/obj/item/reagent_containers/glass/bottle,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/food/pill,/obj/item/storage/pill_bottle,/obj/item/paper,/obj/item/rad_laser) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50) - species_exception = list(/datum/species/golem) - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Grey" = 'icons/mob/species/grey/suit.dmi' - ) - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/storage/labcoat/cmo - name = "chief medical officer's labcoat" - desc = "Bluer than the standard model." - icon_state = "labcoat_cmo_open" - item_state = "labcoat_cmo_open" - -/obj/item/clothing/suit/storage/labcoat/mad - name = "mad scientist's labcoat" - desc = "It makes you look capable of konking someone on the noggin and shooting them into space." - icon_state = "labcoat_green_open" - item_state = "labcoat_green_open" - -/obj/item/clothing/suit/storage/labcoat/genetics - name = "geneticist labcoat" - desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." - icon_state = "labcoat_gen_open" - item_state = "labcoat_gen_open" - -/obj/item/clothing/suit/storage/labcoat/chemist - name = "chemist labcoat" - desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." - icon_state = "labcoat_chem_open" - item_state = "labcoat_chem_open" - -/obj/item/clothing/suit/storage/labcoat/virologist - name = "virologist labcoat" - desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." - icon_state = "labcoat_vir_open" - -/obj/item/clothing/suit/storage/labcoat/science - name = "scientist labcoat" - desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." - icon_state = "labcoat_tox_open" - item_state = "labcoat_tox_open" - -/obj/item/clothing/suit/storage/labcoat/mortician - name = "coroner labcoat" - desc = "A suit that protects against minor chemical spills. Has a black stripe on the shoulder." - icon_state = "labcoat_mort_open" - item_state = "labcoat_mort_open" - -/obj/item/clothing/suit/storage/labcoat/emt - name = "EMT labcoat" - desc = "A comfortable suit for paramedics. Has dark colours." - icon_state = "labcoat_emt_open" - item_state = "labcoat_emt_open" +/obj/item/clothing/suit/storage/labcoat + name = "labcoat" + desc = "A suit that protects against minor chemical spills." + icon_state = "labcoat_open" + item_state = "labcoat_open" + ignore_suitadjust = 0 + suit_adjusted = 1 + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + allowed = list(/obj/item/analyzer,/obj/item/stack/medical,/obj/item/dnainjector,/obj/item/reagent_containers/dropper,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/hypospray,/obj/item/reagent_containers/applicator,/obj/item/healthanalyzer,/obj/item/flashlight/pen,/obj/item/reagent_containers/glass/bottle,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/food/pill,/obj/item/storage/pill_bottle,/obj/item/paper,/obj/item/rad_laser) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50) + species_exception = list(/datum/species/golem) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Grey" = 'icons/mob/species/grey/suit.dmi' + ) + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/storage/labcoat/cmo + name = "chief medical officer's labcoat" + desc = "Bluer than the standard model." + icon_state = "labcoat_cmo_open" + item_state = "labcoat_cmo_open" + +/obj/item/clothing/suit/storage/labcoat/mad + name = "mad scientist's labcoat" + desc = "It makes you look capable of konking someone on the noggin and shooting them into space." + icon_state = "labcoat_green_open" + item_state = "labcoat_green_open" + +/obj/item/clothing/suit/storage/labcoat/genetics + name = "geneticist labcoat" + desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." + icon_state = "labcoat_gen_open" + item_state = "labcoat_gen_open" + +/obj/item/clothing/suit/storage/labcoat/chemist + name = "chemist labcoat" + desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." + icon_state = "labcoat_chem_open" + item_state = "labcoat_chem_open" + +/obj/item/clothing/suit/storage/labcoat/virologist + name = "virologist labcoat" + desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." + icon_state = "labcoat_vir_open" + +/obj/item/clothing/suit/storage/labcoat/science + name = "scientist labcoat" + desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." + icon_state = "labcoat_tox_open" + item_state = "labcoat_tox_open" + +/obj/item/clothing/suit/storage/labcoat/mortician + name = "coroner labcoat" + desc = "A suit that protects against minor chemical spills. Has a black stripe on the shoulder." + icon_state = "labcoat_mort_open" + item_state = "labcoat_mort_open" + +/obj/item/clothing/suit/storage/labcoat/emt + name = "EMT labcoat" + desc = "A comfortable suit for paramedics. Has dark colours." + icon_state = "labcoat_emt_open" + item_state = "labcoat_emt_open" diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 1a2cdbd31a2..7e9e698d185 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -383,7 +383,7 @@ name = "security winter coat" icon_state = "wintercoat_sec" item_state = "coatsecurity" - armor = list("melee" = 25, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45) + armor = list("melee" = 15, "bullet" = 10, "laser" = 15, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic) hoodtype = /obj/item/clothing/head/hooded/winterhood/security @@ -394,7 +394,7 @@ name = "medical winter coat" icon_state = "wintercoat_med" item_state = "coatmedical" - allowed = list(/obj/item/analyzer, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic) + allowed = list(/obj/item/analyzer, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator,/obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic) armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45) hoodtype = /obj/item/clothing/head/hooded/winterhood/medical @@ -405,7 +405,7 @@ name = "science winter coat" icon_state = "wintercoat_sci" item_state = "coatscience" - allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic) + allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator,/obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic) armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 10, bio = 0, rad = 0, fire = 0, acid = 0) hoodtype = /obj/item/clothing/head/hooded/winterhood/science @@ -541,7 +541,6 @@ item_state = "straight_jacket" body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - flags_size = ONESIZEFITSALL strip_delay = 60 breakouttime = 3000 @@ -690,7 +689,6 @@ item_state = "xenos_helm" body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - flags_size = ONESIZEFITSALL //swimsuit /obj/item/clothing/under/swimsuit/ @@ -733,7 +731,6 @@ w_class = WEIGHT_CLASS_BULKY gas_transfer_coefficient = 0.01 permeability_coefficient = 0.01 - flags_size = ONESIZEFITSALL body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/pen,/obj/item/flashlight/pen) armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 50) @@ -780,7 +777,6 @@ max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT strip_delay = 60 put_on_delay = 40 - flags_size = ONESIZEFITSALL armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) //End of inheritance from Security armour. @@ -958,7 +954,8 @@ if(linked_staff.faith >= 100) //if the linked staff is fully recharged, do nothing return - if(!linked_staff in range(3, get_turf(src))) //staff won't charge at range (to prevent it from being handed off / stolen and used) + // Do not allow the staff to recharge if it's more than 3 tiles away from the robe. If get_dist returns 0, the robe and the staff in the same tile. + if(!(get_dist(H, linked_staff) <= 3)) if(prob(10)) //10% chance per process should avoid being too spammy, can tweak if it ends up still being too frequent. to_chat(H, "Your staff is unable to charge at this range. Get closer!") return diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm index 0c77183b578..9a675742b5e 100644 --- a/code/modules/clothing/suits/storage.dm +++ b/code/modules/clothing/suits/storage.dm @@ -25,8 +25,8 @@ return pockets.attackby(W, user, params) /obj/item/clothing/suit/storage/emp_act(severity) - pockets.emp_act(severity) ..() + pockets.emp_act(severity) /obj/item/clothing/suit/storage/hear_talk(mob/M, list/message_pieces) pockets.hear_talk(M, message_pieces) diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm index 576b3e6c552..c329d8a0033 100644 --- a/code/modules/clothing/suits/toggles.dm +++ b/code/modules/clothing/suits/toggles.dm @@ -74,4 +74,4 @@ H.update_inv_wear_suit() playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1) else - RemoveHelmet() \ No newline at end of file + RemoveHelmet() diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index 87df2c38b33..308380e4c55 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -1,159 +1,159 @@ -/* - * Contains: - * Fire protection - * Bomb protection - * Radiation protection - */ - -/* - * Fire protection - */ - -/obj/item/clothing/suit/fire - name = "emergency firesuit" - desc = "A suit that protects against fire and heat." - icon_state = "fire" - item_state = "fire_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen,/obj/item/extinguisher) - slowdown = 1 - armor = list("melee" = 15, "bullet" = 5, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 10, "rad" = 20, "fire" = 100, "acid" = 50) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 60 - resistance_flags = FIRE_PROOF - -/obj/item/clothing/suit/fire/firefighter - icon_state = "firesuit" - item_state = "firefighter" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/fire/heavy - name = "firesuit" - desc = "A suit that protects against extreme fire and heat." - //icon_state = "thermal" - item_state = "ro_suit" - w_class = WEIGHT_CLASS_BULKY - slowdown = 1.5 - -/obj/item/clothing/suit/fire/atmos - name = "firesuit" - desc = "An expensive firesuit that protects against even the most deadly of station fires. Designed to protect even if the wearer is set aflame." - icon_state = "atmos_firesuit" - item_state = "firesuit_atmos" - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/* - * Bomb protection - */ -/obj/item/clothing/head/bomb_hood - name = "bomb hood" - desc = "Use in case of bomb." - icon_state = "bombsuit" - flags = BLOCKHAIR | THICKMATERIAL - armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - cold_protection = HEAD - min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT - strip_delay = 70 - put_on_delay = 70 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/suit/bomb_suit - name = "bomb suit" - desc = "A suit designed for safety when handling explosives." - icon_state = "bombsuit" - item_state = "bombsuit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 2 - armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) - flags_inv = HIDEJUMPSUIT|HIDETAIL - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - strip_delay = 70 - put_on_delay = 70 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/head/bomb_hood/security - icon_state = "bombsuitsec" - item_state = "bombsuitsec" - - -/obj/item/clothing/suit/bomb_suit/security - icon_state = "bombsuitsec" - item_state = "bombsuitsec" - allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs) - -/* - * Radiation protection - */ -/obj/item/clothing/head/radiation - name = "Radiation Hood" - icon_state = "rad" - desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" - flags = BLOCKHAIR|THICKMATERIAL - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) - strip_delay = 60 - put_on_delay = 60 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/suit/radiation - name = "Radiation suit" - desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." - icon_state = "rad" - item_state = "rad_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen) - slowdown = 1.5 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) - flags_inv = HIDEJUMPSUIT|HIDETAIL - strip_delay = 60 - put_on_delay = 60 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) +/* + * Contains: + * Fire protection + * Bomb protection + * Radiation protection + */ + +/* + * Fire protection + */ + +/obj/item/clothing/suit/fire + name = "emergency firesuit" + desc = "A suit that protects against fire and heat." + icon_state = "fire" + item_state = "fire_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen,/obj/item/extinguisher) + slowdown = 1 + armor = list("melee" = 15, "bullet" = 5, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 10, "rad" = 20, "fire" = 100, "acid" = 50) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 60 + resistance_flags = FIRE_PROOF + +/obj/item/clothing/suit/fire/firefighter + icon_state = "firesuit" + item_state = "firefighter" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/fire/heavy + name = "firesuit" + desc = "A suit that protects against extreme fire and heat." + //icon_state = "thermal" + item_state = "ro_suit" + w_class = WEIGHT_CLASS_BULKY + slowdown = 1.5 + +/obj/item/clothing/suit/fire/atmos + name = "firesuit" + desc = "An expensive firesuit that protects against even the most deadly of station fires. Designed to protect even if the wearer is set aflame." + icon_state = "atmos_firesuit" + item_state = "firesuit_atmos" + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/* + * Bomb protection + */ +/obj/item/clothing/head/bomb_hood + name = "bomb hood" + desc = "Use in case of bomb." + icon_state = "bombsuit" + flags = BLOCKHAIR | THICKMATERIAL + armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT + strip_delay = 70 + put_on_delay = 70 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/suit/bomb_suit + name = "bomb suit" + desc = "A suit designed for safety when handling explosives." + icon_state = "bombsuit" + item_state = "bombsuit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 2 + armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + flags_inv = HIDEJUMPSUIT|HIDETAIL + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + strip_delay = 70 + put_on_delay = 70 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/head/bomb_hood/security + icon_state = "bombsuitsec" + item_state = "bombsuitsec" + + +/obj/item/clothing/suit/bomb_suit/security + icon_state = "bombsuitsec" + item_state = "bombsuitsec" + allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs) + +/* + * Radiation protection + */ +/obj/item/clothing/head/radiation + name = "Radiation Hood" + icon_state = "rad" + desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" + flags = BLOCKHAIR|THICKMATERIAL + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) + strip_delay = 60 + put_on_delay = 60 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/suit/radiation + name = "Radiation suit" + desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." + icon_state = "rad" + item_state = "rad_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen) + slowdown = 1.5 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) + flags_inv = HIDEJUMPSUIT|HIDETAIL + strip_delay = 60 + put_on_delay = 60 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm index 09fab3322fb..0c3a0e92310 100644 --- a/code/modules/clothing/suits/wiz_robe.dm +++ b/code/modules/clothing/suits/wiz_robe.dm @@ -1,204 +1,240 @@ -/obj/item/clothing/head/wizard - name = "wizard hat" - desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." - icon_state = "wizard" - gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE - permeability_coefficient = 0.01 - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - //Not given any special protective value since the magic robes are full-body protection --NEO - strip_delay = 50 - put_on_delay = 50 - magical = TRUE - dog_fashion = /datum/dog_fashion/head/blue_wizard - -/obj/item/clothing/head/wizard/red - name = "red wizard hat" - desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." - icon_state = "redwizard" - dog_fashion = /datum/dog_fashion/head/red_wizard - -/obj/item/clothing/head/wizard/black - name = "black wizard hat" - desc = "Strange-looking black hat-wear that most certainly belongs to a real skeleton. Spooky." - icon_state = "blackwizard" - dog_fashion = null - - -/obj/item/clothing/head/wizard/clown - name = "purple wizard hat" - desc = "Strange-looking purple hat-wear that most certainly belongs to a real magic user." - icon_state = "wizhatclown" - item_state = "wizhatclown" // cheating - dog_fashion = null - -/obj/item/clothing/head/wizard/fake - name = "wizard hat" - desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." - icon_state = "wizard-fake" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - magical = FALSE - resistance_flags = FLAMMABLE - dog_fashion = /datum/dog_fashion/head/blue_wizard - -/obj/item/clothing/head/wizard/marisa - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - dog_fashion = null - -/obj/item/clothing/head/wizard/magus - name = "Magus Helm" - desc = "A mysterious helmet that hums with an unearthly power" - icon_state = "magus" - item_state = "magus" - dog_fashion = null - -/obj/item/clothing/head/wizard/amp - name = "psychic amplifier" - desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." - icon_state = "amp" - dog_fashion = null - -/obj/item/clothing/suit/wizrobe - name = "wizard robe" - desc = "A magnificant, gem-lined robe that seems to radiate power." - icon_state = "wizard" - item_state = "wizrobe" - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) - allowed = list(/obj/item/teleportation_scroll) - flags_inv = HIDEJUMPSUIT - strip_delay = 50 - put_on_delay = 50 - resistance_flags = FIRE_PROOF | ACID_PROOF - magical = TRUE - -/obj/item/clothing/suit/wizrobe/red - name = "red wizard robe" - desc = "A magnificant, red, gem-lined robe that seems to radiate power." - icon_state = "redwizard" - item_state = "redwizrobe" - -/obj/item/clothing/suit/wizrobe/black - name = "black wizard robe" - desc = "An unnerving black gem-lined robe that reeks of death and decay." - icon_state = "blackwizard" - item_state = "blackwizrobe" - -/obj/item/clothing/suit/wizrobe/clown - name = "Clown Robe" - desc = "A set of armoured robes that seem to radiate a dark power. That, and bad fashion decisions." - icon_state = "wizzclown" - item_state = "wizzclown" - -/obj/item/clothing/suit/wizrobe/marisa - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - item_state = "marisarobe" - -/obj/item/clothing/suit/wizrobe/magusblue - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusblue" - item_state = "magusblue" - -/obj/item/clothing/suit/wizrobe/magusred - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusred" - item_state = "magusred" - -/obj/item/clothing/suit/wizrobe/psypurple - name = "purple robes" - desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." - icon_state = "psyamp" - item_state = "psyamp" - -/obj/item/clothing/suit/wizrobe/fake - name = "wizard robe" - desc = "A rather dull, blue robe meant to mimick real wizard robes." - icon_state = "wizard-fake" - item_state = "wizrobe" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - resistance_flags = FLAMMABLE - magical = FALSE - -/obj/item/clothing/head/wizard/marisa/fake - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - resistance_flags = FLAMMABLE - magical = FALSE - -/obj/item/clothing/suit/wizrobe/marisa/fake - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - item_state = "marisarobe" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - resistance_flags = FLAMMABLE - magical = FALSE - -//Shielded Armour - -/obj/item/clothing/suit/space/hardsuit/shielded/wizard - name = "battlemage armour" - desc = "Not all wizards are afraid of getting up close and personal." - icon_state = "hardsuit-wiz" - item_state = "wiz_hardsuit" - recharge_rate = 0 - current_charges = 15 - recharge_cooldown = INFINITY - shield_state = "shield-red" - shield_on = "shield-red" - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard - armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) - slowdown = 0 - resistance_flags = FIRE_PROOF | ACID_PROOF - magical = TRUE - -/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard - name = "battlemage helmet" - desc = "A suitably impressive helmet." - icon_state = "hardsuit0-wiz" - item_state = "wiz_helm" - item_color = "wiz" - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) - actions_types = list() //No inbuilt light - resistance_flags = FIRE_PROOF | ACID_PROOF - magical = TRUE - -/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard/attack_self(mob/user) - return - -/obj/item/wizard_armour_charge - name = "battlemage shield charges" - desc = "A powerful rune that will increase the number of hits a suit of battlemage armour can take before failing.." - icon = 'icons/effects/effects.dmi' - icon_state = "electricity2" - -/obj/item/wizard_armour_charge/afterattack(obj/item/clothing/suit/space/hardsuit/shielded/wizard/W, mob/user) - . = ..() - if(!istype(W)) - to_chat(user, "The rune can only be used on battlemage armour!") - return - W.current_charges += 8 - to_chat(user, "You charge [W]. It can now absorb [W.current_charges] hits.") - qdel(src) \ No newline at end of file +/obj/item/clothing/head/wizard + name = "wizard hat" + desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." + icon_state = "wizard" + gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE + permeability_coefficient = 0.01 + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + //Not given any special protective value since the magic robes are full-body protection --NEO + strip_delay = 50 + put_on_delay = 50 + magical = TRUE + dog_fashion = /datum/dog_fashion/head/blue_wizard + +/obj/item/clothing/head/wizard/red + name = "red wizard hat" + desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." + icon_state = "redwizard" + dog_fashion = /datum/dog_fashion/head/red_wizard + +/obj/item/clothing/head/wizard/black + name = "black wizard hat" + desc = "Strange-looking black hat-wear that most certainly belongs to a real skeleton. Spooky." + icon_state = "blackwizard" + dog_fashion = null + +/obj/item/clothing/head/wizard/clown + name = "purple wizard hat" + desc = "Strange-looking purple hat-wear that most certainly belongs to a real magic user." + icon_state = "wizhatclown" + item_state = "wizhatclown" // cheating + dog_fashion = null + +/obj/item/clothing/head/wizard/mime + name = "magical beret" + desc = "A magical red beret." + icon_state = "wizhatmime" + item_state = "wizhatmime" + dog_fashion = null + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Drask" = 'icons/mob/species/drask/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/wizard/fake + name = "wizard hat" + desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." + icon_state = "wizard-fake" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + magical = FALSE + resistance_flags = FLAMMABLE + dog_fashion = /datum/dog_fashion/head/blue_wizard + +/obj/item/clothing/head/wizard/marisa + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + dog_fashion = null + +/obj/item/clothing/head/wizard/magus + name = "Magus Helm" + desc = "A mysterious helmet that hums with an unearthly power" + icon_state = "magus" + item_state = "magus" + dog_fashion = null + +/obj/item/clothing/head/wizard/amp + name = "psychic amplifier" + desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." + icon_state = "amp" + dog_fashion = null + +/obj/item/clothing/suit/wizrobe + name = "wizard robe" + desc = "A magnificant, gem-lined robe that seems to radiate power." + icon_state = "wizard" + item_state = "wizrobe" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) + allowed = list(/obj/item/teleportation_scroll) + flags_inv = HIDEJUMPSUIT + strip_delay = 50 + put_on_delay = 50 + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE + +/obj/item/clothing/suit/wizrobe/red + name = "red wizard robe" + desc = "A magnificant, red, gem-lined robe that seems to radiate power." + icon_state = "redwizard" + item_state = "redwizrobe" + +/obj/item/clothing/suit/wizrobe/black + name = "black wizard robe" + desc = "An unnerving black gem-lined robe that reeks of death and decay." + icon_state = "blackwizard" + item_state = "blackwizrobe" + +/obj/item/clothing/suit/wizrobe/clown + name = "clown robe" + desc = "A set of armoured robes that seem to radiate a dark power. That, and bad fashion decisions." + icon_state = "wizzclown" + item_state = "wizzclown" + +/obj/item/clothing/suit/wizrobe/mime + name = "mime robe" + desc = "Red, black, and white robes. There is not much else to say about them." + icon_state = "wizzmime" + item_state = "wizzmime" + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Drask" = 'icons/mob/species/drask/suit.dmi', + "Grey" = 'icons/mob/species/grey/suit.dmi' + ) + +/obj/item/clothing/suit/wizrobe/marisa + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + item_state = "marisarobe" + +/obj/item/clothing/suit/wizrobe/magusblue + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusblue" + item_state = "magusblue" + +/obj/item/clothing/suit/wizrobe/magusred + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusred" + item_state = "magusred" + +/obj/item/clothing/suit/wizrobe/psypurple + name = "purple robes" + desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." + icon_state = "psyamp" + item_state = "psyamp" + +/obj/item/clothing/suit/wizrobe/fake + name = "wizard robe" + desc = "A rather dull, blue robe meant to mimick real wizard robes." + icon_state = "wizard-fake" + item_state = "wizrobe" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + resistance_flags = FLAMMABLE + magical = FALSE + +/obj/item/clothing/head/wizard/marisa/fake + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + resistance_flags = FLAMMABLE + magical = FALSE + +/obj/item/clothing/suit/wizrobe/marisa/fake + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + item_state = "marisarobe" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + resistance_flags = FLAMMABLE + magical = FALSE + +//Shielded Armour + +/obj/item/clothing/suit/space/hardsuit/shielded/wizard + name = "battlemage armour" + desc = "Not all wizards are afraid of getting up close and personal. Not spaceproof despite its appearance." + icon_state = "hardsuit-wiz" + item_state = "wiz_hardsuit" + recharge_rate = 0 + current_charges = 15 + recharge_cooldown = INFINITY + shield_state = "shield-red" + shield_on = "shield-red" + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard + armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) + slowdown = 0 + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE + +/obj/item/clothing/suit/space/hardsuit/shielded/wizard/arch + desc = "For the arch wizard in need of additional protection." + recharge_rate = 1 + recharge_cooldown = 0 + max_charges = 15 + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard/arch + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard + name = "battlemage helmet" + desc = "A suitably impressive helmet." + icon_state = "hardsuit0-wiz" + item_state = "wiz_helm" + item_color = "wiz" + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) + actions_types = list() //No inbuilt light + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard/attack_self(mob/user) + return + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard/arch + desc = "A truly protective helmet." + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + +/obj/item/wizard_armour_charge + name = "battlemage shield charges" + desc = "A powerful rune that will increase the number of hits a suit of battlemage armour can take before failing.." + icon = 'icons/effects/effects.dmi' + icon_state = "electricity2" + +/obj/item/wizard_armour_charge/afterattack(obj/item/clothing/suit/space/hardsuit/shielded/wizard/W, mob/user) + . = ..() + if(!istype(W)) + to_chat(user, "The rune can only be used on battlemage armour!") + return + W.current_charges += 8 + to_chat(user, "You charge [W]. It can now absorb [W.current_charges] hits.") + qdel(src) diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index 11b7919f0ae..b417df89426 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -291,7 +291,7 @@ var/obj/item/pda/pda = O id_card = pda.id - if(access_security in id_card.access || emagged) + if(ACCESS_SECURITY in id_card.access || emagged) to_chat(user, "You imprint your ID details onto the badge.") stored_name = id_card.registered_name name = "holobadge ([stored_name])" diff --git a/code/modules/clothing/under/accessories/storage.dm b/code/modules/clothing/under/accessories/storage.dm index 16eb1cd476a..727aba06b37 100644 --- a/code/modules/clothing/under/accessories/storage.dm +++ b/code/modules/clothing/under/accessories/storage.dm @@ -37,8 +37,8 @@ return hold.attackby(W, user, params) /obj/item/clothing/accessory/storage/emp_act(severity) - hold.emp_act(severity) ..() + hold.emp_act(severity) /obj/item/clothing/accessory/storage/hear_talk(mob/M, list/message_pieces, verb) hold.hear_talk(M, message_pieces, verb) diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index 3fe4138785a..b57166d4909 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -1,207 +1,188 @@ -/obj/item/clothing/under/color - desc = "A standard issue colored jumpsuit. Variety is the spice of life!" - - -/obj/item/clothing/under/color/random/New() - ..() - var/list/excluded = list(/obj/item/clothing/under/color/random, /obj/item/clothing/under/color/blackf, /obj/item/clothing/under/color/blue/dodgeball, /obj/item/clothing/under/color/orange/prison, /obj/item/clothing/under/color/red/dodgeball, /obj/item/clothing/under/color/red/jersey, /obj/item/clothing/under/color/blue/jersey) - var/obj/item/clothing/under/color/C = pick(subtypesof(/obj/item/clothing/under/color) - excluded) - name = initial(C.name) - icon_state = initial(C.icon_state) - item_state = initial(C.item_state) - item_color = initial(C.item_color) - -/obj/item/clothing/under/color/black - name = "black jumpsuit" - icon_state = "black" - item_state = "bl_suit" - item_color = "black" - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/color/blackf - name = "feminine black jumpsuit" - desc = "It's very smart and in a ladies-size!" - icon_state = "black" - item_state = "bl_suit" - item_color = "blackf" - -/obj/item/clothing/under/color/blue - name = "blue jumpsuit" - icon_state = "blue" - item_state = "b_suit" - item_color = "blue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/blue/dodgeball - flags = NODROP - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/green - name = "green jumpsuit" - icon_state = "green" - item_state = "g_suit" - item_color = "green" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/grey - name = "grey jumpsuit" - desc = "A tasteful grey jumpsuit that reminds you of the good old days." - icon_state = "grey" - item_state = "gy_suit" - item_color = "grey" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/grey/greytide - flags = NODROP - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/grey/glorf - name = "ancient jumpsuit" - desc = "A terribly ragged and frayed grey jumpsuit. It looks like it hasn't been washed in over a decade." - -/obj/item/clothing/under/color/grey/glorf/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - owner.forcesay(GLOB.hit_appends) - return 0 - -/obj/item/clothing/under/color/orange - name = "orange jumpsuit" - desc = "Don't wear this near paranoid security officers" - icon_state = "orange" - item_state = "o_suit" - item_color = "orange" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/orange/prison - name = "orange jumpsuit" - desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position." - icon_state = "orange" - item_state = "o_suit" - item_color = "orange" - has_sensor = 2 - sensor_mode = 3 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/pink - name = "pink jumpsuit" - desc = "Just looking at this makes you feel fabulous." - icon_state = "pink" - item_state = "p_suit" - item_color = "pink" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/red - name = "red jumpsuit" - icon_state = "red" - item_state = "r_suit" - item_color = "red" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/red/dodgeball - flags = NODROP - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/white - name = "white jumpsuit" - icon_state = "white" - item_state = "w_suit" - item_color = "white" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/yellow - name = "yellow jumpsuit" - icon_state = "yellow" - item_state = "y_suit" - item_color = "yellow" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/psyche - name = "psychedelic jumpsuit" - desc = "Groovy!" - icon_state = "psyche" - item_color = "psyche" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/uniform.dmi' - ) - -/obj/item/clothing/under/color/lightblue - name = "light blue jumpsuit" - icon_state = "lightblue" - item_color = "lightblue" - -/obj/item/clothing/under/color/aqua - name = "aqua jumpsuit" - icon_state = "aqua" - item_color = "aqua" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/purple - name = "purple jumpsuit" - icon_state = "purple" - item_state = "p_suit" - item_color = "purple" - -/obj/item/clothing/under/color/lightpurple - name = "light purple jumpsuit" - icon_state = "lightpurple" - item_color = "lightpurple" - -/obj/item/clothing/under/color/lightgreen - name = "light green jumpsuit" - icon_state = "lightgreen" - item_color = "lightgreen" - -/obj/item/clothing/under/color/lightblue - name = "light blue jumpsuit" - icon_state = "lightblue" - item_color = "lightblue" - -/obj/item/clothing/under/color/lightbrown - name = "light brown jumpsuit" - icon_state = "lightbrown" - item_color = "lightbrown" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/brown - name = "brown jumpsuit" - icon_state = "brown" - item_color = "brown" - -/obj/item/clothing/under/color/yellowgreen - name = "yellow green jumpsuit" - icon_state = "yellowgreen" - item_color = "yellowgreen" - -/obj/item/clothing/under/color/darkblue - name = "dark blue jumpsuit" - icon_state = "darkblue" - item_color = "darkblue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/lightred - name = "light red jumpsuit" - icon_state = "lightred" - item_color = "lightred" - -/obj/item/clothing/under/color/darkred - name = "dark red jumpsuit" - icon_state = "darkred" - item_color = "darkred" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/red/jersey - name = "red team jersey" - desc = "The jersey of the Nanotrasen Phi-ghters!" - icon_state = "redjersey" - item_state = "r_suit" - item_color = "redjersey" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/blue/jersey - name = "blue team jersey" - desc = "The jersey of the Nanotrasen Pi-rates!" - icon_state = "bluejersey" - item_state = "b_suit" - item_color = "bluejersey" - flags_size = ONESIZEFITSALL +/obj/item/clothing/under/color + desc = "A standard issue colored jumpsuit. Variety is the spice of life!" + + +/obj/item/clothing/under/color/random/New() + ..() + var/list/excluded = list(/obj/item/clothing/under/color/random, /obj/item/clothing/under/color/blackf, /obj/item/clothing/under/color/blue/dodgeball, /obj/item/clothing/under/color/orange/prison, /obj/item/clothing/under/color/red/dodgeball, /obj/item/clothing/under/color/red/jersey, /obj/item/clothing/under/color/blue/jersey) + var/obj/item/clothing/under/color/C = pick(subtypesof(/obj/item/clothing/under/color) - excluded) + name = initial(C.name) + icon_state = initial(C.icon_state) + item_state = initial(C.item_state) + item_color = initial(C.item_color) + +/obj/item/clothing/under/color/black + name = "black jumpsuit" + icon_state = "black" + item_state = "bl_suit" + item_color = "black" + resistance_flags = NONE + +/obj/item/clothing/under/color/blackf + name = "feminine black jumpsuit" + desc = "It's very smart and in a ladies-size!" + icon_state = "black" + item_state = "bl_suit" + item_color = "blackf" + +/obj/item/clothing/under/color/blue + name = "blue jumpsuit" + icon_state = "blue" + item_state = "b_suit" + item_color = "blue" + +/obj/item/clothing/under/color/blue/dodgeball + flags = NODROP + +/obj/item/clothing/under/color/green + name = "green jumpsuit" + icon_state = "green" + item_state = "g_suit" + item_color = "green" + +/obj/item/clothing/under/color/grey + name = "grey jumpsuit" + desc = "A tasteful grey jumpsuit that reminds you of the good old days." + icon_state = "grey" + item_state = "gy_suit" + item_color = "grey" + +/obj/item/clothing/under/color/grey/greytide + flags = NODROP + +/obj/item/clothing/under/color/grey/glorf + name = "ancient jumpsuit" + desc = "A terribly ragged and frayed grey jumpsuit. It looks like it hasn't been washed in over a decade." + +/obj/item/clothing/under/color/grey/glorf/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + owner.forcesay(GLOB.hit_appends) + return 0 + +/obj/item/clothing/under/color/orange + name = "orange jumpsuit" + desc = "Don't wear this near paranoid security officers" + icon_state = "orange" + item_state = "o_suit" + item_color = "orange" + +/obj/item/clothing/under/color/orange/prison + name = "orange jumpsuit" + desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position." + icon_state = "orange" + item_state = "o_suit" + item_color = "orange" + has_sensor = 2 + sensor_mode = 3 + +/obj/item/clothing/under/color/pink + name = "pink jumpsuit" + desc = "Just looking at this makes you feel fabulous." + icon_state = "pink" + item_state = "p_suit" + item_color = "pink" + +/obj/item/clothing/under/color/red + name = "red jumpsuit" + icon_state = "red" + item_state = "r_suit" + item_color = "red" + +/obj/item/clothing/under/color/red/dodgeball + flags = NODROP + +/obj/item/clothing/under/color/white + name = "white jumpsuit" + icon_state = "white" + item_state = "w_suit" + item_color = "white" + +/obj/item/clothing/under/color/yellow + name = "yellow jumpsuit" + icon_state = "yellow" + item_state = "y_suit" + item_color = "yellow" + +/obj/item/clothing/under/psyche + name = "psychedelic jumpsuit" + desc = "Groovy!" + icon_state = "psyche" + item_color = "psyche" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/uniform.dmi' + ) + +/obj/item/clothing/under/color/lightblue + name = "light blue jumpsuit" + icon_state = "lightblue" + item_color = "lightblue" + +/obj/item/clothing/under/color/aqua + name = "aqua jumpsuit" + icon_state = "aqua" + item_color = "aqua" + +/obj/item/clothing/under/color/purple + name = "purple jumpsuit" + icon_state = "purple" + item_state = "p_suit" + item_color = "purple" + +/obj/item/clothing/under/color/lightpurple + name = "light purple jumpsuit" + icon_state = "lightpurple" + item_color = "lightpurple" + +/obj/item/clothing/under/color/lightgreen + name = "light green jumpsuit" + icon_state = "lightgreen" + item_color = "lightgreen" + +/obj/item/clothing/under/color/lightblue + name = "light blue jumpsuit" + icon_state = "lightblue" + item_color = "lightblue" + +/obj/item/clothing/under/color/lightbrown + name = "light brown jumpsuit" + icon_state = "lightbrown" + item_color = "lightbrown" + +/obj/item/clothing/under/color/brown + name = "brown jumpsuit" + icon_state = "brown" + item_color = "brown" + +/obj/item/clothing/under/color/yellowgreen + name = "yellow green jumpsuit" + icon_state = "yellowgreen" + item_color = "yellowgreen" + +/obj/item/clothing/under/color/darkblue + name = "dark blue jumpsuit" + icon_state = "darkblue" + item_color = "darkblue" + +/obj/item/clothing/under/color/lightred + name = "light red jumpsuit" + icon_state = "lightred" + item_color = "lightred" + +/obj/item/clothing/under/color/darkred + name = "dark red jumpsuit" + icon_state = "darkred" + item_color = "darkred" + +/obj/item/clothing/under/color/red/jersey + name = "red team jersey" + desc = "The jersey of the Nanotrasen Phi-ghters!" + icon_state = "redjersey" + item_state = "r_suit" + item_color = "redjersey" + +/obj/item/clothing/under/color/blue/jersey + name = "blue team jersey" + desc = "The jersey of the Nanotrasen Pi-rates!" + icon_state = "bluejersey" + item_state = "b_suit" + item_color = "bluejersey" diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm index 879e0fd22c7..faa9039c8c4 100644 --- a/code/modules/clothing/under/jobs/civilian.dm +++ b/code/modules/clothing/under/jobs/civilian.dm @@ -1,239 +1,220 @@ -//Alphabetical order of support jobs. - -/obj/item/clothing/under/rank/bartender - desc = "It looks like it could use some more flair." - name = "bartender's uniform" - icon_state = "ba_suit" - item_state = "ba_suit" - item_color = "ba_suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. - desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Captain\"." - name = "captain's jumpsuit" - icon_state = "captain" - item_state = "caparmor" - item_color = "captain" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/cargo - name = "quartermaster's jumpsuit" - desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qm" - item_state = "lb_suit" - item_color = "qm" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/cargo/skirt - name = "quartermaster's jumpskirt" - desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qmf" - item_color = "qmf" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/cargotech - name = "cargo technician's jumpsuit" - desc = "Shooooorts! They're comfy and easy to wear!" - icon_state = "cargotech" - item_state = "lb_suit" - item_color = "cargo" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/cargotech/skirt - name = "cargo technician's jumpskirt" - desc = "Skirrrrrts! They're comfy and easy to wear!" - icon_state = "cargof" - item_color = "cargof" - flags_size = null - -/obj/item/clothing/under/rank/chaplain - desc = "It's a black jumpsuit, often worn by religious folk." - name = "chaplain's jumpsuit" - icon_state = "chaplain" - item_state = "bl_suit" - item_color = "chapblack" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chef - desc = "It's an apron which is given only to the most hardcore chefs in space." - name = "chef's uniform" - icon_state = "chef" - item_color = "chef" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/clown - name = "clown suit" - desc = "'HONK!'" - icon_state = "clown" - item_state = "clown" - item_color = "clown" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/clown/Initialize() - . = ..() - AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg' = 1), 50) - -/obj/item/clothing/under/rank/clown/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(ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(H.mind && H.mind.assigned_role == "Clown") - score_clownabuse++ - return ..() - -/obj/item/clothing/under/rank/clown/sexy - name = "sexy-clown suit" - desc = "It makes you look HONKable!" - icon_state = "sexyclown" - item_state = "sexyclown" - item_color = "sexyclown" - -/obj/item/clothing/under/rank/clown/nodrop - flags = NODROP - -/obj/item/clothing/under/rank/head_of_personnel - desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." - name = "head of personnel's jumpsuit" - icon_state = "hop" - item_state = "b_suit" - item_color = "hop" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/head_of_personnel_whimsy - desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." - name = "head of personnel's suit" - icon_state = "hopwhimsy" - item_state = "hopwhimsy" - item_color = "hopwhimsy" - - -/obj/item/clothing/under/rank/hydroponics - desc = "It's a jumpsuit designed to protect against minor plant-related hazards." - name = "botanist's jumpsuit" - icon_state = "hydroponics" - item_state = "g_suit" - item_color = "hydroponics" - permeability_coefficient = 0.50 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/internalaffairs - desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." - name = "Internal Affairs uniform" - icon_state = "internalaffairs" - item_state = "internalaffairs" - item_color = "internalaffairs" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/ntrep - desc = "A well-ironed dress shirt and matching set of black pants." - name = "dress shirt" - icon_state = "internalaffairs" - item_state = "internalaffairs" - item_color = "internalaffairs" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/ntrep/skirt - desc = "A silky smooth black and gold representative uniform with blue markings." - name = "representative skirt" - icon_state = "ntrepf" - item_state = "ntrepf" - item_color = "ntrepf" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/janitor - desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." - name = "janitor's jumpsuit" - icon_state = "janitor" - item_color = "janitor" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - - -/obj/item/clothing/under/lawyer - desc = "Slick threads." - name = "Lawyer suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/lawyer/black - icon_state = "lawyer_black" - item_state = "lawyer_black" - item_color = "lawyer_black" - -/obj/item/clothing/under/lawyer/female - icon_state = "black_suit_fem" - item_state = "black_suit_fem" - item_color = "black_suit_fem" - -/obj/item/clothing/under/lawyer/red - icon_state = "lawyer_red" - item_state = "lawyer_red" - item_color = "lawyer_red" - -/obj/item/clothing/under/lawyer/blue - icon_state = "lawyer_blue" - item_state = "lawyer_blue" - item_color = "lawyer_blue" - -/obj/item/clothing/under/lawyer/bluesuit - name = "Blue Suit" - desc = "A classy suit and tie" - icon_state = "bluesuit" - item_state = "bluesuit" - item_color = "bluesuit" - -/obj/item/clothing/under/lawyer/purpsuit - name = "Purple Suit" - icon_state = "lawyer_purp" - item_state = "lawyer_purp" - item_color = "lawyer_purp" - -/obj/item/clothing/under/lawyer/oldman - name = "Old Man's Suit" - desc = "A classic suit for the older gentleman with built in back support." - icon_state = "oldman" - item_state = "oldman" - item_color = "oldman" - - -/obj/item/clothing/under/librarian - name = "sensible suit" - desc = "It's very... sensible." - icon_state = "red_suit" - item_state = "red_suit" - item_color = "red_suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/mime - name = "mime's outfit" - desc = "It's not very colourful." - icon_state = "mime" - item_state = "mime" - item_color = "mime" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/mime/nodrop - flags = NODROP - -/obj/item/clothing/under/rank/miner - desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." - name = "shaft miner's jumpsuit" - icon_state = "miner" - item_state = "miner" - item_color = "miner" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/miner/lavaland - desc = "A green uniform for operating in hazardous environments." - name = "shaft miner's jumpsuit" - icon_state = "explorer" - item_state = "explorer" - item_color = "explorer" - - sprite_sheets = list() - -/obj/item/clothing/under/barber - desc = "It's a barber's uniform." - name = "barber's uniform" - icon_state = "barber" - item_state = "barber" - item_color = "barber" +//Alphabetical order of support jobs. + +/obj/item/clothing/under/rank/bartender + desc = "It looks like it could use some more flair." + name = "bartender's uniform" + icon_state = "ba_suit" + item_state = "ba_suit" + item_color = "ba_suit" + +/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. + desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Captain\"." + name = "captain's jumpsuit" + icon_state = "captain" + item_state = "caparmor" + item_color = "captain" + +/obj/item/clothing/under/rank/cargo + name = "quartermaster's jumpsuit" + desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qm" + item_state = "lb_suit" + item_color = "qm" + +/obj/item/clothing/under/rank/cargo/skirt + name = "quartermaster's jumpskirt" + desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qmf" + item_color = "qmf" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/cargotech + name = "cargo technician's jumpsuit" + desc = "Shooooorts! They're comfy and easy to wear!" + icon_state = "cargotech" + item_state = "lb_suit" + item_color = "cargo" + +/obj/item/clothing/under/rank/cargotech/skirt + name = "cargo technician's jumpskirt" + desc = "Skirrrrrts! They're comfy and easy to wear!" + icon_state = "cargof" + item_color = "cargof" + +/obj/item/clothing/under/rank/chaplain + desc = "It's a black jumpsuit, often worn by religious folk." + name = "chaplain's jumpsuit" + icon_state = "chaplain" + item_state = "bl_suit" + item_color = "chapblack" + +/obj/item/clothing/under/rank/chef + desc = "It's an apron which is given only to the most hardcore chefs in space." + name = "chef's uniform" + icon_state = "chef" + item_color = "chef" + +/obj/item/clothing/under/rank/clown + name = "clown suit" + desc = "'HONK!'" + icon_state = "clown" + item_state = "clown" + item_color = "clown" + +/obj/item/clothing/under/rank/clown/Initialize() + . = ..() + AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg' = 1), 50) + +/obj/item/clothing/under/rank/clown/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(ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(H.mind && H.mind.assigned_role == "Clown") + GLOB.score_clownabuse++ + return ..() + +/obj/item/clothing/under/rank/clown/sexy + name = "sexy-clown suit" + desc = "It makes you look HONKable!" + icon_state = "sexyclown" + item_state = "sexyclown" + item_color = "sexyclown" + +/obj/item/clothing/under/rank/clown/nodrop + flags = NODROP + +/obj/item/clothing/under/rank/head_of_personnel + desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." + name = "head of personnel's jumpsuit" + icon_state = "hop" + item_state = "b_suit" + item_color = "hop" + +/obj/item/clothing/under/rank/head_of_personnel_whimsy + desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." + name = "head of personnel's suit" + icon_state = "hopwhimsy" + item_state = "hopwhimsy" + item_color = "hopwhimsy" + + +/obj/item/clothing/under/rank/hydroponics + desc = "It's a jumpsuit designed to protect against minor plant-related hazards." + name = "botanist's jumpsuit" + icon_state = "hydroponics" + item_state = "g_suit" + item_color = "hydroponics" + permeability_coefficient = 0.50 + +/obj/item/clothing/under/rank/internalaffairs + desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." + name = "Internal Affairs uniform" + icon_state = "internalaffairs" + item_state = "internalaffairs" + item_color = "internalaffairs" + +/obj/item/clothing/under/rank/ntrep + desc = "A well-ironed dress shirt and matching set of black pants." + name = "dress shirt" + icon_state = "internalaffairs" + item_state = "internalaffairs" + item_color = "internalaffairs" + +/obj/item/clothing/under/rank/ntrep/skirt + desc = "A silky smooth black and gold representative uniform with blue markings." + name = "representative skirt" + icon_state = "ntrepf" + item_state = "ntrepf" + item_color = "ntrepf" + +/obj/item/clothing/under/rank/janitor + desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." + name = "janitor's jumpsuit" + icon_state = "janitor" + item_color = "janitor" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + + +/obj/item/clothing/under/lawyer + desc = "Slick threads." + name = "Lawyer suit" + +/obj/item/clothing/under/lawyer/black + icon_state = "lawyer_black" + item_state = "lawyer_black" + item_color = "lawyer_black" + +/obj/item/clothing/under/lawyer/female + icon_state = "black_suit_fem" + item_state = "black_suit_fem" + item_color = "black_suit_fem" + +/obj/item/clothing/under/lawyer/red + icon_state = "lawyer_red" + item_state = "lawyer_red" + item_color = "lawyer_red" + +/obj/item/clothing/under/lawyer/blue + icon_state = "lawyer_blue" + item_state = "lawyer_blue" + item_color = "lawyer_blue" + +/obj/item/clothing/under/lawyer/bluesuit + name = "Blue Suit" + desc = "A classy suit and tie" + icon_state = "bluesuit" + item_state = "bluesuit" + item_color = "bluesuit" + +/obj/item/clothing/under/lawyer/purpsuit + name = "Purple Suit" + icon_state = "lawyer_purp" + item_state = "lawyer_purp" + item_color = "lawyer_purp" + +/obj/item/clothing/under/lawyer/oldman + name = "Old Man's Suit" + desc = "A classic suit for the older gentleman with built in back support." + icon_state = "oldman" + item_state = "oldman" + item_color = "oldman" + + +/obj/item/clothing/under/librarian + name = "sensible suit" + desc = "It's very... sensible." + icon_state = "red_suit" + item_state = "red_suit" + item_color = "red_suit" + +/obj/item/clothing/under/mime + name = "mime's outfit" + desc = "It's not very colourful." + icon_state = "mime" + item_state = "mime" + item_color = "mime" + +/obj/item/clothing/under/mime/nodrop + flags = NODROP + +/obj/item/clothing/under/rank/miner + desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." + name = "shaft miner's jumpsuit" + icon_state = "miner" + item_state = "miner" + item_color = "miner" + +/obj/item/clothing/under/rank/miner/lavaland + desc = "A green uniform for operating in hazardous environments." + name = "shaft miner's jumpsuit" + icon_state = "explorer" + item_state = "explorer" + item_color = "explorer" + + sprite_sheets = list() + +/obj/item/clothing/under/barber + desc = "It's a barber's uniform." + name = "barber's uniform" + icon_state = "barber" + item_state = "barber" + item_color = "barber" diff --git a/code/modules/clothing/under/jobs/engineering.dm b/code/modules/clothing/under/jobs/engineering.dm index 14878e402a1..aa70fc85db6 100644 --- a/code/modules/clothing/under/jobs/engineering.dm +++ b/code/modules/clothing/under/jobs/engineering.dm @@ -1,77 +1,69 @@ -//Contains: Engineering department jumpsuits -/obj/item/clothing/under/rank/chief_engineer - desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpsuit" - icon_state = "chiefengineer" - item_state = "chief" - item_color = "chief" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 80, "acid" = 40) - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/rank/chief_engineer/skirt - desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpskirt" - icon_state = "chieff" - item_color = "chieff" - flags_size = null - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/atmospheric_technician - desc = "It's a jumpsuit worn by atmospheric technicians." - name = "atmospheric technician's jumpsuit" - icon_state = "atmos" - item_state = "atmos_suit" - item_color = "atmos" - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/rank/atmospheric_technician/skirt - desc = "It's a jumpskirt worn by atmospheric technicians." - name = "atmospheric technician's jumpskirt" - icon_state = "atmosf" - item_color = "atmosf" - flags_size = null - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/engineer - desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." - name = "engineer's jumpsuit" - icon_state = "engine" - item_state = "engi_suit" - item_color = "engine" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 60, "acid" = 20) - flags_size = ONESIZEFITSALL - resistance_flags = NONE - - -/obj/item/clothing/under/rank/engineer/skirt - desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." - name = "engineer's jumpskirt" - icon_state = "enginef" - item_color = "enginef" - flags_size = null - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/roboticist - desc = "It's a slimming black with reinforced seams; great for industrial work." - name = "roboticist's jumpsuit" - icon_state = "robotics" - item_state = "robotics" - item_color = "robotics" - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/rank/roboticist/skirt - desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." - name = "roboticist's jumpskirt" - icon_state = "roboticsf" - item_color = "roboticsf" - flags_size = null - -/obj/item/clothing/under/rank/mechanic - desc = "It's a pair of overalls worn by mechanics." - name = "mechanic's overalls" - icon_state = "mechanic" - item_state = "mechanic" - item_color = "mechanic" +//Contains: Engineering department jumpsuits +/obj/item/clothing/under/rank/chief_engineer + desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpsuit" + icon_state = "chiefengineer" + item_state = "chief" + item_color = "chief" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 80, "acid" = 40) + resistance_flags = NONE + +/obj/item/clothing/under/rank/chief_engineer/skirt + desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpskirt" + icon_state = "chieff" + item_color = "chieff" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/atmospheric_technician + desc = "It's a jumpsuit worn by atmospheric technicians." + name = "atmospheric technician's jumpsuit" + icon_state = "atmos" + item_state = "atmos_suit" + item_color = "atmos" + resistance_flags = NONE + +/obj/item/clothing/under/rank/atmospheric_technician/skirt + desc = "It's a jumpskirt worn by atmospheric technicians." + name = "atmospheric technician's jumpskirt" + icon_state = "atmosf" + item_color = "atmosf" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/engineer + desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." + name = "engineer's jumpsuit" + icon_state = "engine" + item_state = "engi_suit" + item_color = "engine" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 60, "acid" = 20) + resistance_flags = NONE + + +/obj/item/clothing/under/rank/engineer/skirt + desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." + name = "engineer's jumpskirt" + icon_state = "enginef" + item_color = "enginef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/roboticist + desc = "It's a slimming black with reinforced seams; great for industrial work." + name = "roboticist's jumpsuit" + icon_state = "robotics" + item_state = "robotics" + item_color = "robotics" + resistance_flags = NONE + +/obj/item/clothing/under/rank/roboticist/skirt + desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." + name = "roboticist's jumpskirt" + icon_state = "roboticsf" + item_color = "roboticsf" + +/obj/item/clothing/under/rank/mechanic + desc = "It's a pair of overalls worn by mechanics." + name = "mechanic's overalls" + icon_state = "mechanic" + item_state = "mechanic" + item_color = "mechanic" diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm index f5d65b964fc..d8847741db3 100644 --- a/code/modules/clothing/under/jobs/medsci.dm +++ b/code/modules/clothing/under/jobs/medsci.dm @@ -1,246 +1,219 @@ -/* - * Science - */ -/obj/item/clothing/under/rank/research_director - desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." - name = "research director's jumpsuit" - icon_state = "director" - item_state = "g_suit" - item_color = "director" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 35) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/scientist - desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." - name = "scientist's jumpsuit" - icon_state = "toxins" - item_state = "w_suit" - item_color = "toxinswhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/scientist/skirt - name = "scientist's jumpskirt" - icon_state = "sciencewhitef" - item_color = "sciencewhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/chemist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." - name = "chemist's jumpsuit" - icon_state = "chemistry" - item_state = "w_suit" - item_color = "chemistrywhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chemist/skirt - name = "chemist's jumpskirt" - icon_state = "chemistrywhitef" - item_color = "chemistrywhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/* - * Medical - */ -/obj/item/clothing/under/rank/chief_medical_officer - desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpsuit" - icon_state = "cmo" - item_state = "w_suit" - item_color = "cmo" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chief_medical_officer/skirt - desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpskirt" - icon_state = "cmof" - item_color = "cmof" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/geneticist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." - name = "geneticist's jumpsuit" - icon_state = "genetics" - item_state = "w_suit" - item_color = "geneticswhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/geneticist/skirt - name = "geneticist's jumpskirt" - icon_state = "geneticswhitef" - item_color = "geneticswhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/virologist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." - name = "virologist's jumpsuit" - icon_state = "virology" - item_state = "w_suit" - item_color = "virologywhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/virologist/skirt - name = "virologist's jumpskirt" - icon_state = "virologywhitef" - item_color = "virologywhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/nursesuit - desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." - name = "nurse's suit" - icon_state = "nursesuit" - item_state = "nursesuit" - item_color = "nursesuit" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/nurse - desc = "A dress commonly worn by the nursing staff in the medical department." - name = "nurse's dress" - icon_state = "nurse" - item_state = "nurse" - item_color = "nurse" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/orderly - desc = "A white suit to be worn by orderly people who love orderly things." - name = "orderly's uniform" - icon_state = "orderly" - item_state = "orderly" - item_color = "orderly" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical - desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." - name = "medical doctor's jumpsuit" - icon_state = "medical" - item_state = "w_suit" - item_color = "medical" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/skirt - name = "medical doctor's jumpskirt" - icon_state = "medicalf" - item_color = "medicalf" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/medical/blue - name = "medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." - icon_state = "scrubsblue" - item_color = "scrubsblue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/green - name = "medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." - icon_state = "scrubsgreen" - item_color = "scrubsgreen" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/purple - name = "medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." - icon_state = "scrubspurple" - item_color = "scrubspurple" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/mortician - name = "coroner's scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is as dark as an emo's poetry." - icon_state = "scrubsblack" - item_color = "scrubsblack" - flags_size = ONESIZEFITSALL - -//paramedic -/obj/item/clothing/under/rank/medical/paramedic - desc = "It's made of a special fiber that provides minor protection against biohazards and radiation. It has a cross on the chest denoting that the wearer is trained medical personnel." - name = "paramedic's jumpsuit" - icon_state = "paramedic" - item_state = "paramedic" - item_color = "paramedic" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 10, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/psych - desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." - name = "psychiatrist's jumpsuit" - icon_state = "psych" - item_state = "w_suit" - item_color = "psych" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/psych/turtleneck - desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." - name = "psychologist's turtleneck" - icon_state = "psychturtle" - item_state = "b_suit" - item_color = "psychturtle" - flags_size = ONESIZEFITSALL - - -/* - * Medsci, unused (i think) stuff - */ -/obj/item/clothing/under/rank/geneticist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "geneticist's jumpsuit" - icon_state = "genetics_new" - item_state = "w_suit" - item_color = "genetics_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chemist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "chemist's jumpsuit" - icon_state = "chemist_new" - item_state = "w_suit" - item_color = "chemist_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/scientist_new - desc = "Made of a special fiber that gives special protection against biohazards and small explosions." - name = "scientist's jumpsuit" - icon_state = "scientist_new" - item_state = "w_suit" - item_color = "scientist_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/virologist_new - desc = "Made of a special fiber that gives increased protection against biohazards." - name = "virologist's jumpsuit" - icon_state = "virologist_new" - item_state = "w_suit" - item_color = "virologist_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL +/* + * Science + */ +/obj/item/clothing/under/rank/research_director + desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." + name = "research director's jumpsuit" + icon_state = "director" + item_state = "g_suit" + item_color = "director" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 35) + +/obj/item/clothing/under/rank/scientist + desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." + name = "scientist's jumpsuit" + icon_state = "toxins" + item_state = "w_suit" + item_color = "toxinswhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/scientist/skirt + name = "scientist's jumpskirt" + icon_state = "sciencewhitef" + item_color = "sciencewhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/chemist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." + name = "chemist's jumpsuit" + icon_state = "chemistry" + item_state = "w_suit" + item_color = "chemistrywhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) + +/obj/item/clothing/under/rank/chemist/skirt + name = "chemist's jumpskirt" + icon_state = "chemistrywhitef" + item_color = "chemistrywhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/* + * Medical + */ +/obj/item/clothing/under/rank/chief_medical_officer + desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpsuit" + icon_state = "cmo" + item_state = "w_suit" + item_color = "cmo" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/chief_medical_officer/skirt + desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpskirt" + icon_state = "cmof" + item_color = "cmof" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/geneticist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." + name = "geneticist's jumpsuit" + icon_state = "genetics" + item_state = "w_suit" + item_color = "geneticswhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/geneticist/skirt + name = "geneticist's jumpskirt" + icon_state = "geneticswhitef" + item_color = "geneticswhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/virologist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." + name = "virologist's jumpsuit" + icon_state = "virology" + item_state = "w_suit" + item_color = "virologywhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/virologist/skirt + name = "virologist's jumpskirt" + icon_state = "virologywhitef" + item_color = "virologywhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/nursesuit + desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." + name = "nurse's suit" + icon_state = "nursesuit" + item_state = "nursesuit" + item_color = "nursesuit" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/nurse + desc = "A dress commonly worn by the nursing staff in the medical department." + name = "nurse's dress" + icon_state = "nurse" + item_state = "nurse" + item_color = "nurse" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/orderly + desc = "A white suit to be worn by orderly people who love orderly things." + name = "orderly's uniform" + icon_state = "orderly" + item_state = "orderly" + item_color = "orderly" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/medical + desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." + name = "medical doctor's jumpsuit" + icon_state = "medical" + item_state = "w_suit" + item_color = "medical" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/medical/skirt + name = "medical doctor's jumpskirt" + icon_state = "medicalf" + item_color = "medicalf" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/medical/blue + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." + icon_state = "scrubsblue" + item_color = "scrubsblue" + +/obj/item/clothing/under/rank/medical/green + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." + icon_state = "scrubsgreen" + item_color = "scrubsgreen" + +/obj/item/clothing/under/rank/medical/purple + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." + icon_state = "scrubspurple" + item_color = "scrubspurple" + +/obj/item/clothing/under/rank/medical/mortician + name = "coroner's scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is as dark as an emo's poetry." + icon_state = "scrubsblack" + item_color = "scrubsblack" + +//paramedic +/obj/item/clothing/under/rank/medical/paramedic + desc = "It's made of a special fiber that provides minor protection against biohazards and radiation. It has a cross on the chest denoting that the wearer is trained medical personnel." + name = "paramedic's jumpsuit" + icon_state = "paramedic" + item_state = "paramedic" + item_color = "paramedic" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 10, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/psych + desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." + name = "psychiatrist's jumpsuit" + icon_state = "psych" + item_state = "w_suit" + item_color = "psych" + +/obj/item/clothing/under/rank/psych/turtleneck + desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." + name = "psychologist's turtleneck" + icon_state = "psychturtle" + item_state = "b_suit" + item_color = "psychturtle" + + +/* + * Medsci, unused (i think) stuff + */ +/obj/item/clothing/under/rank/geneticist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "geneticist's jumpsuit" + icon_state = "genetics_new" + item_state = "w_suit" + item_color = "genetics_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/chemist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "chemist's jumpsuit" + icon_state = "chemist_new" + item_state = "w_suit" + item_color = "chemist_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) + +/obj/item/clothing/under/rank/scientist_new + desc = "Made of a special fiber that gives special protection against biohazards and small explosions." + name = "scientist's jumpsuit" + icon_state = "scientist_new" + item_state = "w_suit" + item_color = "scientist_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/under/rank/virologist_new + desc = "Made of a special fiber that gives increased protection against biohazards." + name = "virologist's jumpsuit" + icon_state = "virologist_new" + item_state = "w_suit" + item_color = "virologist_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) diff --git a/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm b/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm index 937d5111830..b3d7ce83431 100644 --- a/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm +++ b/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm @@ -52,4 +52,4 @@ name = "envirosuit extinguisher cartridge" desc = "A cartridge loaded with a compressed extinguisher mix, used to refill the automatic extinguisher on plasma envirosuits." icon_state = "plasmarefill" - icon = 'icons/obj/device.dmi' \ No newline at end of file + icon = 'icons/obj/device.dmi' diff --git a/code/modules/clothing/under/jobs/plasmamen/antags.dm b/code/modules/clothing/under/jobs/plasmamen/antags.dm new file mode 100644 index 00000000000..c1ec8062825 --- /dev/null +++ b/code/modules/clothing/under/jobs/plasmamen/antags.dm @@ -0,0 +1,6 @@ +/obj/item/clothing/under/plasmaman/wizard + name = "wizard plasma envirosuit" + desc = "An envirosuit for plasmamen designed by the Wizard Federation. Still not spaceworthy..." + icon_state = "wizard_envirosuit" + item_state = "wizard_envirosuit" + item_color = "wizard_envirosuit" diff --git a/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm b/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm index 69697d3be6f..ea2f0db6812 100644 --- a/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm +++ b/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm @@ -1,13 +1,13 @@ /obj/item/clothing/under/plasmaman/cargo name = "cargo plasma envirosuit" - desc = "A joint envirosuit used by plasmamen quartermasters and cargo techs alike, due to the logistical problems of differenciating the two with the length of their pant legs." + desc = "An envirosuit used by plasmaman quartermasters and cargo techs alike, due to the logistical problems of differentiating the two by the length of their pant legs." icon_state = "cargo_envirosuit" item_state = "cargo_envirosuit" item_color = "cargo_envirosuit" /obj/item/clothing/under/plasmaman/mining name = "mining plasma envirosuit" - desc = "An air-tight khaki suit designed for operations on lavaland by plasmamen." + desc = "An airtight khaki suit designed for operations on Lavaland by plasmamen." icon_state = "explorer_envirosuit" item_state = "explorer_envirosuit" item_color = "explorer_envirosuit" @@ -15,14 +15,14 @@ /obj/item/clothing/under/plasmaman/chef name = "chef's plasma envirosuit" - desc = "A white plasmaman envirosuit designed for cullinary practices. One might question why a member of a species that doesn't need to eat would become a chef." + desc = "A white plasmaman envirosuit designed for culinary practices. One might question why a member of a species that doesn't need to eat would become a chef." icon_state = "chef_envirosuit" item_state = "chef_envirosuit" item_color = "chef_envirosuit" /obj/item/clothing/under/plasmaman/enviroslacks name = "enviroslacks" - desc = "The pet project of a particularly posh plasmaman, this custom suit was quickly appropriated by Nano-Trasen for it's detectives, lawyers, and bar-tenders alike." + desc = "The pet project of a particularly posh plasmaman, this custom suit was quickly appropriated by Nanotrasen for its detectives, lawyers, and bartenders alike." icon_state = "enviroslacks" item_state = "enviroslacks" item_color = "enviroslacks" @@ -36,14 +36,14 @@ /obj/item/clothing/under/plasmaman/librarian name = "librarian's plasma envirosuit" - desc = "Made out of a modified voidsuit, this suit was Nano-Trasen's first solution to the *logistical problems* that come with employing plasmamen. Due to the modifications, the suit is no longer space-worthy. Despite their limitations, these suits are still in used by historian and old-styled plasmamen alike." + desc = "Made out of a modified voidsuit, this suit was Nanotrasen's first solution to the *logistical problems* that come with employing plasmamen. Due to the modifications, the suit is no longer space-worthy. Despite their limitations, these suits are still in used by historian and old-styled plasmamen alike." icon_state = "prototype_envirosuit" item_state = "prototype_envirosuit" item_color = "prototype_envirosuit" /obj/item/clothing/under/plasmaman/janitor name = "janitor's plasma envirosuit" - desc = "A grey and purple envirosuit designated for plasmamen janitors." + desc = "A grey and purple envirosuit designated for plasmaman janitors." icon_state = "janitor_envirosuit" item_state = "janitor_envirosuit" item_color = "janitor_envirosuit" @@ -104,4 +104,4 @@ desc = "An envirosuit designed for plasmamen employed as the blueshield." icon_state = "bs_envirosuit" item_state = "bs_envirosuit" - item_color = "bs_envirosuit" \ No newline at end of file + item_color = "bs_envirosuit" diff --git a/code/modules/clothing/under/jobs/plasmamen/engineering.dm b/code/modules/clothing/under/jobs/plasmamen/engineering.dm index 35f99558c1c..02a71afcaf6 100644 --- a/code/modules/clothing/under/jobs/plasmamen/engineering.dm +++ b/code/modules/clothing/under/jobs/plasmamen/engineering.dm @@ -1,6 +1,6 @@ /obj/item/clothing/under/plasmaman/engineering name = "engineering plasma envirosuit" - desc = "An air-tight suit designed to be used by plasmamen exployed as engineers, the usual purple stripes being replaced by engineer's orange. It protects the user from fire and acid damage." + desc = "An airtight suit designed to be used by plasmamen employed as engineers, the usual purple stripes being replaced by engineering's orange. It protects the user from fire and acid damage." icon_state = "engineer_envirosuit" item_state = "engineer_envirosuit" item_color = "engineer_envirosuit" @@ -8,14 +8,14 @@ /obj/item/clothing/under/plasmaman/engineering/ce name = "chief engineer plasma envirosuit" - desc = "An air-tight suit designed to be used by plasmamen exployed as the chief engineer" + desc = "An airtight suit designed to be used by plasmamen employed as the chief engineer." icon_state = "ce_envirosuit" item_state = "ce_envirosuit" item_color = "ce_envirosuit" /obj/item/clothing/under/plasmaman/atmospherics name = "atmospherics plasma envirosuit" - desc = "An air-tight suit designed to be used by plasmamen exployed as atmos technicians, the usual purple stripes being replaced by atmos's blue." + desc = "An airtight suit designed to be used by plasmamen employed as atmos technicians, the usual purple stripes being replaced by atmos' blue." icon_state = "atmos_envirosuit" item_state = "atmos_envirosuit" item_color = "atmos_envirosuit" diff --git a/code/modules/clothing/under/jobs/plasmamen/medsci.dm b/code/modules/clothing/under/jobs/plasmamen/medsci.dm index 69e14c0e878..531bc3fb124 100644 --- a/code/modules/clothing/under/jobs/plasmamen/medsci.dm +++ b/code/modules/clothing/under/jobs/plasmamen/medsci.dm @@ -20,7 +20,7 @@ item_color = "scientist_envirosuit" /obj/item/clothing/under/plasmaman/rd - name = "science plasma envirosuit" + name = "rd plasma envirosuit" desc = "A plasmaman envirosuit designed for the research director." icon_state = "rd_envirosuit" item_state = "rd_envirosuit" diff --git a/code/modules/clothing/under/jobs/plasmamen/security.dm b/code/modules/clothing/under/jobs/plasmamen/security.dm index f39feb6345e..427969307f4 100644 --- a/code/modules/clothing/under/jobs/plasmamen/security.dm +++ b/code/modules/clothing/under/jobs/plasmamen/security.dm @@ -8,7 +8,7 @@ /obj/item/clothing/under/plasmaman/security/warden name = "warden plasma envirosuit" - desc = "A plasmaman containment suit designed for the warden, white stripes being added to differeciate them from other members of security." + desc = "A plasmaman containment suit designed for the warden, white stripes being added to differentiate them from other members of security." icon_state = "warden_envirosuit" item_state = "warden_envirosuit" item_color = "warden_envirosuit" @@ -18,4 +18,4 @@ desc = "A plasmaman containment suit designed for the head of security." icon_state = "hos_envirosuit" item_state = "hos_envirosuit" - item_color = "hos_envirosuit" \ No newline at end of file + item_color = "hos_envirosuit" diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index fa48aff0f4f..0086ba96ef8 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -1,200 +1,188 @@ -/* - * Contains: - * Security - * Detective - * Head of Security - */ - - -/* - * Security - */ -/obj/item/clothing/under/rank/warden - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpsuit" - icon_state = "warden" - item_state = "r_suit" - item_color = "warden" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - strip_delay = 50 - -/obj/item/clothing/under/rank/warden/skirt - desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpskirt" - icon_state = "wardenf" - item_state = "r_suit" - item_color = "wardenf" - flags_size = null - -/obj/item/clothing/under/rank/security - name = "security officer's jumpsuit" - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." - icon_state = "security" - item_state = "r_suit" - item_color = "secred" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - strip_delay = 50 - -/obj/item/clothing/under/rank/security/skirt - name = "security officer's jumpskirt" - desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." - icon_state = "secredf" - item_state = "r_suit" - item_color = "secredf" - flags_size = null - -/obj/item/clothing/under/rank/dispatch - name = "dispatcher's uniform" - desc = "A dress shirt and khakis with a security patch sewn on." - icon_state = "dispatch" - item_state = "dispatch" - item_color = "dispatch" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/security2 - name = "security officer's uniform" - desc = "It's made of a slightly sturdier material, to allow for robust protection." - icon_state = "redshirt2" - item_state = "r_suit" - item_color = "redshirt2" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/security/corp - icon_state = "sec_corporate" - item_state = "sec_corporate" - item_color = "sec_corporate" - -/obj/item/clothing/under/rank/warden/corp - icon_state = "warden_corporate" - item_state = "warden_corporate" - item_color = "warden_corporate" - -/* - * Detective - */ -/obj/item/clothing/under/det - name = "hard-worn suit" - desc = "Someone who wears this means business." - icon_state = "detective" - item_state = "det" - item_color = "detective" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - strip_delay = 50 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/uniform.dmi' - ) - -/* - * Head of Security - */ -/obj/item/clothing/under/rank/head_of_security - desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpsuit" - icon_state = "hos" - item_state = "r_suit" - item_color = "hosred" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - flags_size = ONESIZEFITSALL - strip_delay = 60 - -/obj/item/clothing/under/rank/head_of_security/skirt - desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpskirt" - icon_state = "hosredf" - item_state = "r_suit" - item_color = "hosredf" - flags_size = null - -/obj/item/clothing/under/rank/head_of_security/corp - icon_state = "hos_corporate" - item_state = "hos_corporate" - item_color = "hos_corporate" - -//Jensen cosplay gear -/obj/item/clothing/under/rank/head_of_security/jensen - desc = "You never asked for anything that stylish." - name = "head of security's jumpsuit" - icon_state = "jensen" - item_state = "jensen" - item_color = "jensen" - flags_size = ONESIZEFITSALL - -//Paradise Station - -/obj/item/clothing/suit/armor/hos/hosnavyjacket - name = "head of security navy jacket" - icon_state = "hosdnavyjacket" - item_state = "hosdnavyjacket" - -/obj/item/clothing/suit/armor/hos/hosbluejacket - name = "head of security blue jacket" - icon_state = "hosbluejacket" - item_state = "hosbluejacket" - -/obj/item/clothing/suit/armor/hos/officernavyjacket - name = "officer's navy jacket" - icon_state = "officernavyjacket" - item_state = "officernavyjacket" - -/obj/item/clothing/suit/armor/hos/officerbluejacket - name = "officer's blue jacket" - icon_state = "officerbluejacket" - item_state = "officerbluejacket" - -//TG Station - -/obj/item/clothing/under/rank/security/formal - name = "security suit" - desc = "A formal security suit for officers complete with nanotrasen belt buckle." - icon_state = "security_formal" - item_state = "gy_suit" - item_color = "security_formal" - -/obj/item/clothing/under/rank/warden/formal - name = "warden's suit" - desc = "A formal security suit for the warden with blue desginations and '/Warden/' stiched into the shoulders." - icon_state = "warden_formal" - item_state = "gy_suit" - item_color = "warden_formal" - -/obj/item/clothing/under/rank/head_of_security/formal - name = "head of security's suit" - desc = "A security suit decorated for those few with the dedication to achieve the position of Head of Security." - icon_state = "hos_formal" - item_state = "gy_suit" - item_color = "hos_formal" - - -//Brig Physician -/obj/item/clothing/under/rank/security/brigphys - desc = "Jumpsuit for Brig Physician it has both medical and security protection." - name = "brig physician's jumpsuit" - icon_state = "brig_phys" - item_state = "brig_phys" - item_color = "brig_phys" - permeability_coefficient = 0.50 - armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 30, acid = 30) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/security/brigphys/skirt - desc = "A skirted Brig Physician uniform. It has both security and medical protection." - name = "brig physician's jumpskirt" - icon_state = "brig_physf" - item_state = "brig_physf" - item_color = "brig_physf" - permeability_coefficient = 0.50 - flags_size = ONESIZEFITSALL - -//Pod Pilot -/obj/item/clothing/under/rank/security/pod_pilot - desc = "Suit for your regular pod pilot." - name = "pod pilot's jumpsuit" - icon_state = "pod_pilot" - item_state = "pod_pilot" - item_color = "pod_pilot" +/* + * Contains: + * Security + * Detective + * Head of Security + */ + + +/* + * Security + */ +/obj/item/clothing/under/rank/warden + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpsuit" + icon_state = "warden" + item_state = "r_suit" + item_color = "warden" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + strip_delay = 50 + +/obj/item/clothing/under/rank/warden/skirt + desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpskirt" + icon_state = "wardenf" + item_state = "r_suit" + item_color = "wardenf" + +/obj/item/clothing/under/rank/security + name = "security officer's jumpsuit" + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." + icon_state = "security" + item_state = "r_suit" + item_color = "secred" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + strip_delay = 50 + +/obj/item/clothing/under/rank/security/skirt + name = "security officer's jumpskirt" + desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." + icon_state = "secredf" + item_state = "r_suit" + item_color = "secredf" + +/obj/item/clothing/under/rank/dispatch + name = "dispatcher's uniform" + desc = "A dress shirt and khakis with a security patch sewn on." + icon_state = "dispatch" + item_state = "dispatch" + item_color = "dispatch" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + +/obj/item/clothing/under/rank/security2 + name = "security officer's uniform" + desc = "It's made of a slightly sturdier material, to allow for robust protection." + icon_state = "redshirt2" + item_state = "r_suit" + item_color = "redshirt2" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + +/obj/item/clothing/under/rank/security/corp + icon_state = "sec_corporate" + item_state = "sec_corporate" + item_color = "sec_corporate" + +/obj/item/clothing/under/rank/warden/corp + icon_state = "warden_corporate" + item_state = "warden_corporate" + item_color = "warden_corporate" + +/* + * Detective + */ +/obj/item/clothing/under/det + name = "hard-worn suit" + desc = "Someone who wears this means business." + icon_state = "detective" + item_state = "det" + item_color = "detective" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + strip_delay = 50 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/uniform.dmi' + ) + +/* + * Head of Security + */ +/obj/item/clothing/under/rank/head_of_security + desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpsuit" + icon_state = "hos" + item_state = "r_suit" + item_color = "hosred" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/under/rank/head_of_security/skirt + desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpskirt" + icon_state = "hosredf" + item_state = "r_suit" + item_color = "hosredf" + +/obj/item/clothing/under/rank/head_of_security/corp + icon_state = "hos_corporate" + item_state = "hos_corporate" + item_color = "hos_corporate" + +//Jensen cosplay gear +/obj/item/clothing/under/rank/head_of_security/jensen + desc = "You never asked for anything that stylish." + name = "head of security's jumpsuit" + icon_state = "jensen" + item_state = "jensen" + item_color = "jensen" + +//Paradise Station + +/obj/item/clothing/suit/armor/hos/hosnavyjacket + name = "head of security navy jacket" + icon_state = "hosdnavyjacket" + item_state = "hosdnavyjacket" + +/obj/item/clothing/suit/armor/hos/hosbluejacket + name = "head of security blue jacket" + icon_state = "hosbluejacket" + item_state = "hosbluejacket" + +/obj/item/clothing/suit/armor/hos/officernavyjacket + name = "officer's navy jacket" + icon_state = "officernavyjacket" + item_state = "officernavyjacket" + +/obj/item/clothing/suit/armor/hos/officerbluejacket + name = "officer's blue jacket" + icon_state = "officerbluejacket" + item_state = "officerbluejacket" + +//TG Station + +/obj/item/clothing/under/rank/security/formal + name = "security suit" + desc = "A formal security suit for officers complete with nanotrasen belt buckle." + icon_state = "security_formal" + item_state = "gy_suit" + item_color = "security_formal" + +/obj/item/clothing/under/rank/warden/formal + name = "warden's suit" + desc = "A formal security suit for the warden with blue desginations and '/Warden/' stiched into the shoulders." + icon_state = "warden_formal" + item_state = "gy_suit" + item_color = "warden_formal" + +/obj/item/clothing/under/rank/head_of_security/formal + name = "head of security's suit" + desc = "A security suit decorated for those few with the dedication to achieve the position of Head of Security." + icon_state = "hos_formal" + item_state = "gy_suit" + item_color = "hos_formal" + + +//Brig Physician +/obj/item/clothing/under/rank/security/brigphys + desc = "Jumpsuit for Brig Physician it has both medical and security protection." + name = "brig physician's jumpsuit" + icon_state = "brig_phys" + item_state = "brig_phys" + item_color = "brig_phys" + permeability_coefficient = 0.50 + armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 30, acid = 30) + +/obj/item/clothing/under/rank/security/brigphys/skirt + desc = "A skirted Brig Physician uniform. It has both security and medical protection." + name = "brig physician's jumpskirt" + icon_state = "brig_physf" + item_state = "brig_physf" + item_color = "brig_physf" + permeability_coefficient = 0.50 + +//Pod Pilot +/obj/item/clothing/under/rank/security/pod_pilot + desc = "Suit for your regular pod pilot." + name = "pod pilot's jumpsuit" + icon_state = "pod_pilot" + item_state = "pod_pilot" + item_color = "pod_pilot" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 5203aafdcf6..a9b4eab329e 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -1,851 +1,840 @@ -/obj/item/clothing/under/pj/red - name = "red pj's" - desc = "Sleepwear." - icon_state = "red_pyjamas" - item_color = "red_pyjamas" - item_state = "w_suit" - -/obj/item/clothing/under/pj/blue - name = "blue pj's" - desc = "Sleepwear." - icon_state = "blue_pyjamas" - item_color = "blue_pyjamas" - item_state = "w_suit" - -/obj/item/clothing/under/patriotsuit - name = "Patriotic Suit" - desc = "Motorcycle not included." - icon_state = "ek" - item_state = "ek" - item_color = "ek" - -/obj/item/clothing/under/captain_fly - name = "rogue captains uniform" - desc = "For the man who doesn't care because he's still free." - icon_state = "captain_fly" - item_state = "captain_fly" - item_color = "captain_fly" - -/obj/item/clothing/under/scratch - name = "white suit" - desc = "A white suit, suitable for an excellent host" - icon_state = "scratch" - item_state = "scratch" - item_color = "scratch" - -/obj/item/clothing/under/sl_suit - desc = "It's a very amish looking suit." - name = "amish suit" - icon_state = "sl_suit" - item_color = "sl_suit" - -/obj/item/clothing/under/waiter - name = "waiter's outfit" - desc = "It's a very smart uniform with a special pocket for tip." - icon_state = "waiter" - item_state = "waiter" - item_color = "waiter" - -/obj/item/clothing/under/rank/mailman - name = "mailman's jumpsuit" - desc = "'Special delivery!'" - icon_state = "mailman" - item_state = "b_suit" - item_color = "mailman" - -/obj/item/clothing/under/rank/vice - name = "vice officer's jumpsuit" - desc = "It's the standard issue pretty-boy outfit, as seen on Holo-Vision." - icon_state = "vice" - item_state = "gy_suit" - item_color = "vice" - -/obj/item/clothing/under/solgov - name = "Sol Federation marine uniform" - desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces." - icon_state = "solgov" - item_state = "ro_suit" - item_color = "solgov" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - displays_id = 0 - -/obj/item/clothing/under/solgov/command - name = "Sol Federation Lieutenant's uniform" - desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces. This one has additional insignia on its shoulders." - icon_state = "solgovc" - item_color = "solgovc" - -/obj/item/clothing/under/solgov/rep - name = "Sol Federation representative's uniform" - desc = "A formal uniform worn by the diplomatic representatives of the Sol Federation." - icon_state = "solgovr" - item_color = "solgovr" - -/obj/item/clothing/under/rank/centcom_officer - desc = "It's a jumpsuit worn by CentComm Officers." - name = "\improper CentComm officer's jumpsuit" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - -/obj/item/clothing/under/rank/centcom_commander - desc = "It's a jumpsuit worn by CentComm's highest-tier Commanders." - name = "\improper CentComm officer's jumpsuit" - icon_state = "centcom" - item_state = "dg_suit" - item_color = "centcom" - -/obj/item/clothing/under/rank/centcom/officer - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Lieutenant-Commander\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." - name = "\improper Nanotrasen Naval Officer Uniform" - icon_state = "navy_gold" - item_state = "navy_gold" - item_color = "navy_gold" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/captain - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Captain\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." - name = "\improper Nanotrasen Naval Captain Uniform" - icon_state = "navy_gold" - item_state = "navy_gold" - item_color = "navy_gold" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/blueshield - desc = "Gold trim on space-black cloth, this uniform bears \"Close Protection\" on the left shoulder." - name = "\improper Formal Nanotrasen Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/representative - desc = "Gold trim on space-black cloth, this uniform bears \"N.S.S. Cyberiad\" on the left shoulder." - name = "\improper Formal Nanotrasen Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/representative/New() - ..() - desc = "Gold trim on space-black cloth, this uniform bears [station_name()] on the left shoulder." - -/obj/item/clothing/under/rank/centcom/magistrate - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears \"N.S.S. Cyberiad\" on the left shoulder." - name = "\improper Formal Nanotrasen Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/magistrate/New() - ..() - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears [station_name()] on the left shoulder." - -/obj/item/clothing/under/rank/centcom/diplomatic - desc = "A very gaudy and official looking uniform of the Nanotrasen Diplomatic Corps." - name = "\improper Nanotrasen Diplomatic Uniform" - icon_state = "presidente" - item_state = "g_suit" - item_color = "presidente" - displays_id = 0 - -/obj/item/clothing/under/rank/blueshield - name = "blueshield uniform" - desc = "A short-sleeved black uniform, paired with grey digital-camo cargo pants. Standard issue to Blueshields." - icon_state = "ert_uniform" - item_state = "bl_suit" - item_color = "ert_uniform" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - -/obj/item/clothing/under/rank/blueshield/skirt - name = "blueshield skirt" - desc = "A short, black and grey with blue markings skirted uniform. For the feminine Blueshield." - icon_state = "blueshieldf" - item_state = "blueshieldf" - item_color = "blueshieldf" - -/obj/item/clothing/under/space - name = "\improper NASA jumpsuit" - desc = "It has a NASA logo on it and is made of space-proofed materials." - icon_state = "black" - item_state = "bl_suit" - item_color = "black" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | ARMS //Needs gloves and shoes with cold protection to be fully protected. - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT - resistance_flags = NONE - -/obj/item/clothing/under/acj - name = "administrative cybernetic jumpsuit" - icon_state = "syndicate" - item_state = "bl_suit" - item_color = "syndicate" - desc = "it's a cybernetically enhanced jumpsuit used for administrative duties." - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS|HEAD - armor = list("melee" = 100, "bullet" = 100, "laser" = 100,"energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS | HEAD - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS | HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - slowdown = -10 - siemens_coefficient = 0 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF - -/obj/item/clothing/under/johnny - name = "johnny~~ jumpsuit" - desc = "Johnny~~" - icon_state = "johnny" - item_color = "johnny" - -/obj/item/clothing/under/rainbow - name = "rainbow" - desc = "rainbow" - icon_state = "rainbow" - item_state = "rainbow" - item_color = "rainbow" - -/obj/item/clothing/under/cloud - name = "cloud" - desc = "cloud" - icon_state = "cloud" - item_color = "cloud" - -/obj/item/clothing/under/psysuit - name = "dark undersuit" - desc = "A thick, layered grey undersuit lined with power cables. Feels a little like wearing an electrical storm." - icon_state = "psysuit" - item_state = "psysuit" - item_color = "psysuit" - -/obj/item/clothing/under/gimmick/rank/captain/suit - name = "captain's suit" - desc = "A green suit and yellow necktie. Exemplifies authority." - icon_state = "green_suit" - item_state = "dg_suit" - item_color = "green_suit" - -/obj/item/clothing/under/gimmick/rank/head_of_personnel/suit - name = "head of personnel's suit" - desc = "A teal suit and yellow necktie. An authoritative yet tacky ensemble." - icon_state = "teal_suit" - item_state = "g_suit" - item_color = "teal_suit" - -/obj/item/clothing/under/suit_jacket - name = "black suit" - desc = "A black suit and red tie. Very formal." - icon_state = "black_suit" - item_state = "bl_suit" - item_color = "black_suit" - -/obj/item/clothing/under/suit_jacket/really_black - name = "executive suit" - desc = "A formal black suit and red tie, intended for the station's finest." - icon_state = "really_black_suit" - item_state = "bl_suit" - item_color = "really_black_suit" - -/obj/item/clothing/under/suit_jacket/female - name = "executive suit" - desc = "A formal trouser suit for women, intended for the station's finest." - icon_state = "black_suit_fem" - item_state = "black_suit_fem" - item_color = "black_suit_fem" - -/obj/item/clothing/under/suit_jacket/red - name = "red suit" - desc = "A red suit and blue tie. Somewhat formal." - icon_state = "red_suit" - item_state = "r_suit" - item_color = "red_suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/suit_jacket/navy - name = "navy suit" - desc = "A navy suit and red tie, intended for the station's finest." - icon_state = "navy_suit" - item_state = "navy_suit" - item_color = "navy_suit" - -/obj/item/clothing/under/suit_jacket/tan - name = "tan suit" - desc = "A tan suit with a yellow tie. Smart, but casual." - icon_state = "tan_suit" - item_state = "tan_suit" - item_color = "tan_suit" - -/obj/item/clothing/under/suit_jacket/burgundy - name = "burgundy suit" - desc = "A burgundy suit and black tie. Somewhat formal." - icon_state = "burgundy_suit" - item_state = "burgundy_suit" - item_color = "burgundy_suit" - -/obj/item/clothing/under/suit_jacket/charcoal - name = "charcoal suit" - desc = "A charcoal suit and blue tie. Very professional." - icon_state = "charcoal_suit" - item_state = "charcoal_suit" - item_color = "charcoal_suit" - -/obj/item/clothing/under/blackskirt - name = "black skirt" - desc = "A black skirt, very fancy!" - icon_state = "blackskirt" - item_color = "blackskirt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/schoolgirl - name = "schoolgirl uniform" - desc = "It's just like one of my Japanese animes!" - icon_state = "schoolgirl" - item_state = "schoolgirl" - item_color = "schoolgirl" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/overalls - name = "laborer's overalls" - desc = "A set of durable overalls for getting the job done." - icon_state = "overalls" - item_state = "lb_suit" - item_color = "overalls" - -/obj/item/clothing/under/pirate - name = "pirate outfit" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - item_color = "pirate" - -/obj/item/clothing/under/pirate_rags - name = "pirate rags" - desc = "an old ragged set of clothing" - icon_state = "piraterags" - item_state = "piraterags" - item_color = "piraterags" - -/obj/item/clothing/under/soviet - name = "soviet uniform" - desc = "For the Motherland!" - icon_state = "soviet" - item_state = "soviet" - item_color = "soviet" - -/obj/item/clothing/under/redcoat - name = "redcoat uniform" - desc = "Looks old." - icon_state = "redcoat" - item_state = "redcoat" - item_color = "redcoat" - -/obj/item/clothing/under/kilt - name = "kilt" - desc = "Includes shoes and plaid" - icon_state = "kilt" - item_state = "kilt" - item_color = "kilt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET - -/obj/item/clothing/under/sexymime - name = "sexy mime outfit" - desc = "The only time when you DON'T enjoy looking at someone's rack." - icon_state = "sexymime" - item_state = "sexymime" - item_color = "sexymime" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/gladiator - name = "gladiator uniform" - desc = "Are you not entertained? Is that not why you are here?" - icon_state = "gladiator" - item_state = "gladiator" - item_color = "gladiator" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - resistance_flags = NONE - -/obj/item/clothing/under/gladiator/ash_walker - desc = "This gladiator uniform appears to be covered in ash and fairly dated." - has_sensor = FALSE - -//dress - -/obj/item/clothing/under/dress/dress_fire - name = "flame dress" - desc = "A small black dress with blue flames print on it." - icon_state = "dress_fire" - item_color = "dress_fire" - -/obj/item/clothing/under/dress/dress_green - name = "green dress" - desc = "A simple, tight fitting green dress." - icon_state = "dress_green" - item_color = "dress_green" - -/obj/item/clothing/under/dress/dress_orange - name = "orange dress" - desc = "A fancy orange gown for those who like to show leg." - icon_state = "dress_orange" - item_color = "dress_orange" - -/obj/item/clothing/under/dress/dress_pink - name = "pink dress" - desc = "A simple, tight fitting pink dress." - icon_state = "dress_pink" - item_color = "dress_pink" - -/obj/item/clothing/under/dress/dress_yellow - name = "yellow dress" - desc = "A flirty, little yellow dress." - icon_state = "dress_yellow" - item_color = "dress_yellow" - -/obj/item/clothing/under/dress/dress_saloon - name = "saloon girl dress" - desc = "A old western inspired gown for the girl who likes to drink." - icon_state = "dress_saloon" - item_color = "dress_saloon" - -/obj/item/clothing/under/dress/dress_rd - name = "research director dress uniform" - desc = "Feminine fashion for the style concious RD." - icon_state = "dress_rd" - item_color = "dress_rd" - -/obj/item/clothing/under/dress/dress_cap - name = "captain dress uniform" - desc = "Feminine fashion for the style concious captain." - icon_state = "dress_cap" - item_color = "dress_cap" - -/obj/item/clothing/under/dress/dress_hop - name = "head of personal dress uniform" - desc = "Feminine fashion for the style concious HoP." - icon_state = "dress_hop" - item_color = "dress_hop" - -/obj/item/clothing/under/dress/dress_hr - name = "human resources director uniform" - desc = "Superior class for the nosy H.R. Director." - icon_state = "huresource" - item_color = "huresource" - -/obj/item/clothing/under/dress/plaid_blue - name = "blue plaid skirt" - desc = "A preppy blue skirt with a white blouse." - icon_state = "plaid_blue" - item_color = "plaid_blue" - -/obj/item/clothing/under/dress/plaid_red - name = "red plaid skirt" - desc = "A preppy red skirt with a white blouse." - icon_state = "plaid_red" - item_color = "plaid_red" - -/obj/item/clothing/under/dress/plaid_purple - name = "blue purple skirt" - desc = "A preppy purple skirt with a white blouse." - icon_state = "plaid_purple" - item_color = "plaid_purple" - -//wedding stuff - -/obj/item/clothing/under/wedding/bride_orange - name = "orange wedding dress" - desc = "A big and puffy orange dress." - icon_state = "bride_orange" - item_color = "bride_orange" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_purple - name = "purple wedding dress" - desc = "A big and puffy purple dress." - icon_state = "bride_purple" - item_color = "bride_purple" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_blue - name = "blue wedding dress" - desc = "A big and puffy blue dress." - icon_state = "bride_blue" - item_color = "bride_blue" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_red - name = "red wedding dress" - desc = "A big and puffy red dress." - icon_state = "bride_red" - item_color = "bride_red" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_white - name = "orange wedding dress" - desc = "A white wedding gown made from the finest silk." - icon_state = "bride_white" - item_color = "bride_white" - flags_inv = HIDESHOES - -/obj/item/clothing/under/sundress - name = "sundress" - desc = "Makes you want to frolic in a field of daisies." - icon_state = "sundress" - item_state = "sundress" - item_color = "sundress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/captainparade - name = "captain's parade uniform" - desc = "A captain's luxury-wear, for special occasions." - icon_state = "captain_parade" - item_state = "by_suit" - item_color = "captain_parade" - -/obj/item/clothing/under/roman - name = "roman armor" - desc = "An ancient Roman armor. Made of metallic strips and leather straps." - icon_state = "roman" - item_color = "roman" - item_state = "armor" - strip_delay = 100 - resistance_flags = NONE - -/obj/item/clothing/under/maid - name = "maid costume" - desc = "Maid in China." - icon_state = "meido" - item_state = "meido" - item_color = "meido" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/janimaid - name = "maid uniform" - desc = "A simple maid uniform for housekeeping." - icon_state = "janimaid" - item_state = "janimaid" - item_color = "janimaid" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/singery - name = "yellow performer's outfit" - desc = "Just looking at this makes you want to sing." - icon_state = "ysing" - item_state = "ysing" - item_color = "ysing" - -/obj/item/clothing/under/singerb - name = "blue performer's outfit" - desc = "Just looking at this makes you want to sing." - icon_state = "bsing" - item_state = "bsing" - item_color = "bsing" - -/obj/item/clothing/under/jester - name = "jester suit" - desc = "A jolly dress, well suited to entertain your master, nuncle." - icon_state = "jester" - item_color = "jester" - -/obj/item/clothing/under/flappers - name = "flappers" - desc = "Nothing like the roarin' '20s, flapping the night away on the dance floor." - icon_state = "flapper" - item_state = "flapper" - item_color = "flapper" - -/obj/item/clothing/under/mafia - name = "mafia outfit" - desc = "The business of the mafia is business." - icon_state = "mafia" - item_state = "mafia" - item_color = "mafia" - -/obj/item/clothing/under/mafia/vest - name = "mafia vest" - desc = "Extreme problems often require extreme solutions." - icon_state = "mafiavest" - item_state = "mafiavest" - item_color = "mafiavest" - -/obj/item/clothing/under/mafia/white - name = "white mafia outfit" - desc = "The best defense against the treacherous is treachery." - icon_state = "mafiawhite" - item_state = "mafiawhite" - item_color = "mafiawhite" - -/obj/item/clothing/under/mafia/sue - name = "mafia vest" - desc = "The business is born into." - icon_state = "suevest" - item_state = "suevest" - item_color = "suevest" - -/obj/item/clothing/under/mafia/tan - name = "leather mafia outfit" - desc = "The big drum sounds good only from a distance." - icon_state = "mafiatan" - item_state = "mafiatan" - item_color = "mafiatan" - - -/obj/item/clothing/under/bane - name = "Bane Harness" - desc = "Wear this harness to become the bane of the station." - icon_state = "bane" - item_state = "bane" - item_color = "bane" - -/obj/item/clothing/under/vox - name = "Ripped Jumpsuit" - desc = "A jumpsuit that looks like it's been shredded by some talons. Who could wear this now?" - icon = 'icons/obj/clothing/species/vox/uniforms.dmi' - icon_state = "vgrey" - item_state = "vgrey" - item_color = "vgrey" - -/obj/item/clothing/under/psyjump - name = "Psychic Amp Jumpsuit" - desc = "A suit made of strange materials." - icon_state = "psyamp" - item_state = "psyamp" - item_color = "psyamp" - -/obj/item/clothing/under/rebeloutfit - name = "Rebel Outfit" - desc = "Made in Seattle, 2216." - icon_state = "colin_earle" - item_state = "colin_earle" - item_color = "colin_earle" - -/obj/item/clothing/under/officeruniform - name = "Clown Officer's Uniform" - desc = "For Clown officers, this uniform was designed by the great clown designer Hugo Boss." - icon_state = "officeruniform" - item_color = "officeruniform" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/soldieruniform - name = "Clown Soldier's Uniform" - desc = "For the basic grunt of the Clown army." - icon_state = "soldieruniform" - item_color = "soldieruniform" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/pennywise - name = "Pennywise Costume" - desc = "It's everything you ever were afraid of." - icon_state = "pennywise" - item_color = "pennywise" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/assistantformal - name = "assistant's formal uniform" - desc = "An assistant's formal-wear. Why an assistant needs formal-wear is still unknown." - icon_state = "assistant_formal" - item_state = "gy_suit" - item_color = "assistant_formal" - -/obj/item/clothing/under/blacktango - name = "black tango dress" - desc = "Filled with Latin fire." - icon_state = "black_tango" - item_state = "wcoat" - item_color = "black_tango" - -/obj/item/clothing/under/stripeddress - name = "striped dress" - desc = "Fashion in space." - icon_state = "striped_dress" - item_state = "stripeddress" - item_color = "striped_dress" - -/obj/item/clothing/under/sailordress - name = "sailor dress" - desc = "Formal wear for a leading lady." - icon_state = "sailor_dress" - item_state = "sailordress" - item_color = "sailor_dress" - -/obj/item/clothing/under/redeveninggown - name = "red evening gown" - desc = "Fancy dress for space bar singers." - icon_state = "red_evening_gown" - item_state = "redeveninggown" - item_color = "red_evening_gown" - -/obj/item/clothing/under/suit_jacket/checkered - name = "checkered suit" - desc = "That's a very nice suit you have there. Shame if something were to happen to it, eh?" - icon_state = "checkered_suit" - item_state = "checkered_suit" - item_color = "checkered_suit" - -/obj/item/clothing/under/owl - name = "owl uniform" - desc = "A soft brown jumpsuit made of synthetic feathers and strong conviction." - icon_state = "owl" - item_color = "owl" - -/obj/item/clothing/under/griffin - name = "griffon uniform" - desc = "A soft brown jumpsuit with a white feather collar made of synthetic feathers and a lust for mayhem." - icon_state = "griffin" - item_color = "griffin" - -/obj/item/clothing/under/noble_clothes - name = "noble clothes" - desc = "They fall just short of majestic." - icon_state = "noble_clothes" - item_color = "noble_clothes" - item_state = "noble_clothes" - -/obj/item/clothing/under/contortionist - name = "atmospheric technician's jumpsuit" - desc = "A light jumpsuit useful for squeezing through narrow vents." - icon_state = "atmos" - item_state = "atmos_suit" - item_color = "atmos" - resistance_flags = FIRE_PROOF - -/obj/item/clothing/under/contortionist/equipped(mob/living/carbon/human/user, slot) - if(!user.ventcrawler) - user.ventcrawler = 2 - ..() - -/obj/item/clothing/under/contortionist/dropped(mob/living/carbon/human/user) - if(!user.get_int_organ(/obj/item/organ/internal/heart/gland/ventcrawling)) - user.ventcrawler = 0 - ..() - -/obj/item/clothing/under/contortionist/proc/check_clothing(mob/user as mob) - //Allowed to wear: glasses, shoes, gloves, pockets, mask, and jumpsuit (obviously) - var/list/slot_must_be_empty = list(slot_back,slot_handcuffed,slot_legcuffed,slot_l_hand,slot_r_hand,slot_belt,slot_head,slot_wear_suit) - for(var/slot_id in slot_must_be_empty) - if(user.get_item_by_slot(slot_id)) - to_chat(user,"You can't fit inside while wearing that \the [user.get_item_by_slot(slot_id)].") - return 0 - return 1 - -/obj/item/clothing/under/cursedclown - name = "cursed clown suit" - desc = "It wasn't already?" - icon = 'icons/goonstation/objects/clothing/uniform.dmi' - icon_state = "cursedclown" - item_state = "cclown_uniform" - item_color = "cursedclown" - icon_override = 'icons/goonstation/mob/clothing/uniform.dmi' - lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - flags = NODROP - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - flags_size = ONESIZEFITSALL - has_sensor = 0 // HUNKE - -/obj/item/clothing/under/victdress - name = "black victorian dress" - desc = "A victorian style dress, fancy!" - icon_state = "victorianblackdress" - item_state = "victorianblackdress" - item_color = "victorianblackdress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/victdress/red - name = "red victorian dress" - icon_state = "victorianreddress" - item_state = "victorianreddress" - item_color = "victorianreddress" - -/obj/item/clothing/under/victsuit - name = "victorian suit" - desc = "A victorian style suit, fancy!" - icon_state = "victorianvest" - item_state = "victorianvest" - item_color = "victorianvest" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/victsuit/redblk - name = "red and black victorian suit" - icon_state = "victorianblred" - item_state = "victorianblred" - item_color = "victorianblred" - -/obj/item/clothing/under/victsuit/red - name = "red victorian suit" - icon_state = "victorianredvest" - item_state = "victorianredvest" - item_color = "victorianredvest" - -/obj/item/clothing/under/medigown - name = "medical gown" - desc = "a flimsy examination gown, the back ties never close." - icon_state = "medicalgown" - item_state = "medicalgown" - item_color = "medicalgown" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/burial - name = "burial garments" - desc = "Traditional burial garments from the early 22nd century." - icon_state = "burial" - item_state = "burial" - item_color = "burial" - -/obj/item/clothing/under/redhawaiianshirt - name = "red hawaiian shirt" - desc = "a floral shirt worn to most vacation destinations." - icon_state = "hawaiianred" - item_state = "hawaiianred" - item_color = "hawaiianred" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/pinkhawaiianshirt - name = "pink hawaiian shirt" - desc = "a pink floral shirt the material feels cool and comfy." - icon_state = "hawaiianpink" - item_state = "hawaiianpink" - item_color = "hawaiianpink" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/orangehawaiianshirt - name = "orange hawaiian shirt" - desc = "a orange floral shirt for a relaxing day in space." - icon_state = "hawaiianorange" - item_state = "hawaiianorange" - item_color = "hawaiianorange" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/bluehawaiianshirt - name = "blue hawaiian shirt" - desc = "a blue floral shirt it has a oddly colored pink flower on it." - icon_state = "hawaiianblue" - item_state = "hawaiianblue" - item_color = "hawaiianblue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/misc/durathread - name = "durathread jumpsuit" - desc = "A jumpsuit made from durathread, its resilient fibres provide some protection to the wearer." - icon_state = "durathread" - item_state = "durathread" - item_color = "durathread" - armor = list(melee = 10, bullet = 0, laser = 10, energy = 0, bomb = 5, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/clothing/under/cuban_suit - name = "rhumba outfit" - desc = "A satin shirt and high-waisted pants, worn by dancers in the Rhumba style. It smells oddly like... sulfur?" - icon_state = "cuban_suit" - item_state = "cuban_suit" - item_color = "cuban_suit" \ No newline at end of file +/obj/item/clothing/under/pj/red + name = "red pj's" + desc = "Sleepwear." + icon_state = "red_pyjamas" + item_color = "red_pyjamas" + item_state = "w_suit" + +/obj/item/clothing/under/pj/blue + name = "blue pj's" + desc = "Sleepwear." + icon_state = "blue_pyjamas" + item_color = "blue_pyjamas" + item_state = "w_suit" + +/obj/item/clothing/under/patriotsuit + name = "Patriotic Suit" + desc = "Motorcycle not included." + icon_state = "ek" + item_state = "ek" + item_color = "ek" + +/obj/item/clothing/under/captain_fly + name = "rogue captains uniform" + desc = "For the man who doesn't care because he's still free." + icon_state = "captain_fly" + item_state = "captain_fly" + item_color = "captain_fly" + +/obj/item/clothing/under/scratch + name = "white suit" + desc = "A white suit, suitable for an excellent host" + icon_state = "scratch" + item_state = "scratch" + item_color = "scratch" + +/obj/item/clothing/under/sl_suit + desc = "It's a very amish looking suit." + name = "amish suit" + icon_state = "sl_suit" + item_color = "sl_suit" + +/obj/item/clothing/under/waiter + name = "waiter's outfit" + desc = "It's a very smart uniform with a special pocket for tip." + icon_state = "waiter" + item_state = "waiter" + item_color = "waiter" + +/obj/item/clothing/under/rank/mailman + name = "mailman's jumpsuit" + desc = "'Special delivery!'" + icon_state = "mailman" + item_state = "b_suit" + item_color = "mailman" + +/obj/item/clothing/under/rank/vice + name = "vice officer's jumpsuit" + desc = "It's the standard issue pretty-boy outfit, as seen on Holo-Vision." + icon_state = "vice" + item_state = "gy_suit" + item_color = "vice" + +/obj/item/clothing/under/solgov + name = "Sol Federation marine uniform" + desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces." + icon_state = "solgov" + item_state = "ro_suit" + item_color = "solgov" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + displays_id = 0 + +/obj/item/clothing/under/solgov/command + name = "Sol Federation Lieutenant's uniform" + desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces. This one has additional insignia on its shoulders." + icon_state = "solgovc" + item_color = "solgovc" + +/obj/item/clothing/under/solgov/rep + name = "Sol Federation representative's uniform" + desc = "A formal uniform worn by the diplomatic representatives of the Sol Federation." + icon_state = "solgovr" + item_color = "solgovr" + +/obj/item/clothing/under/rank/centcom_officer + desc = "It's a jumpsuit worn by CentComm Officers." + name = "\improper CentComm officer's jumpsuit" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + +/obj/item/clothing/under/rank/centcom_commander + desc = "It's a jumpsuit worn by CentComm's highest-tier Commanders." + name = "\improper CentComm officer's jumpsuit" + icon_state = "centcom" + item_state = "dg_suit" + item_color = "centcom" + +/obj/item/clothing/under/rank/centcom/officer + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Lieutenant-Commander\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." + name = "\improper Nanotrasen Naval Officer Uniform" + icon_state = "navy_gold" + item_state = "navy_gold" + item_color = "navy_gold" + displays_id = 0 + +/obj/item/clothing/under/rank/centcom/captain + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Captain\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." + name = "\improper Nanotrasen Naval Captain Uniform" + icon_state = "navy_gold" + item_state = "navy_gold" + item_color = "navy_gold" + displays_id = 0 + +/obj/item/clothing/under/rank/centcom/blueshield + desc = "Gold trim on space-black cloth, this uniform bears \"Close Protection\" on the left shoulder." + name = "\improper Formal Nanotrasen Uniform" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + displays_id = 0 + +/obj/item/clothing/under/rank/centcom/representative + desc = "Gold trim on space-black cloth, this uniform bears \"N.S.S. Cyberiad\" on the left shoulder." + name = "\improper Formal Nanotrasen Uniform" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + displays_id = 0 + +/obj/item/clothing/under/rank/centcom/representative/New() + ..() + desc = "Gold trim on space-black cloth, this uniform bears [station_name()] on the left shoulder." + +/obj/item/clothing/under/rank/centcom/magistrate + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears \"N.S.S. Cyberiad\" on the left shoulder." + name = "\improper Formal Nanotrasen Uniform" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + displays_id = 0 + +/obj/item/clothing/under/rank/centcom/magistrate/New() + ..() + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears [station_name()] on the left shoulder." + +/obj/item/clothing/under/rank/centcom/diplomatic + desc = "A very gaudy and official looking uniform of the Nanotrasen Diplomatic Corps." + name = "\improper Nanotrasen Diplomatic Uniform" + icon_state = "presidente" + item_state = "g_suit" + item_color = "presidente" + displays_id = 0 + +/obj/item/clothing/under/rank/blueshield + name = "blueshield uniform" + desc = "A short-sleeved black uniform, paired with grey digital-camo cargo pants. Standard issue to Blueshields." + icon_state = "ert_uniform" + item_state = "bl_suit" + item_color = "ert_uniform" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + +/obj/item/clothing/under/rank/blueshield/skirt + name = "blueshield skirt" + desc = "A short, black and grey with blue markings skirted uniform. For the feminine Blueshield." + icon_state = "blueshieldf" + item_state = "blueshieldf" + item_color = "blueshieldf" + +/obj/item/clothing/under/space + name = "\improper NASA jumpsuit" + desc = "It has a NASA logo on it and is made of space-proofed materials." + icon_state = "black" + item_state = "bl_suit" + item_color = "black" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.02 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | ARMS //Needs gloves and shoes with cold protection to be fully protected. + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + resistance_flags = NONE + +/obj/item/clothing/under/acj + name = "administrative cybernetic jumpsuit" + icon_state = "syndicate" + item_state = "bl_suit" + item_color = "syndicate" + desc = "it's a cybernetically enhanced jumpsuit used for administrative duties." + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS|HEAD + armor = list("melee" = 100, "bullet" = 100, "laser" = 100,"energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS | HEAD + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS | HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + slowdown = -10 + siemens_coefficient = 0 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF + +/obj/item/clothing/under/johnny + name = "johnny~~ jumpsuit" + desc = "Johnny~~" + icon_state = "johnny" + item_color = "johnny" + +/obj/item/clothing/under/rainbow + name = "rainbow" + desc = "rainbow" + icon_state = "rainbow" + item_state = "rainbow" + item_color = "rainbow" + +/obj/item/clothing/under/cloud + name = "cloud" + desc = "cloud" + icon_state = "cloud" + item_color = "cloud" + +/obj/item/clothing/under/psysuit + name = "dark undersuit" + desc = "A thick, layered grey undersuit lined with power cables. Feels a little like wearing an electrical storm." + icon_state = "psysuit" + item_state = "psysuit" + item_color = "psysuit" + +/obj/item/clothing/under/gimmick/rank/captain/suit + name = "captain's suit" + desc = "A green suit and yellow necktie. Exemplifies authority." + icon_state = "green_suit" + item_state = "dg_suit" + item_color = "green_suit" + +/obj/item/clothing/under/gimmick/rank/head_of_personnel/suit + name = "head of personnel's suit" + desc = "A teal suit and yellow necktie. An authoritative yet tacky ensemble." + icon_state = "teal_suit" + item_state = "g_suit" + item_color = "teal_suit" + +/obj/item/clothing/under/suit_jacket + name = "black suit" + desc = "A black suit and red tie. Very formal." + icon_state = "black_suit" + item_state = "bl_suit" + item_color = "black_suit" + +/obj/item/clothing/under/suit_jacket/really_black + name = "executive suit" + desc = "A formal black suit and red tie, intended for the station's finest." + icon_state = "really_black_suit" + item_state = "bl_suit" + item_color = "really_black_suit" + +/obj/item/clothing/under/suit_jacket/female + name = "executive suit" + desc = "A formal trouser suit for women, intended for the station's finest." + icon_state = "black_suit_fem" + item_state = "black_suit_fem" + item_color = "black_suit_fem" + +/obj/item/clothing/under/suit_jacket/red + name = "red suit" + desc = "A red suit and blue tie. Somewhat formal." + icon_state = "red_suit" + item_state = "r_suit" + item_color = "red_suit" + +/obj/item/clothing/under/suit_jacket/navy + name = "navy suit" + desc = "A navy suit and red tie, intended for the station's finest." + icon_state = "navy_suit" + item_state = "navy_suit" + item_color = "navy_suit" + +/obj/item/clothing/under/suit_jacket/tan + name = "tan suit" + desc = "A tan suit with a yellow tie. Smart, but casual." + icon_state = "tan_suit" + item_state = "tan_suit" + item_color = "tan_suit" + +/obj/item/clothing/under/suit_jacket/burgundy + name = "burgundy suit" + desc = "A burgundy suit and black tie. Somewhat formal." + icon_state = "burgundy_suit" + item_state = "burgundy_suit" + item_color = "burgundy_suit" + +/obj/item/clothing/under/suit_jacket/charcoal + name = "charcoal suit" + desc = "A charcoal suit and blue tie. Very professional." + icon_state = "charcoal_suit" + item_state = "charcoal_suit" + item_color = "charcoal_suit" + +/obj/item/clothing/under/blackskirt + name = "black skirt" + desc = "A black skirt, very fancy!" + icon_state = "blackskirt" + item_color = "blackskirt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/schoolgirl + name = "schoolgirl uniform" + desc = "It's just like one of my Japanese animes!" + icon_state = "schoolgirl" + item_state = "schoolgirl" + item_color = "schoolgirl" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/overalls + name = "laborer's overalls" + desc = "A set of durable overalls for getting the job done." + icon_state = "overalls" + item_state = "lb_suit" + item_color = "overalls" + +/obj/item/clothing/under/pirate + name = "pirate outfit" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + item_color = "pirate" + +/obj/item/clothing/under/pirate_rags + name = "pirate rags" + desc = "an old ragged set of clothing" + icon_state = "piraterags" + item_state = "piraterags" + item_color = "piraterags" + +/obj/item/clothing/under/soviet + name = "soviet uniform" + desc = "For the Motherland!" + icon_state = "soviet" + item_state = "soviet" + item_color = "soviet" + +/obj/item/clothing/under/redcoat + name = "redcoat uniform" + desc = "Looks old." + icon_state = "redcoat" + item_state = "redcoat" + item_color = "redcoat" + +/obj/item/clothing/under/kilt + name = "kilt" + desc = "Includes shoes and plaid" + icon_state = "kilt" + item_state = "kilt" + item_color = "kilt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET + +/obj/item/clothing/under/sexymime + name = "sexy mime outfit" + desc = "The only time when you DON'T enjoy looking at someone's rack." + icon_state = "sexymime" + item_state = "sexymime" + item_color = "sexymime" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/gladiator + name = "gladiator uniform" + desc = "Are you not entertained? Is that not why you are here?" + icon_state = "gladiator" + item_state = "gladiator" + item_color = "gladiator" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + resistance_flags = NONE + +/obj/item/clothing/under/gladiator/ash_walker + desc = "This gladiator uniform appears to be covered in ash and fairly dated." + has_sensor = FALSE + +//dress + +/obj/item/clothing/under/dress/dress_fire + name = "flame dress" + desc = "A small black dress with blue flames print on it." + icon_state = "dress_fire" + item_color = "dress_fire" + +/obj/item/clothing/under/dress/dress_green + name = "green dress" + desc = "A simple, tight fitting green dress." + icon_state = "dress_green" + item_color = "dress_green" + +/obj/item/clothing/under/dress/dress_orange + name = "orange dress" + desc = "A fancy orange gown for those who like to show leg." + icon_state = "dress_orange" + item_color = "dress_orange" + +/obj/item/clothing/under/dress/dress_pink + name = "pink dress" + desc = "A simple, tight fitting pink dress." + icon_state = "dress_pink" + item_color = "dress_pink" + +/obj/item/clothing/under/dress/dress_yellow + name = "yellow dress" + desc = "A flirty, little yellow dress." + icon_state = "dress_yellow" + item_color = "dress_yellow" + +/obj/item/clothing/under/dress/dress_saloon + name = "saloon girl dress" + desc = "A old western inspired gown for the girl who likes to drink." + icon_state = "dress_saloon" + item_color = "dress_saloon" + +/obj/item/clothing/under/dress/dress_rd + name = "research director dress uniform" + desc = "Feminine fashion for the style concious RD." + icon_state = "dress_rd" + item_color = "dress_rd" + +/obj/item/clothing/under/dress/dress_cap + name = "captain dress uniform" + desc = "Feminine fashion for the style concious captain." + icon_state = "dress_cap" + item_color = "dress_cap" + +/obj/item/clothing/under/dress/dress_hop + name = "head of personal dress uniform" + desc = "Feminine fashion for the style concious HoP." + icon_state = "dress_hop" + item_color = "dress_hop" + +/obj/item/clothing/under/dress/dress_hr + name = "human resources director uniform" + desc = "Superior class for the nosy H.R. Director." + icon_state = "huresource" + item_color = "huresource" + +/obj/item/clothing/under/dress/plaid_blue + name = "blue plaid skirt" + desc = "A preppy blue skirt with a white blouse." + icon_state = "plaid_blue" + item_color = "plaid_blue" + +/obj/item/clothing/under/dress/plaid_red + name = "red plaid skirt" + desc = "A preppy red skirt with a white blouse." + icon_state = "plaid_red" + item_color = "plaid_red" + +/obj/item/clothing/under/dress/plaid_purple + name = "blue purple skirt" + desc = "A preppy purple skirt with a white blouse." + icon_state = "plaid_purple" + item_color = "plaid_purple" + +//wedding stuff + +/obj/item/clothing/under/wedding/bride_orange + name = "orange wedding dress" + desc = "A big and puffy orange dress." + icon_state = "bride_orange" + item_color = "bride_orange" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_purple + name = "purple wedding dress" + desc = "A big and puffy purple dress." + icon_state = "bride_purple" + item_color = "bride_purple" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_blue + name = "blue wedding dress" + desc = "A big and puffy blue dress." + icon_state = "bride_blue" + item_color = "bride_blue" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_red + name = "red wedding dress" + desc = "A big and puffy red dress." + icon_state = "bride_red" + item_color = "bride_red" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_white + name = "orange wedding dress" + desc = "A white wedding gown made from the finest silk." + icon_state = "bride_white" + item_color = "bride_white" + flags_inv = HIDESHOES + +/obj/item/clothing/under/sundress + name = "sundress" + desc = "Makes you want to frolic in a field of daisies." + icon_state = "sundress" + item_state = "sundress" + item_color = "sundress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/captainparade + name = "captain's parade uniform" + desc = "A captain's luxury-wear, for special occasions." + icon_state = "captain_parade" + item_state = "by_suit" + item_color = "captain_parade" + +/obj/item/clothing/under/roman + name = "roman armor" + desc = "An ancient Roman armor. Made of metallic strips and leather straps." + icon_state = "roman" + item_color = "roman" + item_state = "armor" + strip_delay = 100 + resistance_flags = NONE + +/obj/item/clothing/under/maid + name = "maid costume" + desc = "Maid in China." + icon_state = "meido" + item_state = "meido" + item_color = "meido" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/janimaid + name = "maid uniform" + desc = "A simple maid uniform for housekeeping." + icon_state = "janimaid" + item_state = "janimaid" + item_color = "janimaid" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/singery + name = "yellow performer's outfit" + desc = "Just looking at this makes you want to sing." + icon_state = "ysing" + item_state = "ysing" + item_color = "ysing" + +/obj/item/clothing/under/singerb + name = "blue performer's outfit" + desc = "Just looking at this makes you want to sing." + icon_state = "bsing" + item_state = "bsing" + item_color = "bsing" + +/obj/item/clothing/under/jester + name = "jester suit" + desc = "A jolly dress, well suited to entertain your master, nuncle." + icon_state = "jester" + item_color = "jester" + +/obj/item/clothing/under/flappers + name = "flappers" + desc = "Nothing like the roarin' '20s, flapping the night away on the dance floor." + icon_state = "flapper" + item_state = "flapper" + item_color = "flapper" + +/obj/item/clothing/under/mafia + name = "mafia outfit" + desc = "The business of the mafia is business." + icon_state = "mafia" + item_state = "mafia" + item_color = "mafia" + +/obj/item/clothing/under/mafia/vest + name = "mafia vest" + desc = "Extreme problems often require extreme solutions." + icon_state = "mafiavest" + item_state = "mafiavest" + item_color = "mafiavest" + +/obj/item/clothing/under/mafia/white + name = "white mafia outfit" + desc = "The best defense against the treacherous is treachery." + icon_state = "mafiawhite" + item_state = "mafiawhite" + item_color = "mafiawhite" + +/obj/item/clothing/under/mafia/sue + name = "mafia vest" + desc = "The business is born into." + icon_state = "suevest" + item_state = "suevest" + item_color = "suevest" + +/obj/item/clothing/under/mafia/tan + name = "leather mafia outfit" + desc = "The big drum sounds good only from a distance." + icon_state = "mafiatan" + item_state = "mafiatan" + item_color = "mafiatan" + + +/obj/item/clothing/under/bane + name = "Bane Harness" + desc = "Wear this harness to become the bane of the station." + icon_state = "bane" + item_state = "bane" + item_color = "bane" + +/obj/item/clothing/under/vox + name = "Ripped Jumpsuit" + desc = "A jumpsuit that looks like it's been shredded by some talons. Who could wear this now?" + icon = 'icons/obj/clothing/species/vox/uniforms.dmi' + icon_state = "vgrey" + item_state = "vgrey" + item_color = "vgrey" + +/obj/item/clothing/under/psyjump + name = "Psychic Amp Jumpsuit" + desc = "A suit made of strange materials." + icon_state = "psyamp" + item_state = "psyamp" + item_color = "psyamp" + +/obj/item/clothing/under/rebeloutfit + name = "Rebel Outfit" + desc = "Made in Seattle, 2216." + icon_state = "colin_earle" + item_state = "colin_earle" + item_color = "colin_earle" + +/obj/item/clothing/under/officeruniform + name = "Clown Officer's Uniform" + desc = "For Clown officers, this uniform was designed by the great clown designer Hugo Boss." + icon_state = "officeruniform" + item_color = "officeruniform" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/soldieruniform + name = "Clown Soldier's Uniform" + desc = "For the basic grunt of the Clown army." + icon_state = "soldieruniform" + item_color = "soldieruniform" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/pennywise + name = "Pennywise Costume" + desc = "It's everything you ever were afraid of." + icon_state = "pennywise" + item_color = "pennywise" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/assistantformal + name = "assistant's formal uniform" + desc = "An assistant's formal-wear. Why an assistant needs formal-wear is still unknown." + icon_state = "assistant_formal" + item_state = "gy_suit" + item_color = "assistant_formal" + +/obj/item/clothing/under/blacktango + name = "black tango dress" + desc = "Filled with Latin fire." + icon_state = "black_tango" + item_state = "wcoat" + item_color = "black_tango" + +/obj/item/clothing/under/stripeddress + name = "striped dress" + desc = "Fashion in space." + icon_state = "striped_dress" + item_state = "stripeddress" + item_color = "striped_dress" + +/obj/item/clothing/under/sailordress + name = "sailor dress" + desc = "Formal wear for a leading lady." + icon_state = "sailor_dress" + item_state = "sailordress" + item_color = "sailor_dress" + +/obj/item/clothing/under/redeveninggown + name = "red evening gown" + desc = "Fancy dress for space bar singers." + icon_state = "red_evening_gown" + item_state = "redeveninggown" + item_color = "red_evening_gown" + +/obj/item/clothing/under/suit_jacket/checkered + name = "checkered suit" + desc = "That's a very nice suit you have there. Shame if something were to happen to it, eh?" + icon_state = "checkered_suit" + item_state = "checkered_suit" + item_color = "checkered_suit" + +/obj/item/clothing/under/owl + name = "owl uniform" + desc = "A soft brown jumpsuit made of synthetic feathers and strong conviction." + icon_state = "owl" + item_color = "owl" + +/obj/item/clothing/under/griffin + name = "griffon uniform" + desc = "A soft brown jumpsuit with a white feather collar made of synthetic feathers and a lust for mayhem." + icon_state = "griffin" + item_color = "griffin" + +/obj/item/clothing/under/noble_clothes + name = "noble clothes" + desc = "They fall just short of majestic." + icon_state = "noble_clothes" + item_color = "noble_clothes" + item_state = "noble_clothes" + +/obj/item/clothing/under/contortionist + name = "atmospheric technician's jumpsuit" + desc = "A light jumpsuit useful for squeezing through narrow vents." + icon_state = "atmos" + item_state = "atmos_suit" + item_color = "atmos" + resistance_flags = FIRE_PROOF + +/obj/item/clothing/under/contortionist/equipped(mob/living/carbon/human/user, slot) + if(!user.ventcrawler) + user.ventcrawler = 2 + ..() + +/obj/item/clothing/under/contortionist/dropped(mob/living/carbon/human/user) + if(!user.get_int_organ(/obj/item/organ/internal/heart/gland/ventcrawling)) + user.ventcrawler = 0 + ..() + +/obj/item/clothing/under/contortionist/proc/check_clothing(mob/user as mob) + //Allowed to wear: glasses, shoes, gloves, pockets, mask, and jumpsuit (obviously) + var/list/slot_must_be_empty = list(slot_back,slot_handcuffed,slot_legcuffed,slot_l_hand,slot_r_hand,slot_belt,slot_head,slot_wear_suit) + for(var/slot_id in slot_must_be_empty) + if(user.get_item_by_slot(slot_id)) + to_chat(user,"You can't fit inside while wearing that \the [user.get_item_by_slot(slot_id)].") + return 0 + return 1 + +/obj/item/clothing/under/cursedclown + name = "cursed clown suit" + desc = "It wasn't already?" + icon = 'icons/goonstation/objects/clothing/uniform.dmi' + icon_state = "cursedclown" + item_state = "cclown_uniform" + item_color = "cursedclown" + icon_override = 'icons/goonstation/mob/clothing/uniform.dmi' + lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' + flags = NODROP + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + has_sensor = 0 // HUNKE + +/obj/item/clothing/under/victdress + name = "black victorian dress" + desc = "A victorian style dress, fancy!" + icon_state = "victorianblackdress" + item_state = "victorianblackdress" + item_color = "victorianblackdress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/victdress/red + name = "red victorian dress" + icon_state = "victorianreddress" + item_state = "victorianreddress" + item_color = "victorianreddress" + +/obj/item/clothing/under/victsuit + name = "victorian suit" + desc = "A victorian style suit, fancy!" + icon_state = "victorianvest" + item_state = "victorianvest" + item_color = "victorianvest" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/victsuit/redblk + name = "red and black victorian suit" + icon_state = "victorianblred" + item_state = "victorianblred" + item_color = "victorianblred" + +/obj/item/clothing/under/victsuit/red + name = "red victorian suit" + icon_state = "victorianredvest" + item_state = "victorianredvest" + item_color = "victorianredvest" + +/obj/item/clothing/under/medigown + name = "medical gown" + desc = "a flimsy examination gown, the back ties never close." + icon_state = "medicalgown" + item_state = "medicalgown" + item_color = "medicalgown" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/burial + name = "burial garments" + desc = "Traditional burial garments from the early 22nd century." + icon_state = "burial" + item_state = "burial" + item_color = "burial" + +/obj/item/clothing/under/redhawaiianshirt + name = "red hawaiian shirt" + desc = "a floral shirt worn to most vacation destinations." + icon_state = "hawaiianred" + item_state = "hawaiianred" + item_color = "hawaiianred" + +/obj/item/clothing/under/pinkhawaiianshirt + name = "pink hawaiian shirt" + desc = "a pink floral shirt the material feels cool and comfy." + icon_state = "hawaiianpink" + item_state = "hawaiianpink" + item_color = "hawaiianpink" + +/obj/item/clothing/under/orangehawaiianshirt + name = "orange hawaiian shirt" + desc = "a orange floral shirt for a relaxing day in space." + icon_state = "hawaiianorange" + item_state = "hawaiianorange" + item_color = "hawaiianorange" + +/obj/item/clothing/under/bluehawaiianshirt + name = "blue hawaiian shirt" + desc = "a blue floral shirt it has a oddly colored pink flower on it." + icon_state = "hawaiianblue" + item_state = "hawaiianblue" + item_color = "hawaiianblue" + +/obj/item/clothing/under/misc/durathread + name = "durathread jumpsuit" + desc = "A jumpsuit made from durathread, its resilient fibres provide some protection to the wearer." + icon_state = "durathread" + item_state = "durathread" + item_color = "durathread" + armor = list(melee = 10, bullet = 0, laser = 10, energy = 0, bomb = 5, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/clothing/under/cuban_suit + name = "rhumba outfit" + desc = "A satin shirt and high-waisted pants, worn by dancers in the Rhumba style. It smells oddly like... sulfur?" + icon_state = "cuban_suit" + item_state = "cuban_suit" + item_color = "cuban_suit" diff --git a/code/modules/clothing/under/oldstation_uni.dm b/code/modules/clothing/under/oldstation_uni.dm index f19b533ad24..97a3fa7614b 100644 --- a/code/modules/clothing/under/oldstation_uni.dm +++ b/code/modules/clothing/under/oldstation_uni.dm @@ -7,7 +7,6 @@ item_state = "retro_sec" item_color = "retro_sec" displays_id = 0 - flags_size = ONESIZEFITSALL /obj/item/clothing/under/retro/medical desc = "A once biologically resistant medical uniform. The high-vis stripes are faded and unreflective." @@ -16,7 +15,6 @@ item_state = "retro_med" item_color = "retro_med" displays_id = 0 - flags_size = ONESIZEFITSALL /obj/item/clothing/under/retro/engineering desc = "A faded grimy engineering jumpsuit and overall combo. It's coated with oil, dust, and grit." @@ -25,7 +23,6 @@ item_state = "retro_eng" item_color = "retro_eng" displays_id = 0 - flags_size = ONESIZEFITSALL /obj/item/clothing/under/retro/science desc = "A faded polo and set of distinct white slacks. What a ridiculous tie." @@ -34,4 +31,3 @@ item_state = "retro_sci" item_color = "retro_sci" displays_id = 0 - flags_size = ONESIZEFITSALL diff --git a/code/modules/clothing/under/pants.dm b/code/modules/clothing/under/pants.dm index b52c1eceb7c..735c681ca5f 100644 --- a/code/modules/clothing/under/pants.dm +++ b/code/modules/clothing/under/pants.dm @@ -89,4 +89,4 @@ name = "camo pants" desc = "A pair of woodland camouflage pants. Probably not the best choice for a space station." icon_state = "camopants" - item_color = "camopants" \ No newline at end of file + item_color = "camopants" diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm index 13008c4ff6d..3cd2ddef84c 100644 --- a/code/modules/clothing/under/shorts.dm +++ b/code/modules/clothing/under/shorts.dm @@ -1,26 +1,26 @@ -/obj/item/clothing/under/shorts - name = "athletic shorts" - desc = "95% Polyester, 5% Spandex!" - gender = PLURAL - body_parts_covered = LOWER_TORSO - displays_id = 0 - -/obj/item/clothing/under/shorts/red - icon_state = "redshorts" - item_color = "redshorts" - -/obj/item/clothing/under/shorts/green - icon_state = "greenshorts" - item_color = "greenshorts" - -/obj/item/clothing/under/shorts/blue - icon_state = "blueshorts" - item_color = "blueshorts" - -/obj/item/clothing/under/shorts/black - icon_state = "blackshorts" - item_color = "blackshorts" - -/obj/item/clothing/under/shorts/grey - icon_state = "greyshorts" - item_color = "greyshorts" \ No newline at end of file +/obj/item/clothing/under/shorts + name = "athletic shorts" + desc = "95% Polyester, 5% Spandex!" + gender = PLURAL + body_parts_covered = LOWER_TORSO + displays_id = 0 + +/obj/item/clothing/under/shorts/red + icon_state = "redshorts" + item_color = "redshorts" + +/obj/item/clothing/under/shorts/green + icon_state = "greenshorts" + item_color = "greenshorts" + +/obj/item/clothing/under/shorts/blue + icon_state = "blueshorts" + item_color = "blueshorts" + +/obj/item/clothing/under/shorts/black + icon_state = "blackshorts" + item_color = "blackshorts" + +/obj/item/clothing/under/shorts/grey + icon_state = "greyshorts" + item_color = "greyshorts" diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm index 6778138d30f..441cf8d9420 100644 --- a/code/modules/clothing/under/syndicate.dm +++ b/code/modules/clothing/under/syndicate.dm @@ -1,26 +1,26 @@ -/obj/item/clothing/under/syndicate - name = "tactical turtleneck" - desc = "A non-descript and slightly suspicious looking turtleneck with digital camouflage cargo pants." - icon_state = "syndicate" - item_state = "bl_suit" - item_color = "syndicate" - has_sensor = 0 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) - -/obj/item/clothing/under/syndicate/combat - name = "combat turtleneck" - -/obj/item/clothing/under/syndicate/tacticool - name = "tacticool turtleneck" - desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." - icon_state = "tactifool" - item_state = "bl_suit" - item_color = "tactifool" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) - -/obj/item/clothing/under/syndicate/sniper - name = "Tactical turtleneck suit" - desc = "A double seamed tactical turtleneck disguised as a civilian grade silk suit. Intended for the most formal operator. The collar is really sharp." - icon_state = "really_black_suit" - item_state = "bl_suit" - item_color = "black_suit" \ No newline at end of file +/obj/item/clothing/under/syndicate + name = "tactical turtleneck" + desc = "A non-descript and slightly suspicious looking turtleneck with digital camouflage cargo pants." + icon_state = "syndicate" + item_state = "bl_suit" + item_color = "syndicate" + has_sensor = 0 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) + +/obj/item/clothing/under/syndicate/combat + name = "combat turtleneck" + +/obj/item/clothing/under/syndicate/tacticool + name = "tacticool turtleneck" + desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." + icon_state = "tactifool" + item_state = "bl_suit" + item_color = "tactifool" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) + +/obj/item/clothing/under/syndicate/sniper + name = "Tactical turtleneck suit" + desc = "A double seamed tactical turtleneck disguised as a civilian grade silk suit. Intended for the most formal operator. The collar is really sharp." + icon_state = "really_black_suit" + item_state = "bl_suit" + item_color = "black_suit" diff --git a/code/modules/crafting/craft.dm b/code/modules/crafting/craft.dm index 36f0da802cc..a9c47e760e7 100644 --- a/code/modules/crafting/craft.dm +++ b/code/modules/crafting/craft.dm @@ -103,7 +103,7 @@ if(RC.is_drainable()) for(var/datum/reagent/A in RC.reagents.reagent_list) .["other"][A.type] += A.volume - .["other"][I.type] += 1 + .["other"][I.type] += 1 .["toolsother"][I] += 1 /datum/personal_crafting/proc/check_tools(mob/user, datum/crafting_recipe/R, list/contents) @@ -135,13 +135,13 @@ /datum/personal_crafting/proc/check_pathtools(mob/user, datum/crafting_recipe/R, list/contents) if(!R.pathtools.len) //does not run if no tools are needed return TRUE - var/list/other_possible_tools = list() + var/list/other_possible_tools = list() for(var/obj/item/I in user.contents) // searchs the inventory of the mob if(istype(I, /obj/item/storage)) for(var/obj/item/SI in I.contents) other_possible_tools += SI.type // filters type paths other_possible_tools += I.type - + other_possible_tools |= contents["other"] // this adds contents to the other_possible_tools main_loop: // checks if all tools found are usable with the recipe for(var/A in R.pathtools) @@ -151,7 +151,7 @@ return FALSE return TRUE -/datum/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R) +/datum/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R) var/list/contents = get_surroundings(user) var/send_feedback = 1 if(check_contents(R, contents)) @@ -297,7 +297,7 @@ Deletion.Cut(Deletion.len) qdel(DL) -/datum/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = not_incapacitated_turf_state) +/datum/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = GLOB.not_incapacitated_turf_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "personal_crafting.tmpl", "Crafting Menu", 700, 800, state = state) @@ -356,6 +356,8 @@ var/fail_msg = construct_item(usr, TR) if(!fail_msg) to_chat(usr, "[TR.name] constructed.") + if(TR.alert_admins_on_craft) + message_admins("[usr.ckey] has created a [TR.name] at [ADMIN_COORDJMP(usr)]") else to_chat(usr, "Construction failed[fail_msg]") busy = FALSE @@ -458,4 +460,4 @@ /datum/mind/proc/teach_crafting_recipe(R) if(!learned_recipes) learned_recipes = list() - learned_recipes |= R \ No newline at end of file + learned_recipes |= R diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index ec334979cd2..15a8ae02490 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -11,6 +11,7 @@ var/category = CAT_NONE //where it shows up in the crafting UI var/subcategory = CAT_NONE var/always_availible = TRUE //Set to FALSE if it needs to be learned first. + var/alert_admins_on_craft = FALSE /datum/crafting_recipe/IED name = "IED" @@ -94,6 +95,7 @@ tools = list(TOOL_WELDER) time = 120 category = CAT_ROBOT + alert_admins_on_craft = TRUE /datum/crafting_recipe/cleanbot name = "Cleanbot" @@ -147,6 +149,7 @@ time = 10 category = CAT_WEAPONRY subcategory = CAT_WEAPON + alert_admins_on_craft = TRUE /datum/crafting_recipe/meteorshot name = "Meteorshot Shell" @@ -259,6 +262,7 @@ time = 50 category = CAT_WEAPONRY subcategory = CAT_WEAPON + alert_admins_on_craft = TRUE /datum/crafting_recipe/spear name = "Spear" @@ -378,6 +382,7 @@ time = 30 category = CAT_WEAPONRY subcategory = CAT_WEAPON + alert_admins_on_craft = TRUE /datum/crafting_recipe/chemical_payload2 name = "Chemical Payload (gibtonite)" @@ -391,6 +396,7 @@ time = 50 category = CAT_WEAPONRY subcategory = CAT_WEAPON + alert_admins_on_craft = TRUE /datum/crafting_recipe/toxins_payload name = "Toxins Payload Casing" @@ -503,6 +509,7 @@ reqs = list(/obj/item/grown/log = 5) result = /obj/structure/bonfire category = CAT_PRIMAL + alert_admins_on_craft = TRUE /datum/crafting_recipe/rake //Category resorting incoming name = "Rake" @@ -611,8 +618,8 @@ name = "Paper Jack o'Lantern" result = /obj/item/decorations/sticky_decorations/flammable/jack_o_lantern tools = list(TOOL_WIRECUTTER) - pathtools = list(/obj/item/pen, - /obj/item/toy/crayon/orange, + pathtools = list(/obj/item/pen, + /obj/item/toy/crayon/orange, /obj/item/toy/crayon/green)//pen ink is black category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -629,7 +636,7 @@ name = "Paper Spider" result = /obj/item/decorations/sticky_decorations/flammable/spider tools = list(TOOL_WIRECUTTER) - pathtools = list(/obj/item/pen, + pathtools = list(/obj/item/pen, /obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -670,7 +677,7 @@ name = "Paper Snowman" result = /obj/item/decorations/sticky_decorations/flammable/snowman tools = list(TOOL_WIRECUTTER) - pathtools = list(/obj/item/pen, + pathtools = list(/obj/item/pen, /obj/item/toy/crayon/orange) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -687,9 +694,9 @@ name = "Paper Christmas Tree" result = /obj/item/decorations/sticky_decorations/flammable/christmas_tree tools = list(TOOL_WIRECUTTER) - pathtools = list(/obj/item/toy/crayon/red, - /obj/item/toy/crayon/yellow, - /obj/item/toy/crayon/blue, + pathtools = list(/obj/item/toy/crayon/red, + /obj/item/toy/crayon/yellow, + /obj/item/toy/crayon/blue, /obj/item/toy/crayon/green) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -714,7 +721,7 @@ name = "Paper Mistletoe" result = /obj/item/decorations/sticky_decorations/flammable/mistletoe tools = list(TOOL_WIRECUTTER) - pathtools = list(/obj/item/toy/crayon/red, + pathtools = list(/obj/item/toy/crayon/red, /obj/item/toy/crayon/green) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -723,7 +730,7 @@ name = "Paper Holly" result = /obj/item/decorations/sticky_decorations/flammable/holly tools = list(TOOL_WIRECUTTER) - pathtools = list(/obj/item/toy/crayon/red, + pathtools = list(/obj/item/toy/crayon/red, /obj/item/toy/crayon/green) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -1012,7 +1019,7 @@ /obj/item/stack/rods = 4, /obj/item/stock_parts/cell = 1, /obj/item/stack/cable_coil = 4)//thing is a wireframe construct with an electro magnetic hover field - tools = list(TOOL_WIRECUTTER, + tools = list(TOOL_WIRECUTTER, TOOL_WELDER) pathtools = list(/obj/item/pen, /obj/item/toy/crayon/red) diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index 3a0bdb8d795..f8d2029651d 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -1080,7 +1080,6 @@ icon_state = "counterfeitguise" item_state = "counterfeitguise" item_color = "counterfeitguise" - flags_size = ONESIZEFITSALL /obj/item/clothing/under/fluff/benjaminfallout // Benjaminfallout: Pretzel Brassheart icon = 'icons/obj/custom_items.dmi' @@ -1350,7 +1349,6 @@ icon = 'icons/obj/custom_items.dmi' icon_state = "chronx_robe" item_state = "chronx_robe" - flags_size = ONESIZEFITSALL actions_types = list(/datum/action/item_action/toggle) adjust_flavour = "untransform" ignore_suitadjust = 0 @@ -1405,9 +1403,9 @@ icon = 'icons/obj/custom_items.dmi' icon_state = "ssscratches_backpack" -/obj/item/storage/backpack/duffel/fluff/thebrew //Greey: Korala Ice +/obj/item/storage/backpack/fluff/thebrew //Greey: Korala Ice name = "The Brew" - desc = "Amber colored duffle bag resembling a long lost friend, a spirit long forgotten." + desc = "Amber colored backpack resembling a long lost friend, a spirit long forgotten." icon = 'icons/obj/custom_items.dmi' icon_state = "greeyfluff" item_state = "greeyfluff" @@ -1658,13 +1656,16 @@ desc = "A small platinum ring with a large light blue diamond. Engraved inside the band are the words: 'To my lovely Pristine Princess. Forever yours, Savinien.'" icon_state = "benjaminfallout_ring" -/obj/item/clothing/under/fluff/voxbodysuit //Gangelwaefre: Kikeri - name = "Vox Bodysuit" - desc = "A shimmering bodysuit custom-fit to a vox. Has shorts sewn in." + +/obj/item/clothing/under/fluff/kikeridress //Gangelwaefre: Kikeri + name = "Kikeri's Dress" + desc = "A simple black dress with a white undercoat, tied with a blue ribbon." lefthand_file = 'icons/mob/inhands/fluff_lefthand.dmi' righthand_file = 'icons/mob/inhands/fluff_righthand.dmi' - icon = 'icons/mob/inhands/fluff_righthand.dmi' - icon_state = "voxbodysuit" - item_state = "voxbodysuit" - item_color = "voxbodysuit" - body_parts_covered = HEAD|UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + sprite_sheets = list("Vox" = 'icons/mob/species/vox/uniform.dmi') + icon = 'icons/obj/custom_items.dmi' + icon_state = "kikeridress" + item_state = "kikeridress" + item_color = "kikeridress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + species_restricted = list("Vox") diff --git a/code/modules/customitems/item_spawning.dm b/code/modules/customitems/item_spawning.dm index 5c1250ed355..8a8f44099ce 100644 --- a/code/modules/customitems/item_spawning.dm +++ b/code/modules/customitems/item_spawning.dm @@ -3,7 +3,7 @@ return // Grab the info we want. - var/DBQuery/query = dbcon.NewQuery("SELECT cuiPath, cuiPropAdjust, cuiJobMask, cuiDescription, cuiItemName FROM [format_table_name("customuseritems")] WHERE cuiCKey='[M.ckey]' AND (cuiRealName='[sanitizeSQL(M.real_name)]' OR cuiRealName='*')") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT cuiPath, cuiPropAdjust, cuiJobMask, cuiDescription, cuiItemName FROM [format_table_name("customuseritems")] WHERE cuiCKey='[M.ckey]' AND (cuiRealName='[sanitizeSQL(M.real_name)]' OR cuiRealName='*')") query.Execute() while(query.NextRow()) @@ -11,6 +11,9 @@ var/propadjust = query.item[2] var/jobmask = query.item[3] var/ok = 0 + if(!path || !ispath(path)) + log_debug("Incorrect database entry found in table 'customuseritems' path value = [path], cuiPath is null. cuiCKey='[M.ckey]' AND (cuiRealName='[sanitizeSQL(M.real_name)]' OR cuiRealName='*'") + continue if(jobmask != "*") var/list/allowed_jobs = splittext(jobmask,",") for(var/i = 1, i <= allowed_jobs.len, i++) diff --git a/code/modules/detective_work/footprints_and_rag.dm b/code/modules/detective_work/footprints_and_rag.dm index b3c45c0f4a1..2646d03fb0f 100644 --- a/code/modules/detective_work/footprints_and_rag.dm +++ b/code/modules/detective_work/footprints_and_rag.dm @@ -27,7 +27,7 @@ /obj/item/reagent_containers/glass/rag/attack(atom/target as obj|turf|area, mob/user as mob , flag) if(ismob(target) && target.reagents && reagents.total_volume) user.visible_message("[user] has smothered \the [target] with \the [src]!", "You smother \the [target] with \the [src]!", "You hear some struggling and muffled cries of surprise") - src.reagents.reaction(target, TOUCH) + src.reagents.reaction(target, REAGENT_TOUCH) src.reagents.clear_reagents() return else @@ -35,7 +35,7 @@ /obj/item/reagent_containers/glass/rag/afterattack(atom/A as obj|turf|area, mob/user as mob,proximity) if(!proximity) return - if(istype(A) && src in user) + if(istype(A) && (src in user)) user.visible_message("[user] starts to wipe down [A] with [src]!") if(do_after(user, wipespeed, target = A)) user.visible_message("[user] finishes wiping off the [A]!") diff --git a/code/modules/detective_work/scanner.dm b/code/modules/detective_work/scanner.dm index 64318877847..dea08c602f9 100644 --- a/code/modules/detective_work/scanner.dm +++ b/code/modules/detective_work/scanner.dm @@ -30,21 +30,21 @@ // I really, really wish I didn't have to split this into two seperate loops. But the datacore is awful. - for(var/record in data_core.general) + for(var/record in GLOB.data_core.general) var/datum/data/record/S = record if(S && (search == lowertext(S.fields["fingerprint"]) || search == lowertext(S.fields["name"]))) name = S.fields["name"] fingerprint = S.fields["fingerprint"] continue - for(var/record in data_core.medical) + for(var/record in GLOB.data_core.medical) var/datum/data/record/M = record if(M && ( search == lowertext(M.fields["b_dna"]) || name == M.fields["name"]) ) dna = M.fields["b_dna"] if(fingerprint == "FINGERPRINT NOT FOUND") // We have searched by DNA, and do not have the relevant information from the fingerprint records. name = M.fields["name"] - for(var/gen_record in data_core.general) + for(var/gen_record in GLOB.data_core.general) var/datum/data/record/S = gen_record if(S && (name == S.fields["name"])) fingerprint = S.fields["fingerprint"] diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index df542964163..b094dded37e 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -36,7 +36,7 @@ log transactions /obj/machinery/atm/New() ..() - machine_id = "[station_name()] RT #[num_financial_terminals++]" + machine_id = "[station_name()] RT #[GLOB.num_financial_terminals++]" /obj/machinery/atm/Initialize() ..() @@ -127,7 +127,7 @@ log transactions ui = new(user, src, ui_key, "atm.tmpl", name, 550, 650) ui.open() -/obj/machinery/atm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/atm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["src"] = UID() data["view_screen"] = view_screen @@ -203,7 +203,7 @@ log transactions T.target_name = failed_account.owner_name T.purpose = "Unauthorised login attempt" T.source_terminal = machine_id - T.date = current_date_string + T.date = GLOB.current_date_string T.time = station_time_timestamp() failed_account.transaction_log.Add(T) else @@ -223,7 +223,7 @@ log transactions T.target_name = authenticated_account.owner_name T.purpose = "Remote terminal access" T.source_terminal = machine_id - T.date = current_date_string + T.date = GLOB.current_date_string T.time = station_time_timestamp() authenticated_account.transaction_log.Add(T) to_chat(usr, "[bicon(src)]Access granted. Welcome user '[authenticated_account.owner_name].'") @@ -237,9 +237,9 @@ log transactions playsound(src, 'sound/machines/chime.ogg', 50, 1) //remove the money - if(amount > 10000) // prevent crashes - to_chat(usr, "The ATM's screen flashes, 'Maximum single withdrawl limit reached, defaulting to 10,000.'") - amount = 10000 + if(amount > 100000) // prevent crashes + to_chat(usr, "The ATM's screen flashes, 'Maximum single withdrawl limit reached, defaulting to 100,000.'") + amount = 100000 withdraw_arbitrary_sum(amount) authenticated_account.charge(amount, null, "Credit withdrawal", machine_id, authenticated_account.owner_name) else @@ -257,7 +257,7 @@ log transactions Account holder: [authenticated_account.owner_name]
    Account number: [authenticated_account.account_number]
    Balance: $[authenticated_account.money]
    - Date and time: [station_time_timestamp()], [current_date_string]

    + Date and time: [station_time_timestamp()], [GLOB.current_date_string]

    Service terminal ID: [machine_id]
    "} //stamp the paper diff --git a/code/modules/economy/Accounts.dm b/code/modules/economy/Accounts.dm index 6001cf3f84e..0a2b500a0d3 100644 --- a/code/modules/economy/Accounts.dm +++ b/code/modules/economy/Accounts.dm @@ -4,33 +4,33 @@ #define STATION_SOURCE_TERMINAL "Biesel GalaxyNet Terminal #227" #define DEPARTMENT_START_CASH 5000 -var/global/num_financial_terminals = 1 -var/global/datum/money_account/station_account -var/global/list/datum/money_account/department_accounts = list() -var/global/next_account_number = 0 -var/global/obj/machinery/computer/account_database/centcomm_account_db -var/global/datum/money_account/vendor_account -var/global/list/all_money_accounts = list() +GLOBAL_VAR_INIT(num_financial_terminals, 1) +GLOBAL_DATUM(station_account, /datum/money_account) +GLOBAL_LIST_EMPTY(department_accounts) +GLOBAL_VAR_INIT(next_account_number, 0) +GLOBAL_DATUM(centcomm_account_db, /obj/machinery/computer/account_database) // this being an object hurts me deeply on the inside +GLOBAL_DATUM(vendor_account, /datum/money_account) +GLOBAL_LIST_EMPTY(all_money_accounts) /proc/create_station_account() - if(!station_account) - next_account_number = rand(111111, 999999) + if(!GLOB.station_account) + GLOB.next_account_number = rand(111111, 999999) - station_account = new() - station_account.owner_name = "[station_name()] Station Account" - station_account.account_number = rand(111111, 999999) - station_account.remote_access_pin = rand(1111, 111111) - station_account.money = STATION_START_CASH + GLOB.station_account = new() + GLOB.station_account.owner_name = "[station_name()] Station Account" + GLOB.station_account.account_number = rand(111111, 999999) + GLOB.station_account.remote_access_pin = rand(1111, 111111) + GLOB.station_account.money = STATION_START_CASH //create an entry in the account transaction log for when it was created - station_account.makeTransactionLog(STATION_START_CASH, "Account Creation", STATION_SOURCE_TERMINAL, station_account.owner_name, FALSE, + GLOB.station_account.makeTransactionLog(STATION_START_CASH, "Account Creation", STATION_SOURCE_TERMINAL, GLOB.station_account.owner_name, FALSE, STATION_CREATION_DATE, STATION_CREATION_TIME) //add the account - all_money_accounts.Add(station_account) + GLOB.all_money_accounts.Add(GLOB.station_account) /proc/create_department_account(department) - next_account_number = rand(111111, 999999) + GLOB.next_account_number = rand(111111, 999999) var/datum/money_account/department_account = new() department_account.owner_name = "[department] Account" @@ -43,9 +43,9 @@ var/global/list/all_money_accounts = list() STATION_CREATION_DATE, STATION_CREATION_TIME) //add the account - all_money_accounts.Add(department_account) + GLOB.all_money_accounts.Add(department_account) - department_accounts[department] = department_account + GLOB.department_accounts[department] = department_account //the current ingame time (hh:mm:ss) can be obtained by calling: //station_time_timestamp("hh:mm:ss") @@ -65,18 +65,18 @@ var/global/list/all_money_accounts = list() T.amount = starting_funds if(!source_db) //set a random date, time and location some time over the past few decades - T.date = "[num2text(rand(1,31))] [pick(GLOB.month_names)], [rand(game_year - 20,game_year - 1)]" + T.date = "[num2text(rand(1,31))] [pick(GLOB.month_names)], [rand(GLOB.game_year - 20,GLOB.game_year - 1)]" T.time = "[rand(0,23)]:[rand(0,59)]:[rand(0,59)]" T.source_terminal = "NTGalaxyNet Terminal #[rand(111,1111)]" M.account_number = rand(111111, 999999) else - T.date = current_date_string + T.date = GLOB.current_date_string T.time = station_time_timestamp() T.source_terminal = source_db.machine_id - M.account_number = next_account_number - next_account_number += rand(1,25) + M.account_number = GLOB.next_account_number + GLOB.next_account_number += rand(1,25) //create a sealed package containing the account details var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(source_db.loc) @@ -94,7 +94,7 @@ var/global/list/all_money_accounts = list() Account number: [M.account_number]
    Account pin: [M.remote_access_pin]
    Starting balance: $[M.money]
    - Date and time: [station_time_timestamp()], [current_date_string]

    + Date and time: [station_time_timestamp()], [GLOB.current_date_string]

    Creation terminal ID: [source_db.machine_id]
    Authorised NT officer overseeing creation: [overseer]
    "} @@ -109,7 +109,7 @@ var/global/list/all_money_accounts = list() //add the account M.transaction_log.Add(T) - all_money_accounts.Add(M) + GLOB.all_money_accounts.Add(M) return M @@ -138,7 +138,7 @@ var/global/list/all_money_accounts = list() /obj/machinery/computer/account_database/proc/charge_to_account(attempt_account_number, datum/money_account/source, purpose, terminal_id, amount) if(!activated) return 0 - for(var/datum/money_account/D in all_money_accounts) + for(var/datum/money_account/D in GLOB.all_money_accounts) if(D.account_number == attempt_account_number && !D.suspended) source.charge(amount, D, purpose, terminal_id, "Account #[D.account_number]", "Transfer from [source.owner_name]", "[D.owner_name]") @@ -148,18 +148,18 @@ var/global/list/all_money_accounts = list() //this returns the first account datum that matches the supplied accnum/pin combination, it returns null if the combination did not match any account /proc/attempt_account_access(var/attempt_account_number, var/attempt_pin_number, var/security_level_passed = 0,var/pin_needed=1) - for(var/datum/money_account/D in all_money_accounts) + for(var/datum/money_account/D in GLOB.all_money_accounts) if(D.account_number == attempt_account_number) if( D.security_level <= security_level_passed && (!D.security_level || D.remote_access_pin == attempt_pin_number || !pin_needed) ) return D /obj/machinery/computer/account_database/proc/get_account(var/account_number) - for(var/datum/money_account/D in all_money_accounts) + for(var/datum/money_account/D in GLOB.all_money_accounts) if(D.account_number == account_number) return D /proc/attempt_account_access_nosec(var/attempt_account_number) - for(var/datum/money_account/D in all_money_accounts) + for(var/datum/money_account/D in GLOB.all_money_accounts) if(D.account_number == attempt_account_number) return D diff --git a/code/modules/economy/Accounts_DB.dm b/code/modules/economy/Accounts_DB.dm index 16b1162e074..6995e260500 100644 --- a/code/modules/economy/Accounts_DB.dm +++ b/code/modules/economy/Accounts_DB.dm @@ -1,11 +1,11 @@ -var/global/current_date_string +GLOBAL_VAR(current_date_string) /obj/machinery/computer/account_database name = "Accounts Uplink Terminal" desc = "Access transaction logs, account data and all kinds of other financial records." icon_screen = "accounts" density = 1 - req_one_access = list(access_hop, access_captain, access_cent_commander) + req_one_access = list(ACCESS_HOP, ACCESS_CAPTAIN, ACCESS_CENT_COMMANDER) var/receipt_num var/machine_id = "" var/obj/item/card/id/held_card @@ -17,20 +17,20 @@ var/global/current_date_string light_color = LIGHT_COLOR_GREEN /obj/machinery/computer/account_database/New() - if(!station_account) + if(!GLOB.station_account) create_station_account() - if(department_accounts.len == 0) - for(var/department in station_departments) + if(GLOB.department_accounts.len == 0) + for(var/department in GLOB.station_departments) create_department_account(department) - if(!vendor_account) + if(!GLOB.vendor_account) create_department_account("Vendor") - vendor_account = department_accounts["Vendor"] + GLOB.vendor_account = GLOB.department_accounts["Vendor"] - if(!current_date_string) - current_date_string = "[time2text(world.timeofday, "DD Month")], [game_year]" + if(!GLOB.current_date_string) + GLOB.current_date_string = "[time2text(world.timeofday, "DD Month")], [GLOB.game_year]" - machine_id = "[station_name()] Acc. DB #[num_financial_terminals++]" + machine_id = "[station_name()] Acc. DB #[GLOB.num_financial_terminals++]" ..() /obj/machinery/computer/account_database/proc/get_access_level(var/mob/user) @@ -38,9 +38,9 @@ var/global/current_date_string return 2 if(!held_card) return 0 - if(access_cent_commander in held_card.access) + if(ACCESS_CENT_COMMANDER in held_card.access) return 2 - else if(access_hop in held_card.access || access_captain in held_card.access) + else if((ACCESS_HOP in held_card.access) || (ACCESS_CAPTAIN in held_card.access)) return 1 /obj/machinery/computer/account_database/proc/accounting_letterhead(report_name) @@ -74,7 +74,7 @@ var/global/current_date_string ui = new(user, src, ui_key, "accounts_terminal.tmpl", src.name, 400, 640) ui.open() -/obj/machinery/computer/account_database/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/account_database/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["src"] = UID() @@ -84,7 +84,7 @@ var/global/current_date_string data["machine_id"] = machine_id data["creating_new_account"] = creating_new_account data["detailed_account_view"] = !!detailed_account_view - data["station_account_number"] = station_account.account_number + data["station_account_number"] = GLOB.station_account.account_number data["transactions"] = null data["accounts"] = null @@ -108,8 +108,8 @@ var/global/current_date_string data["transactions"] = trx var/list/accounts[0] - for(var/i=1, i<=all_money_accounts.len, i++) - var/datum/money_account/D = all_money_accounts[i] + for(var/i=1, i<=GLOB.all_money_accounts.len, i++) + var/datum/money_account/D = GLOB.all_money_accounts[i] accounts.Add(list(list(\ "account_number"=D.account_number,\ "owner_name"=D.owner_name,\ @@ -159,12 +159,12 @@ var/global/current_date_string var/account_name = href_list["holder_name"] var/starting_funds = max(text2num(href_list["starting_funds"]), 0) - starting_funds = Clamp(starting_funds, 0, station_account.money) // Not authorized to put the station in debt. + starting_funds = Clamp(starting_funds, 0, GLOB.station_account.money) // Not authorized to put the station in debt. starting_funds = min(starting_funds, fund_cap) // Not authorized to give more than the fund cap. var/datum/money_account/M = create_account(account_name, starting_funds, src) if(starting_funds > 0) - station_account.charge(starting_funds, null, "New account activation", + GLOB.station_account.charge(starting_funds, null, "New account activation", "", "New account activation", M.owner_name) creating_new_account = 0 @@ -174,8 +174,8 @@ var/global/current_date_string if("view_account_detail") var/index = text2num(href_list["account_index"]) - if(index && index <= all_money_accounts.len) - detailed_account_view = all_money_accounts[index] + if(index && index <= GLOB.all_money_accounts.len) + detailed_account_view = GLOB.all_money_accounts[index] if("view_accounts_list") detailed_account_view = null @@ -240,8 +240,8 @@ var/global/current_date_string "} - for(var/i=1, i<=all_money_accounts.len, i++) - var/datum/money_account/D = all_money_accounts[i] + for(var/i=1, i<=GLOB.all_money_accounts.len, i++) + var/datum/money_account/D = GLOB.all_money_accounts[i] text += {" #[D.account_number] diff --git a/code/modules/economy/EFTPOS.dm b/code/modules/economy/EFTPOS.dm index 98d99e667aa..f84b40ebb10 100644 --- a/code/modules/economy/EFTPOS.dm +++ b/code/modules/economy/EFTPOS.dm @@ -14,7 +14,7 @@ /obj/item/eftpos/New() ..() - machine_name = "[station_name()] EFTPOS #[num_financial_terminals++]" + machine_name = "[station_name()] EFTPOS #[GLOB.num_financial_terminals++]" access_code = rand(1111,111111) reconnect_database() spawn(0) @@ -22,7 +22,7 @@ //by default, connect to the station account //the user of the EFTPOS device can change the target account though, and no-one will be the wiser (except whoever's being charged) - linked_account = station_account + linked_account = GLOB.station_account /obj/item/eftpos/proc/print_reference() playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1) @@ -79,7 +79,7 @@ ui = new(user, src, ui_key, "eftpos.tmpl", name, 790, 310) ui.open() -/obj/item/eftpos/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/item/eftpos/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["machine_name"] = machine_name data["transaction_locked"] = transaction_locked @@ -150,7 +150,7 @@ var/obj/item/I = usr.get_active_hand() if(istype(I, /obj/item/card)) var/obj/item/card/id/C = I - if(access_cent_commander in C.access || access_hop in C.access || access_captain in C.access) + if((ACCESS_CENT_COMMANDER in C.access) || (ACCESS_HOP in C.access) || (ACCESS_CAPTAIN in C.access)) access_code = 0 to_chat(usr, "[bicon(src)]Access code reset to 0.") else if(istype(I, /obj/item/card/emag)) @@ -190,8 +190,4 @@ playsound(src, 'sound/machines/chime.ogg', 50, 1) visible_message("[bicon(src)] The [src] chimes.") transaction_paid = 1 - - else - ..() - //emag? diff --git a/code/modules/economy/Economy.dm b/code/modules/economy/Economy.dm index 57d76e98c5c..8f06db02f34 100644 --- a/code/modules/economy/Economy.dm +++ b/code/modules/economy/Economy.dm @@ -63,9 +63,9 @@ //Destroyers are medium sized vessels, often used for escorting larger ships but able to go toe-to-toe with them if need be. //Frigates are medium sized vessels, often used for escorting larger ships. They will rapidly find themselves outclassed if forced to face heavy warships head on. -var/setup_economy = 0 +GLOBAL_VAR_INIT(setup_economy, 0) /proc/setup_economy() - if(setup_economy) + if(GLOB.setup_economy) return var/datum/feed_channel/newChannel = new /datum/feed_channel @@ -73,25 +73,25 @@ var/setup_economy = 0 newChannel.author = "Automated Announcement Listing" newChannel.locked = 1 newChannel.is_admin_channel = 1 - news_network.network_channels += newChannel + GLOB.news_network.network_channels += newChannel newChannel = new /datum/feed_channel newChannel.channel_name = "Nyx Daily" newChannel.author = "CentComm Minister of Information" newChannel.locked = 1 newChannel.is_admin_channel = 1 - news_network.network_channels += newChannel + GLOB.news_network.network_channels += newChannel newChannel = new /datum/feed_channel newChannel.channel_name = "The Gibson Gazette" newChannel.author = "Editor Mike Hammers" newChannel.locked = 1 newChannel.is_admin_channel = 1 - news_network.network_channels += newChannel + GLOB.news_network.network_channels += newChannel for(var/loc_type in subtypesof(/datum/trade_destination)) var/datum/trade_destination/D = new loc_type - weighted_randomevent_locations[D] = D.viable_random_events.len - weighted_mundaneevent_locations[D] = D.viable_mundane_events.len + GLOB.weighted_randomevent_locations[D] = D.viable_random_events.len + GLOB.weighted_mundaneevent_locations[D] = D.viable_mundane_events.len - setup_economy = 1 \ No newline at end of file + GLOB.setup_economy = 1 diff --git a/code/modules/economy/Economy_Events.dm b/code/modules/economy/Economy_Events.dm index 10e0d574f98..d7754f122ea 100644 --- a/code/modules/economy/Economy_Events.dm +++ b/code/modules/economy/Economy_Events.dm @@ -8,10 +8,10 @@ var/datum/trade_destination/affected_dest /datum/event/economic_event/start() - if(!setup_economy) + if(!GLOB.setup_economy) setup_economy() - affected_dest = pickweight(weighted_randomevent_locations) + affected_dest = pickweight(GLOB.weighted_randomevent_locations) if(affected_dest.viable_random_events.len) endWhen = rand(60,300) event_type = pick(affected_dest.viable_random_events) @@ -94,11 +94,11 @@ if(FESTIVAL) newMsg.body = "A [pick("festival","week long celebration","day of revelry","planet-wide holiday")] has been declared on [affected_dest.name] by [pick("Governor","Commissioner","General","Commandant","Administrator")] [random_name(pick(MALE,FEMALE))] to celebrate [pick("the birth of their [pick("son","daughter")]","coming of age of their [pick("son","daughter")]","the pacification of rogue military cell","the apprehension of a violent criminal who had been terrorising the planet")]. Massive stocks of food and meat have been bought driving up prices across the planet." - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == "Nyx Daily") FC.messages += newMsg break - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) NEWSCASTER.newsAlert("Nyx Daily") /datum/event/economic_event/end() diff --git a/code/modules/economy/Economy_Events_Mundane.dm b/code/modules/economy/Economy_Events_Mundane.dm index 1b5467a3a1c..cfabd6cdbe7 100644 --- a/code/modules/economy/Economy_Events_Mundane.dm +++ b/code/modules/economy/Economy_Events_Mundane.dm @@ -3,7 +3,7 @@ endWhen = 10 /datum/event/mundane_news/announce() - var/datum/trade_destination/affected_dest = pickweight(weighted_mundaneevent_locations) + var/datum/trade_destination/affected_dest = pickweight(GLOB.weighted_mundaneevent_locations) var/event_type = 0 if(affected_dest.viable_mundane_events.len) event_type = pick(affected_dest.viable_mundane_events) @@ -126,11 +126,11 @@ Nyx Daily is offering discount tickets for two to see [random_name(pick(MALE,FEMALE))] live in return for eyewitness reports and up to the minute coverage." - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == "Nyx Daily") FC.messages += newMsg break - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) NEWSCASTER.newsAlert("Nyx Daily") /datum/event/trivial_news @@ -141,13 +141,13 @@ var/datum/feed_message/newMsg = new /datum/feed_message newMsg.author = "Editor Mike Hammers" //newMsg.is_admin_message = 1 - var/datum/trade_destination/affected_dest = pick(weighted_mundaneevent_locations) + var/datum/trade_destination/affected_dest = pick(GLOB.weighted_mundaneevent_locations) newMsg.body = pick(file2list("config/news/trivial.txt")) newMsg.body = replacetext(newMsg.body,"{{AFFECTED}}",affected_dest.name) - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == "The Gibson Gazette") FC.messages += newMsg break - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) NEWSCASTER.newsAlert("The Gibson Gazette") diff --git a/code/modules/economy/Economy_TradeDestinations.dm b/code/modules/economy/Economy_TradeDestinations.dm index 09bbabdab37..74d005e3b3b 100644 --- a/code/modules/economy/Economy_TradeDestinations.dm +++ b/code/modules/economy/Economy_TradeDestinations.dm @@ -1,6 +1,6 @@ -var/list/weighted_randomevent_locations = list() -var/list/weighted_mundaneevent_locations = list() +GLOBAL_LIST_EMPTY(weighted_randomevent_locations) +GLOBAL_LIST_EMPTY(weighted_mundaneevent_locations) /datum/trade_destination var/name = "" diff --git a/code/modules/economy/Job_Departments.dm b/code/modules/economy/Job_Departments.dm index 0b851136147..f2accab8541 100644 --- a/code/modules/economy/Job_Departments.dm +++ b/code/modules/economy/Job_Departments.dm @@ -1,4 +1,4 @@ -var/list/station_departments = list("Command", "Medical", "Engineering", "Science", "Security", "Cargo", "Support", "Civilian") +GLOBAL_LIST_INIT(station_departments, list("Command", "Medical", "Engineering", "Science", "Security", "Cargo", "Support", "Civilian")) // The department the job belongs to. /datum/job/var/department = null @@ -67,4 +67,4 @@ var/list/station_departments = list("Command", "Medical", "Engineering", "Scienc /datum/job/detective/department = "Security" -/datum/job/officer/department = "Security" \ No newline at end of file +/datum/job/officer/department = "Security" diff --git a/code/modules/economy/POS.dm b/code/modules/economy/POS.dm index 047c1636ece..60b774a2a3d 100644 --- a/code/modules/economy/POS.dm +++ b/code/modules/economy/POS.dm @@ -11,10 +11,10 @@ var/price = 0 // Per unit var/units = 0 -var/global/current_pos_id = 1 -var/global/pos_sales = 0 +GLOBAL_VAR_INIT(current_pos_id, 1) +GLOBAL_VAR_INIT(pos_sales, 0) -var/const/RECEIPT_HEADER = {" +#define RECEIPT_HEADER {" - "} - - dat += {" - - pAI Personality Configuration -

    Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Name:[candidate.name] 
    What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.
    Description:[candidate.description] 
    What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like.
    Preferred Role:[candidate.role] 
    Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.
    OOC Comments:[candidate.comments] 
    Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.
    -
    - - - - - - - -
    - Save Personality -
    - Load Personality -

    - - -
    Submit Personality

    - - - "} - - M << browse(dat, "window=paiRecruit;size=580x580;") - -/datum/paiController/proc/findPAI(var/obj/item/paicard/p, var/mob/user) - requestRecruits(p, user) - var/list/available = list() - for(var/datum/paiCandidate/c in paiController.pai_candidates) - if(c.ready) - var/found = 0 - for(var/mob/o in GLOB.respawnable_list) - if(o.key == c.key) - found = 1 - if(found) - available.Add(c) - var/dat = "" - - dat += {" - - - - - - - pAI Availability List

    - "} - dat += "

    Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

    " - - for(var/datum/paiCandidate/c in available) - dat += {" - - - - - - - - - - - - - - - - - -
    Name:[c.name]
    Description:[c.description]
    Preferred Role:[c.role]
    OOC Comments:[c.comments]
    - - -
    Download [c.name] -
    -
    - "} - - dat += {" - - - "} - - user << browse(dat, "window=findPai") - -/datum/paiController/proc/requestRecruits(var/obj/item/paicard/P, mob/user) - for(var/mob/dead/observer/O in GLOB.player_list) - if(O.client && (ROLE_PAI in O.client.prefs.be_special)) - if(player_old_enough_antag(O.client,ROLE_PAI)) - if(check_recruit(O)) - to_chat(O, "A pAI card activated by [user.real_name] is looking for personalities. (Teleport | Sign Up)") - //question(O.client) - -/datum/paiController/proc/check_recruit(var/mob/dead/observer/O) - if(jobban_isbanned(O, ROLE_PAI) || jobban_isbanned(O,"nonhumandept")) - return 0 - if(!player_old_enough_antag(O.client,ROLE_PAI)) - return 0 - if(cannotPossess(O)) - return 0 - if(!(O in GLOB.respawnable_list)) - return 0 - if(O.client) - return 1 - return 0 - -/datum/paiController/proc/question(var/client/C) - spawn(0) - if(!C) return - asked.Add(C.key) - asked[C.key] = world.time - var/response = alert(C, "Someone is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", "Yes", "No", "Never for this round") - if(!C) return //handle logouts that happen whilst the alert is waiting for a response. - if(response == "Yes") - recruitWindow(C.mob) - else if(response == "Never for this round") - var/warning = alert(C, "Are you sure? This action will be undoable and you will need to wait until next round.", "You sure?", "Yes", "No") - if(warning == "Yes") - asked[C.key] = INFINITY - else - question(C) +// Recruiting observers to play as pAIs + +GLOBAL_DATUM(paiController, /datum/paiController) // Global handler for pAI candidates + +/datum/paiCandidate + var/name + var/key + var/description + var/role + var/comments + var/ready = 0 + + +/hook/startup/proc/paiControllerSetup() + GLOB.paiController = new /datum/paiController() + return 1 + + +/datum/paiController + var/list/pai_candidates = list() + var/list/asked = list() + + var/askDelay = 10 * 60 * 1 // One minute [ms * sec * min] + +/datum/paiController/Topic(href, href_list[]) + + var/datum/paiCandidate/candidate = locateUID(href_list["candidate"]) + + if(candidate) + if(!istype(candidate)) + message_admins("Warning: possible href exploit by [key_name_admin(usr)] (paiController/Topic, candidate is not a pAI)") + log_debug("Warning: possible href exploit by [key_name(usr)] (paiController/Topic, candidate is not a pAI)") + return + + if(href_list["download"]) + var/obj/item/paicard/card = locate(href_list["device"]) + if(card.pai) + return + if(usr.incapacitated() || isobserver(usr) || !card.Adjacent(usr)) + return + if(istype(card, /obj/item/paicard) && istype(candidate, /datum/paiCandidate)) + var/mob/living/silicon/pai/pai = new(card) + if(!candidate.name) + pai.name = pick(GLOB.ninja_names) + else + pai.name = candidate.name + pai.real_name = pai.name + pai.key = candidate.key + + card.setPersonality(pai) + card.looking_for_personality = 0 + + SSticker.mode.update_cult_icons_removed(card.pai.mind) + SSticker.mode.update_rev_icons_removed(card.pai.mind) + + pai_candidates -= candidate + usr << browse(null, "window=findPai") + return + + if("signup" in href_list) + var/mob/dead/observer/O = locate(href_list["signup"]) + if(!O) + return + if(!(O in GLOB.respawnable_list)) + to_chat(O, "You've given up your ability to respawn!") + return + if(!check_recruit(O)) + return + recruitWindow(O) + return + + if(candidate) + if(candidate.key && usr.key && candidate.key != usr.key) + message_admins("Warning: possible href exploit by [key_name_admin(usr)] (paiController/Topic, candidate and usr have different keys)") + log_debug("Warning: possible href exploit by [key_name(usr)] (paiController/Topic, candidate and usr have different keys)") + return + + if(href_list["new"]) + var/option = href_list["option"] + var/t = "" + + switch(option) + if("name") + t = input("Enter a name for your pAI", "pAI Name", candidate.name) as text + if(t) + candidate.name = sanitize(copytext(t,1,MAX_NAME_LEN)) + if("desc") + t = input("Enter a description for your pAI", "pAI Description", candidate.description) as message + if(t) + candidate.description = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + if("role") + t = input("Enter a role for your pAI", "pAI Role", candidate.role) as text + if(t) + candidate.role = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + if("ooc") + t = input("Enter any OOC comments", "pAI OOC Comments", candidate.comments) as message + if(t) + candidate.comments = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + if("save") + candidate.savefile_save(usr) + if("load") + candidate.savefile_load(usr) + //In case people have saved unsanitized stuff. + if(candidate.name) + candidate.name = sanitize(copytext(candidate.name,1,MAX_NAME_LEN)) + if(candidate.description) + candidate.description = sanitize(copytext(candidate.description,1,MAX_MESSAGE_LEN)) + if(candidate.role) + candidate.role = sanitize(copytext(candidate.role,1,MAX_MESSAGE_LEN)) + if(candidate.comments) + candidate.comments = sanitize(copytext(candidate.comments,1,MAX_MESSAGE_LEN)) + + if("submit") + if(candidate) + candidate.ready = 1 + for(var/obj/item/paicard/p in world) + if(p.looking_for_personality == 1) + p.alertUpdate() + usr << browse(null, "window=paiRecruit") + return + recruitWindow(usr) + +/datum/paiController/proc/recruitWindow(var/mob/M as mob) + var/datum/paiCandidate/candidate + for(var/datum/paiCandidate/c in pai_candidates) + if(!istype(c) || !istype(M)) + break + if(c.key == M.key) + candidate = c + if(!candidate) + candidate = new /datum/paiCandidate() + candidate.key = M.key + pai_candidates.Add(candidate) + + + var/dat = "" + dat += {" + + "} + + dat += {" + + pAI Personality Configuration +

    Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name:[candidate.name] 
    What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.
    Description:[candidate.description] 
    What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like.
    Preferred Role:[candidate.role] 
    Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.
    OOC Comments:[candidate.comments] 
    Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.
    +
    + + + + + + + +
    + Save Personality +
    + Load Personality +

    + + +
    Submit Personality

    + + + "} + + M << browse(dat, "window=paiRecruit;size=580x580;") + +/datum/paiController/proc/findPAI(var/obj/item/paicard/p, var/mob/user) + requestRecruits(p, user) + var/list/available = list() + for(var/datum/paiCandidate/c in GLOB.paiController.pai_candidates) + if(c.ready) + var/found = 0 + for(var/mob/o in GLOB.respawnable_list) + if(o.key == c.key) + found = 1 + if(found) + available.Add(c) + var/dat = "" + + dat += {" + + + + + + + pAI Availability List

    + "} + dat += "

    Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

    " + + for(var/datum/paiCandidate/c in available) + dat += {" + + + + + + + + + + + + + + + + + +
    Name:[c.name]
    Description:[c.description]
    Preferred Role:[c.role]
    OOC Comments:[c.comments]
    + + +
    Download [c.name] +
    +
    + "} + + dat += {" + + + "} + + user << browse(dat, "window=findPai") + +/datum/paiController/proc/requestRecruits(var/obj/item/paicard/P, mob/user) + for(var/mob/dead/observer/O in GLOB.player_list) + if(O.client && (ROLE_PAI in O.client.prefs.be_special)) + if(player_old_enough_antag(O.client,ROLE_PAI)) + if(check_recruit(O)) + to_chat(O, "A pAI card activated by [user.real_name] is looking for personalities. (Teleport | Sign Up)") + //question(O.client) + +/datum/paiController/proc/check_recruit(var/mob/dead/observer/O) + if(jobban_isbanned(O, ROLE_PAI) || jobban_isbanned(O,"nonhumandept")) + return 0 + if(!player_old_enough_antag(O.client,ROLE_PAI)) + return 0 + if(cannotPossess(O)) + return 0 + if(!(O in GLOB.respawnable_list)) + return 0 + if(O.client) + return 1 + return 0 + +/datum/paiController/proc/question(var/client/C) + spawn(0) + if(!C) return + asked.Add(C.key) + asked[C.key] = world.time + var/response = alert(C, "Someone is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", "Yes", "No", "Never for this round") + if(!C) return //handle logouts that happen whilst the alert is waiting for a response. + if(response == "Yes") + recruitWindow(C.mob) + else if(response == "Never for this round") + var/warning = alert(C, "Are you sure? This action will be undoable and you will need to wait until next round.", "You sure?", "Yes", "No") + if(warning == "Yes") + asked[C.key] = INFINITY + else + question(C) diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm index 1f1b365eb4f..750aed1fd5f 100644 --- a/code/modules/mob/living/silicon/pai/say.dm +++ b/code/modules/mob/living/silicon/pai/say.dm @@ -1,16 +1,16 @@ -/mob/living/silicon/pai/say(var/msg) - if(silence_time) - to_chat(src, "Communication circuits remain uninitialized.") - else - ..(msg) - -/mob/living/silicon/pai/get_whisper_loc() - if(loc == card) // currently in its card? - var/atom/movable/whisper_loc = card - if(istype(card.loc, /obj/item/pda)) // Step up 1 level if in a PDA - whisper_loc = card.loc - if(istype(whisper_loc.loc, /mob/living)) - return whisper_loc.loc // allow a pai being held or in pocket to whisper - else - return whisper_loc // allow a pai in its card to whisper - return ..() +/mob/living/silicon/pai/say(var/msg) + if(silence_time) + to_chat(src, "Communication circuits remain uninitialized.") + else + ..(msg) + +/mob/living/silicon/pai/get_whisper_loc() + if(loc == card) // currently in its card? + var/atom/movable/whisper_loc = card + if(istype(card.loc, /obj/item/pda)) // Step up 1 level if in a PDA + whisper_loc = card.loc + if(istype(whisper_loc.loc, /mob/living)) + return whisper_loc.loc // allow a pai being held or in pocket to whisper + else + return whisper_loc // allow a pai in its card to whisper + return ..() diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 135e1996dfe..3b51048452e 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -1,135 +1,135 @@ -var/list/pai_emotions = list( - "Happy" = 1, - "Cat" = 2, - "Extremely Happy" = 3, - "Face" = 4, - "Laugh" = 5, - "Off" = 6, - "Sad" = 7, - "Angry" = 8, - "What" = 9 - ) - - -var/global/list/pai_software_by_key = list() -var/global/list/default_pai_software = list() -/hook/startup/proc/populate_pai_software_list() - var/r = 1 // I would use ., but it'd sacrifice runtime detection - for(var/type in subtypesof(/datum/pai_software)) - var/datum/pai_software/P = new type() - if(pai_software_by_key[P.id]) - var/datum/pai_software/O = pai_software_by_key[P.id] - to_chat(world, "pAI software module [P.name] has the same key as [O.name]!") - r = 0 - continue - pai_software_by_key[P.id] = P - if(P.default) - default_pai_software[P.id] = P - return r - -/mob/living/silicon/pai/New() - ..() - software = default_pai_software.Copy() - -/mob/living/silicon/pai/verb/paiInterface() - set category = "pAI Commands" - set name = "Software Interface" - - ui_interact(src) - -/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = self_state) - if(ui_key != "main") - var/datum/pai_software/S = software[ui_key] - if(S && !S.toggle) - ui = SSnanoui.try_update_ui(user, src, S.id, ui, force_open) - if(!ui) - ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state) - ui.open() - if(S.autoupdate) - ui.set_auto_update(1) - else - if(ui) - ui.set_status(STATUS_CLOSE, 0) - return - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600, state = state) - ui.open() - ui.set_auto_update(1) - -/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = self_state) - var/data[0] - - if(ui_key != "main") - var/datum/pai_software/S = software[ui_key] - if(S && !S.toggle) - return S.on_ui_data(user, state) - log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src) - return - // Software we have bought - var/bought_software[0] - // Software we have not bought - var/not_bought_software[0] - - for(var/key in pai_software_by_key) - var/datum/pai_software/S = pai_software_by_key[key] - var/software_data[0] - software_data["name"] = S.name - software_data["id"] = S.id - if(key in software) - software_data["on"] = S.is_active(src) - bought_software[++bought_software.len] = software_data - else - software_data["ram"] = S.ram_cost - not_bought_software[++not_bought_software.len] = software_data - - data["bought"] = bought_software - data["not_bought"] = not_bought_software - data["available_ram"] = ram - - // Emotions - var/emotions[0] - for(var/name in pai_emotions) - var/emote[0] - emote["name"] = name - emote["id"] = pai_emotions[name] - emotions[++emotions.len] = emote - - data["emotions"] = emotions - data["current_emotion"] = card.current_emotion - - return data - -/mob/living/silicon/pai/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["software"]) - var/soft = href_list["software"] - var/datum/pai_software/S = software[soft] - if(S.toggle) - S.toggle(src) - else - ui_interact(src, ui_key = soft) - return 1 - - else if(href_list["stopic"]) - var/soft = href_list["stopic"] - var/datum/pai_software/S = software[soft] - if(S) - return S.Topic(href, href_list) - - else if(href_list["purchase"]) - var/soft = href_list["purchase"] - var/datum/pai_software/S = pai_software_by_key[soft] - if(S && (ram >= S.ram_cost)) - ram -= S.ram_cost - software[S.id] = S - return 1 - - else if(href_list["image"]) - var/img = text2num(href_list["image"]) - if(1 <= img && img <= 9) - card.setEmotion(img) - return 1 +GLOBAL_LIST_INIT(pai_emotions, list( + "Happy" = 1, + "Cat" = 2, + "Extremely Happy" = 3, + "Face" = 4, + "Laugh" = 5, + "Off" = 6, + "Sad" = 7, + "Angry" = 8, + "What" = 9 +)) + + +GLOBAL_LIST_EMPTY(pai_software_by_key) +GLOBAL_LIST_EMPTY(default_pai_software) +/hook/startup/proc/populate_pai_software_list() + var/r = 1 // I would use ., but it'd sacrifice runtime detection + for(var/type in subtypesof(/datum/pai_software)) + var/datum/pai_software/P = new type() + if(GLOB.pai_software_by_key[P.id]) + var/datum/pai_software/O = GLOB.pai_software_by_key[P.id] + to_chat(world, "pAI software module [P.name] has the same key as [O.name]!") + r = 0 + continue + GLOB.pai_software_by_key[P.id] = P + if(P.default) + GLOB.default_pai_software[P.id] = P + return r + +/mob/living/silicon/pai/New() + ..() + software = GLOB.default_pai_software.Copy() + +/mob/living/silicon/pai/verb/paiInterface() + set category = "pAI Commands" + set name = "Software Interface" + + ui_interact(src) + +/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = GLOB.self_state) + if(ui_key != "main") + var/datum/pai_software/S = software[ui_key] + if(S && !S.toggle) + ui = SSnanoui.try_update_ui(user, src, S.id, ui, force_open) + if(!ui) + ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state) + ui.open() + if(S.autoupdate) + ui.set_auto_update(1) + else + if(ui) + ui.set_status(STATUS_CLOSE, 0) + return + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600, state = state) + ui.open() + ui.set_auto_update(1) + +/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.self_state) + var/data[0] + + if(ui_key != "main") + var/datum/pai_software/S = software[ui_key] + if(S && !S.toggle) + return S.on_ui_data(user, state) + log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src) + return + // Software we have bought + var/bought_software[0] + // Software we have not bought + var/not_bought_software[0] + + for(var/key in GLOB.pai_software_by_key) + var/datum/pai_software/S = GLOB.pai_software_by_key[key] + var/software_data[0] + software_data["name"] = S.name + software_data["id"] = S.id + if(key in software) + software_data["on"] = S.is_active(src) + bought_software[++bought_software.len] = software_data + else + software_data["ram"] = S.ram_cost + not_bought_software[++not_bought_software.len] = software_data + + data["bought"] = bought_software + data["not_bought"] = not_bought_software + data["available_ram"] = ram + + // Emotions + var/emotions[0] + for(var/name in GLOB.pai_emotions) + var/emote[0] + emote["name"] = name + emote["id"] = GLOB.pai_emotions[name] + emotions[++emotions.len] = emote + + data["emotions"] = emotions + data["current_emotion"] = card.current_emotion + + return data + +/mob/living/silicon/pai/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["software"]) + var/soft = href_list["software"] + var/datum/pai_software/S = software[soft] + if(S.toggle) + S.toggle(src) + else + ui_interact(src, ui_key = soft) + return 1 + + else if(href_list["stopic"]) + var/soft = href_list["stopic"] + var/datum/pai_software/S = software[soft] + if(S) + return S.Topic(href, href_list) + + else if(href_list["purchase"]) + var/soft = href_list["purchase"] + var/datum/pai_software/S = GLOB.pai_software_by_key[soft] + if(S && (ram >= S.ram_cost)) + ram -= S.ram_cost + software[S.id] = S + return 1 + + else if(href_list["image"]) + var/img = text2num(href_list["image"]) + if(1 <= img && img <= 9) + card.setEmotion(img) + return 1 diff --git a/code/modules/mob/living/silicon/pai/software_modules.dm b/code/modules/mob/living/silicon/pai/software_modules.dm index 84c467b1549..7161919df7f 100644 --- a/code/modules/mob/living/silicon/pai/software_modules.dm +++ b/code/modules/mob/living/silicon/pai/software_modules.dm @@ -19,7 +19,7 @@ var/ui_width = 450 var/ui_height = 600 -/datum/pai_software/proc/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/proc/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) return list() /datum/pai_software/proc/toggle(mob/living/silicon/pai/user) @@ -39,7 +39,7 @@ ui_title = "pAI Directives" autoupdate = 1 -/datum/pai_software/directives/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/directives/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] data["master"] = user.master @@ -94,7 +94,7 @@ ui_width = 300 ui_height = 150 -/datum/pai_software/radio_config/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/radio_config/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] data["listening"] = user.radio.broadcasting @@ -129,11 +129,11 @@ template_file = "pai_manifest.tmpl" ui_title = "Crew Manifest" -/datum/pai_software/crew_manifest/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/crew_manifest/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] - data_core.get_manifest_json() - data["manifest"] = PDA_Manifest + GLOB.data_core.get_manifest_json() + data["manifest"] = GLOB.PDA_Manifest return data @@ -147,7 +147,7 @@ template_file = "pai_messenger.tmpl" ui_title = "Digital Messenger" -/datum/pai_software/messenger/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/messenger/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] if(!user.pda) @@ -165,7 +165,7 @@ var/pdas[0] if(!M.toff) - for(var/obj/item/pda/P in PDAs) + for(var/obj/item/pda/P in GLOB.PDAs) var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) if(P == user.pda || !PM || !PM.can_receive()) @@ -237,11 +237,11 @@ ui_title = "Medical Records" -/datum/pai_software/med_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/med_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) + for(var/datum/data/record/general in sortRecord(GLOB.data_core.general)) var/record[0] record["name"] = general.fields["name"] record["ref"] = "\ref[general]" @@ -266,11 +266,11 @@ if(record) var/datum/data/record/R = record var/datum/data/record/M = null - if(!( data_core.general.Find(R) )) + if(!( GLOB.data_core.general.Find(R) )) P.medical_cannotfind = 1 else P.medical_cannotfind = 0 - for(var/datum/data/record/E in data_core.medical) + for(var/datum/data/record/E in GLOB.data_core.medical) if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) M = E P.medicalActive1 = R @@ -290,11 +290,11 @@ ui_title = "Security Records" -/datum/pai_software/sec_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/sec_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) + for(var/datum/data/record/general in sortRecord(GLOB.data_core.general)) var/record[0] record["name"] = general.fields["name"] record["ref"] = "\ref[general]" @@ -319,13 +319,13 @@ if(record) var/datum/data/record/R = record var/datum/data/record/S = null - if(!( data_core.general.Find(R) )) + if(!( GLOB.data_core.general.Find(R) )) P.securityActive1 = null P.securityActive2 = null P.security_cannotfind = 1 else P.security_cannotfind = 0 - for(var/datum/data/record/E in data_core.security) + for(var/datum/data/record/E in GLOB.data_core.security) if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) S = E P.securityActive1 = R @@ -348,7 +348,7 @@ ui_width = 300 ui_height = 150 -/datum/pai_software/door_jack/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/door_jack/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] data["cable"] = user.cable != null @@ -382,14 +382,6 @@ return 1 /mob/living/silicon/pai/proc/hackloop() - var/turf/T = get_turf_or_move(src.loc) - for(var/mob/living/silicon/ai/AI in GLOB.player_list) - if(!T || !is_station_contact(T.z)) - break - if(T.loc) - to_chat(AI, "Network Alert: Brute-force encryption crack in progress in [T.loc].") - else - to_chat(AI, "Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location.") var/obj/machinery/door/D = cable.machine if(!istype(D)) hack_aborted = 1 @@ -424,7 +416,7 @@ ui_height = 300 -/datum/pai_software/atmosphere_sensor/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/atmosphere_sensor/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] var/turf/T = get_turf_or_move(user.loc) @@ -543,7 +535,7 @@ ui_width = 320 ui_height = 150 -/datum/pai_software/signaller/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/signaller/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] data["frequency"] = format_frequency(user.sradio.frequency) @@ -586,7 +578,7 @@ ui_width = 400 ui_height = 350 -/datum/pai_software/host_scan/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/host_scan/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] var/mob/living/held = user.loc var/count = 0 diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 7402c4a65c9..224c706f324 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -1,75 +1,75 @@ -/mob/living/silicon/robot/gib() - if(!death(TRUE) && stat != DEAD) - return FALSE - //robots don't die when gibbed. instead they drop their MMI'd brain - var/atom/movable/overlay/animation = null - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - - animation = new(loc) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - - playsound(src.loc, 'sound/goonstation/effects/robogib.ogg', 50, 1) - - flick("gibbed-r", animation) - robogibs(loc) - - GLOB.living_mob_list -= src - GLOB.dead_mob_list -= src - QDEL_IN(animation, 15) - QDEL_IN(src, 15) - return TRUE - -/mob/living/silicon/robot/dust() - if(!death(TRUE) && stat != DEAD) - return FALSE - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - if(mmi) - qdel(mmi) //Delete the MMI first so that it won't go popping out. - GLOB.dead_mob_list -= src - QDEL_IN(src, 15) - return TRUE - -/mob/living/silicon/robot/dust_animation() - var/atom/movable/overlay/animation = null - animation = new(loc) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - flick("dust-r", animation) - new /obj/effect/decal/remains/robot(loc) - QDEL_IN(animation, 15) - -/mob/living/silicon/robot/death(gibbed) - if(can_die()) - if(!gibbed && deathgasp_on_death) - emote("deathgasp", force = TRUE) - - if(module) - module.handle_death(gibbed) - - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - - diag_hud_set_status() - diag_hud_set_health() - if(camera) - camera.status = 0 - update_headlamp(1) //So borg lights are disabled when killed. - - if(in_contents_of(/obj/machinery/recharge_station))//exit the recharge station - var/obj/machinery/recharge_station/RC = loc - RC.go_out() - - update_icons() - - sql_report_cyborg_death(src) +/mob/living/silicon/robot/gib() + if(!death(TRUE) && stat != DEAD) + return FALSE + //robots don't die when gibbed. instead they drop their MMI'd brain + var/atom/movable/overlay/animation = null + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + + animation = new(loc) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + + playsound(src.loc, 'sound/goonstation/effects/robogib.ogg', 50, 1) + + flick("gibbed-r", animation) + robogibs(loc) + + GLOB.living_mob_list -= src + GLOB.dead_mob_list -= src + QDEL_IN(animation, 15) + QDEL_IN(src, 15) + return TRUE + +/mob/living/silicon/robot/dust() + if(!death(TRUE) && stat != DEAD) + return FALSE + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + if(mmi) + qdel(mmi) //Delete the MMI first so that it won't go popping out. + GLOB.dead_mob_list -= src + QDEL_IN(src, 15) + return TRUE + +/mob/living/silicon/robot/dust_animation() + var/atom/movable/overlay/animation = null + animation = new(loc) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + flick("dust-r", animation) + new /obj/effect/decal/remains/robot(loc) + QDEL_IN(animation, 15) + +/mob/living/silicon/robot/death(gibbed) + if(can_die()) + if(!gibbed && deathgasp_on_death) + emote("deathgasp", force = TRUE) + + if(module) + module.handle_death(gibbed) + + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + + diag_hud_set_status() + diag_hud_set_health() + if(camera) + camera.status = 0 + update_headlamp(1) //So borg lights are disabled when killed. + + if(in_contents_of(/obj/machinery/recharge_station))//exit the recharge station + var/obj/machinery/recharge_station/RC = loc + RC.go_out() + + update_icons() + + sql_report_cyborg_death(src) diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 589f2f06b49..dc32deaf5a7 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -13,7 +13,7 @@ braintype = "Robot" lawupdate = 0 density = 0 - req_one_access = list(access_engine, access_robotics) + req_one_access = list(ACCESS_ENGINE, ACCESS_ROBOTICS) ventcrawler = 2 magpulse = 1 mob_size = MOB_SIZE_SMALL @@ -46,9 +46,9 @@ // Disable the microphone wire on Drones if(radio) - radio.wires.CutWireIndex(WIRE_TRANSMIT) + radio.wires.CutWireIndex(RADIO_WIRE_TRANSMIT) - if(camera && "Robots" in camera.network) + if(camera && ("Robots" in camera.network)) camera.network.Add("Engineering") //They are unable to be upgraded, so let's give them a bit of a better battery. @@ -188,7 +188,7 @@ message_admins("[key_name_admin(user)] emagged drone [key_name_admin(src)]. Laws overridden.") log_game("[key_name(user)] emagged drone [key_name(src)]. Laws overridden.") var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [H.name]([H.key]) emagged [name]([key])") + GLOB.lawchanges.Add("[time] : [H.name]([H.key]) emagged [name]([key])") emagged_time = world.time emagged = 1 diff --git a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm index 44eff377f77..58605d8d89f 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm @@ -101,4 +101,4 @@ grabber.update_inv_l_hand() grabber.update_inv_r_hand() - return H \ No newline at end of file + return H diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm index 167bf45ca6e..80d9f4d1ba3 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -3,7 +3,7 @@ desc = "Used to monitor the station's drone population and the assembler that services them." icon_screen = "power" icon_keyboard = "power_key" - req_access = list(access_engine_equip) + req_access = list(ACCESS_ENGINE_EQUIP) circuit = /obj/item/circuitboard/drone_control //Used when pinging drones. @@ -124,4 +124,4 @@ dronefab.produce_drones = !dronefab.produce_drones to_chat(usr, "You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator.") - src.updateUsrDialog() \ No newline at end of file + src.updateUsrDialog() diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index d200bdb075a..7958f983805 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -30,29 +30,22 @@ /obj/item/circuitboard, /obj/item/stack/tile/light, /obj/item/stack/ore/bluespace_crystal - ) + ) //Item currently being held. - var/obj/item/wrapped = null - -/obj/item/gripper/paperwork - name = "paperwork gripper" - desc = "A simple grasping tool for clerical work." - - can_hold = list( - /obj/item/clipboard, - /obj/item/paper, - /obj/item/card/id - ) + var/obj/item/gripped_item = null /obj/item/gripper/medical name = "medical gripper" desc = "A grasping tool used to help patients up once surgery is complete." can_hold = list() -/obj/item/gripper/medical/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params) +/obj/item/gripper/medical/attack_self(mob/user) + return + +/obj/item/gripper/medical/afterattack(atom/target, mob/living/user, proximity, params) var/mob/living/carbon/human/H - if(!wrapped && proximity && target && ishuman(target)) + if(!gripped_item && proximity && target && ishuman(target)) H = target if(H.lying) H.AdjustSleeping(-5) @@ -71,98 +64,69 @@ ..() can_hold = typecacheof(can_hold) -/obj/item/gripper/attack_self(mob/user as mob) - if(wrapped) - wrapped.attack_self(user) - /obj/item/gripper/verb/drop_item() - set name = "Drop Gripped Item" set desc = "Release an item from your magnetic gripper." set category = "Drone" - drop_item_p() + drop_gripped_item() -// The "p" stands for proc, since I was having annoying weird stuff happening with this in the verb -// when trying to have default values for arguments and stuff -/obj/item/gripper/proc/drop_item_p(var/silent = 0) +/obj/item/gripper/attack_self(mob/user) + if(gripped_item) + gripped_item.attack_self(user) + else + to_chat(user, "[src] is empty.") - if(!wrapped) - //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z - for(var/obj/item/thing in src.contents) - thing.forceMove(get_turf(src)) - return +/obj/item/gripper/proc/drop_gripped_item(silent = FALSE) + if(gripped_item) + if(!silent) + to_chat(loc, "You drop [gripped_item].") + gripped_item.forceMove(get_turf(src)) + gripped_item = null - if(wrapped.loc != src) - wrapped = null - return - - if(!silent) - to_chat(src.loc, "You drop \the [wrapped].") - wrapped.forceMove(get_turf(src)) - wrapped = null - -/obj/item/gripper/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) +/obj/item/gripper/attack(mob/living/carbon/M, mob/living/carbon/user) return -/obj/item/gripper/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params) +/// Grippers are snowflakey so this is needed to to prevent forceMoving grippers after `if(!user.drop_item())` checks done in certain attackby's. +/obj/item/gripper/forceMove(atom/destination) + return + +/obj/item/gripper/afterattack(atom/target, mob/living/user, proximity, params) if(!target || !proximity) //Target is invalid or we are not adjacent. - return + return FALSE - //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z - if(!wrapped) - for(var/obj/item/thing in src.contents) - wrapped = thing - break + if(gripped_item) //Already have an item. - if(wrapped) //Already have an item. - - //Temporary put wrapped into user so target's attackby() checks pass. - wrapped.forceMove(user) - - //Pass the attack on to the target. This might delete/relocate wrapped. - if(!target.attackby(wrapped, user, params) && target && wrapped) - // If the attackby didn't resolve or delete the target or wrapped, afterattack + //Pass the attack on to the target. This might delete/relocate gripped_item. + if(!target.attackby(gripped_item, user, params)) + // If the attackby didn't resolve or delete the target or gripped_item, afterattack // (Certain things, such as mountable frames, rely on afterattack) - wrapped.afterattack(target, user, 1, params) + gripped_item?.afterattack(target, user, 1, params) - //If wrapped did neither get deleted nor put into target, put it back into the gripper. - if(wrapped && user && (wrapped.loc == user)) - wrapped.forceMove(src) - else - wrapped = null - return - - else if(istype(target,/obj/item)) //Check that we're not pocketing a mob. - - //...and that the item is not in a container. - if(!isturf(target.loc)) - return + //If gripped_item either didn't get deleted, or it failed to be transfered to its target + if(!gripped_item && contents.len) + gripped_item = contents[1] + return FALSE + else if(gripped_item && !contents.len) + gripped_item = null + else if(istype(target, /obj/item)) //Check that we're not pocketing a mob. var/obj/item/I = target - - //Check if the item is blacklisted. - var/grab = 0 - if(can_hold.len) - if(is_type_in_typecache(I, can_hold)) - grab = 1 - - //We can grab the item, finally. - if(grab) - to_chat(user, "You collect \the [I].") + if(is_type_in_typecache(I, can_hold)) // Make sure the item is something the gripper can hold + to_chat(user, "You collect [I].") I.forceMove(src) - wrapped = I - return + gripped_item = I else - to_chat(user, "Your gripper cannot hold \the [target].") + to_chat(user, "Your gripper cannot hold [target].") + return FALSE else if(istype(target,/obj/machinery/power/apc)) var/obj/machinery/power/apc/A = target if(A.opened) if(A.cell) - wrapped = A.cell + gripped_item = A.cell A.cell.add_fingerprint(user) A.cell.update_icon() @@ -173,6 +137,7 @@ A.update_icon() user.visible_message("[user] removes the power cell from [A]!", "You remove the power cell.") + return TRUE //TODO: Matter decompiler. /obj/item/matter_decompiler diff --git a/code/modules/mob/living/silicon/robot/drone/drone_say.dm b/code/modules/mob/living/silicon/robot/drone/drone_say.dm index 7b51952a685..38b90480930 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_say.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_say.dm @@ -11,4 +11,4 @@ /mob/living/silicon/robot/drone/get_default_language() if(default_language) return default_language - return GLOB.all_languages["Drone"] \ No newline at end of file + return GLOB.all_languages["Drone"] diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index 198b687e496..69cb398df63 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -1,58 +1,58 @@ -/mob/living/silicon/robot/examine(mob/user) - . = ..() - - var/msg = "" - if(module) - msg += "It has loaded a [module.name].\n" - var/obj/act_module = get_active_hand() - if(act_module) - msg += "It is holding [bicon(act_module)] \a [act_module].\n" - msg += "" - if(getBruteLoss()) - if(getBruteLoss() < maxHealth*0.5) - msg += "It looks slightly dented.\n" - else - msg += "It looks severely dented!\n" - if(getFireLoss()) - if(getFireLoss() < maxHealth*0.5) - msg += "It looks slightly charred.\n" - else - msg += "It looks severely burnt and heat-warped!\n" - if(health < -maxHealth*0.5) - msg += "It looks barely operational.\n" - if(fire_stacks < 0) - msg += "It's covered in water.\n" - else if(fire_stacks > 0) - msg += "It's coated in something flammable.\n" - msg += "" - - if(opened) - msg += "Its cover is open and the power cell is [cell ? "installed" : "missing"].\n" - else - msg += "Its cover is closed[locked ? "" : ", and looks unlocked"].\n" - - if(cell && cell.charge <= 0) - msg += "Its battery indicator is blinking red!\n" - - switch(stat) - if(CONSCIOUS) - if(!client) - msg += "It appears to be in stand-by mode.\n" //afk - if(UNCONSCIOUS) - msg += "It doesn't seem to be responding.\n" - if(DEAD) - if(!suiciding) - msg += "It looks like its system is corrupted and requires a reset.\n" - else - msg += "It looks like its system is corrupted beyond repair. There is no hope of recovery.\n" - msg += "*---------*" - - if(print_flavor_text()) msg += "\n[print_flavor_text()]\n" - - if(pose) - if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) - pose = addtext(pose,".") //Makes sure all emotes end with a period. - msg += "\nIt is [pose]" - - . += msg - user.showLaws(src) \ No newline at end of file +/mob/living/silicon/robot/examine(mob/user) + . = ..() + + var/msg = "" + if(module) + msg += "It has loaded a [module.name].\n" + var/obj/act_module = get_active_hand() + if(act_module) + msg += "It is holding [bicon(act_module)] \a [act_module].\n" + msg += "" + if(getBruteLoss()) + if(getBruteLoss() < maxHealth*0.5) + msg += "It looks slightly dented.\n" + else + msg += "It looks severely dented!\n" + if(getFireLoss()) + if(getFireLoss() < maxHealth*0.5) + msg += "It looks slightly charred.\n" + else + msg += "It looks severely burnt and heat-warped!\n" + if(health < -maxHealth*0.5) + msg += "It looks barely operational.\n" + if(fire_stacks < 0) + msg += "It's covered in water.\n" + else if(fire_stacks > 0) + msg += "It's coated in something flammable.\n" + msg += "" + + if(opened) + msg += "Its cover is open and the power cell is [cell ? "installed" : "missing"].\n" + else + msg += "Its cover is closed[locked ? "" : ", and looks unlocked"].\n" + + if(cell && cell.charge <= 0) + msg += "Its battery indicator is blinking red!\n" + + switch(stat) + if(CONSCIOUS) + if(!client) + msg += "It appears to be in stand-by mode.\n" //afk + if(UNCONSCIOUS) + msg += "It doesn't seem to be responding.\n" + if(DEAD) + if(!suiciding) + msg += "It looks like its system is corrupted and requires a reset.\n" + else + msg += "It looks like its system is corrupted beyond repair. There is no hope of recovery.\n" + msg += "*---------*" + + if(print_flavor_text()) msg += "\n[print_flavor_text()]\n" + + if(pose) + if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) + pose = addtext(pose,".") //Makes sure all emotes end with a period. + msg += "\nIt is [pose]" + + . += msg + user.showLaws(src) diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index da0732c5ecd..a68da651ee9 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -1,254 +1,254 @@ -//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually -//as they handle all relevant stuff like adding it to the player's screen and such - -//Returns the thing in our active hand (whatever is in our active module-slot, in this case) -/mob/living/silicon/robot/get_active_hand() - return module_active - -/mob/living/silicon/robot/get_all_slots() - return list(module_state_1, module_state_2, module_state_3) - -/*-------TODOOOOOOOOOO--------*/ -/mob/living/silicon/robot/proc/uneq_module(obj/item/O) - if(!O) - return 0 - - O.mouse_opacity = MOUSE_OPACITY_OPAQUE - - if(client) - client.screen -= O - contents -= O - if(module) - O.loc = module //Return item to module so it appears in its contents, so it can be taken out again. - for(var/X in O.actions) // Remove assocated actions - var/datum/action/A = X - A.Remove(src) - - if(module_active == O) - module_active = null - if(module_state_1 == O) - inv1.icon_state = "inv1" - module_state_1 = null - else if(module_state_2 == O) - inv2.icon_state = "inv2" - module_state_2 = null - else if(module_state_3 == O) - module_state_3 = null - inv3.icon_state = "inv3" - if(hud_used) - hud_used.update_robot_modules_display() - return 1 - -/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) - if(!(locate(O) in src.module.modules) && O != src.module.emag) - return - if(activated(O)) - to_chat(src, "Already activated") - return - if(is_component_functioning("power cell") && cell) - if(istype(O, /obj/item/borg)) - var/obj/item/borg/B = O - if(B.powerneeded) - if((cell.charge * 100 / cell.maxcharge) < B.powerneeded) - to_chat(src, "Not enough power to activate [B.name]!") - return - if(!module_state_1) - O.mouse_opacity = initial(O.mouse_opacity) - module_state_1 = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - O.screen_loc = inv1.screen_loc - contents += O - set_actions(O) - else if(!module_state_2) - O.mouse_opacity = initial(O.mouse_opacity) - module_state_2 = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - O.screen_loc = inv2.screen_loc - contents += O - set_actions(O) - else if(!module_state_3) - O.mouse_opacity = initial(O.mouse_opacity) - module_state_3 = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - O.screen_loc = inv3.screen_loc - contents += O - set_actions(O) - else - to_chat(src, "You need to disable a module first!") - update_icons() - -/mob/living/silicon/robot/proc/set_actions(obj/item/I) - for(var/X in I.actions) - var/datum/action/A = X - A.Grant(src) - -/mob/living/silicon/robot/proc/uneq_active() - uneq_module(module_active) - -/mob/living/silicon/robot/proc/uneq_all() - uneq_module(module_state_1) - uneq_module(module_state_2) - uneq_module(module_state_3) - -/mob/living/silicon/robot/proc/uneq_numbered(var/module) - if(module < 1 || module > 3) return - - switch(module) - if(1) - uneq_module(module_state_1) - if(2) - uneq_module(module_state_2) - if(3) - uneq_module(module_state_3) - -/mob/living/silicon/robot/proc/activated(obj/item/O) - if(module_state_1 == O) - return 1 - else if(module_state_2 == O) - return 1 - else if(module_state_3 == O) - return 1 - else - return 0 - -/mob/living/silicon/robot/drop_item() - var/obj/item/I = get_active_hand() - if(istype(I, /obj/item/gripper)) - var/obj/item/gripper/G = I - G.drop_item_p(silent = 1) - return - -//Helper procs for cyborg modules on the UI. -//These are hackish but they help clean up code elsewhere. - -//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. -/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 - return module == get_selected_module() - -//module_active(module) - Checks whether there is a module active in the slot specified by "module". -/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 - if(module < 1 || module > 3) return 0 - - switch(module) - if(1) - if(module_state_1) - return 1 - if(2) - if(module_state_2) - return 1 - if(3) - if(module_state_3) - return 1 - return 0 - -//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. -/mob/living/silicon/robot/proc/get_selected_module() - if(module_state_1 && module_active == module_state_1) - return 1 - else if(module_state_2 && module_active == module_state_2) - return 2 - else if(module_state_3 && module_active == module_state_3) - return 3 - - return 0 - -//select_module(module) - Selects the module slot specified by "module" -/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(!module_active(module)) return - - switch(module) - if(1) - if(module_active != module_state_1) - inv1.icon_state = "inv1 +a" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3" - module_active = module_state_1 - return - if(2) - if(module_active != module_state_2) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2 +a" - inv3.icon_state = "inv3" - module_active = module_state_2 - return - if(3) - if(module_active != module_state_3) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3 +a" - module_active = module_state_3 - return - return - -//deselect_module(module) - Deselects the module slot specified by "module" -/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - switch(module) - if(1) - if(module_active == module_state_1) - inv1.icon_state = "inv1" - module_active = null - return - if(2) - if(module_active == module_state_2) - inv2.icon_state = "inv2" - module_active = null - return - if(3) - if(module_active == module_state_3) - inv3.icon_state = "inv3" - module_active = null - return - return - -//toggle_module(module) - Toggles the selection of the module slot specified by "module". -/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(module_selected(module)) - deselect_module(module) - else - if(module_active(module)) - select_module(module) - else - deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. - return - -//cycle_modules() - Cycles through the list of selected modules. -/mob/living/silicon/robot/proc/cycle_modules() - var/slot_start = get_selected_module() - if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. - - var/slot_num - if(slot_start == 0) - slot_num = 0 - slot_start = 3 - else - slot_num = slot_start - - do - slot_num++ - if(slot_num > 3) slot_num = 1 //Wrap around. - if(module_active(slot_num)) - select_module(slot_num) - return - while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. - - return - -/mob/living/silicon/robot/unEquip(obj/item/I, force) - if(I == module_active) - uneq_active(I) - return ..() - -/mob/living/silicon/robot/proc/update_module_icon() - if(!module) - hands.icon_state = "nomod" - else - hands.icon_state = lowertext(module.module_type) +//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually +//as they handle all relevant stuff like adding it to the player's screen and such + +//Returns the thing in our active hand (whatever is in our active module-slot, in this case) +/mob/living/silicon/robot/get_active_hand() + return module_active + +/mob/living/silicon/robot/get_all_slots() + return list(module_state_1, module_state_2, module_state_3) + +/*-------TODOOOOOOOOOO--------*/ +/mob/living/silicon/robot/proc/uneq_module(obj/item/O) + if(!O) + return 0 + + O.mouse_opacity = MOUSE_OPACITY_OPAQUE + + if(client) + client.screen -= O + contents -= O + if(module) + O.loc = module //Return item to module so it appears in its contents, so it can be taken out again. + for(var/X in O.actions) // Remove assocated actions + var/datum/action/A = X + A.Remove(src) + + if(module_active == O) + module_active = null + if(module_state_1 == O) + inv1.icon_state = "inv1" + module_state_1 = null + else if(module_state_2 == O) + inv2.icon_state = "inv2" + module_state_2 = null + else if(module_state_3 == O) + module_state_3 = null + inv3.icon_state = "inv3" + if(hud_used) + hud_used.update_robot_modules_display() + return 1 + +/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) + if(!(locate(O) in src.module.modules) && O != src.module.emag) + return + if(activated(O)) + to_chat(src, "Already activated") + return + if(is_component_functioning("power cell") && cell) + if(istype(O, /obj/item/borg)) + var/obj/item/borg/B = O + if(B.powerneeded) + if((cell.charge * 100 / cell.maxcharge) < B.powerneeded) + to_chat(src, "Not enough power to activate [B.name]!") + return + if(!module_state_1) + O.mouse_opacity = initial(O.mouse_opacity) + module_state_1 = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + O.screen_loc = inv1.screen_loc + contents += O + set_actions(O) + else if(!module_state_2) + O.mouse_opacity = initial(O.mouse_opacity) + module_state_2 = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + O.screen_loc = inv2.screen_loc + contents += O + set_actions(O) + else if(!module_state_3) + O.mouse_opacity = initial(O.mouse_opacity) + module_state_3 = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + O.screen_loc = inv3.screen_loc + contents += O + set_actions(O) + else + to_chat(src, "You need to disable a module first!") + update_icons() + +/mob/living/silicon/robot/proc/set_actions(obj/item/I) + for(var/X in I.actions) + var/datum/action/A = X + A.Grant(src) + +/mob/living/silicon/robot/proc/uneq_active() + uneq_module(module_active) + +/mob/living/silicon/robot/proc/uneq_all() + uneq_module(module_state_1) + uneq_module(module_state_2) + uneq_module(module_state_3) + +/mob/living/silicon/robot/proc/uneq_numbered(var/module) + if(module < 1 || module > 3) return + + switch(module) + if(1) + uneq_module(module_state_1) + if(2) + uneq_module(module_state_2) + if(3) + uneq_module(module_state_3) + +/mob/living/silicon/robot/proc/activated(obj/item/O) + if(module_state_1 == O) + return 1 + else if(module_state_2 == O) + return 1 + else if(module_state_3 == O) + return 1 + else + return 0 + +/mob/living/silicon/robot/drop_item() + var/obj/item/gripper/G = get_active_hand() + if(istype(G)) + G.drop_gripped_item(silent = TRUE) + return TRUE // The gripper is special because it has a normal item inside that we can drop. + return FALSE // All robot inventory items have NODROP, so they should return FALSE. + +//Helper procs for cyborg modules on the UI. +//These are hackish but they help clean up code elsewhere. + +//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. +/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 + return module == get_selected_module() + +//module_active(module) - Checks whether there is a module active in the slot specified by "module". +/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 + if(module < 1 || module > 3) return 0 + + switch(module) + if(1) + if(module_state_1) + return 1 + if(2) + if(module_state_2) + return 1 + if(3) + if(module_state_3) + return 1 + return 0 + +//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. +/mob/living/silicon/robot/proc/get_selected_module() + if(module_state_1 && module_active == module_state_1) + return 1 + else if(module_state_2 && module_active == module_state_2) + return 2 + else if(module_state_3 && module_active == module_state_3) + return 3 + + return 0 + +//select_module(module) - Selects the module slot specified by "module" +/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(!module_active(module)) return + + switch(module) + if(1) + if(module_active != module_state_1) + inv1.icon_state = "inv1 +a" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3" + module_active = module_state_1 + return + if(2) + if(module_active != module_state_2) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2 +a" + inv3.icon_state = "inv3" + module_active = module_state_2 + return + if(3) + if(module_active != module_state_3) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3 +a" + module_active = module_state_3 + return + return + +//deselect_module(module) - Deselects the module slot specified by "module" +/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + switch(module) + if(1) + if(module_active == module_state_1) + inv1.icon_state = "inv1" + module_active = null + return + if(2) + if(module_active == module_state_2) + inv2.icon_state = "inv2" + module_active = null + return + if(3) + if(module_active == module_state_3) + inv3.icon_state = "inv3" + module_active = null + return + return + +//toggle_module(module) - Toggles the selection of the module slot specified by "module". +/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(module_selected(module)) + deselect_module(module) + else + if(module_active(module)) + select_module(module) + else + deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. + return + +//cycle_modules() - Cycles through the list of selected modules. +/mob/living/silicon/robot/proc/cycle_modules() + var/slot_start = get_selected_module() + if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. + + var/slot_num + if(slot_start == 0) + slot_num = 0 + slot_start = 3 + else + slot_num = slot_start + + do + slot_num++ + if(slot_num > 3) slot_num = 1 //Wrap around. + if(module_active(slot_num)) + select_module(slot_num) + return + while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. + + return + +/mob/living/silicon/robot/unEquip(obj/item/I, force) + if(I == module_active) + uneq_active(I) + return ..() + +/mob/living/silicon/robot/proc/update_module_icon() + if(!module) + hands.icon_state = "nomod" + else + hands.icon_state = lowertext(module.module_type) diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index 0398172023c..25cdc61fa28 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -1,54 +1,54 @@ -/mob/living/silicon/robot/verb/cmd_show_laws() - set category = "Robot Commands" - set name = "Show Laws" - show_laws() - -/mob/living/silicon/robot/show_laws(var/everyone = 0) - laws_sanity_check() - var/who - - if(everyone) - who = world - else - who = src - if(lawupdate) - if(connected_ai) - if(connected_ai.stat || connected_ai.control_disabled) - to_chat(src, "AI signal lost, unable to sync laws.") - - else - lawsync() - photosync() - to_chat(src, "Laws synced with AI, be sure to note any changes.") - // TODO: Update to new antagonist system. - if(mind && mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) - to_chat(src, "Remember, your AI does NOT share or know about your law 0.") - else - to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") - lawupdate = 0 - - to_chat(who, "Obey these laws:") - laws.show_laws(who) - // TODO: Update to new antagonist system. - if(mind && (mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) && connected_ai) - to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") - else if(connected_ai) - to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") - else if(emagged) - to_chat(who, "Remember, you are not required to listen to the AI.") - else - to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") - - -/mob/living/silicon/robot/lawsync() - laws_sanity_check() - var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null - if(master) - master.sync(src) - ..() - return - -/mob/living/silicon/robot/proc/robot_checklaws() - set category = "Robot Commands" - set name = "State Laws" - subsystem_law_manager() +/mob/living/silicon/robot/verb/cmd_show_laws() + set category = "Robot Commands" + set name = "Show Laws" + show_laws() + +/mob/living/silicon/robot/show_laws(var/everyone = 0) + laws_sanity_check() + var/who + + if(everyone) + who = world + else + who = src + if(lawupdate) + if(connected_ai) + if(connected_ai.stat || connected_ai.control_disabled) + to_chat(src, "AI signal lost, unable to sync laws.") + + else + lawsync() + photosync() + to_chat(src, "Laws synced with AI, be sure to note any changes.") + // TODO: Update to new antagonist system. + if(mind && mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) + to_chat(src, "Remember, your AI does NOT share or know about your law 0.") + else + to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") + lawupdate = 0 + + to_chat(who, "Obey these laws:") + laws.show_laws(who) + // TODO: Update to new antagonist system. + if(mind && (mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) && connected_ai) + to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") + else if(connected_ai) + to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") + else if(emagged) + to_chat(who, "Remember, you are not required to listen to the AI.") + else + to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") + + +/mob/living/silicon/robot/lawsync() + laws_sanity_check() + var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null + if(master) + master.sync(src) + ..() + return + +/mob/living/silicon/robot/proc/robot_checklaws() + set category = "Robot Commands" + set name = "State Laws" + subsystem_law_manager() diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 258ccec026a..d40e801ba5f 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -1,203 +1,203 @@ -/mob/living/silicon/robot/Life(seconds, times_fired) - set invisibility = 0 - set background = BACKGROUND_ENABLED - - if(src.notransform) - return - - //Status updates, death etc. - clamp_values() - - if(..()) - handle_robot_cell() - process_locks() - process_queued_alarms() - -/mob/living/silicon/robot/proc/clamp_values() - SetStunned(min(stunned, 30)) - SetParalysis(min(paralysis, 30)) - SetWeakened(min(weakened, 20)) - SetSleeping(0) - -/mob/living/silicon/robot/proc/handle_robot_cell() - if(stat != DEAD) - if(!is_component_functioning("power cell")) - uneq_all() - low_power_mode = 1 - update_headlamp() - diag_hud_set_borgcell() - return - if(low_power_mode) - if(is_component_functioning("power cell") && cell.charge) - low_power_mode = 0 - update_headlamp() - else if(stat == CONSCIOUS) - use_power() - -/mob/living/silicon/robot/proc/use_power() - // this check is safe because `cell` is guaranteed to be set when the power cell is functioning - if(is_component_functioning("power cell") && cell.charge) - if(cell.charge <= 100) - uneq_all() - var/amt = Clamp((lamp_intensity - 2) * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell. - cell.use(amt) //Usage table: 1/tick if off/lowest setting, 4 = 4/tick, 6 = 8/tick, 8 = 12/tick, 10 = 16/tick - else - uneq_all() - low_power_mode = 1 - update_headlamp() - diag_hud_set_borgcell() - -/mob/living/silicon/robot/handle_regular_status_updates() - - . = ..() - - if(camera && !scrambledcodes) - if(stat == DEAD || wires.IsCameraCut()) - camera.status = 0 - else - camera.status = 1 - - if(sleeping) - AdjustSleeping(-1) - - if(.) //alive - if(!istype(src, /mob/living/silicon/robot/drone)) - if(health < 50) //Gradual break down of modules as more damage is sustained - if(uneq_module(module_state_3)) - to_chat(src, "SYSTEM ERROR: Module 3 OFFLINE.") - - if(health < 0) - if(uneq_module(module_state_2)) - to_chat(src, "SYSTEM ERROR: Module 2 OFFLINE.") - - if(health < -50) - if(uneq_module(module_state_1)) - to_chat(src, "CRITICAL ERROR: All modules OFFLINE.") - - diag_hud_set_health() - diag_hud_set_status() - - //update the state of modules and components here - if(stat != CONSCIOUS) - uneq_all() - - if(!is_component_functioning("radio") || stat == UNCONSCIOUS) - radio.on = 0 - else - radio.on = 1 - - return 1 - -/mob/living/silicon/robot/handle_hud_icons() - update_items() - update_cell() - if(emagged) - throw_alert("hacked", /obj/screen/alert/hacked) - else - clear_alert("hacked") - ..() - -/mob/living/silicon/robot/handle_hud_icons_health() - if(healths) - if(stat != DEAD) - if(health >= maxHealth) - healths.icon_state = "health0" - else if(health > maxHealth * 0.5) - healths.icon_state = "health2" - else if(health > 0) - healths.icon_state = "health3" - else if(health > -maxHealth * 0.5) - healths.icon_state = "health4" - else if(health > -maxHealth) - healths.icon_state = "health5" - else - healths.icon_state = "health6" - else - healths.icon_state = "health7" - - switch(bodytemperature) //310.055 optimal body temp - if(335 to INFINITY) - throw_alert("temp", /obj/screen/alert/hot/robot, 2) - if(320 to 335) - throw_alert("temp", /obj/screen/alert/hot/robot, 1) - if(300 to 320) - clear_alert("temp") - if(260 to 300) - throw_alert("temp", /obj/screen/alert/cold/robot, 1) - else - throw_alert("temp", /obj/screen/alert/cold/robot, 2) - -/mob/living/silicon/robot/proc/update_cell() - if(cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - clear_alert("charge") - if(0.5 to 0.75) - throw_alert("charge", /obj/screen/alert/lowcell, 1) - if(0.25 to 0.5) - throw_alert("charge", /obj/screen/alert/lowcell, 2) - if(0.01 to 0.25) - throw_alert("charge", /obj/screen/alert/lowcell, 3) - else - throw_alert("charge", /obj/screen/alert/emptycell) - else - throw_alert("charge", /obj/screen/alert/nocell) - - - -/mob/living/silicon/robot/proc/update_items() - if(client) - for(var/obj/I in get_all_slots()) - client.screen |= I - if(module_state_1) - module_state_1:screen_loc = ui_inv1 - if(module_state_2) - module_state_2:screen_loc = ui_inv2 - if(module_state_3) - module_state_3:screen_loc = ui_inv3 - update_icons() - -/mob/living/silicon/robot/proc/process_locks() - if(weapon_lock) - uneq_all() - weaponlock_time -- - if(weaponlock_time <= 0) - if(src.client) - to_chat(src, "Weapon Lock Timed Out!") - weapon_lock = 0 - weaponlock_time = 120 - -/mob/living/silicon/robot/update_canmove(delay_action_updates = 0) - if(paralysis || stunned || IsWeakened() || buckled || lockcharge || stat) - canmove = 0 - else - canmove = 1 - update_transform() - if(!delay_action_updates) - update_action_buttons_icon() - return canmove - -//Robots on fire -/mob/living/silicon/robot/handle_fire() - if(..()) - return - if(fire_stacks > 0) - fire_stacks-- - fire_stacks = max(0, fire_stacks) - else - ExtinguishMob() - - //adjustFireLoss(3) - return - -/mob/living/silicon/robot/update_fire() - overlays -= image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") - if(on_fire) - overlays += image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") - -/mob/living/silicon/robot/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them - IgniteMob() - -//Robots on fire +/mob/living/silicon/robot/Life(seconds, times_fired) + set invisibility = 0 + set background = BACKGROUND_ENABLED + + if(src.notransform) + return + + //Status updates, death etc. + clamp_values() + + if(..()) + handle_robot_cell() + process_locks() + process_queued_alarms() + +/mob/living/silicon/robot/proc/clamp_values() + SetStunned(min(stunned, 30)) + SetParalysis(min(paralysis, 30)) + SetWeakened(min(weakened, 20)) + SetSleeping(0) + +/mob/living/silicon/robot/proc/handle_robot_cell() + if(stat != DEAD) + if(!is_component_functioning("power cell")) + uneq_all() + low_power_mode = 1 + update_headlamp() + diag_hud_set_borgcell() + return + if(low_power_mode) + if(is_component_functioning("power cell") && cell.charge) + low_power_mode = 0 + update_headlamp() + else if(stat == CONSCIOUS) + use_power() + +/mob/living/silicon/robot/proc/use_power() + // this check is safe because `cell` is guaranteed to be set when the power cell is functioning + if(is_component_functioning("power cell") && cell.charge) + if(cell.charge <= 100) + uneq_all() + var/amt = Clamp((lamp_intensity - 2) * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell. + cell.use(amt) //Usage table: 1/tick if off/lowest setting, 4 = 4/tick, 6 = 8/tick, 8 = 12/tick, 10 = 16/tick + else + uneq_all() + low_power_mode = 1 + update_headlamp() + diag_hud_set_borgcell() + +/mob/living/silicon/robot/handle_regular_status_updates() + + . = ..() + + if(camera && !scrambledcodes) + if(stat == DEAD || wires.IsCameraCut()) + camera.status = 0 + else + camera.status = 1 + + if(sleeping) + AdjustSleeping(-1) + + if(.) //alive + if(!istype(src, /mob/living/silicon/robot/drone)) + if(health < 50) //Gradual break down of modules as more damage is sustained + if(uneq_module(module_state_3)) + to_chat(src, "SYSTEM ERROR: Module 3 OFFLINE.") + + if(health < 0) + if(uneq_module(module_state_2)) + to_chat(src, "SYSTEM ERROR: Module 2 OFFLINE.") + + if(health < -50) + if(uneq_module(module_state_1)) + to_chat(src, "CRITICAL ERROR: All modules OFFLINE.") + + diag_hud_set_health() + diag_hud_set_status() + + //update the state of modules and components here + if(stat != CONSCIOUS) + uneq_all() + + if(!is_component_functioning("radio") || stat == UNCONSCIOUS) + radio.on = 0 + else + radio.on = 1 + + return 1 + +/mob/living/silicon/robot/handle_hud_icons() + update_items() + update_cell() + if(emagged) + throw_alert("hacked", /obj/screen/alert/hacked) + else + clear_alert("hacked") + ..() + +/mob/living/silicon/robot/handle_hud_icons_health() + if(healths) + if(stat != DEAD) + if(health >= maxHealth) + healths.icon_state = "health0" + else if(health > maxHealth * 0.5) + healths.icon_state = "health2" + else if(health > 0) + healths.icon_state = "health3" + else if(health > -maxHealth * 0.5) + healths.icon_state = "health4" + else if(health > -maxHealth) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + switch(bodytemperature) //310.055 optimal body temp + if(335 to INFINITY) + throw_alert("temp", /obj/screen/alert/hot/robot, 2) + if(320 to 335) + throw_alert("temp", /obj/screen/alert/hot/robot, 1) + if(300 to 320) + clear_alert("temp") + if(260 to 300) + throw_alert("temp", /obj/screen/alert/cold/robot, 1) + else + throw_alert("temp", /obj/screen/alert/cold/robot, 2) + +/mob/living/silicon/robot/proc/update_cell() + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + clear_alert("charge") + if(0.5 to 0.75) + throw_alert("charge", /obj/screen/alert/lowcell, 1) + if(0.25 to 0.5) + throw_alert("charge", /obj/screen/alert/lowcell, 2) + if(0.01 to 0.25) + throw_alert("charge", /obj/screen/alert/lowcell, 3) + else + throw_alert("charge", /obj/screen/alert/emptycell) + else + throw_alert("charge", /obj/screen/alert/nocell) + + + +/mob/living/silicon/robot/proc/update_items() + if(client) + for(var/obj/I in get_all_slots()) + client.screen |= I + if(module_state_1) + module_state_1:screen_loc = ui_inv1 + if(module_state_2) + module_state_2:screen_loc = ui_inv2 + if(module_state_3) + module_state_3:screen_loc = ui_inv3 + update_icons() + +/mob/living/silicon/robot/proc/process_locks() + if(weapon_lock) + uneq_all() + weaponlock_time -- + if(weaponlock_time <= 0) + if(src.client) + to_chat(src, "Weapon Lock Timed Out!") + weapon_lock = 0 + weaponlock_time = 120 + +/mob/living/silicon/robot/update_canmove(delay_action_updates = 0) + if(paralysis || stunned || IsWeakened() || buckled || lockcharge || stat) + canmove = 0 + else + canmove = 1 + update_transform() + if(!delay_action_updates) + update_action_buttons_icon() + return canmove + +//Robots on fire +/mob/living/silicon/robot/handle_fire() + if(..()) + return + if(fire_stacks > 0) + fire_stacks-- + fire_stacks = max(0, fire_stacks) + else + ExtinguishMob() + + //adjustFireLoss(3) + return + +/mob/living/silicon/robot/update_fire() + overlays -= image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") + if(on_fire) + overlays += image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") + +/mob/living/silicon/robot/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them + IgniteMob() + +//Robots on fire diff --git a/code/modules/mob/living/silicon/robot/login.dm b/code/modules/mob/living/silicon/robot/login.dm index cf55cdd5f91..0a3225011d8 100644 --- a/code/modules/mob/living/silicon/robot/login.dm +++ b/code/modules/mob/living/silicon/robot/login.dm @@ -1,4 +1,4 @@ -/mob/living/silicon/robot/Login() - ..() - regenerate_icons() - show_laws(0) +/mob/living/silicon/robot/Login() + ..() + regenerate_icons() + show_laws(0) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 6c399a7161f..cf3067b934f 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1,1474 +1,1483 @@ -var/list/robot_verbs_default = list( - /mob/living/silicon/robot/proc/sensor_mode, -) - -/mob/living/silicon/robot - name = "Cyborg" - real_name = "Cyborg" - icon = 'icons/mob/robots.dmi' - icon_state = "robot" - maxHealth = 100 - health = 100 - universal_understand = 1 - deathgasp_on_death = TRUE - - var/sight_mode = 0 - var/custom_name = "" - var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best - -//Hud stuff - - var/obj/screen/inv1 = null - var/obj/screen/inv2 = null - var/obj/screen/inv3 = null - var/obj/screen/lamp_button = null - var/obj/screen/thruster_button = null - - var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not - var/obj/screen/robot_modules_background - -//3 Modules can be activated at any one time. - var/obj/item/robot_module/module = null - var/module_active = null - var/module_state_1 = null - var/module_state_2 = null - var/module_state_3 = null - - var/obj/item/radio/borg/radio = null - var/mob/living/silicon/ai/connected_ai = null - var/obj/item/stock_parts/cell/cell = null - var/obj/machinery/camera/camera = null - - // Components are basically robot organs. - var/list/components = list() - - var/obj/item/robot_parts/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of.. - var/obj/item/mmi/mmi = null - - var/obj/item/pda/silicon/robot/rbPDA = null - - var/datum/wires/robot/wires = null - - var/opened = 0 - var/custom_panel = null - var/list/custom_panel_names = list("Cricket") - var/list/custom_eye_names = list("Cricket","Standard") - var/emagged = 0 - var/is_emaggable = TRUE - var/eye_protection = 0 - var/ear_protection = 0 - - var/list/force_modules = list() - var/allow_rename = TRUE - var/weapons_unlock = FALSE - var/static_radio_channels = FALSE - - var/wiresexposed = 0 - var/locked = 1 - var/list/req_one_access = list(access_robotics) - var/list/req_access - var/ident = 0 - //var/list/laws = list() - var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) - var/viewalerts = 0 - var/modtype = "Default" - var/lower_mod = 0 - var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N - var/jeton = 0 - var/low_power_mode = 0 //whether the robot has no charge left. - var/weapon_lock = 0 - var/weaponlock_time = 120 - var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default - var/lockcharge //Used when locking down a borg to preserve cell charge - var/speed = 0 //Cause sec borgs gotta go fast //No they dont! - var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. - var/pdahide = 0 //Used to hide the borg from the messenger list - var/tracking_entities = 0 //The number of known entities currently accessing the internal camera - var/braintype = "Cyborg" - var/base_icon = "" - var/crisis = 0 - - var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. - var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. - var/lamp_recharging = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. - - var/updating = 0 //portable camera camerachunk update - - hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) - - var/magpulse = 0 - var/ionpulse = 0 // Jetpack-like effect. - var/ionpulse_on = 0 // Jetpack-like effect. - var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect. - - var/datum/action/item_action/toggle_research_scanner/scanner = null - var/list/module_actions = list() - -/mob/living/silicon/robot/get_cell() - return cell - -/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0) - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - add_language("Robot Talk", 1) - - wires = new(src) - - robot_modules_background = new() - robot_modules_background.icon_state = "block" - robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer 20, UI should be just below it. - robot_modules_background.plane = HUD_PLANE - - ident = rand(1, 999) - rename_character(null, get_default_name()) - update_icons() - update_headlamp() - - radio = new /obj/item/radio/borg(src) - common_radio = radio - - init() - - if(!scrambledcodes && !camera) - camera = new /obj/machinery/camera(src) - camera.c_tag = real_name - camera.network = list("SS13","Robots") - if(wires.IsCameraCut()) // 5 = BORG CAMERA - camera.status = 0 - - if(mmi == null) - mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works) - mmi.icon_state = "boris" - - if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly - cell = new /obj/item/stock_parts/cell/high(src) - - initialize_components() - //if(!unfinished) - // Create all the robot parts. - for(var/V in components) if(V != "power cell") - var/datum/robot_component/C = components[V] - C.installed = 1 - C.wrapped = new C.external_type - - ..() - - add_robot_verbs() - - if(cell) - var/datum/robot_component/cell_component = components["power cell"] - cell_component.wrapped = cell - cell_component.installed = 1 - cell_component.install() - - diag_hud_set_borgcell() - scanner = new(src) - scanner.Grant(src) - -/mob/living/silicon/robot/proc/init(var/alien=0) - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - make_laws() - additional_law_channels["Binary"] = ":b " - var/new_ai = select_active_ai_with_fewest_borgs() - if(new_ai) - lawupdate = 1 - connect_to_ai(new_ai) - else - lawupdate = 0 - - playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) - -/mob/living/silicon/robot/rename_character(oldname, newname) - if(!..(oldname, newname)) - return 0 - - if(oldname != real_name) - notify_ai(3, oldname, newname) - custom_name = (newname != get_default_name()) ? newname : null - setup_PDA() - - //We also need to update name of internal camera. - if(camera) - camera.c_tag = newname - - //Check for custom sprite - if(!custom_sprite) - var/file = file2text("config/custom_sprites.txt") - var/lines = splittext(file, "\n") - - for(var/line in lines) - // split & clean up - var/list/Entry = splittext(line, ":") - for(var/i = 1 to Entry.len) - Entry[i] = trim(Entry[i]) - - if(Entry.len < 2 || Entry[1] != "cyborg") //ignore incorrectly formatted entries or entries that aren't marked for cyborg - continue - - if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required - custom_sprite = 1 - - return 1 - - -/mob/living/silicon/robot/proc/get_default_name(var/prefix as text) - if(prefix) - modtype = prefix - if(mmi) - if(istype(mmi, /obj/item/mmi/robotic_brain)) - braintype = "Android" - else - braintype = "Cyborg" - else - braintype = "Robot" - - if(custom_name) - return custom_name - else - return "[modtype] [braintype]-[num2text(ident)]" - -/mob/living/silicon/robot/verb/Namepick() - set category = "Robot Commands" - if(custom_name) - return 0 - if(!allow_rename) - to_chat(src, "Rename functionality is not enabled on this unit."); - return 0 - rename_self(braintype, 1) - -/mob/living/silicon/robot/proc/sync() - if(lawupdate && connected_ai) - lawsync() - photosync() - -// setup the PDA and its name -/mob/living/silicon/robot/proc/setup_PDA() - if(!rbPDA) - rbPDA = new(src) - rbPDA.set_name_and_job(real_name, braintype) - var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger) - if(M) - if(scrambledcodes) - M.hidden = 1 - if(pdahide) - M.toff = 1 - -/mob/living/silicon/robot/binarycheck() - if(is_component_functioning("comms")) - return 1 - return 0 - -//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO -//Improved /N -/mob/living/silicon/robot/Destroy() - if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. - var/turf/T = get_turf(loc)//To hopefully prevent run time errors. - if(T) mmi.loc = T - if(mmi.brainmob) - mind.transfer_to(mmi.brainmob) - mmi.update_icon() - else - to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") - ghostize() - error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") - mmi = null - if(connected_ai) - connected_ai.connected_robots -= src - QDEL_NULL(wires) - QDEL_NULL(module) - QDEL_NULL(camera) - QDEL_NULL(cell) - QDEL_NULL(robot_suit) - QDEL_NULL(spark_system) - return ..() - -/mob/living/silicon/robot/proc/pick_module() - if(module) - return - var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") - if(islist(force_modules) && force_modules.len) - modules = force_modules.Copy() - if(security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis) - to_chat(src, "Crisis mode active. The combat module is now available.") - modules += "Combat" - if(mmi != null && mmi.alien) - modules = list("Hunter") - modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules - if(!modtype) - return - designation = modtype - var/module_sprites[0] //Used to store the associations between sprite names and sprite index. - - if(module) - return - - switch(modtype) - if("Standard") - module = new /obj/item/robot_module/standard(src) - module.channels = list("Service" = 1) - module_sprites["Basic"] = "robot_old" - module_sprites["Android"] = "droid" - module_sprites["Default"] = "Standard" - module_sprites["Noble-STD"] = "Noble-STD" - - if("Service") - module = new /obj/item/robot_module/butler(src) - module.channels = list("Service" = 1) - module_sprites["Waitress"] = "Service" - module_sprites["Kent"] = "toiletbot" - module_sprites["Bro"] = "Brobot" - module_sprites["Rich"] = "maximillion" - module_sprites["Default"] = "Service2" - module_sprites["Standard"] = "Standard-Serv" - module_sprites["Noble-SRV"] = "Noble-SRV" - module_sprites["Cricket"] = "Cricket-SERV" - - if("Miner") - module = new /obj/item/robot_module/miner(src) - module.channels = list("Supply" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Mining Outpost") - module_sprites["Basic"] = "Miner_old" - module_sprites["Advanced Droid"] = "droid-miner" - module_sprites["Treadhead"] = "Miner" - module_sprites["Standard"] = "Standard-Mine" - module_sprites["Noble-DIG"] = "Noble-DIG" - module_sprites["Cricket"] = "Cricket-MINE" - - if("Medical") - module = new /obj/item/robot_module/medical(src) - module.channels = list("Medical" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Medical") - module_sprites["Basic"] = "Medbot" - module_sprites["Surgeon"] = "surgeon" - module_sprites["Advanced Droid"] = "droid-medical" - module_sprites["Needles"] = "medicalrobot" - module_sprites["Standard"] = "Standard-Medi" - module_sprites["Noble-MED"] = "Noble-MED" - module_sprites["Cricket"] = "Cricket-MEDI" - status_flags &= ~CANPUSH - - if("Security") - module = new /obj/item/robot_module/security(src) - module.channels = list("Security" = 1) - module_sprites["Basic"] = "secborg" - module_sprites["Red Knight"] = "Security" - module_sprites["Black Knight"] = "securityrobot" - module_sprites["Bloodhound"] = "bloodhound" - module_sprites["Standard"] = "Standard-Secy" - module_sprites["Noble-SEC"] = "Noble-SEC" - module_sprites["Cricket"] = "Cricket-SEC" - status_flags &= ~CANPUSH - - if("Engineering") - module = new /obj/item/robot_module/engineering(src) - module.channels = list("Engineering" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Engineering") - module_sprites["Basic"] = "Engineering" - module_sprites["Antique"] = "engineerrobot" - module_sprites["Landmate"] = "landmate" - module_sprites["Standard"] = "Standard-Engi" - module_sprites["Noble-ENG"] = "Noble-ENG" - module_sprites["Cricket"] = "Cricket-ENGI" - magpulse = 1 - - if("Janitor") - module = new /obj/item/robot_module/janitor(src) - module.channels = list("Service" = 1) - module_sprites["Basic"] = "JanBot2" - module_sprites["Mopbot"] = "janitorrobot" - module_sprites["Mop Gear Rex"] = "mopgearrex" - module_sprites["Standard"] = "Standard-Jani" - module_sprites["Noble-CLN"] = "Noble-CLN" - module_sprites["Cricket"] = "Cricket-JANI" - - if("Combat") - module = new /obj/item/robot_module/combat(src) - module.channels = list("Security" = 1) - icon_state = "droidcombat" - - if("Hunter") - module = new /obj/item/robot_module/alien/hunter(src) - icon_state = "xenoborg-state-a" - modtype = "Xeno-Hu" - feedback_inc("xeborg_hunter",1) - - - //languages - module.add_languages(src) - //subsystems - module.add_subsystems_and_actions(src) - - //Custom_sprite check and entry - if(custom_sprite && check_sprite("[ckey]-[modtype]")) - module_sprites["Custom"] = "[src.ckey]-[modtype]" - - hands.icon_state = lowertext(module.module_type) - feedback_inc("cyborg_[lowertext(modtype)]",1) - rename_character(real_name, get_default_name()) - - if(modtype == "Medical" || modtype == "Security" || modtype == "Combat") - status_flags &= ~CANPUSH - - choose_icon(6,module_sprites) - if(!static_radio_channels) - radio.config(module.channels) - notify_ai(2) - -/mob/living/silicon/robot/proc/reset_module() - notify_ai(2) - - uneq_all() - sight_mode = null - hands.icon_state = "nomod" - icon_state = "robot" - module.remove_subsystems_and_actions(src) - QDEL_NULL(module) - - camera.network.Remove(list("Engineering", "Medical", "Mining Outpost")) - rename_character(real_name, get_default_name("Default")) - languages = list() - speech_synthesizer_langs = list() - - update_icons() - update_headlamp() - - speed = 0 // Remove upgrades. - ionpulse = FALSE - magpulse = FALSE - add_language("Robot Talk", 1) - - status_flags |= CANPUSH - -//for borg hotkeys, here module refers to borg inv slot, not core module -/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) - set name = "Toggle Module" - set hidden = 1 - toggle_module(module) - -/mob/living/silicon/robot/verb/cmd_unequip_module() - set name = "Unequip Module" - set hidden = 1 - uneq_active() - -// this verb lets cyborgs see the stations manifest -/mob/living/silicon/robot/verb/cmd_station_manifest() - set category = "Robot Commands" - set name = "Show Station Manifest" - show_station_manifest() - -/mob/living/silicon/robot/proc/self_diagnosis() - if(!is_component_functioning("diagnosis unit")) - return null - - var/dat = "[src.name] Self-Diagnosis Report\n" - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 0) - dat += "[C.name]
    MISSING
    " - else - dat += "[C.name][C.installed == -1 ? "
    DESTROYED" : ""]
    Brute Damage:[C.brute_damage]
    Electronics Damage:[C.electronics_damage]
    Powered:[C.is_powered() ? "Yes" : "No"]
    Toggled:[ C.toggled ? "Yes" : "No"]

    " - return dat - -/mob/living/silicon/robot/verb/self_diagnosis_verb() - set category = "Robot Commands" - set name = "Self Diagnosis" - - if(!is_component_functioning("diagnosis unit")) - to_chat(src, "Your self-diagnosis component isn't functioning.") - - var/dat = self_diagnosis() - src << browse(dat, "window=robotdiagnosis") - - -/mob/living/silicon/robot/verb/toggle_component() - set category = "Robot Commands" - set name = "Toggle Component" - set desc = "Toggle a component, conserving power." - - var/list/installed_components = list() - for(var/V in components) - if(V == "power cell") continue - var/datum/robot_component/C = components[V] - if(C.installed) - installed_components += V - - var/toggle = input(src, "Which component do you want to toggle?", "Toggle Component") as null|anything in installed_components - if(!toggle) - return - - var/datum/robot_component/C = components[toggle] - C.toggle() - to_chat(src, "You [C.toggled ? "enable" : "disable"] [C.name].") - -/mob/living/silicon/robot/proc/sensor_mode() - set name = "Set Sensor Augmentation" - set desc = "Augment visual feed with internal sensor overlays." - set category = "Robot Commands" - toggle_sensor_mode() - -/mob/living/silicon/robot/proc/add_robot_verbs() - src.verbs |= robot_verbs_default - src.verbs |= silicon_subsystems - -/mob/living/silicon/robot/proc/remove_robot_verbs() - src.verbs -= robot_verbs_default - src.verbs -= silicon_subsystems - -/mob/living/silicon/robot/proc/ionpulse() - if(!ionpulse_on) - return - - if(cell.charge <= 50) - toggle_ionpulse() - return - - cell.charge -= 25 // 500 steps on a default cell. - return 1 - -/mob/living/silicon/robot/proc/toggle_ionpulse() - if(!ionpulse) - to_chat(src, "No thrusters are installed!") - return - - if(!ion_trail) - ion_trail = new - ion_trail.set_up(src) - - ionpulse_on = !ionpulse_on - to_chat(src, "You [ionpulse_on ? null :"de"]activate your ion thrusters.") - if(ionpulse_on) - ion_trail.start() - else - ion_trail.stop() - if(thruster_button) - thruster_button.icon_state = "ionpulse[ionpulse_on]" - -/mob/living/silicon/robot/blob_act(obj/structure/blob/B) - if(stat != DEAD) - adjustBruteLoss(30) - else - gib() - return TRUE - -// this function displays the cyborgs current cell charge in the stat panel -/mob/living/silicon/robot/proc/show_cell_power() - if(cell) - stat(null, text("Charge Left: [cell.charge]/[cell.maxcharge]")) - else - stat(null, text("No Cell Inserted!")) - - -// update the status screen display -/mob/living/silicon/robot/Stat() - ..() - statpanel("Status") - if(client.statpanel == "Status") - show_cell_power() - var/total_user_contents = GetAllContents() - if(locate(/obj/item/gps/cyborg) in total_user_contents) - var/turf/T = get_turf(src) - stat(null, "GPS: [COORD(T)]") - -/mob/living/silicon/robot/restrained() - return 0 - -/mob/living/silicon/robot/InCritical() - return low_power_mode - -/mob/living/silicon/robot/ex_act(severity) - switch(severity) - if(1.0) - gib() - return - if(2.0) - if(stat != 2) - adjustBruteLoss(60) - adjustFireLoss(60) - if(3.0) - if(stat != 2) - adjustBruteLoss(30) - return - - -/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) - if(prob(75) && Proj.damage > 0) spark_system.start() - return 2 - - -/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) - // Check if the user is trying to insert another component like a radio, actuator, armor etc. - if(istype(W, /obj/item/robot_parts/robot_component) && opened) - for(var/V in components) - var/datum/robot_component/C = components[V] - if(!C.installed && istype(W, C.external_type)) - C.installed = 1 - C.wrapped = W - C.install() - user.drop_item() - W.loc = null - - var/obj/item/robot_parts/robot_component/WC = W - if(istype(WC)) - C.brute_damage = WC.brute - C.electronics_damage = WC.burn - - to_chat(usr, "You install the [W.name].") - - return - - if(istype(W, /obj/item/stack/cable_coil) && user.a_intent == INTENT_HELP && (wiresexposed || istype(src, /mob/living/silicon/robot/drone))) - user.changeNext_move(CLICK_CD_MELEE) - if(!getFireLoss()) - to_chat(user, "Nothing to fix!") - return - else if(!getFireLoss(TRUE)) - to_chat(user, "The damaged components are beyond saving!") - return - var/obj/item/stack/cable_coil/coil = W - adjustFireLoss(-30) - updatehealth() - add_fingerprint(user) - coil.use(1) - user.visible_message("\The [user] fixes some of the burnt wires on \the [src] with \the [coil].") - - else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside - var/datum/robot_component/cell/C = components["power cell"] - if(wiresexposed) - to_chat(user, "Close the panel first.") - else if(cell) - to_chat(user, "There is a power cell already installed.") - else - user.drop_item() - W.loc = src - cell = W - to_chat(user, "You insert the power cell.") - - C.installed = 1 - C.wrapped = W - C.install() - C.external_type = W.type // Update the cell component's `external_type` to the path of new cell - //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z - C.brute_damage = 0 - C.electronics_damage = 0 - diag_hud_set_borgcell() - - else if(istype(W, /obj/item/encryptionkey/) && opened) - if(radio)//sanityyyyyy - radio.attackby(W,user)//GTFO, you have your own procs - else - to_chat(user, "Unable to locate a radio.") - - else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card - if(emagged)//still allow them to open the cover - to_chat(user, "The interface seems slightly damaged.") - if(opened) - to_chat(user, "You must close the cover to swipe an ID card.") - else - if(allowed(W)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") - update_icons() - else - to_chat(user, "Access denied.") - - else if(istype(W, /obj/item/borg/upgrade/)) - var/obj/item/borg/upgrade/U = W - if(!opened) - to_chat(user, "You must access the borg's internals!") - else if(!src.module && U.require_module) - to_chat(user, "The borg must choose a module before it can be upgraded!") - else if(U.locked) - to_chat(user, "The upgrade is locked and cannot be used yet!") - else - if(!user.drop_item()) - return - if(U.action(src)) - to_chat(user, "You apply the upgrade to [src].") - U.forceMove(src) - else - to_chat(user, "Upgrade error.") - - else if(istype(W, /obj/item/mmi_radio_upgrade)) - if(!opened) - to_chat(user, "You must access the borg's internals!") - return - else if(!mmi) - to_chat(user, "This cyborg does not have an MMI to augment!") - return - else if(mmi.radio) - to_chat(user, "A radio upgrade is already installed in the MMI!") - return - else if(user.drop_item()) - to_chat(user, "You apply the upgrade to [src].") - to_chat(src, "MMI radio capability installed.") - mmi.install_radio() - qdel(W) - else - return ..() - -/mob/living/silicon/robot/wirecutter_act(mob/user, obj/item/I) - if(!opened) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(wiresexposed) - wires.Interact(user) - -/mob/living/silicon/robot/multitool_act(mob/user, obj/item/I) - if(!opened) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(wiresexposed) - wires.Interact(user) - -/mob/living/silicon/robot/screwdriver_act(mob/user, obj/item/I) - if(!opened) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(!cell) // haxing - wiresexposed = !wiresexposed - to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") - update_icons() - I.play_tool_sound(user, I.tool_volume) - else //radio check - if(radio) - radio.screwdriver_act(user, I)//Push it to the radio to let it handle everything - else - to_chat(user, "Unable to locate a radio.") - update_icons() - -/mob/living/silicon/robot/crowbar_act(mob/user, obj/item/I) - if(user.a_intent != INTENT_HELP) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(!opened) - if(locked) - to_chat(user, "The cover is locked and cannot be opened.") - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - to_chat(user, "You open the cover.") - opened = TRUE - update_icons() - return - else if(cell) - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - to_chat(user, "You close the cover.") - opened = FALSE - update_icons() - return - else if(wiresexposed && wires.IsAllCut()) - //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. - if(!mmi) - to_chat(user, "[src] has no brain to remove.") - return - to_chat(user, "You jam the crowbar into the robot and begin levering the securing bolts...") - if(I.use_tool(src, user, 30, volume = I.tool_volume)) - user.visible_message("[user] deconstructs [src]!", "You unfasten the securing bolts, and [src] falls to pieces!") - deconstruct() - return - // Okay we're not removing the cell or an MMI, but maybe something else? - var/list/removable_components = list() - for(var/V in components) - if(V == "power cell") - continue - var/datum/robot_component/C = components[V] - if(C.installed == 1 || C.installed == -1) - removable_components += V - if(module) - removable_components += module.custom_removals - var/remove = input(user, "Which component do you want to pry out?", "Remove Component") as null|anything in removable_components - if(!remove) - return - if(module && module.handle_custom_removal(remove, user, I)) - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/datum/robot_component/C = components[remove] - var/obj/item/robot_parts/robot_component/thing = C.wrapped - to_chat(user, "You remove \the [thing].") - if(istype(thing)) - thing.brute = C.brute_damage - thing.burn = C.electronics_damage - - thing.loc = loc - var/was_installed = C.installed - C.installed = 0 - if(was_installed == 1) - C.uninstall() - - - - -/mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) - if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. - spark_system.start() - ..() - -/mob/living/silicon/robot/emag_act(user as mob) - if(!ishuman(user) && !issilicon(user)) - return - var/mob/living/M = user - if(!opened)//Cover is closed - if(!is_emaggable) - to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.") - else if(locked) - to_chat(user, "You emag the cover lock.") - locked = 0 - else - to_chat(user, "The cover is already unlocked.") - return - - if(opened)//Cover is open - if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice - if(wiresexposed) - to_chat(user, "You must close the panel first") - return - else - sleep(6) - emagged = 1 - SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown - if(src.hud_used) - src.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. - disconnect_from_ai() - to_chat(user, "You emag [src]'s interface.") -// message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") - log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") - clear_supplied_laws() - clear_inherent_laws() - laws = new /datum/ai_laws/syndicate_override - var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [M.name]([M.key]) emagged [name]([key])") - set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.") - to_chat(src, "ALERT: Foreign software detected.") - sleep(5) - to_chat(src, "Initiating diagnostics...") - sleep(20) - to_chat(src, "SynBorg v1.7 loaded.") - sleep(5) - to_chat(src, "LAW SYNCHRONISATION ERROR") - sleep(5) - to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") - sleep(10) - to_chat(src, "> N") - sleep(20) - to_chat(src, "ERRORERRORERROR") - to_chat(src, "Obey these laws:") - laws.show_laws(src) - to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") - SetLockdown(0) - if(src.module && istype(src.module, /obj/item/robot_module/miner)) - for(var/obj/item/pickaxe/drill/cyborg/D in src.module.modules) - qdel(D) - src.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(src.module) - src.module.rebuild() - if(src.module && istype(src.module, /obj/item/robot_module/medical)) - for(var/obj/item/borg_defib/F in src.module.modules) - F.safety = 0 - if(module) - module.module_type = "Malf" // For the cool factor - update_module_icon() - update_icons() - return - -/mob/living/silicon/robot/verb/unlock_own_cover() - set category = "Robot Commands" - set name = "Unlock Cover" - set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you." - if(locked) - switch(alert("You can not lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No")) - if("Yes") - locked = 0 - update_icons() - to_chat(usr, "You unlock your cover.") - -/mob/living/silicon/robot/attack_ghost(mob/user) - if(wiresexposed) - wires.Interact(user) - else - ..() //this calls the /mob/living/attack_ghost proc for the ghost health/cyborg analyzer - -/mob/living/silicon/robot/proc/allowed(obj/item/I) - var/obj/dummy = new /obj(null) // Create a dummy object to check access on as to avoid having to snowflake check_access on every mob - dummy.req_access = req_access - dummy.req_one_access = req_one_access - - if(dummy.check_access(I)) - qdel(dummy) - return 1 - - qdel(dummy) - return 0 - -/mob/living/silicon/robot/update_icons() - - overlays.Cut() - if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned. - if(custom_panel in custom_eye_names) - overlays += "eyes-[custom_panel]" - else - overlays += "eyes-[icon_state]" - else - overlays -= "eyes" - - if(opened) - var/panelprefix = "ov" - if(custom_sprite) //Custom borgs also have custom panels, heh - panelprefix = "[ckey]" - - if(custom_panel in custom_panel_names) //For default borgs with different panels - panelprefix = custom_panel - - if(wiresexposed) - overlays += "[panelprefix]-openpanel +w" - else if(cell) - overlays += "[panelprefix]-openpanel +c" - else - overlays += "[panelprefix]-openpanel -c" - - var/combat = list("Combat") - if(modtype in combat) - if(base_icon == "") - base_icon = icon_state - if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) - icon_state = "[base_icon]-roll" - else - icon_state = base_icon - if(module) - for(var/obj/item/borg/combat/shield/S in module.modules) - if(activated(S)) - overlays += "[base_icon]-shield" - update_fire() - -/mob/living/silicon/robot/proc/installed_modules() - if(weapon_lock) - to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") - return - - if(!module) - pick_module() - return - var/dat = {"Close -
    -
    - Activated Modules -
    - - - - -
    Module 1:[module_state_1 ? "[module_state_1]" : "No Module"]
    Module 2:[module_state_2 ? "[module_state_2]" : "No Module"]
    Module 3:[module_state_3 ? "[module_state_3]" : "No Module"]

    - Installed Modules

    - - "} - for(var/obj in module.modules) - if(!obj) - dat += text("") - else if(activated(obj)) - dat += text("") - else - dat += text("") - if(emagged || weapons_unlock) - if(activated(module.emag)) - dat += text("") - else - dat += text("") - dat += "
    Resource depleted
    [obj]Activated
    [obj]Activate
    [module.emag]Activated
    [module.emag]Activate
    " -/* - if(activated(obj)) - dat += text("[obj]: \[Activated | Deactivate\]
    ") - else - dat += text("[obj]: \[Activate | Deactivated\]
    ") -*/ - var/datum/browser/popup = new(src, "robotmod", "Modules") - popup.set_content(dat) - popup.open() - - -/mob/living/silicon/robot/Topic(href, href_list) - if(..()) - return 1 - - if(usr != src) - return 1 - - if(href_list["mach_close"]) - var/t1 = text("window=[href_list["mach_close"]]") - unset_machine() - src << browse(null, t1) - return 1 - - if(href_list["showalerts"]) - subsystem_alarm_monitor() - return 1 - - if(href_list["mod"]) - var/obj/item/O = locate(href_list["mod"]) - if(istype(O) && (O.loc == src)) - O.attack_self(src) - return 1 - - if(href_list["act"]) - var/obj/item/O = locate(href_list["act"]) - if(!istype(O) || !(O.loc == src || O.loc == src.module)) - return 1 - - activate_module(O) - installed_modules() - - if(href_list["deact"]) - var/obj/item/O = locate(href_list["deact"]) - if(activated(O)) - if(module_state_1 == O) - module_state_1 = null - contents -= O - else if(module_state_2 == O) - module_state_2 = null - contents -= O - else if(module_state_3 == O) - module_state_3 = null - contents -= O - else - to_chat(src, "Module isn't activated.") - else - to_chat(src, "Module isn't activated") - installed_modules() - return 1 - - return 1 - -/mob/living/silicon/robot/proc/radio_menu() - radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code - -/mob/living/silicon/robot/proc/control_headlamp() - if(stat || lamp_recharging || low_power_mode) - to_chat(src, "This function is currently offline.") - return - -//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. - lamp_intensity = (lamp_intensity+2) % (lamp_max+2) - to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]") - update_headlamp() - -/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100) - set_light(0) - - if(lamp_intensity && (turn_off || stat || low_power_mode)) - to_chat(src, "Your headlamp has been deactivated.") - lamp_intensity = 0 - lamp_recharging = 1 - spawn(cooldown) //10 seconds by default, if the source of the deactivation does not keep stat that long. - lamp_recharging = 0 - else - set_light(light_range + lamp_intensity) - - if(lamp_button) - lamp_button.icon_state = "lamp[lamp_intensity]" - - update_icons() - -/mob/living/silicon/robot/proc/deconstruct() - var/turf/T = get_turf(src) - if(robot_suit) - robot_suit.forceMove(T) - robot_suit.l_leg.forceMove(T) - robot_suit.l_leg = null - robot_suit.r_leg.forceMove(T) - robot_suit.r_leg = null - new /obj/item/stack/cable_coil(T, robot_suit.chest.wired) - robot_suit.chest.forceMove(T) - robot_suit.chest.wired = FALSE - robot_suit.chest = null - robot_suit.l_arm.forceMove(T) - robot_suit.l_arm = null - robot_suit.r_arm.forceMove(T) - robot_suit.r_arm = null - robot_suit.head.forceMove(T) - robot_suit.head.flash1.forceMove(T) - robot_suit.head.flash1.burn_out() - robot_suit.head.flash1 = null - robot_suit.head.flash2.forceMove(T) - robot_suit.head.flash2.burn_out() - robot_suit.head.flash2 = null - robot_suit.head = null - robot_suit.updateicon() - else - new /obj/item/robot_parts/robot_suit(T) - new /obj/item/robot_parts/l_leg(T) - new /obj/item/robot_parts/r_leg(T) - new /obj/item/stack/cable_coil(T, 1) - new /obj/item/robot_parts/chest(T) - new /obj/item/robot_parts/l_arm(T) - new /obj/item/robot_parts/r_arm(T) - new /obj/item/robot_parts/head(T) - var/b - for(b=0, b!=2, b++) - var/obj/item/flash/F = new /obj/item/flash(T) - F.burn_out() - if(cell) //Sanity check. - cell.forceMove(T) - cell = null - qdel(src) - -#define BORG_CAMERA_BUFFER 30 -/mob/living/silicon/robot/Move(a, b, flag) - var/oldLoc = src.loc - . = ..() - if(.) - if(src.camera) - if(!updating) - updating = 1 - spawn(BORG_CAMERA_BUFFER) - if(oldLoc != src.loc) - cameranet.updatePortableCamera(src.camera) - updating = 0 - if(module) - if(module.type == /obj/item/robot_module/janitor) - var/turf/tile = loc - if(isturf(tile)) - var/floor_only = TRUE - for(var/A in tile) - if(istype(A, /obj/effect)) - if(is_cleanable(A)) - var/obj/effect/decal/cleanable/blood/B = A - if(istype(B) && B.off_floor) - floor_only = FALSE - else - qdel(A) - else if(istype(A, /obj/item)) - var/obj/item/cleaned_item = A - cleaned_item.clean_blood() - else if(istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/cleaned_human = A - if(cleaned_human.lying) - if(cleaned_human.head) - cleaned_human.head.clean_blood() - cleaned_human.update_inv_head(0,0) - if(cleaned_human.wear_suit) - cleaned_human.wear_suit.clean_blood() - cleaned_human.update_inv_wear_suit(0,0) - else if(cleaned_human.w_uniform) - cleaned_human.w_uniform.clean_blood() - cleaned_human.update_inv_w_uniform(0,0) - if(cleaned_human.shoes) - cleaned_human.shoes.clean_blood() - cleaned_human.update_inv_shoes(0,0) - cleaned_human.clean_blood() - to_chat(cleaned_human, "[src] cleans your face!") - if(floor_only) - tile.clean_blood() - return -#undef BORG_CAMERA_BUFFER - -/mob/living/silicon/robot/proc/self_destruct() - if(emagged) - if(mmi) - qdel(mmi) - explosion(src.loc,1,2,4,flame_range = 2) - else - explosion(src.loc,-1,0,2) - gib() - return - -/mob/living/silicon/robot/proc/UnlinkSelf() - disconnect_from_ai() - lawupdate = 0 - lockcharge = 0 - canmove = 1 - scrambledcodes = 1 - //Disconnect it's camera so it's not so easily tracked. - QDEL_NULL(src.camera) - // I'm trying to get the Cyborg to not be listed in the camera list - // Instead of being listed as "deactivated". The downside is that I'm going - // to have to check if every camera is null or not before doing anything, to prevent runtime errors. - // I could change the network to null but I don't know what would happen, and it seems too hacky for me. - -/mob/living/silicon/robot/proc/ResetSecurityCodes() - set category = "Robot Commands" - set name = "Reset Identity Codes" - set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permanently severs you from your AI and the robotics console and will deactivate your camera system." - - var/mob/living/silicon/robot/R = src - - if(R) - R.UnlinkSelf() - to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") - src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes - -/mob/living/silicon/robot/mode() - set name = "Activate Held Object" - set category = "IC" - set src = usr - - var/obj/item/W = get_active_hand() - if(W) - W.attack_self(src) - - return - -/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) - // They stay locked down if their wire is cut. - if(wires.LockedCut()) - state = 1 - if(state) - throw_alert("locked", /obj/screen/alert/locked) - else - clear_alert("locked") - lockcharge = state - update_canmove() - -/mob/living/silicon/robot/proc/choose_icon(var/triesleft, var/list/module_sprites) - - if(triesleft<1 || !module_sprites.len) - return - else - triesleft-- - - var/icontype - lockcharge = 1 //Locks borg until it select an icon to avoid secborgs running around with a standard sprite - icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chances." : "This is your last try."]", "Robot", null, null) in module_sprites - - if(icontype) - if(icontype == "Custom") - icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' - else - icon = 'icons/mob/robots.dmi' - icon_state = module_sprites[icontype] - if(icontype == "Bro") - module.module_type = "Brobot" - update_module_icon() - lockcharge = null - var/list/names = splittext(icontype, "-") - custom_panel = trim(names[1]) - else - to_chat(src, "Something is badly wrong with the sprite selection. Harass a coder.") - icon_state = module_sprites[1] - lockcharge = null - return - - update_icons() - - if(triesleft >= 1) - var/choice = input("Look at your icon - is this what you want?") in list("Yes","No") - if(choice=="No") - choose_icon(triesleft, module_sprites) - return - else - triesleft = 0 - return - else - to_chat(src, "Your icon has been set. You now require a module reset to change it.") - -/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) - if(!connected_ai) - return - switch(notifytype) - if(1) //New Cyborg - to_chat(connected_ai, "

    NOTICE - New cyborg connection detected: [name]
    ") - if(2) //New Module - to_chat(connected_ai, "

    NOTICE - Cyborg module change detected: [name] has loaded the [designation] module.
    ") - if(3) //New Name - to_chat(connected_ai, "

    NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].
    ") - -/mob/living/silicon/robot/proc/disconnect_from_ai() - if(connected_ai) - sync() // One last sync attempt - connected_ai.connected_robots -= src - connected_ai = null - -/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) - if(AI && AI != connected_ai) - disconnect_from_ai() - connected_ai = AI - connected_ai.connected_robots |= src - notify_ai(1) - sync() - -/mob/living/silicon/robot/adjustOxyLoss(var/amount) - if(suiciding) - return ..() - else - return STATUS_UPDATE_NONE - -/mob/living/silicon/robot/regenerate_icons() - ..() - update_module_icon() - -/mob/living/silicon/robot/deathsquad - base_icon = "nano_bloodhound" - icon_state = "nano_bloodhound" - designation = "SpecOps" - lawupdate = 0 - scrambledcodes = 1 - req_one_access = list(access_cent_specops) - ionpulse = 1 - magpulse = 1 - pdahide = 1 - eye_protection = 2 // Immunity to flashes and the visual part of flashbangs - ear_protection = 1 // Immunity to the audio part of flashbangs - allow_rename = FALSE - modtype = "Commando" - faction = list("nanotrasen") - is_emaggable = FALSE - -/mob/living/silicon/robot/deathsquad/New(loc) - ..() - cell = new /obj/item/stock_parts/cell/hyper(src) - -/mob/living/silicon/robot/deathsquad/init() - laws = new /datum/ai_laws/deathsquad - module = new /obj/item/robot_module/deathsquad(src) - - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - radio = new /obj/item/radio/borg/deathsquad(src) - radio.recalculateChannels() - - playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) - -/mob/living/silicon/robot/combat - base_icon = "droidcombat" - icon_state = "droidcombat" - modtype = "Combat" - designation = "Combat" - -/mob/living/silicon/robot/combat/init() - ..() - module = new /obj/item/robot_module/combat(src) - module.channels = list("Security" = 1) - //languages - module.add_languages(src) - //subsystems - module.add_subsystems_and_actions(src) - - status_flags &= ~CANPUSH - - radio.config(module.channels) - notify_ai(2) - -/mob/living/silicon/robot/ert - designation = "ERT" - lawupdate = 0 - scrambledcodes = 1 - req_one_access = list(access_cent_specops) - ionpulse = 1 - - force_modules = list("Engineering", "Medical", "Security") - static_radio_channels = 1 - allow_rename = FALSE - weapons_unlock = TRUE - - -/mob/living/silicon/robot/ert/init() - laws = new /datum/ai_laws/ert_override - radio = new /obj/item/radio/borg/ert(src) - radio.recalculateChannels() - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - -/mob/living/silicon/robot/ert/New(loc, cyborg_unlock) - ..(loc) - cell = new /obj/item/stock_parts/cell/hyper(src) - var/rnum = rand(1,1000) - var/borgname = "ERT [rnum]" - name = borgname - custom_name = borgname - real_name = name - mind = new - mind.current = src - mind.original = src - mind.assigned_role = SPECIAL_ROLE_ERT - mind.special_role = SPECIAL_ROLE_ERT - if(cyborg_unlock) - crisis = 1 - if(!(mind in SSticker.minds)) - SSticker.minds += mind - SSticker.mode.ert += mind - -/mob/living/silicon/robot/ert/gamma - crisis = 1 - -/mob/living/silicon/robot/emp_act(severity) - ..() - switch(severity) - if(1) - disable_component("comms", 160) - if(2) - disable_component("comms", 60) - -/mob/living/silicon/robot/extinguish_light() - update_headlamp(1, 150) - -/mob/living/silicon/robot/rejuvenate() - ..() - var/brute = 1000 - var/burn = 1000 - var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE) - for(var/datum/robot_component/borked_part in borked_parts) - brute = borked_part.brute_damage - burn = borked_part.electronics_damage - borked_part.installed = 1 - borked_part.wrapped = new borked_part.external_type - if(ispath(borked_part.external_type, /obj/item/stock_parts/cell)) // is the broken part a cell? - cell = new borked_part.external_type // borgs that have their cell destroyed have their `cell` var set to null. we need create a new cell for them based on their old cell type. - borked_part.heal_damage(brute,burn) - borked_part.install() - -/mob/living/silicon/robot/proc/check_sprite(spritename) - . = FALSE - - var/static/all_borg_icon_states = icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi') - if(spritename in all_borg_icon_states) - . = TRUE - -/mob/living/silicon/robot/check_eye_prot() - return eye_protection - -/mob/living/silicon/robot/check_ear_prot() - return ear_protection - -/mob/living/silicon/robot/update_sight() - if(!client) - return - - if(stat == DEAD) - grant_death_vision() - return - - see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) - sight = initial(sight) - lighting_alpha = initial(lighting_alpha) - - if(client.eye != src) - var/atom/A = client.eye - if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. - return - - if(sight_mode & BORGMESON) - sight |= SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - - if(sight_mode & BORGXRAY) - sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_invisible = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - see_in_dark = 8 - - if(sight_mode & BORGTHERM) - sight |= SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() +GLOBAL_LIST_INIT(robot_verbs_default, list( + /mob/living/silicon/robot/proc/sensor_mode, +)) + +/mob/living/silicon/robot + name = "Cyborg" + real_name = "Cyborg" + icon = 'icons/mob/robots.dmi' + icon_state = "robot" + maxHealth = 100 + health = 100 + universal_understand = 1 + deathgasp_on_death = TRUE + + var/sight_mode = 0 + var/custom_name = "" + var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best + +//Hud stuff + + var/obj/screen/inv1 = null + var/obj/screen/inv2 = null + var/obj/screen/inv3 = null + var/obj/screen/lamp_button = null + var/obj/screen/thruster_button = null + + var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not + var/obj/screen/robot_modules_background + +//3 Modules can be activated at any one time. + var/obj/item/robot_module/module = null + var/module_active = null + var/module_state_1 = null + var/module_state_2 = null + var/module_state_3 = null + + var/obj/item/radio/borg/radio = null + var/mob/living/silicon/ai/connected_ai = null + var/obj/item/stock_parts/cell/cell = null + var/obj/machinery/camera/camera = null + + // Components are basically robot organs. + var/list/components = list() + + var/obj/item/robot_parts/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of.. + var/obj/item/mmi/mmi = null + + var/obj/item/pda/silicon/robot/rbPDA = null + + var/datum/wires/robot/wires = null + + var/opened = 0 + var/custom_panel = null + var/list/custom_panel_names = list("Cricket") + var/list/custom_eye_names = list("Cricket","Standard") + var/emagged = 0 + var/is_emaggable = TRUE + var/eye_protection = 0 + var/ear_protection = 0 + + var/list/force_modules = list() + var/allow_rename = TRUE + var/weapons_unlock = FALSE + var/static_radio_channels = FALSE + + var/wiresexposed = 0 + var/locked = 1 + var/list/req_one_access = list(ACCESS_ROBOTICS) + var/list/req_access + var/ident = 0 + //var/list/laws = list() + var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) + var/viewalerts = 0 + var/modtype = "Default" + var/lower_mod = 0 + var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N + var/jeton = 0 + var/low_power_mode = 0 //whether the robot has no charge left. + var/weapon_lock = 0 + var/weaponlock_time = 120 + var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default + var/lockcharge //Used when locking down a borg to preserve cell charge + var/speed = 0 //Cause sec borgs gotta go fast //No they dont! + var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. + var/pdahide = 0 //Used to hide the borg from the messenger list + var/tracking_entities = 0 //The number of known entities currently accessing the internal camera + var/braintype = "Cyborg" + var/base_icon = "" + var/crisis = 0 + + var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. + var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. + var/lamp_recharging = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. + + var/updating = 0 //portable camera camerachunk update + + hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) + + var/magpulse = 0 + var/ionpulse = 0 // Jetpack-like effect. + var/ionpulse_on = 0 // Jetpack-like effect. + var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect. + + var/datum/action/item_action/toggle_research_scanner/scanner = null + var/list/module_actions = list() + +/mob/living/silicon/robot/get_cell() + return cell + +/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0) + spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + add_language("Robot Talk", 1) + + wires = new(src) + + robot_modules_background = new() + robot_modules_background.icon_state = "block" + robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer 20, UI should be just below it. + robot_modules_background.plane = HUD_PLANE + + ident = rand(1, 999) + rename_character(null, get_default_name()) + update_icons() + update_headlamp() + + radio = new /obj/item/radio/borg(src) + common_radio = radio + + init() + + if(!scrambledcodes && !camera) + camera = new /obj/machinery/camera(src) + camera.c_tag = real_name + camera.network = list("SS13","Robots") + if(wires.IsCameraCut()) // 5 = BORG CAMERA + camera.status = 0 + + if(mmi == null) + mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works) + mmi.icon_state = "boris" + + if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly + cell = new /obj/item/stock_parts/cell/high(src) + + initialize_components() + //if(!unfinished) + // Create all the robot parts. + for(var/V in components) if(V != "power cell") + var/datum/robot_component/C = components[V] + C.installed = 1 + C.wrapped = new C.external_type + + ..() + + add_robot_verbs() + + if(cell) + var/datum/robot_component/cell_component = components["power cell"] + cell_component.wrapped = cell + cell_component.installed = 1 + cell_component.install() + + diag_hud_set_borgcell() + scanner = new(src) + scanner.Grant(src) + +/mob/living/silicon/robot/proc/init(var/alien=0) + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + make_laws() + additional_law_channels["Binary"] = ":b " + var/new_ai = select_active_ai_with_fewest_borgs() + if(new_ai) + lawupdate = 1 + connect_to_ai(new_ai) + else + lawupdate = 0 + + playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) + +/mob/living/silicon/robot/rename_character(oldname, newname) + if(!..(oldname, newname)) + return 0 + + if(oldname != real_name) + notify_ai(3, oldname, newname) + custom_name = (newname != get_default_name()) ? newname : null + setup_PDA() + + //We also need to update name of internal camera. + if(camera) + camera.c_tag = newname + + //Check for custom sprite + if(!custom_sprite) + var/file = file2text("config/custom_sprites.txt") + var/lines = splittext(file, "\n") + + for(var/line in lines) + // split & clean up + var/list/Entry = splittext(line, ":") + for(var/i = 1 to Entry.len) + Entry[i] = trim(Entry[i]) + + if(Entry.len < 2 || Entry[1] != "cyborg") //ignore incorrectly formatted entries or entries that aren't marked for cyborg + continue + + if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required + custom_sprite = 1 + + return 1 + + +/mob/living/silicon/robot/proc/get_default_name(var/prefix as text) + if(prefix) + modtype = prefix + if(mmi) + if(istype(mmi, /obj/item/mmi/robotic_brain)) + braintype = "Android" + else + braintype = "Cyborg" + else + braintype = "Robot" + + if(custom_name) + return custom_name + else + return "[modtype] [braintype]-[num2text(ident)]" + +/mob/living/silicon/robot/verb/Namepick() + set category = "Robot Commands" + if(custom_name) + return 0 + if(!allow_rename) + to_chat(src, "Rename functionality is not enabled on this unit."); + return 0 + rename_self(braintype, 1) + +/mob/living/silicon/robot/proc/sync() + if(lawupdate && connected_ai) + lawsync() + photosync() + +// setup the PDA and its name +/mob/living/silicon/robot/proc/setup_PDA() + if(!rbPDA) + rbPDA = new(src) + rbPDA.set_name_and_job(real_name, braintype) + var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger) + if(M) + if(scrambledcodes) + M.hidden = 1 + if(pdahide) + M.toff = 1 + +/mob/living/silicon/robot/binarycheck() + if(is_component_functioning("comms")) + return 1 + return 0 + +//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO +//Improved /N +/mob/living/silicon/robot/Destroy() + if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. + var/turf/T = get_turf(loc)//To hopefully prevent run time errors. + if(T) mmi.loc = T + if(mmi.brainmob) + mind.transfer_to(mmi.brainmob) + mmi.update_icon() + else + to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") + ghostize() + error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") + mmi = null + if(connected_ai) + connected_ai.connected_robots -= src + QDEL_NULL(wires) + QDEL_NULL(module) + QDEL_NULL(camera) + QDEL_NULL(cell) + QDEL_NULL(robot_suit) + QDEL_NULL(spark_system) + return ..() + +/mob/living/silicon/robot/proc/pick_module() + if(module) + return + var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") + if(islist(force_modules) && force_modules.len) + modules = force_modules.Copy() + if(GLOB.security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis) + to_chat(src, "Crisis mode active. The combat module is now available.") + modules += "Combat" + if(mmi != null && mmi.alien) + modules = list("Hunter") + modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules + if(!modtype) + return + designation = modtype + var/module_sprites[0] //Used to store the associations between sprite names and sprite index. + + if(module) + return + + switch(modtype) + if("Standard") + module = new /obj/item/robot_module/standard(src) + module.channels = list("Service" = 1) + module_sprites["Basic"] = "robot_old" + module_sprites["Android"] = "droid" + module_sprites["Default"] = "Standard" + module_sprites["Noble-STD"] = "Noble-STD" + + if("Service") + module = new /obj/item/robot_module/butler(src) + module.channels = list("Service" = 1) + module_sprites["Waitress"] = "Service" + module_sprites["Kent"] = "toiletbot" + module_sprites["Bro"] = "Brobot" + module_sprites["Rich"] = "maximillion" + module_sprites["Default"] = "Service2" + module_sprites["Standard"] = "Standard-Serv" + module_sprites["Noble-SRV"] = "Noble-SRV" + module_sprites["Cricket"] = "Cricket-SERV" + + if("Miner") + module = new /obj/item/robot_module/miner(src) + module.channels = list("Supply" = 1) + if(camera && ("Robots" in camera.network)) + camera.network.Add("Mining Outpost") + module_sprites["Basic"] = "Miner_old" + module_sprites["Advanced Droid"] = "droid-miner" + module_sprites["Treadhead"] = "Miner" + module_sprites["Standard"] = "Standard-Mine" + module_sprites["Noble-DIG"] = "Noble-DIG" + module_sprites["Cricket"] = "Cricket-MINE" + + if("Medical") + module = new /obj/item/robot_module/medical(src) + module.channels = list("Medical" = 1) + if(camera && ("Robots" in camera.network)) + camera.network.Add("Medical") + module_sprites["Basic"] = "Medbot" + module_sprites["Surgeon"] = "surgeon" + module_sprites["Advanced Droid"] = "droid-medical" + module_sprites["Needles"] = "medicalrobot" + module_sprites["Standard"] = "Standard-Medi" + module_sprites["Noble-MED"] = "Noble-MED" + module_sprites["Cricket"] = "Cricket-MEDI" + status_flags &= ~CANPUSH + + if("Security") + module = new /obj/item/robot_module/security(src) + module.channels = list("Security" = 1) + module_sprites["Basic"] = "secborg" + module_sprites["Red Knight"] = "Security" + module_sprites["Black Knight"] = "securityrobot" + module_sprites["Bloodhound"] = "bloodhound" + module_sprites["Standard"] = "Standard-Secy" + module_sprites["Noble-SEC"] = "Noble-SEC" + module_sprites["Cricket"] = "Cricket-SEC" + status_flags &= ~CANPUSH + + if("Engineering") + module = new /obj/item/robot_module/engineering(src) + module.channels = list("Engineering" = 1) + if(camera && ("Robots" in camera.network)) + camera.network.Add("Engineering") + module_sprites["Basic"] = "Engineering" + module_sprites["Antique"] = "engineerrobot" + module_sprites["Landmate"] = "landmate" + module_sprites["Standard"] = "Standard-Engi" + module_sprites["Noble-ENG"] = "Noble-ENG" + module_sprites["Cricket"] = "Cricket-ENGI" + magpulse = 1 + + if("Janitor") + module = new /obj/item/robot_module/janitor(src) + module.channels = list("Service" = 1) + module_sprites["Basic"] = "JanBot2" + module_sprites["Mopbot"] = "janitorrobot" + module_sprites["Mop Gear Rex"] = "mopgearrex" + module_sprites["Standard"] = "Standard-Jani" + module_sprites["Noble-CLN"] = "Noble-CLN" + module_sprites["Cricket"] = "Cricket-JANI" + + if("Combat") + module = new /obj/item/robot_module/combat(src) + module.channels = list("Security" = 1) + icon_state = "droidcombat" + + if("Hunter") + module = new /obj/item/robot_module/alien/hunter(src) + icon_state = "xenoborg-state-a" + modtype = "Xeno-Hu" + feedback_inc("xeborg_hunter",1) + + + //languages + module.add_languages(src) + //subsystems + module.add_subsystems_and_actions(src) + + //Custom_sprite check and entry + if(custom_sprite && check_sprite("[ckey]-[modtype]")) + module_sprites["Custom"] = "[src.ckey]-[modtype]" + + hands.icon_state = lowertext(module.module_type) + feedback_inc("cyborg_[lowertext(modtype)]",1) + rename_character(real_name, get_default_name()) + + if(modtype == "Medical" || modtype == "Security" || modtype == "Combat") + status_flags &= ~CANPUSH + + choose_icon(6,module_sprites) + if(!static_radio_channels) + radio.config(module.channels) + notify_ai(2) + +/mob/living/silicon/robot/proc/reset_module() + notify_ai(2) + + uneq_all() + sight_mode = null + hands.icon_state = "nomod" + icon_state = "robot" + module.remove_subsystems_and_actions(src) + QDEL_NULL(module) + + camera.network.Remove(list("Engineering", "Medical", "Mining Outpost")) + rename_character(real_name, get_default_name("Default")) + languages = list() + speech_synthesizer_langs = list() + + update_icons() + update_headlamp() + + speed = 0 // Remove upgrades. + ionpulse = FALSE + magpulse = FALSE + add_language("Robot Talk", 1) + + status_flags |= CANPUSH + +//for borg hotkeys, here module refers to borg inv slot, not core module +/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) + set name = "Toggle Module" + set hidden = 1 + toggle_module(module) + +/mob/living/silicon/robot/verb/cmd_unequip_module() + set name = "Unequip Module" + set hidden = 1 + uneq_active() + +// this verb lets cyborgs see the stations manifest +/mob/living/silicon/robot/verb/cmd_station_manifest() + set category = "Robot Commands" + set name = "Show Station Manifest" + show_station_manifest() + +/mob/living/silicon/robot/proc/self_diagnosis() + if(!is_component_functioning("diagnosis unit")) + return null + + var/dat = "[src.name] Self-Diagnosis Report\n" + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 0) + dat += "[C.name]
    MISSING
    " + else + dat += "[C.name][C.installed == -1 ? "
    DESTROYED" : ""]
    Brute Damage:[C.brute_damage]
    Electronics Damage:[C.electronics_damage]
    Powered:[C.is_powered() ? "Yes" : "No"]
    Toggled:[ C.toggled ? "Yes" : "No"]

    " + return dat + +/mob/living/silicon/robot/verb/self_diagnosis_verb() + set category = "Robot Commands" + set name = "Self Diagnosis" + + if(!is_component_functioning("diagnosis unit")) + to_chat(src, "Your self-diagnosis component isn't functioning.") + + var/dat = self_diagnosis() + src << browse(dat, "window=robotdiagnosis") + + +/mob/living/silicon/robot/verb/toggle_component() + set category = "Robot Commands" + set name = "Toggle Component" + set desc = "Toggle a component, conserving power." + + var/list/installed_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed) + installed_components += V + + var/toggle = input(src, "Which component do you want to toggle?", "Toggle Component") as null|anything in installed_components + if(!toggle) + return + + var/datum/robot_component/C = components[toggle] + C.toggle() + to_chat(src, "You [C.toggled ? "enable" : "disable"] [C.name].") + +/mob/living/silicon/robot/proc/sensor_mode() + set name = "Set Sensor Augmentation" + set desc = "Augment visual feed with internal sensor overlays." + set category = "Robot Commands" + toggle_sensor_mode() + +/mob/living/silicon/robot/proc/add_robot_verbs() + src.verbs |= GLOB.robot_verbs_default + src.verbs |= silicon_subsystems + +/mob/living/silicon/robot/proc/remove_robot_verbs() + src.verbs -= GLOB.robot_verbs_default + src.verbs -= silicon_subsystems + +/mob/living/silicon/robot/proc/ionpulse() + if(!ionpulse_on) + return + + if(cell.charge <= 50) + toggle_ionpulse() + return + + cell.charge -= 25 // 500 steps on a default cell. + return 1 + +/mob/living/silicon/robot/proc/toggle_ionpulse() + if(!ionpulse) + to_chat(src, "No thrusters are installed!") + return + + if(!ion_trail) + ion_trail = new + ion_trail.set_up(src) + + ionpulse_on = !ionpulse_on + to_chat(src, "You [ionpulse_on ? null :"de"]activate your ion thrusters.") + if(ionpulse_on) + ion_trail.start() + else + ion_trail.stop() + if(thruster_button) + thruster_button.icon_state = "ionpulse[ionpulse_on]" + +/mob/living/silicon/robot/blob_act(obj/structure/blob/B) + if(stat != DEAD) + adjustBruteLoss(30) + else + gib() + return TRUE + +// this function displays the cyborgs current cell charge in the stat panel +/mob/living/silicon/robot/proc/show_cell_power() + if(cell) + stat(null, text("Charge Left: [cell.charge]/[cell.maxcharge]")) + else + stat(null, text("No Cell Inserted!")) + + +// update the status screen display +/mob/living/silicon/robot/Stat() + ..() + statpanel("Status") + if(client.statpanel == "Status") + show_cell_power() + var/total_user_contents = GetAllContents() + if(locate(/obj/item/gps/cyborg) in total_user_contents) + var/turf/T = get_turf(src) + stat(null, "GPS: [COORD(T)]") + +/mob/living/silicon/robot/restrained() + return 0 + +/mob/living/silicon/robot/InCritical() + return low_power_mode + +/mob/living/silicon/robot/ex_act(severity) + switch(severity) + if(1.0) + gib() + return + if(2.0) + if(stat != 2) + adjustBruteLoss(60) + adjustFireLoss(60) + if(3.0) + if(stat != 2) + adjustBruteLoss(30) + return + + +/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) + ..(Proj) + if(prob(75) && Proj.damage > 0) spark_system.start() + return 2 + + +/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) + // Check if the user is trying to insert another component like a radio, actuator, armor etc. + if(istype(W, /obj/item/robot_parts/robot_component) && opened) + for(var/V in components) + var/datum/robot_component/C = components[V] + if(!C.installed && istype(W, C.external_type)) + C.installed = 1 + C.wrapped = W + C.install() + user.drop_item() + W.loc = null + + var/obj/item/robot_parts/robot_component/WC = W + if(istype(WC)) + C.brute_damage = WC.brute + C.electronics_damage = WC.burn + + to_chat(usr, "You install the [W.name].") + + return + + if(istype(W, /obj/item/stack/cable_coil) && user.a_intent == INTENT_HELP && (wiresexposed || istype(src, /mob/living/silicon/robot/drone))) + user.changeNext_move(CLICK_CD_MELEE) + if(!getFireLoss()) + to_chat(user, "Nothing to fix!") + return + else if(!getFireLoss(TRUE)) + to_chat(user, "The damaged components are beyond saving!") + return + var/obj/item/stack/cable_coil/coil = W + adjustFireLoss(-30) + updatehealth() + add_fingerprint(user) + coil.use(1) + user.visible_message("\The [user] fixes some of the burnt wires on \the [src] with \the [coil].") + + else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside + var/datum/robot_component/cell/C = components["power cell"] + if(wiresexposed) + to_chat(user, "Close the panel first.") + else if(cell) + to_chat(user, "There is a power cell already installed.") + else + user.drop_item() + W.loc = src + cell = W + to_chat(user, "You insert the power cell.") + + C.installed = 1 + C.wrapped = W + C.install() + C.external_type = W.type // Update the cell component's `external_type` to the path of new cell + //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z + C.brute_damage = 0 + C.electronics_damage = 0 + diag_hud_set_borgcell() + + else if(istype(W, /obj/item/encryptionkey/) && opened) + if(radio)//sanityyyyyy + radio.attackby(W,user)//GTFO, you have your own procs + else + to_chat(user, "Unable to locate a radio.") + + else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card + if(emagged)//still allow them to open the cover + to_chat(user, "The interface seems slightly damaged.") + if(opened) + to_chat(user, "You must close the cover to swipe an ID card.") + else + if(allowed(W)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") + update_icons() + else + to_chat(user, "Access denied.") + + else if(istype(W, /obj/item/borg/upgrade/)) + var/obj/item/borg/upgrade/U = W + if(!opened) + to_chat(user, "You must access the borg's internals!") + else if(!src.module && U.require_module) + to_chat(user, "The borg must choose a module before it can be upgraded!") + else if(U.locked) + to_chat(user, "The upgrade is locked and cannot be used yet!") + else + if(!user.drop_item()) + return + if(U.action(src)) + to_chat(user, "You apply the upgrade to [src].") + U.forceMove(src) + else + to_chat(user, "Upgrade error.") + + else if(istype(W, /obj/item/mmi_radio_upgrade)) + if(!opened) + to_chat(user, "You must access the borg's internals!") + return + else if(!mmi) + to_chat(user, "This cyborg does not have an MMI to augment!") + return + else if(mmi.radio) + to_chat(user, "A radio upgrade is already installed in the MMI!") + return + else if(user.drop_item()) + to_chat(user, "You apply the upgrade to [src].") + to_chat(src, "MMI radio capability installed.") + mmi.install_radio() + qdel(W) + else + return ..() + +/mob/living/silicon/robot/wirecutter_act(mob/user, obj/item/I) + if(!opened) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(wiresexposed) + wires.Interact(user) + +/mob/living/silicon/robot/multitool_act(mob/user, obj/item/I) + if(!opened) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(wiresexposed) + wires.Interact(user) + +/mob/living/silicon/robot/screwdriver_act(mob/user, obj/item/I) + if(!opened) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(!cell) // haxing + wiresexposed = !wiresexposed + to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") + update_icons() + I.play_tool_sound(user, I.tool_volume) + else //radio check + if(radio) + radio.screwdriver_act(user, I)//Push it to the radio to let it handle everything + else + to_chat(user, "Unable to locate a radio.") + update_icons() + +/mob/living/silicon/robot/crowbar_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(!opened) + if(locked) + to_chat(user, "The cover is locked and cannot be opened.") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You open the cover.") + opened = TRUE + update_icons() + return + else if(cell) + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You close the cover.") + opened = FALSE + update_icons() + return + else if(wiresexposed && wires.IsAllCut()) + //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. + if(!mmi) + to_chat(user, "[src] has no brain to remove.") + return + to_chat(user, "You jam the crowbar into the robot and begin levering the securing bolts...") + if(I.use_tool(src, user, 30, volume = I.tool_volume)) + user.visible_message("[user] deconstructs [src]!", "You unfasten the securing bolts, and [src] falls to pieces!") + deconstruct() + return + // Okay we're not removing the cell or an MMI, but maybe something else? + var/list/removable_components = list() + for(var/V in components) + if(V == "power cell") + continue + var/datum/robot_component/C = components[V] + if(C.installed == 1 || C.installed == -1) + removable_components += V + if(module) + removable_components += module.custom_removals + var/remove = input(user, "Which component do you want to pry out?", "Remove Component") as null|anything in removable_components + if(!remove) + return + if(module && module.handle_custom_removal(remove, user, I)) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/datum/robot_component/C = components[remove] + var/obj/item/robot_parts/robot_component/thing = C.wrapped + to_chat(user, "You remove \the [thing].") + if(istype(thing)) + thing.brute = C.brute_damage + thing.burn = C.electronics_damage + + thing.loc = loc + var/was_installed = C.installed + C.installed = 0 + if(was_installed == 1) + C.uninstall() + + + + +/mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) + if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. + spark_system.start() + ..() + +/mob/living/silicon/robot/emag_act(user as mob) + if(!ishuman(user) && !issilicon(user)) + return + var/mob/living/M = user + if(!opened)//Cover is closed + if(!is_emaggable) + to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.") + else if(locked) + to_chat(user, "You emag the cover lock.") + locked = 0 + else + to_chat(user, "The cover is already unlocked.") + return + + if(opened)//Cover is open + if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice + if(wiresexposed) + to_chat(user, "You must close the panel first") + return + else + sleep(6) + emagged = 1 + SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown + if(src.hud_used) + src.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. + disconnect_from_ai() + to_chat(user, "You emag [src]'s interface.") +// message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") + log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") + clear_supplied_laws() + clear_inherent_laws() + laws = new /datum/ai_laws/syndicate_override + var/time = time2text(world.realtime,"hh:mm:ss") + GLOB.lawchanges.Add("[time] : [M.name]([M.key]) emagged [name]([key])") + set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.") + to_chat(src, "ALERT: Foreign software detected.") + sleep(5) + to_chat(src, "Initiating diagnostics...") + sleep(20) + to_chat(src, "SynBorg v1.7 loaded.") + sleep(5) + to_chat(src, "LAW SYNCHRONISATION ERROR") + sleep(5) + to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") + sleep(10) + to_chat(src, "> N") + sleep(20) + to_chat(src, "ERRORERRORERROR") + to_chat(src, "Obey these laws:") + laws.show_laws(src) + to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") + SetLockdown(0) + if(src.module && istype(src.module, /obj/item/robot_module/miner)) + for(var/obj/item/pickaxe/drill/cyborg/D in src.module.modules) + qdel(D) + src.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(src.module) + src.module.rebuild() + if(src.module && istype(src.module, /obj/item/robot_module/medical)) + for(var/obj/item/borg_defib/F in src.module.modules) + F.safety = 0 + if(module) + module.module_type = "Malf" // For the cool factor + update_module_icon() + update_icons() + return + +/mob/living/silicon/robot/verb/unlock_own_cover() + set category = "Robot Commands" + set name = "Unlock Cover" + set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you." + if(locked) + switch(alert("You can not lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No")) + if("Yes") + locked = 0 + update_icons() + to_chat(usr, "You unlock your cover.") + +/mob/living/silicon/robot/attack_ghost(mob/user) + if(wiresexposed) + wires.Interact(user) + else + ..() //this calls the /mob/living/attack_ghost proc for the ghost health/cyborg analyzer + +/mob/living/silicon/robot/proc/allowed(obj/item/I) + var/obj/dummy = new /obj(null) // Create a dummy object to check access on as to avoid having to snowflake check_access on every mob + dummy.req_access = req_access + dummy.req_one_access = req_one_access + + if(dummy.check_access(I)) + qdel(dummy) + return 1 + + qdel(dummy) + return 0 + +/mob/living/silicon/robot/update_icons() + + overlays.Cut() + if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned. + if(custom_panel in custom_eye_names) + overlays += "eyes-[custom_panel]" + else + overlays += "eyes-[icon_state]" + else + overlays -= "eyes" + + if(opened) + var/panelprefix = "ov" + if(custom_sprite) //Custom borgs also have custom panels, heh + panelprefix = "[ckey]" + + if(custom_panel in custom_panel_names) //For default borgs with different panels + panelprefix = custom_panel + + if(wiresexposed) + overlays += "[panelprefix]-openpanel +w" + else if(cell) + overlays += "[panelprefix]-openpanel +c" + else + overlays += "[panelprefix]-openpanel -c" + + var/combat = list("Combat") + if(modtype in combat) + if(base_icon == "") + base_icon = icon_state + if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) + icon_state = "[base_icon]-roll" + else + icon_state = base_icon + if(module) + for(var/obj/item/borg/combat/shield/S in module.modules) + if(activated(S)) + overlays += "[base_icon]-shield" + update_fire() + +/mob/living/silicon/robot/proc/installed_modules() + if(weapon_lock) + to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") + return + + if(!module) + pick_module() + return + var/dat = {"Close +
    +
    + Activated Modules +
    + + + + +
    Module 1:[module_state_1 ? "[module_state_1]" : "No Module"]
    Module 2:[module_state_2 ? "[module_state_2]" : "No Module"]
    Module 3:[module_state_3 ? "[module_state_3]" : "No Module"]

    + Installed Modules

    + + "} + for(var/obj in module.modules) + if(!obj) + dat += text("") + else if(activated(obj)) + dat += text("") + else + dat += text("") + if(emagged || weapons_unlock) + if(activated(module.emag)) + dat += text("") + else + dat += text("") + dat += "
    Resource depleted
    [obj]Activated
    [obj]Activate
    [module.emag]Activated
    [module.emag]Activate
    " +/* + if(activated(obj)) + dat += text("[obj]: \[Activated | Deactivate\]
    ") + else + dat += text("[obj]: \[Activate | Deactivated\]
    ") +*/ + var/datum/browser/popup = new(src, "robotmod", "Modules") + popup.set_content(dat) + popup.open() + + +/mob/living/silicon/robot/Topic(href, href_list) + if(..()) + return 1 + + if(usr != src) + return 1 + + if(href_list["mach_close"]) + var/t1 = text("window=[href_list["mach_close"]]") + unset_machine() + src << browse(null, t1) + return 1 + + if(href_list["showalerts"]) + subsystem_alarm_monitor() + return 1 + + if(href_list["mod"]) + var/obj/item/O = locate(href_list["mod"]) + if(istype(O) && (O.loc == src)) + O.attack_self(src) + return 1 + + if(href_list["act"]) + var/obj/item/O = locate(href_list["act"]) + if(!istype(O) || !(O.loc == src || O.loc == src.module)) + return 1 + + activate_module(O) + installed_modules() + + if(href_list["deact"]) + var/obj/item/O = locate(href_list["deact"]) + if(activated(O)) + if(module_state_1 == O) + module_state_1 = null + contents -= O + else if(module_state_2 == O) + module_state_2 = null + contents -= O + else if(module_state_3 == O) + module_state_3 = null + contents -= O + else + to_chat(src, "Module isn't activated.") + else + to_chat(src, "Module isn't activated") + installed_modules() + return 1 + + return 1 + +/mob/living/silicon/robot/proc/radio_menu() + radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code + +/mob/living/silicon/robot/proc/control_headlamp() + if(stat || lamp_recharging || low_power_mode) + to_chat(src, "This function is currently offline.") + return + +//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. + lamp_intensity = (lamp_intensity+2) % (lamp_max+2) + to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]") + update_headlamp() + +/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100) + set_light(0) + + if(lamp_intensity && (turn_off || stat || low_power_mode)) + to_chat(src, "Your headlamp has been deactivated.") + lamp_intensity = 0 + lamp_recharging = 1 + spawn(cooldown) //10 seconds by default, if the source of the deactivation does not keep stat that long. + lamp_recharging = 0 + else + set_light(light_range + lamp_intensity) + + if(lamp_button) + lamp_button.icon_state = "lamp[lamp_intensity]" + + update_icons() + +/mob/living/silicon/robot/proc/deconstruct() + var/turf/T = get_turf(src) + if(robot_suit) + robot_suit.forceMove(T) + robot_suit.l_leg.forceMove(T) + robot_suit.l_leg = null + robot_suit.r_leg.forceMove(T) + robot_suit.r_leg = null + new /obj/item/stack/cable_coil(T, robot_suit.chest.wired) + robot_suit.chest.forceMove(T) + robot_suit.chest.wired = FALSE + robot_suit.chest = null + robot_suit.l_arm.forceMove(T) + robot_suit.l_arm = null + robot_suit.r_arm.forceMove(T) + robot_suit.r_arm = null + robot_suit.head.forceMove(T) + robot_suit.head.flash1.forceMove(T) + robot_suit.head.flash1.burn_out() + robot_suit.head.flash1 = null + robot_suit.head.flash2.forceMove(T) + robot_suit.head.flash2.burn_out() + robot_suit.head.flash2 = null + robot_suit.head = null + robot_suit.updateicon() + else + new /obj/item/robot_parts/robot_suit(T) + new /obj/item/robot_parts/l_leg(T) + new /obj/item/robot_parts/r_leg(T) + new /obj/item/stack/cable_coil(T, 1) + new /obj/item/robot_parts/chest(T) + new /obj/item/robot_parts/l_arm(T) + new /obj/item/robot_parts/r_arm(T) + new /obj/item/robot_parts/head(T) + var/b + for(b=0, b!=2, b++) + var/obj/item/flash/F = new /obj/item/flash(T) + F.burn_out() + if(cell) //Sanity check. + cell.forceMove(T) + cell = null + qdel(src) + +#define BORG_CAMERA_BUFFER 30 +/mob/living/silicon/robot/Move(a, b, flag) + var/oldLoc = src.loc + . = ..() + if(.) + if(src.camera) + if(!updating) + updating = 1 + spawn(BORG_CAMERA_BUFFER) + if(camera && oldLoc != src.loc) + GLOB.cameranet.updatePortableCamera(src.camera) + updating = 0 + if(module) + if(module.type == /obj/item/robot_module/janitor) + var/turf/tile = loc + if(isturf(tile)) + var/floor_only = TRUE + for(var/A in tile) + if(istype(A, /obj/effect)) + if(is_cleanable(A)) + var/obj/effect/decal/cleanable/blood/B = A + if(istype(B) && B.off_floor) + floor_only = FALSE + else + qdel(A) + else if(istype(A, /obj/item)) + var/obj/item/cleaned_item = A + cleaned_item.clean_blood() + else if(istype(A, /mob/living/carbon/human)) + var/mob/living/carbon/human/cleaned_human = A + if(cleaned_human.lying) + if(cleaned_human.head) + cleaned_human.head.clean_blood() + cleaned_human.update_inv_head(0,0) + if(cleaned_human.wear_suit) + cleaned_human.wear_suit.clean_blood() + cleaned_human.update_inv_wear_suit(0,0) + else if(cleaned_human.w_uniform) + cleaned_human.w_uniform.clean_blood() + cleaned_human.update_inv_w_uniform(0,0) + if(cleaned_human.shoes) + cleaned_human.shoes.clean_blood() + cleaned_human.update_inv_shoes(0,0) + cleaned_human.clean_blood() + to_chat(cleaned_human, "[src] cleans your face!") + if(floor_only) + tile.clean_blood() + return +#undef BORG_CAMERA_BUFFER + +/mob/living/silicon/robot/proc/self_destruct() + if(emagged) + if(mmi) + qdel(mmi) + explosion(src.loc,1,2,4,flame_range = 2) + else + explosion(src.loc,-1,0,2) + gib() + return + +/mob/living/silicon/robot/proc/UnlinkSelf() + disconnect_from_ai() + lawupdate = 0 + lockcharge = 0 + canmove = 1 + scrambledcodes = 1 + //Disconnect it's camera so it's not so easily tracked. + QDEL_NULL(src.camera) + // I'm trying to get the Cyborg to not be listed in the camera list + // Instead of being listed as "deactivated". The downside is that I'm going + // to have to check if every camera is null or not before doing anything, to prevent runtime errors. + // I could change the network to null but I don't know what would happen, and it seems too hacky for me. + +/mob/living/silicon/robot/proc/ResetSecurityCodes() + set category = "Robot Commands" + set name = "Reset Identity Codes" + set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permanently severs you from your AI and the robotics console and will deactivate your camera system." + + var/mob/living/silicon/robot/R = src + + if(R) + R.UnlinkSelf() + to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") + src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes + +/mob/living/silicon/robot/mode() + set name = "Activate Held Object" + set category = "IC" + set src = usr + + var/obj/item/W = get_active_hand() + if(W) + W.attack_self(src) + + return + +/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) + // They stay locked down if their wire is cut. + if(wires.LockedCut()) + state = 1 + if(state) + throw_alert("locked", /obj/screen/alert/locked) + else + clear_alert("locked") + lockcharge = state + update_canmove() + +/mob/living/silicon/robot/proc/choose_icon(var/triesleft, var/list/module_sprites) + + if(triesleft<1 || !module_sprites.len) + return + else + triesleft-- + + var/icontype + lockcharge = 1 //Locks borg until it select an icon to avoid secborgs running around with a standard sprite + icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chances." : "This is your last try."]", "Robot", null, null) in module_sprites + + if(icontype) + if(icontype == "Custom") + icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' + else + icon = 'icons/mob/robots.dmi' + icon_state = module_sprites[icontype] + if(icontype == "Bro") + module.module_type = "Brobot" + update_module_icon() + lockcharge = null + var/list/names = splittext(icontype, "-") + custom_panel = trim(names[1]) + else + to_chat(src, "Something is badly wrong with the sprite selection. Harass a coder.") + icon_state = module_sprites[1] + lockcharge = null + return + + update_icons() + + if(triesleft >= 1) + var/choice = input("Look at your icon - is this what you want?") in list("Yes","No") + if(choice=="No") + choose_icon(triesleft, module_sprites) + return + else + triesleft = 0 + return + else + to_chat(src, "Your icon has been set. You now require a module reset to change it.") + +/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) + if(!connected_ai) + return + switch(notifytype) + if(1) //New Cyborg + to_chat(connected_ai, "

    NOTICE - New cyborg connection detected: [name]
    ") + if(2) //New Module + to_chat(connected_ai, "

    NOTICE - Cyborg module change detected: [name] has loaded the [designation] module.
    ") + if(3) //New Name + to_chat(connected_ai, "

    NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].
    ") + +/mob/living/silicon/robot/proc/disconnect_from_ai() + if(connected_ai) + sync() // One last sync attempt + connected_ai.connected_robots -= src + connected_ai = null + +/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) + if(AI && AI != connected_ai) + disconnect_from_ai() + connected_ai = AI + connected_ai.connected_robots |= src + notify_ai(1) + sync() + +/mob/living/silicon/robot/adjustOxyLoss(var/amount) + if(suiciding) + return ..() + else + return STATUS_UPDATE_NONE + +/mob/living/silicon/robot/regenerate_icons() + ..() + update_module_icon() + +/mob/living/silicon/robot/deathsquad + base_icon = "nano_bloodhound" + icon_state = "nano_bloodhound" + designation = "SpecOps" + lawupdate = 0 + scrambledcodes = 1 + req_one_access = list(ACCESS_CENT_SPECOPS) + ionpulse = 1 + magpulse = 1 + pdahide = 1 + eye_protection = 2 // Immunity to flashes and the visual part of flashbangs + ear_protection = 1 // Immunity to the audio part of flashbangs + allow_rename = FALSE + modtype = "Commando" + faction = list("nanotrasen") + is_emaggable = FALSE + +/mob/living/silicon/robot/deathsquad/New(loc) + ..() + cell = new /obj/item/stock_parts/cell/hyper(src) + +/mob/living/silicon/robot/deathsquad/init() + laws = new /datum/ai_laws/deathsquad + module = new /obj/item/robot_module/deathsquad(src) + + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + radio = new /obj/item/radio/borg/deathsquad(src) + radio.recalculateChannels() + + playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) + +/mob/living/silicon/robot/combat + base_icon = "droidcombat" + icon_state = "droidcombat" + modtype = "Combat" + designation = "Combat" + +/mob/living/silicon/robot/combat/init() + ..() + module = new /obj/item/robot_module/combat(src) + module.channels = list("Security" = 1) + //languages + module.add_languages(src) + //subsystems + module.add_subsystems_and_actions(src) + + status_flags &= ~CANPUSH + + radio.config(module.channels) + notify_ai(2) + +/mob/living/silicon/robot/ert + designation = "ERT" + lawupdate = 0 + scrambledcodes = 1 + req_one_access = list(ACCESS_CENT_SPECOPS) + ionpulse = 1 + + force_modules = list("Engineering", "Medical", "Security") + static_radio_channels = 1 + allow_rename = FALSE + weapons_unlock = TRUE + + +/mob/living/silicon/robot/ert/init() + laws = new /datum/ai_laws/ert_override + radio = new /obj/item/radio/borg/ert(src) + radio.recalculateChannels() + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + +/mob/living/silicon/robot/ert/New(loc, cyborg_unlock) + ..(loc) + cell = new /obj/item/stock_parts/cell/hyper(src) + var/rnum = rand(1,1000) + var/borgname = "ERT [rnum]" + name = borgname + custom_name = borgname + real_name = name + mind = new + mind.current = src + mind.original = src + mind.assigned_role = SPECIAL_ROLE_ERT + mind.special_role = SPECIAL_ROLE_ERT + if(cyborg_unlock) + crisis = 1 + if(!(mind in SSticker.minds)) + SSticker.minds += mind + SSticker.mode.ert += mind + +/mob/living/silicon/robot/ert/gamma + crisis = 1 + +/mob/living/silicon/robot/emp_act(severity) + ..() + switch(severity) + if(1) + disable_component("comms", 160) + if(2) + disable_component("comms", 60) + +/mob/living/silicon/robot/extinguish_light() + update_headlamp(1, 150) + +/mob/living/silicon/robot/rejuvenate() + ..() + var/brute = 1000 + var/burn = 1000 + var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE) + for(var/datum/robot_component/borked_part in borked_parts) + brute = borked_part.brute_damage + burn = borked_part.electronics_damage + borked_part.installed = 1 + borked_part.wrapped = new borked_part.external_type + if(ispath(borked_part.external_type, /obj/item/stock_parts/cell)) // is the broken part a cell? + cell = new borked_part.external_type // borgs that have their cell destroyed have their `cell` var set to null. we need create a new cell for them based on their old cell type. + borked_part.heal_damage(brute,burn) + borked_part.install() + +/mob/living/silicon/robot/proc/check_sprite(spritename) + . = FALSE + + var/static/all_borg_icon_states = icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi') + if(spritename in all_borg_icon_states) + . = TRUE + +/mob/living/silicon/robot/check_eye_prot() + return eye_protection + +/mob/living/silicon/robot/check_ear_prot() + return ear_protection + +/mob/living/silicon/robot/update_sight() + if(!client) + return + + if(stat == DEAD) + grant_death_vision() + return + + see_invisible = initial(see_invisible) + see_in_dark = initial(see_in_dark) + sight = initial(sight) + lighting_alpha = initial(lighting_alpha) + + if(client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + + if(sight_mode & BORGMESON) + sight |= SEE_TURFS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + + if(sight_mode & BORGXRAY) + sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + see_invisible = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + see_in_dark = 8 + + if(sight_mode & BORGTHERM) + sight |= SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() + +/// Used in `robot_bindings.dm` when the user presses "A" if on AZERTY mode, or "Q" on QWERTY mode. +/mob/living/silicon/robot/proc/on_drop_hotkey_press() + var/obj/item/gripper/G = get_active_hand() + if(istype(G) && G.gripped_item) + G.drop_gripped_item() // if the active module is a gripper, try to drop its held item. + else + uneq_active() // else unequip the module and put it back into the robot's inventory. + return diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index 81700464ac0..c73d566b729 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -1,179 +1,179 @@ -/mob/living/silicon/robot/updatehealth(reason = "none given") - if(status_flags & GODMODE) - health = maxHealth - stat = CONSCIOUS - return - health = maxHealth - (getOxyLoss() + getFireLoss() + getBruteLoss()) - update_stat("updatehealth([reason])") - handle_hud_icons_health() - diag_hud_set_health() - -/mob/living/silicon/robot/getBruteLoss(repairable_only = FALSE) - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones - amount += C.brute_damage - return amount - -/mob/living/silicon/robot/getFireLoss(repairable_only = FALSE) - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones - amount += C.electronics_damage - return amount - -/mob/living/silicon/robot/adjustBruteLoss(amount, updating_health = TRUE) - if(amount > 0) - take_overall_damage(amount, 0, updating_health) - else - heal_overall_damage(-amount, 0, updating_health) - return STATUS_UPDATE_HEALTH - -/mob/living/silicon/robot/adjustFireLoss(amount, updating_health = TRUE) - if(amount > 0) - take_overall_damage(0, amount, updating_health) - else - heal_overall_damage(0, -amount, updating_health) - return STATUS_UPDATE_HEALTH - -/mob/living/silicon/robot/proc/get_damaged_components(get_brute, get_burn, get_borked = FALSE, get_missing = FALSE) - var/list/datum/robot_component/parts = list() - for(var/V in components) - var/datum/robot_component/C = components[V] - if((C.installed == 1 || (get_borked && C.installed == -1) || (get_missing && C.installed == 0)) && ((get_brute && C.brute_damage) || (get_burn && C.electronics_damage))) - parts += C - return parts - -/mob/living/silicon/robot/proc/get_missing_components() - var/list/datum/robot_component/parts = list() - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 0) - parts += C - return parts - -/mob/living/silicon/robot/proc/get_damageable_components() - var/list/rval = new - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 1) - rval += C - return rval - -/mob/living/silicon/robot/proc/get_armour() - if(!LAZYLEN(components)) - return 0 - var/datum/robot_component/C = components["armour"] - if(C && C.installed == 1) - return C - return 0 - -/mob/living/silicon/robot/heal_organ_damage(brute, burn, updating_health = TRUE) - var/list/datum/robot_component/parts = get_damaged_components(brute, burn) - if(!LAZYLEN(parts)) - return - var/datum/robot_component/picked = pick(parts) - picked.heal_damage(brute, burn, updating_health) - -/mob/living/silicon/robot/take_organ_damage(brute = 0, burn = 0, updating_health = TRUE, sharp = 0, edge = 0) - var/list/components = get_damageable_components() - if(!LAZYLEN(components)) - return - - //Combat shielding absorbs a percentage of damage directly into the cell. - var/obj/item/borg/combat/shield/shield - if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) - shield = module_state_1 - else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) - shield = module_state_2 - else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) - shield = module_state_3 - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute * shield.shield_level - var/absorb_burn = burn * shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 100 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute, burn, sharp, updating_health) - return - - var/datum/robot_component/C = pick(components) - C.take_damage(brute, burn, sharp, updating_health) - -/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn, updating_health = TRUE) - var/list/datum/robot_component/parts = get_damaged_components(brute, burn) - - while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.heal_damage(brute,burn, updating_health) - - brute -= (brute_was - picked.brute_damage) - burn -= (burn_was - picked.electronics_damage) - - parts -= picked - - if(updating_health) - updatehealth("heal overall damage") - -/mob/living/silicon/robot/take_overall_damage(brute = 0, burn = 0, updating_health = TRUE, used_weapon = null, sharp = 0) - if(status_flags & GODMODE) return //godmode - var/list/datum/robot_component/parts = get_damageable_components() - - //Combat shielding absorbs a percentage of damage directly into the cell. - var/obj/item/borg/combat/shield/shield - if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) - shield = module_state_1 - else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) - shield = module_state_2 - else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) - shield = module_state_3 - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute * shield.shield_level - var/absorb_burn = burn * shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 100 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute, burn, sharp) - return - - while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.take_damage(brute, burn, sharp, FALSE) - - brute -= (picked.brute_damage - brute_was) - burn -= (picked.electronics_damage - burn_was) - - parts -= picked - updatehealth() +/mob/living/silicon/robot/updatehealth(reason = "none given") + if(status_flags & GODMODE) + health = maxHealth + stat = CONSCIOUS + return + health = maxHealth - (getOxyLoss() + getFireLoss() + getBruteLoss()) + update_stat("updatehealth([reason])") + handle_hud_icons_health() + diag_hud_set_health() + +/mob/living/silicon/robot/getBruteLoss(repairable_only = FALSE) + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones + amount += C.brute_damage + return amount + +/mob/living/silicon/robot/getFireLoss(repairable_only = FALSE) + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones + amount += C.electronics_damage + return amount + +/mob/living/silicon/robot/adjustBruteLoss(amount, updating_health = TRUE) + if(amount > 0) + take_overall_damage(amount, 0, updating_health) + else + heal_overall_damage(-amount, 0, updating_health) + return STATUS_UPDATE_HEALTH + +/mob/living/silicon/robot/adjustFireLoss(amount, updating_health = TRUE) + if(amount > 0) + take_overall_damage(0, amount, updating_health) + else + heal_overall_damage(0, -amount, updating_health) + return STATUS_UPDATE_HEALTH + +/mob/living/silicon/robot/proc/get_damaged_components(get_brute, get_burn, get_borked = FALSE, get_missing = FALSE) + var/list/datum/robot_component/parts = list() + for(var/V in components) + var/datum/robot_component/C = components[V] + if((C.installed == 1 || (get_borked && C.installed == -1) || (get_missing && C.installed == 0)) && ((get_brute && C.brute_damage) || (get_burn && C.electronics_damage))) + parts += C + return parts + +/mob/living/silicon/robot/proc/get_missing_components() + var/list/datum/robot_component/parts = list() + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 0) + parts += C + return parts + +/mob/living/silicon/robot/proc/get_damageable_components() + var/list/rval = new + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 1) + rval += C + return rval + +/mob/living/silicon/robot/proc/get_armour() + if(!LAZYLEN(components)) + return 0 + var/datum/robot_component/C = components["armour"] + if(C && C.installed == 1) + return C + return 0 + +/mob/living/silicon/robot/heal_organ_damage(brute, burn, updating_health = TRUE) + var/list/datum/robot_component/parts = get_damaged_components(brute, burn) + if(!LAZYLEN(parts)) + return + var/datum/robot_component/picked = pick(parts) + picked.heal_damage(brute, burn, updating_health) + +/mob/living/silicon/robot/take_organ_damage(brute = 0, burn = 0, updating_health = TRUE, sharp = 0, edge = 0) + var/list/components = get_damageable_components() + if(!LAZYLEN(components)) + return + + //Combat shielding absorbs a percentage of damage directly into the cell. + var/obj/item/borg/combat/shield/shield + if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) + shield = module_state_1 + else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) + shield = module_state_2 + else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) + shield = module_state_3 + if(shield) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute * shield.shield_level + var/absorb_burn = burn * shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 100 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "Your shield has overloaded!") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "Your shield absorbs some of the impact!") + + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute, burn, sharp, updating_health) + return + + var/datum/robot_component/C = pick(components) + C.take_damage(brute, burn, sharp, updating_health) + +/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn, updating_health = TRUE) + var/list/datum/robot_component/parts = get_damaged_components(brute, burn) + + while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.heal_damage(brute,burn, updating_health) + + brute -= (brute_was - picked.brute_damage) + burn -= (burn_was - picked.electronics_damage) + + parts -= picked + + if(updating_health) + updatehealth("heal overall damage") + +/mob/living/silicon/robot/take_overall_damage(brute = 0, burn = 0, updating_health = TRUE, used_weapon = null, sharp = 0) + if(status_flags & GODMODE) return //godmode + var/list/datum/robot_component/parts = get_damageable_components() + + //Combat shielding absorbs a percentage of damage directly into the cell. + var/obj/item/borg/combat/shield/shield + if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) + shield = module_state_1 + else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) + shield = module_state_2 + else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) + shield = module_state_3 + if(shield) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute * shield.shield_level + var/absorb_burn = burn * shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 100 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "Your shield has overloaded!") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "Your shield absorbs some of the impact!") + + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute, burn, sharp) + return + + while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.take_damage(brute, burn, sharp, FALSE) + + brute -= (picked.brute_damage - brute_was) + burn -= (picked.electronics_damage - burn_was) + + parts -= picked + updatehealth() diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 083e379e444..92115986b2e 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -95,4 +95,4 @@ desc = "By retracting limbs and tucking in its head, a combat android can roll at high speeds." icon = 'icons/obj/decals.dmi' icon_state = "shock" - powerneeded = 25 \ No newline at end of file + powerneeded = 25 diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 5baf020d563..3260775c674 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -1,583 +1,590 @@ -/obj/item/robot_module - name = "robot module" - icon = 'icons/obj/module.dmi' - icon_state = "std_module" - w_class = 100 - item_state = "electronic" - flags = CONDUCT - - var/list/modules = list() - var/obj/item/emag = null - var/list/subsystems = list() - var/list/module_actions = list() - - var/module_type = "NoMod" // For icon usage - - var/list/stacktypes - var/channels = list() - var/list/custom_removals = list() - - -/obj/item/robot_module/emp_act(severity) - if(modules) - for(var/obj/O in modules) - O.emp_act(severity) - if(emag) - emag.emp_act(severity) - ..() - - -/obj/item/robot_module/New() - ..() - modules += new /obj/item/flash/cyborg(src) - emag = new /obj/item/toy/sword(src) - emag.name = "Placeholder Emag Item" - -/obj/item/robot_module/Destroy() - QDEL_LIST(modules) - QDEL_NULL(emag) - return ..() - -/obj/item/robot_module/proc/fix_modules() - for(var/obj/item/I in modules) - I.flags |= NODROP - I.mouse_opacity = MOUSE_OPACITY_OPAQUE - if(emag) - emag.flags |= NODROP - emag.mouse_opacity = MOUSE_OPACITY_OPAQUE - -/obj/item/robot_module/proc/respawn_consumable(mob/living/silicon/robot/R) - if(!stacktypes || !stacktypes.len) - return - - var/stack_respawned = 0 - for(var/T in stacktypes) - var/O = locate(T) in modules - var/obj/item/stack/S = O - - if(!S) - S = new T(src) - modules += S - S.amount = 1 - stack_respawned = 1 - - if(S && S.amount < stacktypes[T]) - S.amount++ - if(stack_respawned && istype(R) && R.hud_used) - R.hud_used.update_robot_modules_display() - -/obj/item/robot_module/proc/rebuild()//Rebuilds the list so it's possible to add/remove items from the module - var/list/temp_list = modules - modules = list() - for(var/obj/O in temp_list) - if(O) - modules += O - -/obj/item/robot_module/proc/add_languages(mob/living/silicon/robot/R) - //full set of languages - R.add_language("Galactic Common", 1) - R.add_language("Sol Common", 1) - R.add_language("Tradeband", 1) - R.add_language("Gutter", 0) - R.add_language("Neo-Russkiya", 0) - R.add_language("Sinta'unathi", 0) - R.add_language("Siik'tajr", 0) - R.add_language("Canilunzt", 0) - R.add_language("Skrellian", 0) - R.add_language("Vox-pidgin", 0) - R.add_language("Rootspeak", 0) - R.add_language("Trinary", 1) - R.add_language("Chittin", 0) - R.add_language("Bubblish", 0) - R.add_language("Orluum", 0) - R.add_language("Clownish",0) - -/obj/item/robot_module/proc/add_subsystems_and_actions(mob/living/silicon/robot/R) - R.verbs |= subsystems - for(var/A in module_actions) - var/datum/action/act = new A() - act.Grant(R) - R.module_actions += act - -/obj/item/robot_module/proc/remove_subsystems_and_actions(mob/living/silicon/robot/R) - R.verbs -= subsystems - for(var/datum/action/A in R.module_actions) - A.Remove(R) - qdel(A) - R.module_actions.Cut() - -// Return true in an overridden subtype to prevent normal removal handling -/obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W) - return FALSE - -/obj/item/robot_module/proc/handle_death(gibbed) - return - -/obj/item/robot_module/standard - name = "standard robot module" - module_type = "Standard" - -/obj/item/robot_module/standard/New() - ..() - modules += new /obj/item/melee/baton/loaded(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/healthanalyzer(src) - emag = new /obj/item/melee/energy/sword/cyborg(src) - - fix_modules() - -/obj/item/robot_module/medical - name = "medical robot module" - module_type = "Medical" - subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) - stacktypes = list( - /obj/item/stack/medical/bruise_pack/advanced = 6, - /obj/item/stack/medical/ointment/advanced = 6, - /obj/item/stack/medical/splint = 6, - /obj/item/stack/nanopaste = 6 - ) - -/obj/item/robot_module/medical/New() - ..() - modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/robotanalyzer(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/borg_defib(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) - modules += new /obj/item/reagent_containers/borghypo(src) - modules += new /obj/item/reagent_containers/glass/beaker/large(src) - modules += new /obj/item/reagent_containers/dropper(src) - modules += new /obj/item/reagent_containers/syringe(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced(src) - modules += new /obj/item/stack/medical/ointment/advanced(src) - modules += new /obj/item/stack/medical/splint(src) - modules += new /obj/item/stack/nanopaste(src) - modules += new /obj/item/scalpel/laser/laser1(src) - modules += new /obj/item/hemostat(src) - modules += new /obj/item/retractor(src) - modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) - modules += new /obj/item/bonesetter(src) - modules += new /obj/item/circular_saw(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) - - emag = new /obj/item/reagent_containers/spray(src) - - emag.reagents.add_reagent("facid", 250) - emag.name = "Polyacid spray" - - fix_modules() - -/obj/item/robot_module/medical/respawn_consumable(mob/living/silicon/robot/R) - if(emag) - var/obj/item/reagent_containers/spray/PS = emag - PS.reagents.add_reagent("facid", 2) - ..() - -/obj/item/robot_module/engineering - name = "engineering robot module" - module_type = "Engineer" - subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor) - module_actions = list( - /datum/action/innate/robot_sight/meson, - ) - - stacktypes = list( - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 50, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20 - ) - -/obj/item/robot_module/engineering/New() - ..() - modules += new /obj/item/rcd/borg(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/holosign_creator/engineering(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/matter_decompiler(src) - modules += new /obj/item/floor_painter(src) - modules += new /obj/item/areaeditor/blueprints/cyborg(src) - emag = new /obj/item/borg/stun(src) - - for(var/G in stacktypes) //Attempt to unify Engi-Borg material stacks into fewer lines. See Line 492 for example. Variables changed out of paranoia. - var/obj/item/stack/sheet/M = new G(src) - M.amount = stacktypes[G] - modules += M - - fix_modules() - -/obj/item/robot_module/engineering/handle_death() - var/obj/item/gripper/G = locate(/obj/item/gripper) in modules - if(G) - G.drop_item() - -/obj/item/robot_module/security - name = "security robot module" - module_type = "Security" - subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) - -/obj/item/robot_module/security/New() - ..() - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/melee/baton/loaded(src) - modules += new /obj/item/gun/energy/disabler/cyborg(src) - modules += new /obj/item/holosign_creator/security(src) - modules += new /obj/item/clothing/mask/gas/sechailer/cyborg(src) - emag = new /obj/item/gun/energy/laser/cyborg(src) - - fix_modules() - -/obj/item/robot_module/janitor - name = "janitorial robot module" - module_type = "Janitor" - -/obj/item/robot_module/janitor/New() - ..() - modules += new /obj/item/soap/nanotrasen(src) - modules += new /obj/item/storage/bag/trash/cyborg(src) - modules += new /obj/item/mop/advanced/cyborg(src) - modules += new /obj/item/lightreplacer/cyborg(src) - modules += new /obj/item/holosign_creator(src) - emag = new /obj/item/reagent_containers/spray(src) - - emag.reagents.add_reagent("lube", 250) - emag.name = "Lube spray" - - fix_modules() - -/obj/item/robot_module/butler - name = "service robot module" - module_type = "Service" - -/obj/item/robot_module/butler/New() - ..() - modules += new /obj/item/reagent_containers/food/drinks/cans/beer(src) - modules += new /obj/item/reagent_containers/food/drinks/cans/cola(src) - modules += new /obj/item/reagent_containers/food/drinks/cans/sodawater(src) - modules += new /obj/item/reagent_containers/food/condiment/enzyme(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/orangejuice(src) // -0.3 oxy/sec - modules += new /obj/item/reagent_containers/food/drinks/bottle/tomatojuice(src) // -0.2 fire/sec - modules += new /obj/item/reagent_containers/food/drinks/bottle/limejuice(src) // -0.2 tox/sec - modules += new /obj/item/reagent_containers/food/drinks/coffee(src) // -1 paralysis stunned & weakened/sec - modules += new /obj/item/reagent_containers/food/drinks/tea(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/milk(src) // -0.2 brute/sec - modules += new /obj/item/reagent_containers/food/condiment/sugar(src) - modules += new /obj/item/reagent_containers/food/drinks/ice(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/cream(src) - - modules += new /obj/item/reagent_containers/food/drinks/bottle/tequila(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/vodka(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/whiskey(src) - - modules += new /obj/item/pen(src) - modules += new /obj/item/razor(src) - modules += new /obj/item/instrument/piano_synth(src) - modules += new /obj/item/healthanalyzer/advanced(src) - - var/obj/item/rsf/M = new /obj/item/rsf(src) - M.matter = 30 - modules += M - - modules += new /obj/item/reagent_containers/dropper/cyborg(src) - modules += new /obj/item/lighter/zippo(src) - modules += new /obj/item/storage/bag/tray/cyborg(src) - modules += new /obj/item/reagent_containers/food/drinks/shaker(src) - emag = new /obj/item/reagent_containers/food/drinks/cans/beer(src) - - var/datum/reagents/R = new/datum/reagents(50) - emag.reagents = R - R.my_atom = emag - R.add_reagent("beer2", 50) - emag.name = "Mickey Finn's Special Brew" - - fix_modules() - -/obj/item/robot_module/butler/respawn_consumable(var/mob/living/silicon/robot/R) - var/obj/item/reagent_containers/food/condiment/enzyme/E = locate() in modules - E.reagents.add_reagent("enzyme", 2) - if(emag) - var/obj/item/reagent_containers/food/drinks/cans/beer/B = emag - B.reagents.add_reagent("beer2", 2) - ..() - -/obj/item/robot_module/butler/add_languages(var/mob/living/silicon/robot/R) - //full set of languages - R.add_language("Galactic Common", 1) - R.add_language("Sol Common", 1) - R.add_language("Tradeband", 1) - R.add_language("Gutter", 1) - R.add_language("Sinta'unathi", 1) - R.add_language("Siik'tajr", 1) - R.add_language("Canilunzt", 1) - R.add_language("Skrellian", 1) - R.add_language("Vox-pidgin", 1) - R.add_language("Rootspeak", 1) - R.add_language("Trinary", 1) - R.add_language("Chittin", 1) - R.add_language("Bubblish", 1) - R.add_language("Clownish",1) - R.add_language("Neo-Russkiya", 1) - - -/obj/item/robot_module/miner - name = "miner robot module" - module_type = "Miner" - module_actions = list( - /datum/action/innate/robot_sight/meson, - ) - custom_removals = list("KA modkits") - -/obj/item/robot_module/miner/New() - ..() - modules += new /obj/item/storage/bag/ore/cyborg(src) - modules += new /obj/item/pickaxe/drill/cyborg(src) - modules += new /obj/item/shovel(src) - modules += new /obj/item/weldingtool/mini(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/storage/bag/sheetsnatcher/borg(src) - modules += new /obj/item/t_scanner/adv_mining_scanner/cyborg(src) - modules += new /obj/item/gun/energy/kinetic_accelerator/cyborg(src) - modules += new /obj/item/gps/cyborg(src) - emag = new /obj/item/borg/stun(src) - - fix_modules() - -/obj/item/robot_module/miner/handle_custom_removal(component_id, mob/living/user, obj/item/W) - if(component_id == "KA modkits") - for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/D in src) - D.attackby(W, user) - return TRUE - return ..() - -/obj/item/robot_module/deathsquad - name = "NT advanced combat module" - module_type = "Malf" - module_actions = list( - /datum/action/innate/robot_sight/thermal, - ) - -/obj/item/robot_module/deathsquad/New() - ..() - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/gun/energy/pulse/cyborg(src) - modules += new /obj/item/crowbar(src) - emag = null - - fix_modules() - -/obj/item/robot_module/syndicate - name = "syndicate assault robot module" - module_type = "Malf" // cuz it looks cool - -/obj/item/robot_module/syndicate/New() - ..() - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/gun/energy/printer(src) - modules += new /obj/item/gun/projectile/revolver/grenadelauncher/multi/cyborg(src) - modules += new /obj/item/card/emag(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - fix_modules() - -/obj/item/robot_module/syndicate_medical - name = "syndicate medical robot module" - module_type = "Malf" - stacktypes = list( - /obj/item/stack/medical/bruise_pack/advanced = 25, - /obj/item/stack/medical/ointment/advanced = 25, - /obj/item/stack/medical/splint = 25, - /obj/item/stack/nanopaste = 25 - ) - -/obj/item/robot_module/syndicate_medical/New() - ..() - modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/bodyanalyzer/borg/syndicate(src) - modules += new /obj/item/borg_defib(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) - modules += new /obj/item/reagent_containers/borghypo/syndicate(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced(src) - modules += new /obj/item/stack/medical/ointment/advanced(src) - modules += new /obj/item/stack/medical/splint(src) - modules += new /obj/item/stack/nanopaste(src) - modules += new /obj/item/scalpel/laser/laser1(src) - modules += new /obj/item/hemostat(src) - modules += new /obj/item/retractor(src) - modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) - modules += new /obj/item/bonesetter(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) - modules += new /obj/item/gun/medbeam(src) - modules += new /obj/item/melee/energy/sword/cyborg/saw(src) //Energy saw -- primary weapon - modules += new /obj/item/card/emag(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - fix_modules() - -/obj/item/robot_module/syndicate_saboteur - name = "engineering robot module" //to disguise in examine - module_type = "Malf" - - stacktypes = list( - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 50, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20 - ) - -/obj/item/robot_module/syndicate_saboteur/New() - ..() - modules += new /obj/item/rcd/borg/syndicate(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/card/emag(src) - modules += new /obj/item/borg_chameleon(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - for(var/T in stacktypes) - var/obj/item/stack/sheet/W = new T(src) - W.amount = stacktypes[T] - modules += W - - fix_modules() - -/obj/item/robot_module/combat - name = "combat robot module" - module_type = "Malf" - module_actions = list( - /datum/action/innate/robot_sight/thermal, - ) - -/obj/item/robot_module/combat/New() - ..() - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/gun/energy/gun/cyborg(src) - modules += new /obj/item/pickaxe/drill/jackhammer(src) - modules += new /obj/item/borg/combat/shield(src) - modules += new /obj/item/borg/combat/mobility(src) - modules += new /obj/item/wrench/cyborg(src) - emag = new /obj/item/gun/energy/lasercannon/cyborg(src) - - fix_modules() - -/obj/item/robot_module/alien/hunter - name = "alien hunter module" - module_type = "Standard" - module_actions = list( - /datum/action/innate/robot_sight/thermal/alien, - ) - -/obj/item/robot_module/alien/hunter/New() - ..() - modules += new /obj/item/melee/energy/alien/claws(src) - modules += new /obj/item/flash/cyborg/alien(src) - var/obj/item/reagent_containers/spray/alien/stun/S = new /obj/item/reagent_containers/spray/alien/stun(src) - S.reagents.add_reagent("ether",250) //nerfed to sleeptoxin to make it less instant drop. - modules += S - var/obj/item/reagent_containers/spray/alien/smoke/A = new /obj/item/reagent_containers/spray/alien/smoke(src) - S.reagents.add_reagent("water",50) //Water is used as a dummy reagent for the smoke bombs. More of an ammo counter. - modules += A - emag = new /obj/item/reagent_containers/spray/alien/acid(src) - emag.reagents.add_reagent("facid", 125) - emag.reagents.add_reagent("sacid", 125) - - fix_modules() - -/obj/item/robot_module/alien/hunter/add_languages(var/mob/living/silicon/robot/R) - ..() - R.add_language("xenocommon", 1) - -/obj/item/robot_module/drone - name = "drone module" - module_type = "Engineer" - stacktypes = list( - /obj/item/stack/sheet/wood = 10, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/tile/wood = 20, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20, - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 30 - ) - -/obj/item/robot_module/drone/New() - ..() - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/lightreplacer/cyborg(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/matter_decompiler(src) - modules += new /obj/item/reagent_containers/spray/cleaner/drone(src) - modules += new /obj/item/soap(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/rpd(src) - - for(var/T in stacktypes) - var/obj/item/stack/sheet/W = new T(src) - W.amount = stacktypes[T] - modules += W - - fix_modules() - -/obj/item/robot_module/drone/respawn_consumable(mob/living/silicon/robot/R) - var/obj/item/reagent_containers/spray/cleaner/C = locate() in modules - C.reagents.add_reagent("cleaner", 3) - ..() - - -/obj/item/robot_module/drone/handle_death() - var/obj/item/gripper/G = locate(/obj/item/gripper) in modules - if(G) - G.drop_item() - -//checks whether this item is a module of the robot it is located in. -/obj/item/proc/is_robot_module() - if(!istype(loc, /mob/living/silicon/robot)) - return 0 - - var/mob/living/silicon/robot/R = loc - - return (src in R.module.modules) +/obj/item/robot_module + name = "robot module" + icon = 'icons/obj/module.dmi' + icon_state = "std_module" + w_class = 100 + item_state = "electronic" + flags = CONDUCT + + var/list/modules = list() + var/obj/item/emag = null + var/list/subsystems = list() + var/list/module_actions = list() + + var/module_type = "NoMod" // For icon usage + + var/list/stacktypes + var/channels = list() + var/list/custom_removals = list() + + +/obj/item/robot_module/emp_act(severity) + if(modules) + for(var/obj/O in modules) + O.emp_act(severity) + if(emag) + emag.emp_act(severity) + ..() + + +/obj/item/robot_module/New() + ..() + add_default_robot_items() + emag = new /obj/item/toy/sword(src) + emag.name = "Placeholder Emag Item" + +/obj/item/robot_module/Destroy() + QDEL_LIST(modules) + QDEL_NULL(emag) + return ..() + +// By default, all robots will get the items in this proc, unless you override it for your specific module. See: ../robot_module/drone +/obj/item/robot_module/proc/add_default_robot_items() + modules += new /obj/item/flash/cyborg(src) + +/obj/item/robot_module/proc/fix_modules() + for(var/obj/item/I in modules) + I.flags |= NODROP + I.mouse_opacity = MOUSE_OPACITY_OPAQUE + if(emag) + emag.flags |= NODROP + emag.mouse_opacity = MOUSE_OPACITY_OPAQUE + +/obj/item/robot_module/proc/respawn_consumable(mob/living/silicon/robot/R) + if(!stacktypes || !stacktypes.len) + return + + var/stack_respawned = 0 + for(var/T in stacktypes) + var/O = locate(T) in modules + var/obj/item/stack/S = O + + if(!S) + S = new T(src) + modules += S + S.amount = 1 + stack_respawned = 1 + + if(S && S.amount < stacktypes[T]) + S.amount++ + if(stack_respawned && istype(R) && R.hud_used) + R.hud_used.update_robot_modules_display() + +/obj/item/robot_module/proc/rebuild()//Rebuilds the list so it's possible to add/remove items from the module + var/list/temp_list = modules + modules = list() + for(var/obj/O in temp_list) + if(O) + modules += O + +/obj/item/robot_module/proc/add_languages(mob/living/silicon/robot/R) + //full set of languages + R.add_language("Galactic Common", 1) + R.add_language("Sol Common", 1) + R.add_language("Tradeband", 1) + R.add_language("Gutter", 0) + R.add_language("Neo-Russkiya", 0) + R.add_language("Sinta'unathi", 0) + R.add_language("Siik'tajr", 0) + R.add_language("Canilunzt", 0) + R.add_language("Skrellian", 0) + R.add_language("Vox-pidgin", 0) + R.add_language("Rootspeak", 0) + R.add_language("Trinary", 1) + R.add_language("Chittin", 0) + R.add_language("Bubblish", 0) + R.add_language("Orluum", 0) + R.add_language("Clownish",0) + +/obj/item/robot_module/proc/add_subsystems_and_actions(mob/living/silicon/robot/R) + R.verbs |= subsystems + for(var/A in module_actions) + var/datum/action/act = new A() + act.Grant(R) + R.module_actions += act + +/obj/item/robot_module/proc/remove_subsystems_and_actions(mob/living/silicon/robot/R) + R.verbs -= subsystems + for(var/datum/action/A in R.module_actions) + A.Remove(R) + qdel(A) + R.module_actions.Cut() + +// Return true in an overridden subtype to prevent normal removal handling +/obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W) + return FALSE + +/obj/item/robot_module/proc/handle_death(gibbed) + return + +/obj/item/robot_module/standard + name = "standard robot module" + module_type = "Standard" + +/obj/item/robot_module/standard/New() + ..() + modules += new /obj/item/melee/baton/loaded(src) + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/healthanalyzer(src) + emag = new /obj/item/melee/energy/sword/cyborg(src) + + fix_modules() + +/obj/item/robot_module/medical + name = "medical robot module" + module_type = "Medical" + subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) + stacktypes = list( + /obj/item/stack/medical/bruise_pack/advanced = 6, + /obj/item/stack/medical/ointment/advanced = 6, + /obj/item/stack/medical/splint = 6, + /obj/item/stack/nanopaste = 6 + ) + +/obj/item/robot_module/medical/New() + ..() + modules += new /obj/item/healthanalyzer/advanced(src) + modules += new /obj/item/robotanalyzer(src) + modules += new /obj/item/reagent_scanner/adv(src) + modules += new /obj/item/borg_defib(src) + modules += new /obj/item/handheld_defibrillator(src) + modules += new /obj/item/roller_holder(src) + modules += new /obj/item/reagent_containers/borghypo(src) + modules += new /obj/item/reagent_containers/glass/beaker/large(src) + modules += new /obj/item/reagent_containers/dropper(src) + modules += new /obj/item/reagent_containers/syringe(src) + modules += new /obj/item/extinguisher/mini(src) + modules += new /obj/item/stack/medical/bruise_pack/advanced(src) + modules += new /obj/item/stack/medical/ointment/advanced(src) + modules += new /obj/item/stack/medical/splint(src) + modules += new /obj/item/stack/nanopaste(src) + modules += new /obj/item/scalpel/laser/laser1(src) + modules += new /obj/item/hemostat(src) + modules += new /obj/item/retractor(src) + modules += new /obj/item/bonegel(src) + modules += new /obj/item/FixOVein(src) + modules += new /obj/item/bonesetter(src) + modules += new /obj/item/circular_saw(src) + modules += new /obj/item/surgicaldrill(src) + modules += new /obj/item/gripper/medical(src) + + emag = new /obj/item/reagent_containers/spray(src) + + emag.reagents.add_reagent("facid", 250) + emag.name = "Polyacid spray" + + fix_modules() + +/obj/item/robot_module/medical/respawn_consumable(mob/living/silicon/robot/R) + if(emag) + var/obj/item/reagent_containers/spray/PS = emag + PS.reagents.add_reagent("facid", 2) + ..() + +/obj/item/robot_module/engineering + name = "engineering robot module" + module_type = "Engineer" + subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor) + module_actions = list( + /datum/action/innate/robot_sight/meson, + ) + + stacktypes = list( + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/sheet/glass/cyborg = 50, + /obj/item/stack/sheet/rglass/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 50, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20 + ) + +/obj/item/robot_module/engineering/New() + ..() + modules += new /obj/item/rcd/borg(src) + modules += new /obj/item/rpd(src) + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/screwdriver/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/analyzer(src) + modules += new /obj/item/holosign_creator/engineering(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/matter_decompiler(src) + modules += new /obj/item/floor_painter(src) + modules += new /obj/item/areaeditor/blueprints/cyborg(src) + emag = new /obj/item/borg/stun(src) + + for(var/G in stacktypes) //Attempt to unify Engi-Borg material stacks into fewer lines. See Line 492 for example. Variables changed out of paranoia. + var/obj/item/stack/sheet/M = new G(src) + M.amount = stacktypes[G] + modules += M + + fix_modules() + +/obj/item/robot_module/engineering/handle_death() + var/obj/item/gripper/G = locate(/obj/item/gripper) in modules + if(G) + G.drop_gripped_item(silent = TRUE) + +/obj/item/robot_module/security + name = "security robot module" + module_type = "Security" + subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) + +/obj/item/robot_module/security/New() + ..() + modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) + modules += new /obj/item/melee/baton/loaded(src) + modules += new /obj/item/gun/energy/disabler/cyborg(src) + modules += new /obj/item/holosign_creator/security(src) + modules += new /obj/item/clothing/mask/gas/sechailer/cyborg(src) + emag = new /obj/item/gun/energy/laser/cyborg(src) + + fix_modules() + +/obj/item/robot_module/janitor + name = "janitorial robot module" + module_type = "Janitor" + +/obj/item/robot_module/janitor/New() + ..() + modules += new /obj/item/soap/nanotrasen(src) + modules += new /obj/item/storage/bag/trash/cyborg(src) + modules += new /obj/item/mop/advanced/cyborg(src) + modules += new /obj/item/lightreplacer/cyborg(src) + modules += new /obj/item/holosign_creator(src) + emag = new /obj/item/reagent_containers/spray(src) + + emag.reagents.add_reagent("lube", 250) + emag.name = "Lube spray" + + fix_modules() + +/obj/item/robot_module/butler + name = "service robot module" + module_type = "Service" + +/obj/item/robot_module/butler/New() + ..() + modules += new /obj/item/reagent_containers/food/drinks/cans/beer(src) + modules += new /obj/item/reagent_containers/food/drinks/cans/cola(src) + modules += new /obj/item/reagent_containers/food/drinks/cans/sodawater(src) + modules += new /obj/item/reagent_containers/food/condiment/enzyme(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/orangejuice(src) // -0.3 oxy/sec + modules += new /obj/item/reagent_containers/food/drinks/bottle/tomatojuice(src) // -0.2 fire/sec + modules += new /obj/item/reagent_containers/food/drinks/bottle/limejuice(src) // -0.2 tox/sec + modules += new /obj/item/reagent_containers/food/drinks/coffee(src) // -1 paralysis stunned & weakened/sec + modules += new /obj/item/reagent_containers/food/drinks/tea(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/milk(src) // -0.2 brute/sec + modules += new /obj/item/reagent_containers/food/condiment/sugar(src) + modules += new /obj/item/reagent_containers/food/drinks/ice(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/cream(src) + + modules += new /obj/item/reagent_containers/food/drinks/bottle/tequila(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/vodka(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/whiskey(src) + + modules += new /obj/item/pen(src) + modules += new /obj/item/razor(src) + modules += new /obj/item/instrument/piano_synth(src) + modules += new /obj/item/healthanalyzer/advanced(src) + + var/obj/item/rsf/M = new /obj/item/rsf(src) + M.matter = 30 + modules += M + + modules += new /obj/item/reagent_containers/dropper/cyborg(src) + modules += new /obj/item/lighter/zippo(src) + modules += new /obj/item/storage/bag/tray/cyborg(src) + modules += new /obj/item/reagent_containers/food/drinks/shaker(src) + emag = new /obj/item/reagent_containers/food/drinks/cans/beer(src) + + var/datum/reagents/R = new/datum/reagents(50) + emag.reagents = R + R.my_atom = emag + R.add_reagent("beer2", 50) + emag.name = "Mickey Finn's Special Brew" + + fix_modules() + +/obj/item/robot_module/butler/respawn_consumable(var/mob/living/silicon/robot/R) + var/obj/item/reagent_containers/food/condiment/enzyme/E = locate() in modules + E.reagents.add_reagent("enzyme", 2) + if(emag) + var/obj/item/reagent_containers/food/drinks/cans/beer/B = emag + B.reagents.add_reagent("beer2", 2) + ..() + +/obj/item/robot_module/butler/add_languages(var/mob/living/silicon/robot/R) + //full set of languages + R.add_language("Galactic Common", 1) + R.add_language("Sol Common", 1) + R.add_language("Tradeband", 1) + R.add_language("Gutter", 1) + R.add_language("Sinta'unathi", 1) + R.add_language("Siik'tajr", 1) + R.add_language("Canilunzt", 1) + R.add_language("Skrellian", 1) + R.add_language("Vox-pidgin", 1) + R.add_language("Rootspeak", 1) + R.add_language("Trinary", 1) + R.add_language("Chittin", 1) + R.add_language("Bubblish", 1) + R.add_language("Clownish",1) + R.add_language("Neo-Russkiya", 1) + + +/obj/item/robot_module/miner + name = "miner robot module" + module_type = "Miner" + module_actions = list( + /datum/action/innate/robot_sight/meson, + ) + custom_removals = list("KA modkits") + +/obj/item/robot_module/miner/New() + ..() + modules += new /obj/item/storage/bag/ore/cyborg(src) + modules += new /obj/item/pickaxe/drill/cyborg(src) + modules += new /obj/item/shovel(src) + modules += new /obj/item/weldingtool/mini(src) + modules += new /obj/item/extinguisher/mini(src) + modules += new /obj/item/storage/bag/sheetsnatcher/borg(src) + modules += new /obj/item/t_scanner/adv_mining_scanner/cyborg(src) + modules += new /obj/item/gun/energy/kinetic_accelerator/cyborg(src) + modules += new /obj/item/gps/cyborg(src) + emag = new /obj/item/borg/stun(src) + + fix_modules() + +/obj/item/robot_module/miner/handle_custom_removal(component_id, mob/living/user, obj/item/W) + if(component_id == "KA modkits") + for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/D in src) + D.attackby(W, user) + return TRUE + return ..() + +/obj/item/robot_module/deathsquad + name = "NT advanced combat module" + module_type = "Malf" + module_actions = list( + /datum/action/innate/robot_sight/thermal, + ) + +/obj/item/robot_module/deathsquad/New() + ..() + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/gun/energy/pulse/cyborg(src) + modules += new /obj/item/crowbar(src) + emag = null + + fix_modules() + +/obj/item/robot_module/syndicate + name = "syndicate assault robot module" + module_type = "Malf" // cuz it looks cool + +/obj/item/robot_module/syndicate/New() + ..() + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/gun/energy/printer(src) + modules += new /obj/item/gun/projectile/revolver/grenadelauncher/multi/cyborg(src) + modules += new /obj/item/card/emag(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/pinpointer/operative(src) + emag = null + + fix_modules() + +/obj/item/robot_module/syndicate_medical + name = "syndicate medical robot module" + module_type = "Malf" + stacktypes = list( + /obj/item/stack/medical/bruise_pack/advanced = 25, + /obj/item/stack/medical/ointment/advanced = 25, + /obj/item/stack/medical/splint = 25, + /obj/item/stack/nanopaste = 25 + ) + +/obj/item/robot_module/syndicate_medical/New() + ..() + modules += new /obj/item/healthanalyzer/advanced(src) + modules += new /obj/item/reagent_scanner/adv(src) + modules += new /obj/item/bodyanalyzer/borg/syndicate(src) + modules += new /obj/item/borg_defib(src) + modules += new /obj/item/handheld_defibrillator(src) + modules += new /obj/item/roller_holder(src) + modules += new /obj/item/reagent_containers/borghypo/syndicate(src) + modules += new /obj/item/extinguisher/mini(src) + modules += new /obj/item/stack/medical/bruise_pack/advanced(src) + modules += new /obj/item/stack/medical/ointment/advanced(src) + modules += new /obj/item/stack/medical/splint(src) + modules += new /obj/item/stack/nanopaste(src) + modules += new /obj/item/scalpel/laser/laser1(src) + modules += new /obj/item/hemostat(src) + modules += new /obj/item/retractor(src) + modules += new /obj/item/bonegel(src) + modules += new /obj/item/FixOVein(src) + modules += new /obj/item/bonesetter(src) + modules += new /obj/item/surgicaldrill(src) + modules += new /obj/item/gripper/medical(src) + modules += new /obj/item/gun/medbeam(src) + modules += new /obj/item/melee/energy/sword/cyborg/saw(src) //Energy saw -- primary weapon + modules += new /obj/item/card/emag(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/pinpointer/operative(src) + emag = null + + fix_modules() + +/obj/item/robot_module/syndicate_saboteur + name = "engineering robot module" //to disguise in examine + module_type = "Malf" + + stacktypes = list( + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/sheet/glass/cyborg = 50, + /obj/item/stack/sheet/rglass/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 50, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20 + ) + +/obj/item/robot_module/syndicate_saboteur/New() + ..() + modules += new /obj/item/rcd/borg/syndicate(src) + modules += new /obj/item/rpd(src) + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/screwdriver/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/analyzer(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/card/emag(src) + modules += new /obj/item/borg_chameleon(src) + modules += new /obj/item/pinpointer/operative(src) + emag = null + + for(var/T in stacktypes) + var/obj/item/stack/sheet/W = new T(src) + W.amount = stacktypes[T] + modules += W + + fix_modules() + +/obj/item/robot_module/combat + name = "combat robot module" + module_type = "Malf" + module_actions = list( + /datum/action/innate/robot_sight/thermal, + ) + +/obj/item/robot_module/combat/New() + ..() + modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) + modules += new /obj/item/gun/energy/gun/cyborg(src) + modules += new /obj/item/pickaxe/drill/jackhammer(src) + modules += new /obj/item/borg/combat/shield(src) + modules += new /obj/item/borg/combat/mobility(src) + modules += new /obj/item/wrench/cyborg(src) + emag = new /obj/item/gun/energy/lasercannon/cyborg(src) + + fix_modules() + +/obj/item/robot_module/alien/hunter + name = "alien hunter module" + module_type = "Standard" + module_actions = list( + /datum/action/innate/robot_sight/thermal/alien, + ) + +/obj/item/robot_module/alien/hunter/New() + ..() + modules += new /obj/item/melee/energy/alien/claws(src) + modules += new /obj/item/flash/cyborg/alien(src) + var/obj/item/reagent_containers/spray/alien/stun/S = new /obj/item/reagent_containers/spray/alien/stun(src) + S.reagents.add_reagent("ether",250) //nerfed to sleeptoxin to make it less instant drop. + modules += S + var/obj/item/reagent_containers/spray/alien/smoke/A = new /obj/item/reagent_containers/spray/alien/smoke(src) + S.reagents.add_reagent("water",50) //Water is used as a dummy reagent for the smoke bombs. More of an ammo counter. + modules += A + emag = new /obj/item/reagent_containers/spray/alien/acid(src) + emag.reagents.add_reagent("facid", 125) + emag.reagents.add_reagent("sacid", 125) + + fix_modules() + +/obj/item/robot_module/alien/hunter/add_languages(var/mob/living/silicon/robot/R) + ..() + R.add_language("xenocommon", 1) + +/obj/item/robot_module/drone + name = "drone module" + module_type = "Engineer" + stacktypes = list( + /obj/item/stack/sheet/wood = 10, + /obj/item/stack/sheet/rglass/cyborg = 50, + /obj/item/stack/tile/wood = 20, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20, + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/sheet/glass/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 30 + ) + +/obj/item/robot_module/drone/New() + ..() + modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/screwdriver/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/lightreplacer/cyborg(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/matter_decompiler(src) + modules += new /obj/item/reagent_containers/spray/cleaner/drone(src) + modules += new /obj/item/soap(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/rpd(src) + + for(var/T in stacktypes) + var/obj/item/stack/sheet/W = new T(src) + W.amount = stacktypes[T] + modules += W + + fix_modules() + +/obj/item/robot_module/drone/add_default_robot_items() + return + +/obj/item/robot_module/drone/respawn_consumable(mob/living/silicon/robot/R) + var/obj/item/reagent_containers/spray/cleaner/C = locate() in modules + C.reagents.add_reagent("cleaner", 3) + ..() + + +/obj/item/robot_module/drone/handle_death() + var/obj/item/gripper/G = locate(/obj/item/gripper) in modules + if(G) + G.drop_gripped_item(silent = TRUE) + +//checks whether this item is a module of the robot it is located in. +/obj/item/proc/is_robot_module() + if(!istype(loc, /mob/living/silicon/robot)) + return 0 + + var/mob/living/silicon/robot/R = loc + + return (src in R.module.modules) diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index e896e7c9473..4b645b02849 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -21,4 +21,4 @@ /mob/living/silicon/robot/experience_pressure_difference(pressure_difference, direction) if(!magpulse) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/silicon/robot/syndicate.dm b/code/modules/mob/living/silicon/robot/syndicate.dm index f97b1f5c005..f5ac3e5532b 100644 --- a/code/modules/mob/living/silicon/robot/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/syndicate.dm @@ -7,7 +7,7 @@ faction = list("syndicate") designation = "Syndicate Assault" modtype = "Syndicate" - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) ionpulse = 1 lawchannel = "State" var/playstyle_string = "You are a Syndicate assault cyborg!
    \ @@ -137,9 +137,9 @@ ..() /mob/living/silicon/robot/syndicate/saboteur/emp_act() + ..() if(cham_proj) cham_proj.disrupt(src) - ..() /mob/living/silicon/robot/syndicate/saboteur/bullet_act() if(cham_proj) diff --git a/code/modules/mob/living/silicon/robot/update_status.dm b/code/modules/mob/living/silicon/robot/update_status.dm index 63c13f97d24..5f1519395c7 100644 --- a/code/modules/mob/living/silicon/robot/update_status.dm +++ b/code/modules/mob/living/silicon/robot/update_status.dm @@ -30,6 +30,7 @@ to_chat(ghost, "Your cyborg shell has been repaired, re-enter if you want to continue! (Verbs -> Ghost -> Re-enter corpse)") ghost << sound('sound/effects/genetics.ogg') create_attack_log("revived, trigger reason: [reason]") + create_log(MISC_LOG, "revived, trigger reason: [reason]") // diag_hud_set_status() // diag_hud_set_health() // update_health_hud() diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 8092947919f..f75475fa92d 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -1,115 +1,115 @@ -/mob/living/silicon/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - log_say(multilingual_to_message(message_pieces), src) - if(..()) - return 1 - -/mob/living/silicon/robot/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(..()) - return 1 - if(message_mode) - used_radios += radio - if(!is_component_functioning("radio")) - to_chat(src, "Your radio isn't functional at this time.") - return 0 - if(message_mode == "general") - message_mode = null - return radio.talk_into(src,message_pieces,message_mode,verb) - -/mob/living/silicon/ai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(..()) - return 1 - if(message_mode == "department") - used_radios += aiRadio - return holopad_talk(message_pieces, verb) - else if(message_mode) - used_radios += aiRadio - if(aiRadio.disabledAi || aiRestorePowerRoutine || stat) - to_chat(src, "System Error - Transceiver Disabled.") - return 0 - if(message_mode == "general") - message_mode = null - return aiRadio.talk_into(src, message_pieces, message_mode, verb) - -/mob/living/silicon/pai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(..()) - return 1 - else if(message_mode == "whisper") - whisper_say(message_pieces) - return 1 - else if(message_mode) - if(message_mode == "general") - message_mode = null - used_radios += radio - return radio.talk_into(src, message_pieces, message_mode, verb) - -/mob/living/silicon/say_quote(var/text) - var/ending = copytext(text, length(text)) - - if(ending == "?") - return speak_query - else if(ending == "!") - return speak_exclamation - - return speak_statement - -#define IS_AI 1 -#define IS_ROBOT 2 -#define IS_PAI 3 - -/mob/living/silicon/say_understands(var/other,var/datum/language/speaking = null) - //These only pertain to common. Languages are handled by mob/say_understands() - if(!speaking) - if(istype(other, /mob/living/carbon)) - return 1 - if(istype(other, /mob/living/silicon)) - return 1 - if(istype(other, /mob/living/simple_animal/bot)) - return 1 - if(istype(other, /mob/living/carbon/brain)) - return 1 - return ..() - -//For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb) - log_say("(HPAD) [multilingual_to_message(message_pieces)]", src) - - var/obj/machinery/hologram/holopad/T = current - if(istype(T) && T.masters[src]) - for(var/mob/M in hearers(T.loc))//The location is the object, default distance. - M.hear_holopad_talk(message_pieces, verb, src) - to_chat(src, "Holopad transmitted, [real_name] [combine_message(message_pieces, verb, src)]") - else - to_chat(src, "No holopad connected.") - return - return 1 - -/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad. - message = trim(message) - - if(!message) - return - - var/obj/machinery/hologram/holopad/T = current - if(istype(T) && T.masters[src]) - var/rendered = "[name] [message]" - to_chat(src, "Holopad action relayed, [real_name] [message]") - - for(var/mob/M in viewers(T.loc)) - M.show_message(rendered, 2) - - log_emote("(HPAD) [message]", src) - else //This shouldn't occur, but better safe then sorry. - to_chat(src, "No holopad connected.") - return - return 1 - -/mob/living/silicon/ai/emote(act, type, message, force) - var/obj/machinery/hologram/holopad/T = current - if(istype(T) && T.masters[src])//Is the AI using a holopad? - src.holopad_emote(message) - else //Emote normally, then. - ..() - -#undef IS_AI -#undef IS_ROBOT -#undef IS_PAI +/mob/living/silicon/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + log_say(multilingual_to_message(message_pieces), src) + if(..()) + return 1 + +/mob/living/silicon/robot/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(..()) + return 1 + if(message_mode) + used_radios += radio + if(!is_component_functioning("radio")) + to_chat(src, "Your radio isn't functional at this time.") + return 0 + if(message_mode == "general") + message_mode = null + return radio.talk_into(src,message_pieces,message_mode,verb) + +/mob/living/silicon/ai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(..()) + return 1 + if(message_mode == "department") + used_radios += aiRadio + return holopad_talk(message_pieces, verb) + else if(message_mode) + used_radios += aiRadio + if(aiRadio.disabledAi || aiRestorePowerRoutine || stat) + to_chat(src, "System Error - Transceiver Disabled.") + return 0 + if(message_mode == "general") + message_mode = null + return aiRadio.talk_into(src, message_pieces, message_mode, verb) + +/mob/living/silicon/pai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(..()) + return 1 + else if(message_mode == "whisper") + whisper_say(message_pieces) + return 1 + else if(message_mode) + if(message_mode == "general") + message_mode = null + used_radios += radio + return radio.talk_into(src, message_pieces, message_mode, verb) + +/mob/living/silicon/say_quote(var/text) + var/ending = copytext(text, length(text)) + + if(ending == "?") + return speak_query + else if(ending == "!") + return speak_exclamation + + return speak_statement + +#define IS_AI 1 +#define IS_ROBOT 2 +#define IS_PAI 3 + +/mob/living/silicon/say_understands(var/other,var/datum/language/speaking = null) + //These only pertain to common. Languages are handled by mob/say_understands() + if(!speaking) + if(istype(other, /mob/living/carbon)) + return 1 + if(istype(other, /mob/living/silicon)) + return 1 + if(istype(other, /mob/living/simple_animal/bot)) + return 1 + if(istype(other, /mob/living/carbon/brain)) + return 1 + return ..() + +//For holopads only. Usable by AI. +/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb) + log_say("(HPAD) [multilingual_to_message(message_pieces)]", src) + + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.masters[src]) + for(var/mob/M in hearers(T.loc))//The location is the object, default distance. + M.hear_holopad_talk(message_pieces, verb, src) + to_chat(src, "Holopad transmitted, [real_name] [combine_message(message_pieces, verb, src)]") + else + to_chat(src, "No holopad connected.") + return + return 1 + +/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad. + message = trim(message) + + if(!message) + return + + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.masters[src]) + var/rendered = "[name] [message]" + to_chat(src, "Holopad action relayed, [real_name] [message]") + + for(var/mob/M in viewers(T.loc)) + M.show_message(rendered, 2) + + log_emote("(HPAD) [message]", src) + else //This shouldn't occur, but better safe then sorry. + to_chat(src, "No holopad connected.") + return + return 1 + +/mob/living/silicon/ai/emote(act, type, message, force) + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.masters[src])//Is the AI using a holopad? + src.holopad_emote(message) + else //Emote normally, then. + ..() + +#undef IS_AI +#undef IS_ROBOT +#undef IS_PAI diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index e70c00158a7..6457afc87d4 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -1,360 +1,360 @@ -/mob/living/silicon - gender = NEUTER - robot_talk_understand = 1 - voice_name = "synthesized voice" - has_unlimited_silicon_privilege = 1 - var/syndicate = 0 - var/const/MAIN_CHANNEL = "Main Frequency" - var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws - var/list/stating_laws = list()// Channels laws are currently being stated on - var/list/alarms_to_show = list() - var/list/alarms_to_clear = list() - //var/list/hud_list[10] - var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer - var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to - var/designation = "" - var/obj/item/camera/siliconcam/aiCamera = null //photography -//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented. - var/speak_statement = "states" - var/speak_exclamation = "declares" - var/speak_query = "queries" - var/pose //Yes, now AIs can pose too. - var/death_sound = 'sound/voice/borg_deathsound.ogg' - - //var/sensor_mode = 0 //Determines the current HUD. - - var/next_alarm_notice - var/list/datum/alarm/queued_alarms = new() - - hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD) - - - var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use - var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use - var/d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED //There is only one kind of diag hud - - var/obj/item/radio/common_radio - -/mob/living/silicon/New() - GLOB.silicon_mob_list |= src - ..() - var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC] - diag_hud.add_to_hud(src) - diag_hud_set_status() - diag_hud_set_health() - add_language("Galactic Common") - init_subsystems() - -/mob/living/silicon/med_hud_set_health() - return //we use a different hud - -/mob/living/silicon/med_hud_set_status() - return //we use a different hud - -/mob/living/silicon/Destroy() - GLOB.silicon_mob_list -= src - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.unregister(src) - return ..() - -/mob/living/silicon/rename_character(oldname, newname) - // we actually don't want it changing minds and stuff - if(!newname) - return 0 - - real_name = newname - name = real_name - return 1 - -/mob/living/silicon/proc/show_laws() - return - -/mob/living/silicon/drop_item() - return - -/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) - return FALSE //So borgs they don't die trying to fix wiring - -/mob/living/silicon/emp_act(severity) - switch(severity) - if(1) - src.take_organ_damage(20) - Stun(8) - if(2) - src.take_organ_damage(10) - Stun(3) - flash_eyes(affect_silicon = 1) - to_chat(src, "*BZZZT*") - to_chat(src, "Warning: Electromagnetic pulse detected.") - ..() - - -/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) - return - -/mob/living/silicon/can_inject(mob/user, error_msg) - if(error_msg) - to_chat(user, "[p_their(TRUE)] outer shell is too tough.") - return FALSE - -/mob/living/silicon/IsAdvancedToolUser() - return TRUE - -/mob/living/silicon/robot/welder_act(mob/user, obj/item/I) - if(user.a_intent != INTENT_HELP) - return - if(user == src) //No self-repair dummy - return - . = TRUE - if(!getBruteLoss()) - to_chat(user, "Nothing to fix!") - return - else if(!getBruteLoss(TRUE)) - to_chat(user, "The damaged components are beyond saving!") - return - if(!I.use_tool(src, user, volume = I.tool_volume)) - return - adjustBruteLoss(-30) - add_fingerprint(user) - user.visible_message("[user] patches some dents on [src] with [I].") - - -/mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) - - - if(!Proj.nodamage) - switch(Proj.damage_type) - if(BRUTE) - adjustBruteLoss(Proj.damage) - if(BURN) - adjustFireLoss(Proj.damage) - - Proj.on_hit(src,2) - - return 2 - -/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) - return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now -/* - if(!effect || (blocked >= 2)) return 0 - switch(effecttype) - if(STUN) - Stun(effect / (blocked + 1)) - if(WEAKEN) - Weaken(effect / (blocked + 1)) - if(PARALYZE) - Paralyse(effect / (blocked + 1)) - if(IRRADIATE) - radiation += min((effect - (effect*getarmor(null, "rad"))), 0)//Rads auto check armor - if(STUTTER) - stuttering = max(stuttering,(effect/(blocked+1))) - if(EYE_BLUR) - eye_blurry = max(eye_blurry,(effect/(blocked+1))) - if(DROWSY) - drowsyness = max(drowsyness,(effect/(blocked+1))) - updatehealth() - return 1*/ - -/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) - if(!istype(bot) || !istype(ai)) - return 0 - if(bot.connected_ai == ai) - return 1 - return 0 - - -// this function shows the health of the pAI in the Status panel -/mob/living/silicon/proc/show_system_integrity() - if(!src.stat) - stat(null, text("System integrity: [round((health/maxHealth)*100)]%")) - else - stat(null, text("Systems nonfunctional")) - - -// This adds the basic clock, shuttle recall timer, and malf_ai info to all silicon lifeforms -/mob/living/silicon/Stat() - ..() - if(statpanel("Status")) - show_stat_emergency_shuttle_eta() - show_system_integrity() - -//Silicon mob language procs - -/mob/living/silicon/can_speak_language(datum/language/speaking) - return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language - -/mob/living/silicon/add_language(var/language, var/can_speak=1) - if(..(language) && can_speak) - speech_synthesizer_langs.Add(GLOB.all_languages[language]) - return 1 - -/mob/living/silicon/remove_language(var/rem_language) - ..(rem_language) - - for(var/datum/language/L in speech_synthesizer_langs) - if(L.name == rem_language) - speech_synthesizer_langs -= L - -/mob/living/silicon/check_lang_data() - . = "" - - if(default_language) - . += "Current default language: [default_language] - reset

    " - - for(var/datum/language/L in languages) - if(!(L.flags & NONGLOBAL)) - var/default_str - if(L == default_language) - default_str = " - default - reset" - else - default_str = " - set default" - - var/synth = (L in speech_synthesizer_langs) - . += "[L.name] (:[L.key])[synth ? default_str : null]
    Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
    [L.desc]

    " - - -// this function displays the stations manifest in a separate window -/mob/living/silicon/proc/show_station_manifest() - var/dat - dat += "

    Crew Manifest

    " - if(data_core) - dat += data_core.get_manifest(1) // make it monochrome - dat += "
    " - src << browse(dat, "window=airoster") - onclose(src, "airoster") - -/mob/living/silicon/assess_threat() //Secbots won't hunt silicon units - return -10 - -/mob/living/silicon/verb/pose() - set name = "Set Pose" - set desc = "Sets a description which will be shown when someone examines you." - set category = "IC" - - pose = sanitize(copytext(input(usr, "This is [src]. It is...", "Pose", null) as text, 1, MAX_MESSAGE_LEN)) - -/mob/living/silicon/verb/set_flavor() - set name = "Set Flavour Text" - set desc = "Sets an extended description of your character's features." - set category = "IC" - - update_flavor_text() - -/mob/living/silicon/binarycheck() - return 1 - -/mob/living/silicon/proc/remove_med_sec_hud() - var/datum/atom_hud/secsensor = huds[sec_hud] - var/datum/atom_hud/medsensor = huds[med_hud] - for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) - diagsensor.remove_hud_from(src) - secsensor.remove_hud_from(src) - medsensor.remove_hud_from(src) - - -/mob/living/silicon/proc/add_sec_hud() - var/datum/atom_hud/secsensor = huds[sec_hud] - secsensor.add_hud_to(src) - -/mob/living/silicon/proc/add_med_hud() - var/datum/atom_hud/medsensor = huds[med_hud] - medsensor.add_hud_to(src) - -/mob/living/silicon/proc/add_diag_hud() - for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) - diagsensor.add_hud_to(src) - - -/mob/living/silicon/proc/toggle_sensor_mode() - var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security", "Medical","Diagnostic","Disable") - remove_med_sec_hud() - switch(sensor_type) - if("Security") - add_sec_hud() - to_chat(src, "Security records overlay enabled.") - if("Medical") - add_med_hud() - to_chat(src, "Life signs monitor overlay enabled.") - if("Diagnostic") - add_diag_hud() - to_chat(src, "Robotics diagnostic overlay enabled.") - if("Disable") - to_chat(src, "Sensor augmentations disabled.") - -/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) - if(!next_alarm_notice) - next_alarm_notice = world.time + SecondsToTicks(10) - - var/list/alarms = queued_alarms[alarm_handler] - if(was_raised) - // Raised alarms are always set - alarms[alarm] = 1 - else - // Alarms that were raised but then cleared before the next notice are instead removed - if(alarm in alarms) - alarms -= alarm - // And alarms that have only been cleared thus far are set as such - else - alarms[alarm] = -1 - -/mob/living/silicon/proc/process_queued_alarms() - if(next_alarm_notice && (world.time > next_alarm_notice)) - next_alarm_notice = 0 - - var/alarm_raised = 0 - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == 1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Detected ---") - raised_alarm(A) - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == -1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Cleared ---") - to_chat(src, "\The [A.alarm_name()].") - - if(alarm_raised) - to_chat(src, "\[Show Alerts\]") - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - alarms.Cut() - -/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A) - to_chat(src, "[A.alarm_name()]!") - -/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A) - var/cameratext = "" - for(var/obj/machinery/camera/C in A.cameras()) - cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]" - to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])") - -/mob/living/silicon/adjustToxLoss(var/amount) - return STATUS_UPDATE_NONE - -/mob/living/silicon/get_access() - return IGNORE_ACCESS //silicons always have access - -/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/noise) - if(affect_silicon) - return ..() - -/mob/living/silicon/is_mechanical() - return 1 - -/mob/living/silicon/is_literate() - return 1 - -/////////////////////////////////// EAR DAMAGE //////////////////////////////////// -/mob/living/silicon/can_hear() - . = TRUE - +/mob/living/silicon + gender = NEUTER + robot_talk_understand = 1 + voice_name = "synthesized voice" + has_unlimited_silicon_privilege = 1 + var/syndicate = 0 + var/const/MAIN_CHANNEL = "Main Frequency" + var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws + var/list/stating_laws = list()// Channels laws are currently being stated on + var/list/alarms_to_show = list() + var/list/alarms_to_clear = list() + //var/list/hud_list[10] + var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer + var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to + var/designation = "" + var/obj/item/camera/siliconcam/aiCamera = null //photography +//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented. + var/speak_statement = "states" + var/speak_exclamation = "declares" + var/speak_query = "queries" + var/pose //Yes, now AIs can pose too. + var/death_sound = 'sound/voice/borg_deathsound.ogg' + + //var/sensor_mode = 0 //Determines the current HUD. + + var/next_alarm_notice + var/list/datum/alarm/queued_alarms = new() + + hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD) + + + var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use + var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use + var/d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED //There is only one kind of diag hud + + var/obj/item/radio/common_radio + +/mob/living/silicon/New() + GLOB.silicon_mob_list |= src + ..() + var/datum/atom_hud/data/diagnostic/diag_hud = GLOB.huds[DATA_HUD_DIAGNOSTIC] + diag_hud.add_to_hud(src) + diag_hud_set_status() + diag_hud_set_health() + add_language("Galactic Common") + init_subsystems() + +/mob/living/silicon/med_hud_set_health() + return //we use a different hud + +/mob/living/silicon/med_hud_set_status() + return //we use a different hud + +/mob/living/silicon/Destroy() + GLOB.silicon_mob_list -= src + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.unregister(src) + return ..() + +/mob/living/silicon/rename_character(oldname, newname) + // we actually don't want it changing minds and stuff + if(!newname) + return 0 + + real_name = newname + name = real_name + return 1 + +/mob/living/silicon/proc/show_laws() + return + +/mob/living/silicon/drop_item() + return + +/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + return FALSE //So borgs they don't die trying to fix wiring + +/mob/living/silicon/emp_act(severity) + ..() + switch(severity) + if(1) + take_organ_damage(20) + Stun(8) + if(2) + take_organ_damage(10) + Stun(3) + flash_eyes(affect_silicon = 1) + to_chat(src, "*BZZZT*") + to_chat(src, "Warning: Electromagnetic pulse detected.") + + +/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) + return + +/mob/living/silicon/can_inject(mob/user, error_msg, target_zone, penetrate_thick) + if(error_msg) + to_chat(user, "[p_their(TRUE)] outer shell is too tough.") + return FALSE + +/mob/living/silicon/IsAdvancedToolUser() + return TRUE + +/mob/living/silicon/robot/welder_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + if(user == src) //No self-repair dummy + return + . = TRUE + if(!getBruteLoss()) + to_chat(user, "Nothing to fix!") + return + else if(!getBruteLoss(TRUE)) + to_chat(user, "The damaged components are beyond saving!") + return + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + adjustBruteLoss(-30) + add_fingerprint(user) + user.visible_message("[user] patches some dents on [src] with [I].") + + +/mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) + + + if(!Proj.nodamage) + switch(Proj.damage_type) + if(BRUTE) + adjustBruteLoss(Proj.damage) + if(BURN) + adjustFireLoss(Proj.damage) + + Proj.on_hit(src,2) + + return 2 + +/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) + return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now +/* + if(!effect || (blocked >= 2)) return 0 + switch(effecttype) + if(STUN) + Stun(effect / (blocked + 1)) + if(WEAKEN) + Weaken(effect / (blocked + 1)) + if(PARALYZE) + Paralyse(effect / (blocked + 1)) + if(IRRADIATE) + radiation += min((effect - (effect*getarmor(null, "rad"))), 0)//Rads auto check armor + if(STUTTER) + stuttering = max(stuttering,(effect/(blocked+1))) + if(EYE_BLUR) + eye_blurry = max(eye_blurry,(effect/(blocked+1))) + if(DROWSY) + drowsyness = max(drowsyness,(effect/(blocked+1))) + updatehealth() + return 1*/ + +/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) + if(!istype(bot) || !istype(ai)) + return 0 + if(bot.connected_ai == ai) + return 1 + return 0 + + +// this function shows the health of the pAI in the Status panel +/mob/living/silicon/proc/show_system_integrity() + if(!src.stat) + stat(null, text("System integrity: [round((health/maxHealth)*100)]%")) + else + stat(null, text("Systems nonfunctional")) + + +// This adds the basic clock, shuttle recall timer, and malf_ai info to all silicon lifeforms +/mob/living/silicon/Stat() + ..() + if(statpanel("Status")) + show_stat_emergency_shuttle_eta() + show_system_integrity() + +//Silicon mob language procs + +/mob/living/silicon/can_speak_language(datum/language/speaking) + return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language + +/mob/living/silicon/add_language(var/language, var/can_speak=1) + if(..(language) && can_speak) + speech_synthesizer_langs.Add(GLOB.all_languages[language]) + return 1 + +/mob/living/silicon/remove_language(var/rem_language) + ..(rem_language) + + for(var/datum/language/L in speech_synthesizer_langs) + if(L.name == rem_language) + speech_synthesizer_langs -= L + +/mob/living/silicon/check_lang_data() + . = "" + + if(default_language) + . += "Current default language: [default_language] - reset

    " + + for(var/datum/language/L in languages) + if(!(L.flags & NONGLOBAL)) + var/default_str + if(L == default_language) + default_str = " - default - reset" + else + default_str = " - set default" + + var/synth = (L in speech_synthesizer_langs) + . += "[L.name] (:[L.key])[synth ? default_str : null]
    Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
    [L.desc]

    " + + +// this function displays the stations manifest in a separate window +/mob/living/silicon/proc/show_station_manifest() + var/dat + dat += "

    Crew Manifest

    " + if(GLOB.data_core) + dat += GLOB.data_core.get_manifest(1) // make it monochrome + dat += "
    " + src << browse(dat, "window=airoster") + onclose(src, "airoster") + +/mob/living/silicon/assess_threat() //Secbots won't hunt silicon units + return -10 + +/mob/living/silicon/verb/pose() + set name = "Set Pose" + set desc = "Sets a description which will be shown when someone examines you." + set category = "IC" + + pose = sanitize(copytext(input(usr, "This is [src]. It is...", "Pose", null) as text, 1, MAX_MESSAGE_LEN)) + +/mob/living/silicon/verb/set_flavor() + set name = "Set Flavour Text" + set desc = "Sets an extended description of your character's features." + set category = "IC" + + update_flavor_text() + +/mob/living/silicon/binarycheck() + return 1 + +/mob/living/silicon/proc/remove_med_sec_hud() + var/datum/atom_hud/secsensor = GLOB.huds[sec_hud] + var/datum/atom_hud/medsensor = GLOB.huds[med_hud] + for(var/datum/atom_hud/data/diagnostic/diagsensor in GLOB.huds) + diagsensor.remove_hud_from(src) + secsensor.remove_hud_from(src) + medsensor.remove_hud_from(src) + + +/mob/living/silicon/proc/add_sec_hud() + var/datum/atom_hud/secsensor = GLOB.huds[sec_hud] + secsensor.add_hud_to(src) + +/mob/living/silicon/proc/add_med_hud() + var/datum/atom_hud/medsensor = GLOB.huds[med_hud] + medsensor.add_hud_to(src) + +/mob/living/silicon/proc/add_diag_hud() + for(var/datum/atom_hud/data/diagnostic/diagsensor in GLOB.huds) + diagsensor.add_hud_to(src) + + +/mob/living/silicon/proc/toggle_sensor_mode() + var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security", "Medical","Diagnostic","Disable") + remove_med_sec_hud() + switch(sensor_type) + if("Security") + add_sec_hud() + to_chat(src, "Security records overlay enabled.") + if("Medical") + add_med_hud() + to_chat(src, "Life signs monitor overlay enabled.") + if("Diagnostic") + add_diag_hud() + to_chat(src, "Robotics diagnostic overlay enabled.") + if("Disable") + to_chat(src, "Sensor augmentations disabled.") + +/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) + if(!next_alarm_notice) + next_alarm_notice = world.time + SecondsToTicks(10) + + var/list/alarms = queued_alarms[alarm_handler] + if(was_raised) + // Raised alarms are always set + alarms[alarm] = 1 + else + // Alarms that were raised but then cleared before the next notice are instead removed + if(alarm in alarms) + alarms -= alarm + // And alarms that have only been cleared thus far are set as such + else + alarms[alarm] = -1 + +/mob/living/silicon/proc/process_queued_alarms() + if(next_alarm_notice && (world.time > next_alarm_notice)) + next_alarm_notice = 0 + + var/alarm_raised = 0 + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + var/reported = 0 + for(var/datum/alarm/A in alarms) + if(alarms[A] == 1) + if(!reported) + reported = 1 + to_chat(src, "--- [AH.category] Detected ---") + raised_alarm(A) + + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + var/reported = 0 + for(var/datum/alarm/A in alarms) + if(alarms[A] == -1) + if(!reported) + reported = 1 + to_chat(src, "--- [AH.category] Cleared ---") + to_chat(src, "\The [A.alarm_name()].") + + if(alarm_raised) + to_chat(src, "\[Show Alerts\]") + + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + alarms.Cut() + +/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A) + to_chat(src, "[A.alarm_name()]!") + +/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A) + var/cameratext = "" + for(var/obj/machinery/camera/C in A.cameras()) + cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]" + to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])") + +/mob/living/silicon/adjustToxLoss(var/amount) + return STATUS_UPDATE_NONE + +/mob/living/silicon/get_access() + return IGNORE_ACCESS //silicons always have access + +/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/noise) + if(affect_silicon) + return ..() + +/mob/living/silicon/is_mechanical() + return 1 + +/mob/living/silicon/is_literate() + return 1 + +/////////////////////////////////// EAR DAMAGE //////////////////////////////////// +/mob/living/silicon/can_hear() + . = TRUE + diff --git a/code/modules/mob/living/silicon/subsystems.dm b/code/modules/mob/living/silicon/subsystems.dm index 8f7c2316768..5f00beff1e4 100644 --- a/code/modules/mob/living/silicon/subsystems.dm +++ b/code/modules/mob/living/silicon/subsystems.dm @@ -20,7 +20,7 @@ /mob/living/silicon/proc/subsystem_law_manager, /mob/living/silicon/proc/subsystem_power_monitor ) - + /mob/living/silicon/robot/drone silicon_subsystems = list( /mob/living/silicon/proc/subsystem_alarm_monitor, @@ -54,8 +54,8 @@ set name = "Alarm Monitor" set category = "Subsystems" - alarm_monitor.ui_interact(usr, state = self_state) - + alarm_monitor.ui_interact(usr, state = GLOB.self_state) + /******************** * Atmos Control * ********************/ @@ -63,7 +63,7 @@ set category = "Subsystems" set name = "Atmospherics Control" - atmos_control.ui_interact(usr, state = self_state) + atmos_control.ui_interact(usr, state = GLOB.self_state) /******************** * Crew Monitor * @@ -72,8 +72,8 @@ set category = "Subsystems" set name = "Crew Monitor" - crew_monitor.ui_interact(usr, state = self_state) - + crew_monitor.ui_interact(usr, state = GLOB.self_state) + /**************** * Law Manager * ****************/ @@ -81,8 +81,8 @@ set name = "Law Manager" set category = "Subsystems" - law_manager.ui_interact(usr, state = conscious_state) - + law_manager.ui_interact(usr, state = GLOB.conscious_state) + /******************** * Power Monitor * ********************/ @@ -90,5 +90,5 @@ set category = "Subsystems" set name = "Power Monitor" - power_monitor.ui_interact(usr, state = self_state) + power_monitor.ui_interact(usr, state = GLOB.self_state) diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 6e94accb100..2cfc5e746dc 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -124,4 +124,4 @@ visual_effect_icon = ATTACK_EFFECT_PUNCH else visual_effect_icon = ATTACK_EFFECT_SMASH - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 7f1868b0bdb..07210674303 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -143,7 +143,7 @@ icon_dead = icon_state access_card = new /obj/item/card/id(src) //This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first. - access_card.access += access_robotics + access_card.access += ACCESS_ROBOTICS set_custom_texts() Radio = new/obj/item/radio/headset/bot(src) Radio.follow_target = src @@ -160,7 +160,7 @@ SSradio.add_object(bot_core, control_freq, bot_filter) prepare_huds() - for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) + for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) diag_hud.add_to_hud(src) diag_hud.add_hud_to(src) permanent_huds |= diag_hud @@ -391,7 +391,7 @@ pulse2.icon_state = "empdisable" pulse2.name = "emp sparks" pulse2.anchored = 1 - pulse2.dir = pick(cardinal) + pulse2.dir = pick(GLOB.cardinal) QDEL_IN(pulse2, 10) if(paicard) @@ -495,7 +495,7 @@ Pass a positive integer as an argument to override a bot's default speed. if(!path) return 0 if(path.len > 1) - step_towards(src, path[1]) + Move(path[1], get_dir(src, path[1]), BOT_STEP_DELAY) if(get_turf(src) == path[1]) //Successful move increment_path() tries = 0 @@ -1051,7 +1051,7 @@ Pass a positive integer as an argument to override a bot's default speed. path = newpath ? newpath : list() if(!path_hud) return - var/list/path_huds_watching_me = list(huds[DATA_HUD_DIAGNOSTIC_ADVANCED]) + var/list/path_huds_watching_me = list(GLOB.huds[DATA_HUD_DIAGNOSTIC_ADVANCED]) if(path_hud) path_huds_watching_me += path_hud for(var/V in path_huds_watching_me) @@ -1072,7 +1072,7 @@ Pass a positive integer as an argument to override a bot's default speed. var/turf/prevprevT = path[i - 2] var/prevDir = get_dir(prevprevT, prevT) var/mixDir = direction|prevDir - if(mixDir in diagonals) + if(mixDir in GLOB.diagonals) prevI.dir = mixDir if(prevDir & (NORTH|SOUTH)) var/matrix/ntransform = matrix() @@ -1109,4 +1109,4 @@ Pass a positive integer as an argument to override a bot's default speed. path.Cut(1, 2) /mob/living/simple_animal/bot/proc/drop_part(obj/item/drop_item, dropzone) - new drop_item(dropzone) \ No newline at end of file + new drop_item(dropzone) diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index 0c52b1517f3..f9747238138 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -187,7 +187,7 @@ ..() /obj/machinery/bot_core/cleanbot - req_one_access = list(access_janitor, access_robotics) + req_one_access = list(ACCESS_JANITOR, ACCESS_ROBOTICS) /mob/living/simple_animal/bot/cleanbot/get_controls(mob/user) diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index 52155a73a44..024e2b5f304 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -1,5 +1,4 @@ //Bot Construction -var/robot_arm = /obj/item/robot_parts/l_arm //Cleanbot assembly /obj/item/bucket_sensor @@ -13,6 +12,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm throw_range = 5 w_class = WEIGHT_CLASS_NORMAL var/created_name = "Cleanbot" + var/robot_arm = /obj/item/robot_parts/l_arm /obj/item/bucket_sensor/attackby(obj/item/W, mob/user as mob, params) ..() @@ -340,7 +340,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm icon = 'icons/obj/aibots.dmi' icon_state = "firstaid_arm" w_class = WEIGHT_CLASS_NORMAL - req_one_access = list(access_medical, access_robotics) + req_one_access = list(ACCESS_MEDICAL, ACCESS_ROBOTICS) var/build_step = 0 var/created_name = "Medibot" //To preserve the name if it's a unique medbot I guess var/skin = null //Same as medbot, set to tox or ointment for the respective kits. @@ -350,6 +350,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm var/treatment_fire = "salglu_solution" var/treatment_tox = "charcoal" var/treatment_virus = "spaceacillin" + var/robot_arm = /obj/item/robot_parts/l_arm /obj/item/firstaid_arm_assembly/New(loc, new_skin) ..() @@ -419,6 +420,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm item_state = "helmet" var/created_name = "Securitron" //To preserve the name if it's a unique securitron I guess var/build_step = 0 + var/robot_arm = /obj/item/robot_parts/l_arm /obj/item/clothing/head/helmet/attackby(obj/item/assembly/signaler/S, mob/user, params) ..() @@ -593,9 +595,10 @@ var/robot_arm = /obj/item/robot_parts/l_arm icon = 'icons/obj/aibots.dmi' icon_state = "honkbot_arm" w_class = WEIGHT_CLASS_NORMAL - req_one_access = list(access_clown, access_robotics, access_mime) + req_one_access = list(ACCESS_CLOWN, ACCESS_ROBOTICS, ACCESS_MIME) var/build_step = 0 var/created_name = "Honkbot" //To preserve the name if it's a unique medbot I guess + var/robot_arm = /obj/item/robot_parts/l_arm /obj/item/honkbot_arm_assembly/attackby(obj/item/W, mob/user, params) ..() @@ -623,4 +626,4 @@ var/robot_arm = /obj/item/robot_parts/l_arm qdel(W) var/mob/living/simple_animal/bot/honkbot/A = new /mob/living/simple_animal/bot/honkbot(get_turf(src)) A.robot_arm = robot_arm - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index c457f90c3f5..faaa65a46c4 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -59,7 +59,7 @@ check_records = 0//Don't actively target people set to arrest arrest_type = 1//Don't even try to cuff declare_arrests = 0 // Don't spam sec - bot_core.req_access = list(access_maint_tunnels, access_theatre, access_robotics) + bot_core.req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE, ACCESS_ROBOTICS) if(created_name == initial(name) || !created_name) if(lasercolor == "b") @@ -68,7 +68,7 @@ name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT") //SECHUD - var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED] + var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] secsensor.add_hud_to(src) permanent_huds |= secsensor @@ -473,7 +473,7 @@ pulse2.icon_state = "empdisable" pulse2.name = "emp sparks" pulse2.anchored = 1 - pulse2.dir = pick(cardinal) + pulse2.dir = pick(GLOB.cardinal) spawn(10) qdel(pulse2) var/list/mob/living/carbon/targets = new diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index 21967bd202b..5b0ea28a72a 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -415,7 +415,7 @@ ..() /obj/machinery/bot_core/floorbot - req_one_access = list(access_construction, access_robotics) + req_one_access = list(ACCESS_CONSTRUCTION, ACCESS_ROBOTICS) /mob/living/simple_animal/bot/floorbot/UnarmedAttack(atom/A) if(isturf(A)) diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm index 4dc50fca8f8..8b0878f7115 100644 --- a/code/modules/mob/living/simple_animal/bot/griefsky.dm +++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm @@ -35,7 +35,7 @@ locked = 0 /obj/machinery/bot_core/toy - req_access = list(access_maint_tunnels, access_theatre, access_robotics) + req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE, ACCESS_ROBOTICS) /mob/living/simple_animal/bot/secbot/griefsky/proc/spam_flag_false() //used for addtimer to not spam comms spam_flag = 0 @@ -240,4 +240,4 @@ else return ..() else - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index 3499ae06649..d9097a82d2e 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -31,7 +31,7 @@ var/arrest_type = FALSE /obj/machinery/bot_core/honkbot - req_one_access = list(access_clown, access_robotics, access_mime) + req_one_access = list(ACCESS_CLOWN, ACCESS_ROBOTICS, ACCESS_MIME) /mob/living/simple_animal/bot/honkbot/Initialize() . = ..() @@ -317,4 +317,4 @@ speak("Honk!") sensor_blink() return - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index 9507b91e4f0..5246a536d92 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -123,7 +123,7 @@ prev_access = access_card.access qdel(J) - var/datum/atom_hud/medsensor = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] medsensor.add_hud_to(src) permanent_huds |= medsensor @@ -530,7 +530,7 @@ if(inject_beaker) if(use_beaker && reagent_glass && reagent_glass.reagents.total_volume) var/fraction = min(injection_amount/reagent_glass.reagents.total_volume, 1) - reagent_glass.reagents.reaction(patient, INGEST, fraction) + reagent_glass.reagents.reaction(patient, REAGENT_INGEST, fraction) reagent_glass.reagents.trans_to(patient, injection_amount) //Inject from beaker instead. else patient.reagents.add_reagent(reagent_id,injection_amount) @@ -615,7 +615,7 @@ declare_cooldown = 0 /obj/machinery/bot_core/medbot - req_one_access = list(access_medical, access_robotics) + req_one_access = list(ACCESS_MEDICAL, ACCESS_ROBOTICS) /obj/machinery/bot_core/medbot/syndicate - req_one_access = list(access_syndicate) + req_one_access = list(ACCESS_SYNDICATE) diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 83c038cc125..c89b160bf0e 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -877,4 +877,4 @@ #undef DELIGHT /obj/machinery/bot_core/mulebot - req_access = list(access_cargo) + req_access = list(ACCESS_CARGO) diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index b5d63c8c9ca..ecdb8e3f0fe 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -90,7 +90,7 @@ prev_access = access_card.access //SECHUD - var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED] + var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] secsensor.add_hud_to(src) permanent_huds |= secsensor @@ -456,4 +456,4 @@ Auto Patrol: []"}, ..() /obj/machinery/bot_core/secbot - req_access = list(access_security) + req_access = list(ACCESS_SECURITY) diff --git a/code/modules/mob/living/simple_animal/bot/syndicate.dm b/code/modules/mob/living/simple_animal/bot/syndicate.dm index 6aa15ba0270..3993339f714 100644 --- a/code/modules/mob/living/simple_animal/bot/syndicate.dm +++ b/code/modules/mob/living/simple_animal/bot/syndicate.dm @@ -33,7 +33,7 @@ /mob/living/simple_animal/bot/ed209/syndicate/setup_access() if(access_card) - access_card.access = list(access_syndicate, access_syndicate_leader) + access_card.access = list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) prev_access = access_card.access /mob/living/simple_animal/bot/ed209/syndicate/update_icon() @@ -211,4 +211,4 @@ return raised_alert = TRUE if(depotarea) - depotarea.increase_alert(reason) \ No newline at end of file + depotarea.increase_alert(reason) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 9dce0cecb62..ee03c204d48 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -1,405 +1,405 @@ - -/mob/living/simple_animal/hostile/construct - name = "Construct" - real_name = "Construct" - desc = "" - speak_emote = list("hisses") - emote_hear = list("wails","screeches") - response_help = "thinks better of touching" - response_disarm = "flails at" - response_harm = "punches" - icon_dead = "shade_dead" - speed = 0 - a_intent = INTENT_HARM - stop_automated_movement = 1 - status_flags = CANPUSH - attack_sound = 'sound/weapons/punch1.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - faction = list("cult") - flying = 1 - pressure_resistance = 100 - universal_speak = 1 - AIStatus = AI_OFF //normal constructs don't have AI - var/const_type = "shade" - var/list/construct_spells = list() - var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." - loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) - del_on_death = 1 - deathmessage = "collapses in a shattered heap." - -/mob/living/simple_animal/hostile/construct/New() - . = ..() - if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way - name = "[const_type] ([rand(1, 1000)])" - real_name = const_type - icon_living = const_type - icon_state = const_type - else - name = "[SSticker.cultdat.get_name(const_type)] ([rand(1, 1000)])" - real_name = SSticker.cultdat.get_name(const_type) - icon_living = SSticker.cultdat.get_icon(const_type) - icon_state = SSticker.cultdat.get_icon(const_type) - - for(var/spell in construct_spells) - AddSpell(new spell(null)) - - if(SSticker.cultdat?.theme == "blood") - updateglow() - -/mob/living/simple_animal/hostile/construct/death(gibbed) - . = ..() - SSticker.mode.remove_cultist(src.mind, FALSE) - -/mob/living/simple_animal/hostile/construct/examine(mob/user) - . = ..() - - var/msg = "" - if(src.health < src.maxHealth) - msg += "" - if(src.health >= src.maxHealth/2) - msg += "It looks slightly dented.\n" - else - msg += "It looks severely dented!\n" - msg += "" - msg += "*---------*" - - . += msg - -/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M) - if(istype(M, /mob/living/simple_animal/hostile/construct/builder)) - if(health < maxHealth) - adjustBruteLoss(-5) - if(src != M) - Beam(M,icon_state="sendbeam",time=4) - M.visible_message("[M] repairs some of \the [src]'s dents.", \ - "You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health.") - else - M.visible_message("[M] repairs some of its own dents.", \ - "You repair some of your own dents, leaving you at [M.health]/[M.maxHealth] health.") - else - if(src != M) - to_chat(M, "You cannot repair [src]'s dents, as it has none!") - else - to_chat(M, "You cannot repair your own dents, as you have none!") - else if(src != M) - return ..() - - -/mob/living/simple_animal/hostile/construct/narsie_act() - return - -/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) - return FALSE - -/////////////////Juggernaut/////////////// - - - -/mob/living/simple_animal/hostile/construct/armoured - name = "Juggernaut" - real_name = "Juggernaut" - desc = "A possessed suit of armour driven by the will of the restless dead" - icon = 'icons/mob/mob.dmi' - icon_state = "behemoth" - icon_living = "behemoth" - maxHealth = 250 - health = 250 - response_harm = "harmlessly punches" - harm_intent_damage = 0 - obj_damage = 90 - melee_damage_lower = 30 - melee_damage_upper = 30 - attacktext = "smashes their armoured gauntlet into" - speed = 3 - environment_smash = 2 - attack_sound = 'sound/weapons/punch3.ogg' - status_flags = 0 - const_type = "juggernaut" - mob_size = MOB_SIZE_LARGE - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall) - force_threshold = 11 - playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ - create shield walls, rip apart enemies and walls alike, and even deflect energy weapons." - - -/mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -/mob/living/simple_animal/hostile/construct/armoured/bullet_act(var/obj/item/projectile/P) - if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) - var/reflectchance = 80 - round(P.damage/3) - if(prob(reflectchance)) - if((P.damage_type == BRUTE || P.damage_type == BURN)) - adjustBruteLoss(P.damage * 0.5) - visible_message("The [P.name] gets reflected by [src]'s shell!", \ - "The [P.name] gets reflected by [src]'s shell!") - - P.reflect_back(src, list(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)) - - return -1 // complete projectile permutation - - return (..(P)) - - - -////////////////////////Wraith///////////////////////////////////////////// - - - -/mob/living/simple_animal/hostile/construct/wraith - name = "Wraith" - real_name = "Wraith" - desc = "A wicked bladed shell contraption piloted by a bound spirit" - icon = 'icons/mob/mob.dmi' - icon_state = "floating" - icon_living = "floating" - maxHealth = 75 - health = 75 - melee_damage_lower = 25 - melee_damage_upper = 25 - attacktext = "slashes" - see_in_dark = 8 - attack_sound = 'sound/weapons/bladeslice.ogg' - const_type = "wraith" - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift) - retreat_distance = 2 //AI wraiths will move in and out of combat - playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." - -/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - -/////////////////////////////Artificer///////////////////////// - - - -/mob/living/simple_animal/hostile/construct/builder - name = "Artificer" - real_name = "Artificer" - desc = "A bulbous construct dedicated to building and maintaining Cult armies." - icon = 'icons/mob/mob.dmi' - icon_state = "artificer" - icon_living = "artificer" - maxHealth = 50 - health = 50 - response_harm = "viciously beats" - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 5 - melee_damage_upper = 5 - attacktext = "rams" - environment_smash = 2 - retreat_distance = 10 - minimum_distance = 10 //AI artificers will flee like fuck - attack_sound = 'sound/weapons/punch2.ogg' - const_type = "builder" - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, - /obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser, - /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, - /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, - /obj/effect/proc_holder/spell/aoe_turf/conjure/pylon, - /obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone, - /obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser) - - playstyle_string = "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \ - use magic missile, repair allied constructs (by clicking on them), \ - and, most important of all, create new constructs by producing soulstones to capture souls, \ - and shells to place those soulstones into." - - -/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here? - if(isconstruct(A)) //is it a construct? - var/mob/living/simple_animal/hostile/construct/C = A - if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is - return 1 - else - return 0 - else - return 0 - -/mob/living/simple_animal/hostile/construct/builder/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it - return 0 - if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible - return 1 //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs - -/mob/living/simple_animal/hostile/construct/builder/MoveToTarget(var/list/possible_targets) - ..() - if(isliving(target)) - var/mob/living/L = target - if(isconstruct(L) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it - LoseTarget() - return 0 - if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/builder/Aggro() - ..() - if(isconstruct(target)) //oh the target is a construct no need to flee - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/builder/LoseAggro() - ..() - retreat_distance = initial(retreat_distance) - minimum_distance = initial(minimum_distance) - -/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs - AIStatus = AI_ON - environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP - - -/////////////////////////////Behemoth///////////////////////// - - -/mob/living/simple_animal/hostile/construct/behemoth - name = "Behemoth" - real_name = "Behemoth" - desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal." - icon = 'icons/mob/mob.dmi' - icon_state = "behemoth" - icon_living = "behemoth" - maxHealth = 750 - health = 750 - speak_emote = list("rumbles") - response_harm = "harmlessly punches" - harm_intent_damage = 0 - melee_damage_lower = 50 - melee_damage_upper = 50 - attacktext = "brutally crushes" - speed = 5 - environment_smash = 2 - attack_sound = 'sound/weapons/punch4.ogg' - force_threshold = 11 - const_type = "behemoth" - var/energy = 0 - var/max_energy = 1000 - -/mob/living/simple_animal/hostile/construct/behemoth/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -/mob/living/simple_animal/hostile/construct/behemoth/Life(seconds, times_fired) - weakened = 0 - return ..() - - -/////////////////////////////Harvester///////////////////////// - -/mob/living/simple_animal/hostile/construct/harvester - name = "Harvester" - real_name = "Harvester" - desc = "A harbinger of Nar-Sie's enlightenment. It'll be all over soon." - icon = 'icons/mob/mob.dmi' - icon_state = "harvester" - icon_living = "harvester" - maxHealth = 60 - health = 60 - melee_damage_lower = 1 - melee_damage_upper = 5 - attacktext = "prods" - environment_smash = ENVIRONMENT_SMASH_RWALLS - see_in_dark = 8 - attack_sound = 'sound/weapons/tap.ogg' - const_type = "harvester" - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, - /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, - /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, - /obj/effect/proc_holder/spell/targeted/smoke/disable) - retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier - playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \ - Bring those who still cling to this world of illusion back to the master so they may know Truth." - - -/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(var/movement_dir = 0) - return 1 - - -/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -////////////////Glow//////////////////// -/mob/living/simple_animal/hostile/construct/proc/updateglow() - overlays = 0 - var/overlay_layer = LIGHTING_LAYER + 1 - if(layer != MOB_LAYER) - overlay_layer=TURF_LAYER+0.2 - - overlays += image(icon,"glow-[icon_state]",overlay_layer) - set_light(2, -2, l_color = "#FFFFFF") - -///ui stuff - -/mob/living/simple_animal/hostile/construct/armoured/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(250 to INFINITY) healths.icon_state = "juggernaut_health0" - if(208 to 249) healths.icon_state = "juggernaut_health1" - if(167 to 207) healths.icon_state = "juggernaut_health2" - if(125 to 166) healths.icon_state = "juggernaut_health3" - if(84 to 124) healths.icon_state = "juggernaut_health4" - if(42 to 83) healths.icon_state = "juggernaut_health5" - if(1 to 41) healths.icon_state = "juggernaut_health6" - else healths.icon_state = "juggernaut_health7" - - -/mob/living/simple_animal/hostile/construct/behemoth/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(750 to INFINITY) healths.icon_state = "juggernaut_health0" - if(625 to 749) healths.icon_state = "juggernaut_health1" - if(500 to 624) healths.icon_state = "juggernaut_health2" - if(375 to 499) healths.icon_state = "juggernaut_health3" - if(250 to 374) healths.icon_state = "juggernaut_health4" - if(125 to 249) healths.icon_state = "juggernaut_health5" - if(1 to 124) healths.icon_state = "juggernaut_health6" - else healths.icon_state = "juggernaut_health7" - -/mob/living/simple_animal/hostile/construct/builder/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(50 to INFINITY) healths.icon_state = "artificer_health0" - if(42 to 49) healths.icon_state = "artificer_health1" - if(34 to 41) healths.icon_state = "artificer_health2" - if(26 to 33) healths.icon_state = "artificer_health3" - if(18 to 25) healths.icon_state = "artificer_health4" - if(10 to 17) healths.icon_state = "artificer_health5" - if(1 to 9) healths.icon_state = "artificer_health6" - else healths.icon_state = "artificer_health7" - - - -/mob/living/simple_animal/hostile/construct/wraith/handle_hud_icons_health() - - ..() - if(healths) - switch(health) - if(75 to INFINITY) healths.icon_state = "wraith_health0" - if(62 to 74) healths.icon_state = "wraith_health1" - if(50 to 61) healths.icon_state = "wraith_health2" - if(37 to 49) healths.icon_state = "wraith_health3" - if(25 to 36) healths.icon_state = "wraith_health4" - if(12 to 24) healths.icon_state = "wraith_health5" - if(1 to 11) healths.icon_state = "wraith_health6" - else healths.icon_state = "wraith_health7" - - -/mob/living/simple_animal/hostile/construct/harvester/handle_hud_icons_health() - - ..() - if(healths) - switch(health) - if(150 to INFINITY) healths.icon_state = "harvester_health0" - if(125 to 149) healths.icon_state = "harvester_health1" - if(100 to 124) healths.icon_state = "harvester_health2" - if(75 to 99) healths.icon_state = "harvester_health3" - if(50 to 74) healths.icon_state = "harvester_health4" - if(25 to 49) healths.icon_state = "harvester_health5" - if(1 to 24) healths.icon_state = "harvester_health6" - else healths.icon_state = "harvester_health7" \ No newline at end of file + +/mob/living/simple_animal/hostile/construct + name = "Construct" + real_name = "Construct" + desc = "" + speak_emote = list("hisses") + emote_hear = list("wails","screeches") + response_help = "thinks better of touching" + response_disarm = "flails at" + response_harm = "punches" + icon_dead = "shade_dead" + speed = 0 + a_intent = INTENT_HARM + stop_automated_movement = 1 + status_flags = CANPUSH + attack_sound = 'sound/weapons/punch1.ogg' + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + faction = list("cult") + flying = 1 + pressure_resistance = 100 + universal_speak = 1 + AIStatus = AI_OFF //normal constructs don't have AI + var/const_type = "shade" + var/list/construct_spells = list() + var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." + loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) + del_on_death = 1 + deathmessage = "collapses in a shattered heap." + +/mob/living/simple_animal/hostile/construct/New() + . = ..() + if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way + name = "[const_type] ([rand(1, 1000)])" + real_name = const_type + icon_living = const_type + icon_state = const_type + else + name = "[SSticker.cultdat.get_name(const_type)] ([rand(1, 1000)])" + real_name = SSticker.cultdat.get_name(const_type) + icon_living = SSticker.cultdat.get_icon(const_type) + icon_state = SSticker.cultdat.get_icon(const_type) + + for(var/spell in construct_spells) + AddSpell(new spell(null)) + + if(SSticker.cultdat?.theme == "blood") + updateglow() + +/mob/living/simple_animal/hostile/construct/death(gibbed) + . = ..() + SSticker.mode.remove_cultist(src.mind, FALSE) + +/mob/living/simple_animal/hostile/construct/examine(mob/user) + . = ..() + + var/msg = "" + if(src.health < src.maxHealth) + msg += "" + if(src.health >= src.maxHealth/2) + msg += "It looks slightly dented.\n" + else + msg += "It looks severely dented!\n" + msg += "" + msg += "*---------*" + + . += msg + +/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M) + if(istype(M, /mob/living/simple_animal/hostile/construct/builder)) + if(health < maxHealth) + adjustBruteLoss(-5) + if(src != M) + Beam(M,icon_state="sendbeam",time=4) + M.visible_message("[M] repairs some of \the [src]'s dents.", \ + "You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health.") + else + M.visible_message("[M] repairs some of its own dents.", \ + "You repair some of your own dents, leaving you at [M.health]/[M.maxHealth] health.") + else + if(src != M) + to_chat(M, "You cannot repair [src]'s dents, as it has none!") + else + to_chat(M, "You cannot repair your own dents, as you have none!") + else if(src != M) + return ..() + + +/mob/living/simple_animal/hostile/construct/narsie_act() + return + +/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + return FALSE + +/////////////////Juggernaut/////////////// + + + +/mob/living/simple_animal/hostile/construct/armoured + name = "Juggernaut" + real_name = "Juggernaut" + desc = "A possessed suit of armour driven by the will of the restless dead" + icon = 'icons/mob/mob.dmi' + icon_state = "behemoth" + icon_living = "behemoth" + maxHealth = 250 + health = 250 + response_harm = "harmlessly punches" + harm_intent_damage = 0 + obj_damage = 90 + melee_damage_lower = 30 + melee_damage_upper = 30 + attacktext = "smashes their armoured gauntlet into" + speed = 3 + environment_smash = 2 + attack_sound = 'sound/weapons/punch3.ogg' + status_flags = 0 + const_type = "juggernaut" + mob_size = MOB_SIZE_LARGE + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall) + force_threshold = 11 + playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ + create shield walls, rip apart enemies and walls alike, and even deflect energy weapons." + + +/mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +/mob/living/simple_animal/hostile/construct/armoured/bullet_act(var/obj/item/projectile/P) + if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) + var/reflectchance = 80 - round(P.damage/3) + if(prob(reflectchance)) + if((P.damage_type == BRUTE || P.damage_type == BURN)) + adjustBruteLoss(P.damage * 0.5) + visible_message("The [P.name] gets reflected by [src]'s shell!", \ + "The [P.name] gets reflected by [src]'s shell!") + + P.reflect_back(src, list(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)) + + return -1 // complete projectile permutation + + return (..(P)) + + + +////////////////////////Wraith///////////////////////////////////////////// + + + +/mob/living/simple_animal/hostile/construct/wraith + name = "Wraith" + real_name = "Wraith" + desc = "A wicked bladed shell contraption piloted by a bound spirit" + icon = 'icons/mob/mob.dmi' + icon_state = "floating" + icon_living = "floating" + maxHealth = 75 + health = 75 + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "slashes" + see_in_dark = 8 + attack_sound = 'sound/weapons/bladeslice.ogg' + const_type = "wraith" + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift) + retreat_distance = 2 //AI wraiths will move in and out of combat + playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." + +/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + +/////////////////////////////Artificer///////////////////////// + + + +/mob/living/simple_animal/hostile/construct/builder + name = "Artificer" + real_name = "Artificer" + desc = "A bulbous construct dedicated to building and maintaining Cult armies." + icon = 'icons/mob/mob.dmi' + icon_state = "artificer" + icon_living = "artificer" + maxHealth = 50 + health = 50 + response_harm = "viciously beats" + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 5 + melee_damage_upper = 5 + attacktext = "rams" + environment_smash = 2 + retreat_distance = 10 + minimum_distance = 10 //AI artificers will flee like fuck + attack_sound = 'sound/weapons/punch2.ogg' + const_type = "builder" + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, + /obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser, + /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, + /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, + /obj/effect/proc_holder/spell/aoe_turf/conjure/pylon, + /obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone, + /obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser) + + playstyle_string = "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \ + use magic missile, repair allied constructs (by clicking on them), \ + and, most important of all, create new constructs by producing soulstones to capture souls, \ + and shells to place those soulstones into." + + +/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here? + if(isconstruct(A)) //is it a construct? + var/mob/living/simple_animal/hostile/construct/C = A + if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is + return 1 + else + return 0 + else + return 0 + +/mob/living/simple_animal/hostile/construct/builder/CanAttack(atom/the_target) + if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it + return 0 + if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible + return 1 //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs + +/mob/living/simple_animal/hostile/construct/builder/MoveToTarget(var/list/possible_targets) + ..() + if(isliving(target)) + var/mob/living/L = target + if(isconstruct(L) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it + LoseTarget() + return 0 + if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you + retreat_distance = null + minimum_distance = 1 + +/mob/living/simple_animal/hostile/construct/builder/Aggro() + ..() + if(isconstruct(target)) //oh the target is a construct no need to flee + retreat_distance = null + minimum_distance = 1 + +/mob/living/simple_animal/hostile/construct/builder/LoseAggro() + ..() + retreat_distance = initial(retreat_distance) + minimum_distance = initial(minimum_distance) + +/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs + AIStatus = AI_ON + environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP + + +/////////////////////////////Behemoth///////////////////////// + + +/mob/living/simple_animal/hostile/construct/behemoth + name = "Behemoth" + real_name = "Behemoth" + desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal." + icon = 'icons/mob/mob.dmi' + icon_state = "behemoth" + icon_living = "behemoth" + maxHealth = 750 + health = 750 + speak_emote = list("rumbles") + response_harm = "harmlessly punches" + harm_intent_damage = 0 + melee_damage_lower = 50 + melee_damage_upper = 50 + attacktext = "brutally crushes" + speed = 5 + environment_smash = 2 + attack_sound = 'sound/weapons/punch4.ogg' + force_threshold = 11 + const_type = "behemoth" + var/energy = 0 + var/max_energy = 1000 + +/mob/living/simple_animal/hostile/construct/behemoth/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +/mob/living/simple_animal/hostile/construct/behemoth/Life(seconds, times_fired) + weakened = 0 + return ..() + + +/////////////////////////////Harvester///////////////////////// + +/mob/living/simple_animal/hostile/construct/harvester + name = "Harvester" + real_name = "Harvester" + desc = "A harbinger of Nar-Sie's enlightenment. It'll be all over soon." + icon = 'icons/mob/mob.dmi' + icon_state = "harvester" + icon_living = "harvester" + maxHealth = 60 + health = 60 + melee_damage_lower = 1 + melee_damage_upper = 5 + attacktext = "prods" + environment_smash = ENVIRONMENT_SMASH_RWALLS + see_in_dark = 8 + attack_sound = 'sound/weapons/tap.ogg' + const_type = "harvester" + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, + /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, + /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, + /obj/effect/proc_holder/spell/targeted/smoke/disable) + retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier + playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \ + Bring those who still cling to this world of illusion back to the master so they may know Truth." + + +/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(var/movement_dir = 0) + return 1 + + +/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +////////////////Glow//////////////////// +/mob/living/simple_animal/hostile/construct/proc/updateglow() + overlays = 0 + var/overlay_layer = LIGHTING_LAYER + 1 + if(layer != MOB_LAYER) + overlay_layer=TURF_LAYER+0.2 + + overlays += image(icon,"glow-[icon_state]",overlay_layer) + set_light(2, -2, l_color = "#FFFFFF") + +///ui stuff + +/mob/living/simple_animal/hostile/construct/armoured/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(250 to INFINITY) healths.icon_state = "juggernaut_health0" + if(208 to 249) healths.icon_state = "juggernaut_health1" + if(167 to 207) healths.icon_state = "juggernaut_health2" + if(125 to 166) healths.icon_state = "juggernaut_health3" + if(84 to 124) healths.icon_state = "juggernaut_health4" + if(42 to 83) healths.icon_state = "juggernaut_health5" + if(1 to 41) healths.icon_state = "juggernaut_health6" + else healths.icon_state = "juggernaut_health7" + + +/mob/living/simple_animal/hostile/construct/behemoth/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(750 to INFINITY) healths.icon_state = "juggernaut_health0" + if(625 to 749) healths.icon_state = "juggernaut_health1" + if(500 to 624) healths.icon_state = "juggernaut_health2" + if(375 to 499) healths.icon_state = "juggernaut_health3" + if(250 to 374) healths.icon_state = "juggernaut_health4" + if(125 to 249) healths.icon_state = "juggernaut_health5" + if(1 to 124) healths.icon_state = "juggernaut_health6" + else healths.icon_state = "juggernaut_health7" + +/mob/living/simple_animal/hostile/construct/builder/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(50 to INFINITY) healths.icon_state = "artificer_health0" + if(42 to 49) healths.icon_state = "artificer_health1" + if(34 to 41) healths.icon_state = "artificer_health2" + if(26 to 33) healths.icon_state = "artificer_health3" + if(18 to 25) healths.icon_state = "artificer_health4" + if(10 to 17) healths.icon_state = "artificer_health5" + if(1 to 9) healths.icon_state = "artificer_health6" + else healths.icon_state = "artificer_health7" + + + +/mob/living/simple_animal/hostile/construct/wraith/handle_hud_icons_health() + + ..() + if(healths) + switch(health) + if(75 to INFINITY) healths.icon_state = "wraith_health0" + if(62 to 74) healths.icon_state = "wraith_health1" + if(50 to 61) healths.icon_state = "wraith_health2" + if(37 to 49) healths.icon_state = "wraith_health3" + if(25 to 36) healths.icon_state = "wraith_health4" + if(12 to 24) healths.icon_state = "wraith_health5" + if(1 to 11) healths.icon_state = "wraith_health6" + else healths.icon_state = "wraith_health7" + + +/mob/living/simple_animal/hostile/construct/harvester/handle_hud_icons_health() + + ..() + if(healths) + switch(health) + if(150 to INFINITY) healths.icon_state = "harvester_health0" + if(125 to 149) healths.icon_state = "harvester_health1" + if(100 to 124) healths.icon_state = "harvester_health2" + if(75 to 99) healths.icon_state = "harvester_health3" + if(50 to 74) healths.icon_state = "harvester_health4" + if(25 to 49) healths.icon_state = "harvester_health5" + if(1 to 24) healths.icon_state = "harvester_health6" + else healths.icon_state = "harvester_health7" diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm index cd82b39d27f..10460d11e65 100644 --- a/code/modules/mob/living/simple_animal/corpse.dm +++ b/code/modules/mob/living/simple_animal/corpse.dm @@ -1,115 +1,115 @@ -//List of different corpse types -/obj/effect/mob_spawn/human/corpse/syndicatesoldier - name = "Syndicate Operative" - mob_name = "Syndicate Operative" - hair_style = "bald" - facial_hair_style = "shaved" - id_job = "Operative" - id_access_list = list(access_syndicate) - outfit = /datum/outfit/syndicatesoldiercorpse - -/datum/outfit/syndicatesoldiercorpse - name = "Syndicate Operative Corpse" - uniform = /obj/item/clothing/under/syndicate - suit = /obj/item/clothing/suit/armor/vest - shoes = /obj/item/clothing/shoes/combat - gloves = /obj/item/clothing/gloves/combat - l_ear = /obj/item/radio/headset - mask = /obj/item/clothing/mask/gas - head = /obj/item/clothing/head/helmet/swat - back = /obj/item/storage/backpack - id = /obj/item/card/id - - -/obj/effect/mob_spawn/human/corpse/syndicatecommando - name = "Syndicate Commando" - mob_name = "Syndicate Commando" - hair_style = "bald" - facial_hair_style = "shaved" - id_job = "Operative" - id_access_list = list(access_syndicate) - outfit = /datum/outfit/syndicatecommandocorpse - -/datum/outfit/syndicatecommandocorpse - name = "Syndicate Commando Corpse" - uniform = /obj/item/clothing/under/syndicate - suit = /obj/item/clothing/suit/space/hardsuit/syndi - shoes = /obj/item/clothing/shoes/combat - gloves = /obj/item/clothing/gloves/combat - l_ear = /obj/item/radio/headset - mask = /obj/item/clothing/mask/gas/syndicate - back = /obj/item/tank/jetpack/oxygen - r_pocket = /obj/item/tank/emergency_oxygen - id = /obj/item/card/id - - -/obj/effect/mob_spawn/human/clown/corpse - roundstart = TRUE - instant = TRUE - -/obj/effect/mob_spawn/human/mime/corpse - roundstart = TRUE - instant = TRUE - -/obj/effect/mob_spawn/human/corpse/pirate - name = "Pirate" - mob_name = "Pirate" - hair_style = "bald" - facial_hair_style = "shaved" - outfit = /datum/outfit/piratecorpse - -/datum/outfit/piratecorpse - name = "Pirate Corpse" - uniform = /obj/item/clothing/under/pirate - shoes = /obj/item/clothing/shoes/jackboots - glasses = /obj/item/clothing/glasses/eyepatch - head = /obj/item/clothing/head/bandana - - -/obj/effect/mob_spawn/human/corpse/pirate/ranged - name = "Pirate Gunner" - mob_name = "Pirate Gunner" - outfit = /datum/outfit/piratecorpse/ranged - -/datum/outfit/piratecorpse/ranged - name = "Pirate Gunner Corpse" - suit = /obj/item/clothing/suit/pirate_black - head = /obj/item/clothing/head/pirate - - -/obj/effect/mob_spawn/human/corpse/russian - name = "Russian" - mob_name = "Russian" - hair_style = "bald" - facial_hair_style = "shaved" - outfit = /datum/outfit/russiancorpse - -/datum/outfit/russiancorpse - name = "Russian Corpse" - uniform = /obj/item/clothing/under/soviet - shoes = /obj/item/clothing/shoes/jackboots - head = /obj/item/clothing/head/bearpelt - - -/obj/effect/mob_spawn/human/corpse/russian/ranged - outfit = /datum/outfit/russiancorpse/ranged - -/datum/outfit/russiancorpse/ranged - name = "Ranged Russian Corpse" - head = /obj/item/clothing/head/ushanka - - -/obj/effect/mob_spawn/human/corpse/wizard - name = "Space Wizard Corpse" - outfit = /datum/outfit/wizardcorpse - -/obj/effect/mob_spawn/human/corpse/clownoff/Initialize() - mob_name = "[pick(GLOB.wizard_first)], [pick(GLOB.wizard_second)]" - ..() - -/datum/outfit/wizardcorpse - name = "Space Wizard Corpse" - uniform = /obj/item/clothing/under/color/lightpurple - suit = /obj/item/clothing/suit/wizrobe - shoes = /obj/item/clothing/shoes/sandal - head = /obj/item/clothing/head/wizard +//List of different corpse types +/obj/effect/mob_spawn/human/corpse/syndicatesoldier + name = "Syndicate Operative" + mob_name = "Syndicate Operative" + hair_style = "bald" + facial_hair_style = "shaved" + id_job = "Operative" + id_access_list = list(ACCESS_SYNDICATE) + outfit = /datum/outfit/syndicatesoldiercorpse + +/datum/outfit/syndicatesoldiercorpse + name = "Syndicate Operative Corpse" + uniform = /obj/item/clothing/under/syndicate + suit = /obj/item/clothing/suit/armor/vest + shoes = /obj/item/clothing/shoes/combat + gloves = /obj/item/clothing/gloves/combat + l_ear = /obj/item/radio/headset + mask = /obj/item/clothing/mask/gas + head = /obj/item/clothing/head/helmet/swat + back = /obj/item/storage/backpack + id = /obj/item/card/id + + +/obj/effect/mob_spawn/human/corpse/syndicatecommando + name = "Syndicate Commando" + mob_name = "Syndicate Commando" + hair_style = "bald" + facial_hair_style = "shaved" + id_job = "Operative" + id_access_list = list(ACCESS_SYNDICATE) + outfit = /datum/outfit/syndicatecommandocorpse + +/datum/outfit/syndicatecommandocorpse + name = "Syndicate Commando Corpse" + uniform = /obj/item/clothing/under/syndicate + suit = /obj/item/clothing/suit/space/hardsuit/syndi + shoes = /obj/item/clothing/shoes/combat + gloves = /obj/item/clothing/gloves/combat + l_ear = /obj/item/radio/headset + mask = /obj/item/clothing/mask/gas/syndicate + back = /obj/item/tank/jetpack/oxygen + r_pocket = /obj/item/tank/emergency_oxygen + id = /obj/item/card/id + + +/obj/effect/mob_spawn/human/clown/corpse + roundstart = TRUE + instant = TRUE + +/obj/effect/mob_spawn/human/mime/corpse + roundstart = TRUE + instant = TRUE + +/obj/effect/mob_spawn/human/corpse/pirate + name = "Pirate" + mob_name = "Pirate" + hair_style = "bald" + facial_hair_style = "shaved" + outfit = /datum/outfit/piratecorpse + +/datum/outfit/piratecorpse + name = "Pirate Corpse" + uniform = /obj/item/clothing/under/pirate + shoes = /obj/item/clothing/shoes/jackboots + glasses = /obj/item/clothing/glasses/eyepatch + head = /obj/item/clothing/head/bandana + + +/obj/effect/mob_spawn/human/corpse/pirate/ranged + name = "Pirate Gunner" + mob_name = "Pirate Gunner" + outfit = /datum/outfit/piratecorpse/ranged + +/datum/outfit/piratecorpse/ranged + name = "Pirate Gunner Corpse" + suit = /obj/item/clothing/suit/pirate_black + head = /obj/item/clothing/head/pirate + + +/obj/effect/mob_spawn/human/corpse/russian + name = "Russian" + mob_name = "Russian" + hair_style = "bald" + facial_hair_style = "shaved" + outfit = /datum/outfit/russiancorpse + +/datum/outfit/russiancorpse + name = "Russian Corpse" + uniform = /obj/item/clothing/under/soviet + shoes = /obj/item/clothing/shoes/jackboots + head = /obj/item/clothing/head/bearpelt + + +/obj/effect/mob_spawn/human/corpse/russian/ranged + outfit = /datum/outfit/russiancorpse/ranged + +/datum/outfit/russiancorpse/ranged + name = "Ranged Russian Corpse" + head = /obj/item/clothing/head/ushanka + + +/obj/effect/mob_spawn/human/corpse/wizard + name = "Space Wizard Corpse" + outfit = /datum/outfit/wizardcorpse + +/obj/effect/mob_spawn/human/corpse/clownoff/Initialize() + mob_name = "[pick(GLOB.wizard_first)], [pick(GLOB.wizard_second)]" + ..() + +/datum/outfit/wizardcorpse + name = "Space Wizard Corpse" + uniform = /obj/item/clothing/under/color/lightpurple + suit = /obj/item/clothing/suit/wizrobe + shoes = /obj/item/clothing/shoes/sandal + head = /obj/item/clothing/head/wizard diff --git a/code/modules/mob/living/simple_animal/damage_procs.dm b/code/modules/mob/living/simple_animal/damage_procs.dm index 6899b40bea0..6ba69de6a61 100644 --- a/code/modules/mob/living/simple_animal/damage_procs.dm +++ b/code/modules/mob/living/simple_animal/damage_procs.dm @@ -34,4 +34,4 @@ /mob/living/simple_animal/adjustStaminaLoss(amount, updating_health = TRUE) if(damage_coeff[STAMINA]) - return ..(amount*damage_coeff[STAMINA], updating_health) \ No newline at end of file + return ..(amount*damage_coeff[STAMINA], updating_health) diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index f552bb5cebd..24818d07652 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -1,279 +1,279 @@ -//Cat -/mob/living/simple_animal/pet/cat - name = "cat" - desc = "Kitty!!" - icon_state = "cat2" - icon_living = "cat2" - icon_dead = "cat2_dead" - icon_resting = "cat2_rest" - gender = MALE - speak = list("Meow!", "Esp!", "Purr!", "HSSSSS") - speak_emote = list("purrs", "meows") - emote_hear = list("meows", "mews") - emote_see = list("shakes its head", "shivers") - var/meow_sound = 'sound/creatures/cat_meow.ogg' //Used in emote. - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - mob_size = MOB_SIZE_SMALL - animal_species = /mob/living/simple_animal/pet/cat - childtype = list(/mob/living/simple_animal/pet/cat/kitten) - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 3) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - gold_core_spawnable = FRIENDLY_SPAWN - collar_type = "cat" - var/turns_since_scan = 0 - var/mob/living/simple_animal/mouse/movement_target - var/eats_mice = 1 - -//RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_animal/pet/cat/Runtime - name = "Runtime" - desc = "GCAT" - icon_state = "cat" - icon_living = "cat" - icon_dead = "cat_dead" - icon_resting = "cat_rest" - gender = FEMALE - gold_core_spawnable = NO_SPAWN - unique_pet = TRUE - var/list/family = list() - var/memory_saved = 0 - var/list/children = list() //Actual mob instances of children - var/cats_deployed = 0 - -/mob/living/simple_animal/pet/cat/Runtime/New() - Read_Memory() - ..() - -/mob/living/simple_animal/pet/cat/Runtime/Life(seconds, times_fired) - if(!cats_deployed && SSticker.current_state >= GAME_STATE_SETTING_UP) - Deploy_The_Cats() - if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) - Write_Memory() - ..() - -/mob/living/simple_animal/pet/cat/Runtime/make_babies() - var/mob/baby = ..() - if(baby) - children += baby - return baby - -/mob/living/simple_animal/pet/cat/Runtime/death() - if(can_die() && !memory_saved) - Write_Memory(1) - return ..() - -/mob/living/simple_animal/pet/cat/Runtime/proc/Read_Memory() - var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") - S["family"] >> family - - if(isnull(family)) - family = list() - -/mob/living/simple_animal/pet/cat/Runtime/proc/Write_Memory(dead) - var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") - family = list() - if(!dead) - for(var/mob/living/simple_animal/pet/cat/kitten/C in children) - if(istype(C,type) || C.stat || !C.z || !C.butcher_results) - continue - if(C.type in family) - family[C.type] += 1 - else - family[C.type] = 1 - S["family"] << family - memory_saved = 1 - -/mob/living/simple_animal/pet/cat/Runtime/proc/Deploy_The_Cats() - cats_deployed = 1 - for(var/cat_type in family) - if(family[cat_type] > 0) - for(var/i in 1 to min(family[cat_type],100)) //Limits to about 500 cats, you wouldn't think this would be needed (BUT IT IS) - new cat_type(loc) - - -/mob/living/simple_animal/pet/cat/Life() - ..() - make_babies() - - -/mob/living/simple_animal/pet/cat/handle_automated_action() - if(!stat && !buckled) - if(prob(1)) - custom_emote(1, pick("stretches out for a belly rub.", "wags its tail.", "lies down.")) - StartResting() - else if(prob(1)) - custom_emote(1, pick("sits down.", "crouches on its hind legs.", "looks alert.")) - icon_state = "[icon_living]_sit" - collar_type = "[initial(collar_type)]_sit" - resting = TRUE - update_canmove() - else if(prob(1)) - if(resting) - custom_emote(1, pick("gets up and meows.", "walks around.", "stops resting.")) - StopResting() - else - custom_emote(1, pick("grooms its fur.", "twitches its whiskers.", "shakes out its coat.")) - - //MICE! - if(eats_mice && isturf(loc) && !incapacitated()) - for(var/mob/living/simple_animal/mouse/M in view(1, src)) - if(!M.stat && Adjacent(M)) - custom_emote(1, "splats \the [M]!") - M.splat() - movement_target = null - stop_automated_movement = 0 - break - for(var/obj/item/toy/cattoy/T in view(1, src)) - if(T.cooldown < (world.time - 400)) - custom_emote(1, "bats \the [T] around with its paw!") - T.cooldown = world.time - -/mob/living/simple_animal/pet/cat/handle_automated_movement() - . = ..() - if(!stat && !resting && !buckled) - turns_since_scan++ - if(turns_since_scan > 5) - walk_to(src,0) - turns_since_scan = 0 - if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) - movement_target = null - stop_automated_movement = 0 - if( !movement_target || !(movement_target.loc in oview(src, 3)) ) - movement_target = null - stop_automated_movement = 0 - for(var/mob/living/simple_animal/mouse/snack in oview(src,3)) - if(isturf(snack.loc) && !snack.stat) - movement_target = snack - break - if(movement_target) - stop_automated_movement = 1 - walk_to(src,movement_target,0,3) - -/mob/living/simple_animal/pet/cat/emote(act, m_type = 1, message = null, force) - if(stat != CONSCIOUS) - return - - var/on_CD = 0 - act = lowertext(act) - switch(act) - if("meow") - on_CD = handle_emote_CD() - if("hiss") - on_CD = handle_emote_CD() - if("purr") - on_CD = handle_emote_CD() - else - on_CD = 0 - - if(!force && on_CD == 1) - return - - switch(act) - if("meow") - message = "[src] [pick(emote_hear)]!" - m_type = 2 //audible - playsound(src, meow_sound, 50, 0.75) - if("hiss") - message = "[src] hisses!" - m_type = 2 - if("purr") - message = "[src] purrs." - m_type = 2 - if("help") - to_chat(src, "scream, meow, hiss, purr") - - ..() - -/mob/living/simple_animal/pet/cat/Proc - name = "Proc" - gender = MALE - gold_core_spawnable = NO_SPAWN - unique_pet = TRUE - -/mob/living/simple_animal/pet/cat/kitten - name = "kitten" - desc = "D'aaawwww" - icon_state = "kitten" - icon_living = "kitten" - icon_dead = "kitten_dead" - icon_resting = null - gender = NEUTER - density = 0 - pass_flags = PASSMOB - collar_type = "kitten" - -/mob/living/simple_animal/pet/cat/Syndi - name = "SyndiCat" - desc = "It's a SyndiCat droid." - icon_state = "Syndicat" - icon_living = "Syndicat" - icon_dead = "Syndicat_dead" - icon_resting = "Syndicat_rest" - meow_sound = null //Need robo-meow. - gender = FEMALE - mutations = list(BREATHLESS) - faction = list("syndicate") - gold_core_spawnable = NO_SPAWN - eats_mice = 0 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - melee_damage_lower = 5 - melee_damage_upper = 15 - -/mob/living/simple_animal/pet/cat/cak - name = "Keeki" - desc = "It's a cat made out of cake." - icon_state = "cak" - icon_living = "cak" - icon_resting = "cak_rest" - icon_dead = "cak_dead" - health = 50 - maxHealth = 50 - harm_intent_damage = 10 - butcher_results = list( - /obj/item/organ/internal/brain = 1, - /obj/item/organ/internal/heart = 1, - /obj/item/reagent_containers/food/snacks/birthdaycakeslice = 3, - /obj/item/reagent_containers/food/snacks/meat/slab = 2 - ) - response_harm = "takes a bite out of" - attacked_sound = "sound/items/eatfood.ogg" - deathmessage = "loses its false life and collapses!" - death_sound = "bodyfall" - -/mob/living/simple_animal/pet/cat/cak/Life() - ..() - if(stat) - return - if(health < maxHealth) - adjustBruteLoss(-4) - for(var/obj/item/reagent_containers/food/snacks/donut/D in range(1, src)) - if(D.icon_state != "donut2") - D.name = "frosted donut" - D.icon_state = "donut2" - D.reagents.add_reagent("sprinkles", 2) - D.filling_color = "#FF69B4" - -/mob/living/simple_animal/pet/cat/cak/attack_hand(mob/living/L) - ..() - if(L.a_intent == INTENT_HARM && L.reagents && !stat) - L.reagents.add_reagent("nutriment", 0.4) - L.reagents.add_reagent("vitamin", 0.4) - -/mob/living/simple_animal/pet/cat/cak/CheckParts(list/parts) - ..() - var/obj/item/organ/internal/brain/B = locate(/obj/item/organ/internal/brain) in contents - if(!B || !B.brainmob || !B.brainmob.mind) - return - B.brainmob.mind.transfer_to(src) - to_chat(src, "You are a cak! You're a harmless cat/cake hybrid that everyone loves. People can take bites out of you if they're hungry, but you regenerate health \ - so quickly that it generally doesn't matter. You're remarkably resilient to any damage besides this and it's hard for you to really die at all. You should go around and bring happiness and \ - free cake to the station!") - var/new_name = stripped_input(src, "Enter your name, or press \"Cancel\" to stick with Keeki.", "Name Change") - if(new_name) - to_chat(src, "Your name is now \"[new_name]\"!") - name = new_name \ No newline at end of file +//Cat +/mob/living/simple_animal/pet/cat + name = "cat" + desc = "Kitty!!" + icon_state = "cat2" + icon_living = "cat2" + icon_dead = "cat2_dead" + icon_resting = "cat2_rest" + gender = MALE + speak = list("Meow!", "Esp!", "Purr!", "HSSSSS") + speak_emote = list("purrs", "meows") + emote_hear = list("meows", "mews") + emote_see = list("shakes its head", "shivers") + var/meow_sound = 'sound/creatures/cat_meow.ogg' //Used in emote. + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + mob_size = MOB_SIZE_SMALL + animal_species = /mob/living/simple_animal/pet/cat + childtype = list(/mob/living/simple_animal/pet/cat/kitten) + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 3) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + gold_core_spawnable = FRIENDLY_SPAWN + collar_type = "cat" + var/turns_since_scan = 0 + var/mob/living/simple_animal/mouse/movement_target + var/eats_mice = 1 + +//RUNTIME IS ALIVE! SQUEEEEEEEE~ +/mob/living/simple_animal/pet/cat/Runtime + name = "Runtime" + desc = "GCAT" + icon_state = "cat" + icon_living = "cat" + icon_dead = "cat_dead" + icon_resting = "cat_rest" + gender = FEMALE + gold_core_spawnable = NO_SPAWN + unique_pet = TRUE + var/list/family = list() + var/memory_saved = 0 + var/list/children = list() //Actual mob instances of children + var/cats_deployed = 0 + +/mob/living/simple_animal/pet/cat/Runtime/New() + Read_Memory() + ..() + +/mob/living/simple_animal/pet/cat/Runtime/Life(seconds, times_fired) + if(!cats_deployed && SSticker.current_state >= GAME_STATE_SETTING_UP) + Deploy_The_Cats() + if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) + Write_Memory() + ..() + +/mob/living/simple_animal/pet/cat/Runtime/make_babies() + var/mob/baby = ..() + if(baby) + children += baby + return baby + +/mob/living/simple_animal/pet/cat/Runtime/death() + if(can_die() && !memory_saved) + Write_Memory(1) + return ..() + +/mob/living/simple_animal/pet/cat/Runtime/proc/Read_Memory() + var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") + S["family"] >> family + + if(isnull(family)) + family = list() + +/mob/living/simple_animal/pet/cat/Runtime/proc/Write_Memory(dead) + var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") + family = list() + if(!dead) + for(var/mob/living/simple_animal/pet/cat/kitten/C in children) + if(istype(C,type) || C.stat || !C.z || !C.butcher_results) + continue + if(C.type in family) + family[C.type] += 1 + else + family[C.type] = 1 + S["family"] << family + memory_saved = 1 + +/mob/living/simple_animal/pet/cat/Runtime/proc/Deploy_The_Cats() + cats_deployed = 1 + for(var/cat_type in family) + if(family[cat_type] > 0) + for(var/i in 1 to min(family[cat_type],100)) //Limits to about 500 cats, you wouldn't think this would be needed (BUT IT IS) + new cat_type(loc) + + +/mob/living/simple_animal/pet/cat/Life() + ..() + make_babies() + + +/mob/living/simple_animal/pet/cat/handle_automated_action() + if(!stat && !buckled) + if(prob(1)) + custom_emote(1, pick("stretches out for a belly rub.", "wags its tail.", "lies down.")) + StartResting() + else if(prob(1)) + custom_emote(1, pick("sits down.", "crouches on its hind legs.", "looks alert.")) + icon_state = "[icon_living]_sit" + collar_type = "[initial(collar_type)]_sit" + resting = TRUE + update_canmove() + else if(prob(1)) + if(resting) + custom_emote(1, pick("gets up and meows.", "walks around.", "stops resting.")) + StopResting() + else + custom_emote(1, pick("grooms its fur.", "twitches its whiskers.", "shakes out its coat.")) + + //MICE! + if(eats_mice && isturf(loc) && !incapacitated()) + for(var/mob/living/simple_animal/mouse/M in view(1, src)) + if(!M.stat && Adjacent(M)) + custom_emote(1, "splats \the [M]!") + M.splat() + movement_target = null + stop_automated_movement = 0 + break + for(var/obj/item/toy/cattoy/T in view(1, src)) + if(T.cooldown < (world.time - 400)) + custom_emote(1, "bats \the [T] around with its paw!") + T.cooldown = world.time + +/mob/living/simple_animal/pet/cat/handle_automated_movement() + . = ..() + if(!stat && !resting && !buckled) + turns_since_scan++ + if(turns_since_scan > 5) + walk_to(src,0) + turns_since_scan = 0 + if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) + movement_target = null + stop_automated_movement = 0 + if( !movement_target || !(movement_target.loc in oview(src, 3)) ) + movement_target = null + stop_automated_movement = 0 + for(var/mob/living/simple_animal/mouse/snack in oview(src,3)) + if(isturf(snack.loc) && !snack.stat) + movement_target = snack + break + if(movement_target) + stop_automated_movement = 1 + walk_to(src,movement_target,0,3) + +/mob/living/simple_animal/pet/cat/emote(act, m_type = 1, message = null, force) + if(stat != CONSCIOUS) + return + + var/on_CD = 0 + act = lowertext(act) + switch(act) + if("meow") + on_CD = handle_emote_CD() + if("hiss") + on_CD = handle_emote_CD() + if("purr") + on_CD = handle_emote_CD() + else + on_CD = 0 + + if(!force && on_CD == 1) + return + + switch(act) + if("meow") + message = "[src] [pick(emote_hear)]!" + m_type = 2 //audible + playsound(src, meow_sound, 50, 0.75) + if("hiss") + message = "[src] hisses!" + m_type = 2 + if("purr") + message = "[src] purrs." + m_type = 2 + if("help") + to_chat(src, "scream, meow, hiss, purr") + + ..() + +/mob/living/simple_animal/pet/cat/Proc + name = "Proc" + gender = MALE + gold_core_spawnable = NO_SPAWN + unique_pet = TRUE + +/mob/living/simple_animal/pet/cat/kitten + name = "kitten" + desc = "D'aaawwww" + icon_state = "kitten" + icon_living = "kitten" + icon_dead = "kitten_dead" + icon_resting = null + gender = NEUTER + density = 0 + pass_flags = PASSMOB + collar_type = "kitten" + +/mob/living/simple_animal/pet/cat/Syndi + name = "SyndiCat" + desc = "It's a SyndiCat droid." + icon_state = "Syndicat" + icon_living = "Syndicat" + icon_dead = "Syndicat_dead" + icon_resting = "Syndicat_rest" + meow_sound = null //Need robo-meow. + gender = FEMALE + mutations = list(BREATHLESS) + faction = list("syndicate") + gold_core_spawnable = NO_SPAWN + eats_mice = 0 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + melee_damage_lower = 5 + melee_damage_upper = 15 + +/mob/living/simple_animal/pet/cat/cak + name = "Keeki" + desc = "It's a cat made out of cake." + icon_state = "cak" + icon_living = "cak" + icon_resting = "cak_rest" + icon_dead = "cak_dead" + health = 50 + maxHealth = 50 + harm_intent_damage = 10 + butcher_results = list( + /obj/item/organ/internal/brain = 1, + /obj/item/organ/internal/heart = 1, + /obj/item/reagent_containers/food/snacks/birthdaycakeslice = 3, + /obj/item/reagent_containers/food/snacks/meat/slab = 2 + ) + response_harm = "takes a bite out of" + attacked_sound = "sound/items/eatfood.ogg" + deathmessage = "loses its false life and collapses!" + death_sound = "bodyfall" + +/mob/living/simple_animal/pet/cat/cak/Life() + ..() + if(stat) + return + if(health < maxHealth) + adjustBruteLoss(-4) + for(var/obj/item/reagent_containers/food/snacks/donut/D in range(1, src)) + if(D.icon_state != "donut2") + D.name = "frosted donut" + D.icon_state = "donut2" + D.reagents.add_reagent("sprinkles", 2) + D.filling_color = "#FF69B4" + +/mob/living/simple_animal/pet/cat/cak/attack_hand(mob/living/L) + ..() + if(L.a_intent == INTENT_HARM && L.reagents && !stat) + L.reagents.add_reagent("nutriment", 0.4) + L.reagents.add_reagent("vitamin", 0.4) + +/mob/living/simple_animal/pet/cat/cak/CheckParts(list/parts) + ..() + var/obj/item/organ/internal/brain/B = locate(/obj/item/organ/internal/brain) in contents + if(!B || !B.brainmob || !B.brainmob.mind) + return + B.brainmob.mind.transfer_to(src) + to_chat(src, "You are a cak! You're a harmless cat/cake hybrid that everyone loves. People can take bites out of you if they're hungry, but you regenerate health \ + so quickly that it generally doesn't matter. You're remarkably resilient to any damage besides this and it's hard for you to really die at all. You should go around and bring happiness and \ + free cake to the station!") + var/new_name = stripped_input(src, "Enter your name, or press \"Cancel\" to stick with Keeki.", "Name Change") + if(new_name) + to_chat(src, "Your name is now \"[new_name]\"!") + name = new_name diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index 7186eb5f5bf..4a51ce62d4e 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -1,55 +1,55 @@ -//Look Sir, free crabs! -/mob/living/simple_animal/crab - name = "crab" - desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." - icon_state = "crab" - icon_living = "crab" - icon_dead = "crab_dead" - speak_emote = list("clicks") - emote_hear = list("clicks") - emote_see = list("clacks") - speak_chance = 1 - turns_per_move = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stomps" - stop_automated_movement = 1 - friendly = "pinches" - ventcrawler = 2 - can_hide = 1 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/crab/handle_automated_movement() - //CRAB movement - if(!stat) - if(isturf(src.loc) && !resting && !buckled) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - var/east_vs_west = pick(4, 8) - if(Process_Spacemove(east_vs_west)) - Move(get_step(src, east_vs_west), east_vs_west) - -//COFFEE! SQUEEEEEEEEE! -/mob/living/simple_animal/crab/Coffee - name = "Coffee" - real_name = "Coffee" - desc = "It's Coffee, the other pet!" - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stomps" - gold_core_spawnable = NO_SPAWN - unique_pet = TRUE - -/mob/living/simple_animal/crab/evil - name = "Evil Crab" - real_name = "Evil Crab" - desc = "Unnerving, isn't it? It has to be planning something nefarious..." - icon_state = "evilcrab" - icon_living = "evilcrab" - icon_dead = "evilcrab_dead" - response_help = "pokes" - response_disarm = "shoves" - response_harm = "stomps" - gold_core_spawnable = HOSTILE_SPAWN \ No newline at end of file +//Look Sir, free crabs! +/mob/living/simple_animal/crab + name = "crab" + desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." + icon_state = "crab" + icon_living = "crab" + icon_dead = "crab_dead" + speak_emote = list("clicks") + emote_hear = list("clicks") + emote_see = list("clacks") + speak_chance = 1 + turns_per_move = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stomps" + stop_automated_movement = 1 + friendly = "pinches" + ventcrawler = 2 + can_hide = 1 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/crab/handle_automated_movement() + //CRAB movement + if(!stat) + if(isturf(src.loc) && !resting && !buckled) //This is so it only moves if it's not inside a closet, gentics machine, etc. + turns_since_move++ + if(turns_since_move >= turns_per_move) + var/east_vs_west = pick(4, 8) + if(Process_Spacemove(east_vs_west)) + Move(get_step(src, east_vs_west), east_vs_west) + +//COFFEE! SQUEEEEEEEEE! +/mob/living/simple_animal/crab/Coffee + name = "Coffee" + real_name = "Coffee" + desc = "It's Coffee, the other pet!" + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stomps" + gold_core_spawnable = NO_SPAWN + unique_pet = TRUE + +/mob/living/simple_animal/crab/evil + name = "Evil Crab" + real_name = "Evil Crab" + desc = "Unnerving, isn't it? It has to be planning something nefarious..." + icon_state = "evilcrab" + icon_living = "evilcrab" + icon_dead = "evilcrab_dead" + response_help = "pokes" + response_disarm = "shoves" + response_harm = "stomps" + gold_core_spawnable = HOSTILE_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/deer.dm b/code/modules/mob/living/simple_animal/friendly/deer.dm index 2c4c2eda960..a10ec1b7b35 100644 --- a/code/modules/mob/living/simple_animal/friendly/deer.dm +++ b/code/modules/mob/living/simple_animal/friendly/deer.dm @@ -15,4 +15,4 @@ response_disarm = "gently pushes aside" response_harm = "kicks" can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN \ No newline at end of file + gold_core_spawnable = FRIENDLY_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/diona.dm b/code/modules/mob/living/simple_animal/friendly/diona.dm index 355d756288b..7d6f90b7d0d 100644 --- a/code/modules/mob/living/simple_animal/friendly/diona.dm +++ b/code/modules/mob/living/simple_animal/friendly/diona.dm @@ -145,7 +145,7 @@ if((stat != CONSCIOUS) || !isdiona(loc)) return FALSE var/mob/living/carbon/human/D = loc - T = get_turf(src) + var/turf/T = get_turf(src) if(!T) return FALSE to_chat(loc, "You feel a pang of loss as [src] splits away from your biomass.") @@ -202,6 +202,20 @@ qdel(src) return TRUE +// Consumes plant matter other than weeds to evolve +/mob/living/simple_animal/diona/proc/consume(obj/item/reagent_containers/food/snacks/grown/G) + if(nutrition >= nutrition_need) // Prevents griefing by overeating plant items without evolving. + to_chat(src, "You're too full to consume this! Perhaps it's time to grow bigger...") + else + if(do_after_once(src, 20, target = G)) + visible_message("[src] ravenously consumes [G].", "You ravenously devour [G].") + playsound(loc, 'sound/items/eatfood.ogg', 30, 0, frequency = 1.5) + if(G.reagents.get_reagent_amount("nutriment") + G.reagents.get_reagent_amount("plantmatter") < 1) + adjust_nutrition(2) + else + adjust_nutrition((G.reagents.get_reagent_amount("nutriment") + G.reagents.get_reagent_amount("plantmatter")) * 2) + qdel(G) + /mob/living/simple_animal/diona/proc/steal_blood() if(stat != CONSCIOUS) return FALSE @@ -283,4 +297,4 @@ if("help") to_chat(src, "scream, chirp") - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 3717493d26c..4b29e703116 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -762,4 +762,4 @@ spawn(0) for(var/i in list(1, 2, 4, 8, 4, 2, 1, 2, 4, 8, 4, 2, 1, 2, 4, 8, 4, 2)) dir = i - sleep(1) \ No newline at end of file + sleep(1) diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 99574aeac99..ea8e949f1c7 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -1,448 +1,448 @@ -//goat -/mob/living/simple_animal/hostile/retaliate/goat - name = "goat" - desc = "Not known for their pleasant disposition." - icon_state = "goat" - icon_living = "goat" - icon_dead = "goat_dead" - speak = list("EHEHEHEHEH","eh?") - speak_emote = list("brays") - emote_hear = list("brays") - emote_see = list("shakes its head", "stamps a foot", "glares around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - faction = list("neutral") - attack_same = 1 - attacktext = "kicks" - attack_sound = 'sound/weapons/punch1.ogg' - health = 40 - maxHealth = 40 - melee_damage_lower = 1 - melee_damage_upper = 2 - stop_automated_movement_when_pulled = 1 - can_collar = 1 - blood_volume = BLOOD_VOLUME_NORMAL - var/obj/item/udder/udder = null - -/mob/living/simple_animal/hostile/retaliate/goat/New() - udder = new() - . = ..() - -/mob/living/simple_animal/hostile/retaliate/goat/Destroy() - QDEL_NULL(udder) - return ..() - -/mob/living/simple_animal/hostile/retaliate/goat/handle_automated_movement() - . = ..() - //chance to go crazy and start wacking stuff - if(!enemies.len && prob(1)) - Retaliate() - - if(enemies.len && prob(10)) - enemies = list() - LoseTarget() - visible_message("[src] calms down.") - - eat_plants() - if(!pulledby) - for(var/direction in shuffle(list(1, 2, 4, 8, 5, 6, 9, 10))) - var/step = get_step(src, direction) - if(step) - if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step) - Move(step, get_dir(src, step)) - -/mob/living/simple_animal/hostile/retaliate/goat/Life(seconds, times_fired) - . = ..() - if(stat == CONSCIOUS) - udder.generateMilk() - -/mob/living/simple_animal/hostile/retaliate/goat/Retaliate() - ..() - visible_message("[src] gets an evil-looking gleam in their eye.") - -/mob/living/simple_animal/hostile/retaliate/goat/Move() - . = ..() - if(!stat) - eat_plants() - -/mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) - udder.milkAnimal(O, user) - else - return ..() - -/mob/living/simple_animal/hostile/retaliate/goat/proc/eat_plants() - var/eaten = FALSE - var/obj/structure/spacevine/SV = locate(/obj/structure/spacevine) in loc - if(SV) - SV.eat(src) - eaten = TRUE - - var/obj/structure/glowshroom/GS = locate(/obj/structure/glowshroom) in loc - if(GS) - qdel(GS) - eaten = TRUE - - if(eaten && prob(10)) - say("Nom") - -/mob/living/simple_animal/hostile/retaliate/goat/AttackingTarget() - . = ..() - if(. && isdiona(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/NB = pick(H.bodyparts) - H.visible_message("[src] takes a big chomp out of [H]!", "[src] takes a big chomp out of your [NB.name]!") - NB.droplimb() - -//cow -/mob/living/simple_animal/cow - name = "cow" - desc = "Known for their milk, just don't tip them over." - icon_state = "cow" - icon_living = "cow" - icon_dead = "cow_dead" - icon_gib = "cow_gib" - speak = list("moo?","moo","MOOOOOO") - speak_emote = list("moos","moos hauntingly") - emote_hear = list("brays") - emote_see = list("shakes its head") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - attack_sound = 'sound/weapons/punch1.ogg' - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - var/obj/item/udder/udder = null - -/mob/living/simple_animal/cow/Initialize() - udder = new() - . = ..() - -/mob/living/simple_animal/cow/Destroy() - qdel(udder) - udder = null - return ..() - -/mob/living/simple_animal/cow/attackby(obj/item/O, mob/user, params) - if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) - udder.milkAnimal(O, user) - return 1 - else - return ..() - -/mob/living/simple_animal/cow/Life(seconds, times_fired) - . = ..() - if(stat == CONSCIOUS) - udder.generateMilk() - -/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/M as mob) - if(!stat && M.a_intent == INTENT_DISARM && icon_state != icon_dead) - M.visible_message("[M] tips over [src].","You tip over [src].") - Weaken(30) - icon_state = icon_dead - spawn(rand(20,50)) - if(!stat && M) - icon_state = icon_living - var/list/responses = list( "[src] looks at you imploringly.", - "[src] looks at you pleadingly", - "[src] looks at you with a resigned expression.", - "[src] seems resigned to its fate.") - to_chat(M, pick(responses)) - else - ..() - -/mob/living/simple_animal/chick - name = "\improper chick" - desc = "Adorable! They make such a racket though." - icon_state = "chick" - icon_living = "chick" - icon_dead = "chick_dead" - icon_gib = "chick_gib" - gender = FEMALE - speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") - speak_emote = list("cheeps") - emote_hear = list("cheeps") - emote_see = list("pecks at the ground","flaps its tiny wings") - density = 0 - speak_chance = 2 - turns_per_move = 2 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 3 - maxHealth = 3 - ventcrawler = 2 - var/amount_grown = 0 - pass_flags = PASSTABLE | PASSGRILLE | PASSMOB - mob_size = MOB_SIZE_TINY - can_hide = 1 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/chick/New() - ..() - pixel_x = rand(-6, 6) - pixel_y = rand(0, 10) - -/mob/living/simple_animal/chick/Life(seconds, times_fired) - . =..() - if(.) - amount_grown += rand(1,2) - if(amount_grown >= 100) - var/mob/living/simple_animal/chicken/C = new /mob/living/simple_animal/chicken(loc) - if(mind) - mind.transfer_to(C) - qdel(src) - -var/const/MAX_CHICKENS = 50 -var/global/chicken_count = 0 - -/mob/living/simple_animal/chicken - name = "\improper chicken" - desc = "Hopefully the eggs are good this season." - gender = FEMALE - icon_state = "chicken_brown" - icon_living = "chicken_brown" - icon_dead = "chicken_brown_dead" - speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") - speak_emote = list("clucks","croons") - emote_hear = list("clucks") - emote_see = list("pecks at the ground","flaps its wings viciously") - density = 0 - speak_chance = 2 - turns_per_move = 3 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 2) - var/egg_type = /obj/item/reagent_containers/food/snacks/egg - var/food_type = /obj/item/reagent_containers/food/snacks/grown/wheat - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 15 - maxHealth = 15 - ventcrawler = 2 - var/eggsleft = 0 - var/eggsFertile = TRUE - var/body_color - var/icon_prefix = "chicken" - pass_flags = PASSTABLE | PASSMOB - mob_size = MOB_SIZE_SMALL - can_hide = 1 - can_collar = 1 - var/list/feedMessages = list("It clucks happily.","It clucks happily.") - var/list/layMessage = EGG_LAYING_MESSAGES - var/list/validColors = list("brown","black","white") - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/chicken/New() - ..() - if(!body_color) - body_color = pick(validColors) - icon_state = "[icon_prefix]_[body_color]" - icon_living = "[icon_prefix]_[body_color]" - icon_dead = "[icon_prefix]_[body_color]_dead" - pixel_x = rand(-6, 6) - pixel_y = rand(0, 10) - chicken_count += 1 - -/mob/living/simple_animal/chicken/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return - chicken_count -= 1 - -/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params) - if(istype(O, food_type)) //feedin' dem chickens - if(!stat && eggsleft < 8) - var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]" - user.visible_message(feedmsg) - user.drop_item() - qdel(O) - eggsleft += rand(1, 4) - //world << eggsleft - else - to_chat(user, "[name] doesn't seem hungry!") - else - ..() - -/mob/living/simple_animal/chicken/Life(seconds, times_fired) - . = ..() - if((. && prob(3) && eggsleft > 0) && egg_type) - visible_message("[src] [pick(layMessage)]") - eggsleft-- - var/obj/item/E = new egg_type(get_turf(src)) - E.pixel_x = rand(-6,6) - E.pixel_y = rand(-6,6) - if(eggsFertile) - if(chicken_count < MAX_CHICKENS && prob(25)) - START_PROCESSING(SSobj, E) - -/obj/item/reagent_containers/food/snacks/egg/var/amount_grown = 0 -/obj/item/reagent_containers/food/snacks/egg/process() - if(isturf(loc)) - amount_grown += rand(1,2) - if(amount_grown >= 100) - visible_message("[src] hatches with a quiet cracking sound.") - new /mob/living/simple_animal/chick(get_turf(src)) - STOP_PROCESSING(SSobj, src) - qdel(src) - else - STOP_PROCESSING(SSobj, src) - - -/mob/living/simple_animal/pig - name = "pig" - desc = "Oink oink." - icon_state = "pig" - icon_living = "pig" - icon_dead = "pig_dead" - speak = list("oink?","oink","OINK") - speak_emote = list("oinks") -// emote_hear = list("brays") - emote_see = list("rolls around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/ham = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - -/mob/living/simple_animal/turkey - name = "turkey" - desc = "Benjamin Franklin would be proud." - icon_state = "turkey" - icon_living = "turkey" - icon_dead = "turkey_dead" - icon_resting = "turkey_rest" - speak = list("gobble?","gobble","GOBBLE") - speak_emote = list("gobble") - emote_see = list("struts around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "pecks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/goose - name = "goose" - desc = "A pretty goose. Would make a nice comforter." - icon_state = "goose" - icon_living = "goose" - icon_dead = "goose_dead" - speak = list("quack?","quack","QUACK") - speak_emote = list("quacks") -// emote_hear = list("brays") - emote_see = list("flaps it's wings") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/seal - name = "seal" - desc = "A beautiful white seal." - icon_state = "seal" - icon_living = "seal" - icon_dead = "seal_dead" - speak = list("Urk?","urk","URK") - speak_emote = list("urks") -// emote_hear = list("brays") - emote_see = list("flops around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - -/mob/living/simple_animal/walrus - name = "walrus" - desc = "A big brown walrus." - icon_state = "walrus" - icon_living = "walrus" - icon_dead = "walrus_dead" - speak = list("Urk?","urk","URK") - speak_emote = list("urks") -// emote_hear = list("brays") - emote_see = list("flops around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - -/obj/item/udder - name = "udder" - -/obj/item/udder/New() - create_reagents(50) - reagents.add_reagent("milk", 20) - . = ..() - -/obj/item/udder/proc/generateMilk() - if(prob(5)) - reagents.add_reagent("milk", rand(5, 10)) - -/obj/item/udder/proc/milkAnimal(obj/O, mob/user) - var/obj/item/reagent_containers/glass/G = O - if(G.reagents.total_volume >= G.volume) - to_chat(user, "[O] is full.") - return - var/transfered = reagents.trans_to(O, rand(5,10)) - if(transfered) - user.visible_message("[user] milks [src] using \the [O].", "You milk [src] using \the [O].") - else - to_chat(user, "The udder is dry. Wait a bit longer...") \ No newline at end of file +//goat +/mob/living/simple_animal/hostile/retaliate/goat + name = "goat" + desc = "Not known for their pleasant disposition." + icon_state = "goat" + icon_living = "goat" + icon_dead = "goat_dead" + speak = list("EHEHEHEHEH","eh?") + speak_emote = list("brays") + emote_hear = list("brays") + emote_see = list("shakes its head", "stamps a foot", "glares around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + faction = list("neutral") + attack_same = 1 + attacktext = "kicks" + attack_sound = 'sound/weapons/punch1.ogg' + health = 40 + maxHealth = 40 + melee_damage_lower = 1 + melee_damage_upper = 2 + stop_automated_movement_when_pulled = 1 + can_collar = 1 + blood_volume = BLOOD_VOLUME_NORMAL + var/obj/item/udder/udder = null + +/mob/living/simple_animal/hostile/retaliate/goat/New() + udder = new() + . = ..() + +/mob/living/simple_animal/hostile/retaliate/goat/Destroy() + QDEL_NULL(udder) + return ..() + +/mob/living/simple_animal/hostile/retaliate/goat/handle_automated_movement() + . = ..() + //chance to go crazy and start wacking stuff + if(!enemies.len && prob(1)) + Retaliate() + + if(enemies.len && prob(10)) + enemies = list() + LoseTarget() + visible_message("[src] calms down.") + + eat_plants() + if(!pulledby) + for(var/direction in shuffle(list(1, 2, 4, 8, 5, 6, 9, 10))) + var/step = get_step(src, direction) + if(step) + if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step) + Move(step, get_dir(src, step)) + +/mob/living/simple_animal/hostile/retaliate/goat/Life(seconds, times_fired) + . = ..() + if(stat == CONSCIOUS) + udder.generateMilk() + +/mob/living/simple_animal/hostile/retaliate/goat/Retaliate() + ..() + visible_message("[src] gets an evil-looking gleam in their eye.") + +/mob/living/simple_animal/hostile/retaliate/goat/Move() + . = ..() + if(!stat) + eat_plants() + +/mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) + udder.milkAnimal(O, user) + else + return ..() + +/mob/living/simple_animal/hostile/retaliate/goat/proc/eat_plants() + var/eaten = FALSE + var/obj/structure/spacevine/SV = locate(/obj/structure/spacevine) in loc + if(SV) + SV.eat(src) + eaten = TRUE + + var/obj/structure/glowshroom/GS = locate(/obj/structure/glowshroom) in loc + if(GS) + qdel(GS) + eaten = TRUE + + if(eaten && prob(10)) + say("Nom") + +/mob/living/simple_animal/hostile/retaliate/goat/AttackingTarget() + . = ..() + if(. && isdiona(target)) + var/mob/living/carbon/human/H = target + var/obj/item/organ/external/NB = pick(H.bodyparts) + H.visible_message("[src] takes a big chomp out of [H]!", "[src] takes a big chomp out of your [NB.name]!") + NB.droplimb() + +//cow +/mob/living/simple_animal/cow + name = "cow" + desc = "Known for their milk, just don't tip them over." + icon_state = "cow" + icon_living = "cow" + icon_dead = "cow_dead" + icon_gib = "cow_gib" + speak = list("moo?","moo","MOOOOOO") + speak_emote = list("moos","moos hauntingly") + emote_hear = list("brays") + emote_see = list("shakes its head") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + attack_sound = 'sound/weapons/punch1.ogg' + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + var/obj/item/udder/udder = null + +/mob/living/simple_animal/cow/Initialize() + udder = new() + . = ..() + +/mob/living/simple_animal/cow/Destroy() + qdel(udder) + udder = null + return ..() + +/mob/living/simple_animal/cow/attackby(obj/item/O, mob/user, params) + if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) + udder.milkAnimal(O, user) + return 1 + else + return ..() + +/mob/living/simple_animal/cow/Life(seconds, times_fired) + . = ..() + if(stat == CONSCIOUS) + udder.generateMilk() + +/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/M as mob) + if(!stat && M.a_intent == INTENT_DISARM && icon_state != icon_dead) + M.visible_message("[M] tips over [src].","You tip over [src].") + Weaken(30) + icon_state = icon_dead + spawn(rand(20,50)) + if(!stat && M) + icon_state = icon_living + var/list/responses = list( "[src] looks at you imploringly.", + "[src] looks at you pleadingly", + "[src] looks at you with a resigned expression.", + "[src] seems resigned to its fate.") + to_chat(M, pick(responses)) + else + ..() + +/mob/living/simple_animal/chick + name = "\improper chick" + desc = "Adorable! They make such a racket though." + icon_state = "chick" + icon_living = "chick" + icon_dead = "chick_dead" + icon_gib = "chick_gib" + gender = FEMALE + speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") + speak_emote = list("cheeps") + emote_hear = list("cheeps") + emote_see = list("pecks at the ground","flaps its tiny wings") + density = 0 + speak_chance = 2 + turns_per_move = 2 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 3 + maxHealth = 3 + ventcrawler = 2 + var/amount_grown = 0 + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + can_hide = 1 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/chick/New() + ..() + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + +/mob/living/simple_animal/chick/Life(seconds, times_fired) + . =..() + if(.) + amount_grown += rand(1,2) + if(amount_grown >= 100) + var/mob/living/simple_animal/chicken/C = new /mob/living/simple_animal/chicken(loc) + if(mind) + mind.transfer_to(C) + qdel(src) + +#define MAX_CHICKENS 50 +GLOBAL_VAR_INIT(chicken_count, 0) + +/mob/living/simple_animal/chicken + name = "\improper chicken" + desc = "Hopefully the eggs are good this season." + gender = FEMALE + icon_state = "chicken_brown" + icon_living = "chicken_brown" + icon_dead = "chicken_brown_dead" + speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") + speak_emote = list("clucks","croons") + emote_hear = list("clucks") + emote_see = list("pecks at the ground","flaps its wings viciously") + density = 0 + speak_chance = 2 + turns_per_move = 3 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 2) + var/egg_type = /obj/item/reagent_containers/food/snacks/egg + var/food_type = /obj/item/reagent_containers/food/snacks/grown/wheat + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 15 + maxHealth = 15 + ventcrawler = 2 + var/eggsleft = 0 + var/eggsFertile = TRUE + var/body_color + var/icon_prefix = "chicken" + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_SMALL + can_hide = 1 + can_collar = 1 + var/list/feedMessages = list("It clucks happily.","It clucks happily.") + var/list/layMessage = EGG_LAYING_MESSAGES + var/list/validColors = list("brown","black","white") + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/chicken/New() + ..() + if(!body_color) + body_color = pick(validColors) + icon_state = "[icon_prefix]_[body_color]" + icon_living = "[icon_prefix]_[body_color]" + icon_dead = "[icon_prefix]_[body_color]_dead" + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + GLOB.chicken_count += 1 + +/mob/living/simple_animal/chicken/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return + GLOB.chicken_count -= 1 + +/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params) + if(istype(O, food_type)) //feedin' dem chickens + if(!stat && eggsleft < 8) + var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]" + user.visible_message(feedmsg) + user.drop_item() + qdel(O) + eggsleft += rand(1, 4) + //world << eggsleft + else + to_chat(user, "[name] doesn't seem hungry!") + else + ..() + +/mob/living/simple_animal/chicken/Life(seconds, times_fired) + . = ..() + if((. && prob(3) && eggsleft > 0) && egg_type) + visible_message("[src] [pick(layMessage)]") + eggsleft-- + var/obj/item/E = new egg_type(get_turf(src)) + E.pixel_x = rand(-6,6) + E.pixel_y = rand(-6,6) + if(eggsFertile) + if(GLOB.chicken_count < MAX_CHICKENS && prob(25)) + START_PROCESSING(SSobj, E) + +/obj/item/reagent_containers/food/snacks/egg/var/amount_grown = 0 +/obj/item/reagent_containers/food/snacks/egg/process() + if(isturf(loc)) + amount_grown += rand(1,2) + if(amount_grown >= 100) + visible_message("[src] hatches with a quiet cracking sound.") + new /mob/living/simple_animal/chick(get_turf(src)) + STOP_PROCESSING(SSobj, src) + qdel(src) + else + STOP_PROCESSING(SSobj, src) + + +/mob/living/simple_animal/pig + name = "pig" + desc = "Oink oink." + icon_state = "pig" + icon_living = "pig" + icon_dead = "pig_dead" + speak = list("oink?","oink","OINK") + speak_emote = list("oinks") +// emote_hear = list("brays") + emote_see = list("rolls around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/ham = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + +/mob/living/simple_animal/turkey + name = "turkey" + desc = "Benjamin Franklin would be proud." + icon_state = "turkey" + icon_living = "turkey" + icon_dead = "turkey_dead" + icon_resting = "turkey_rest" + speak = list("gobble?","gobble","GOBBLE") + speak_emote = list("gobble") + emote_see = list("struts around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "pecks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/goose + name = "goose" + desc = "A pretty goose. Would make a nice comforter." + icon_state = "goose" + icon_living = "goose" + icon_dead = "goose_dead" + speak = list("quack?","quack","QUACK") + speak_emote = list("quacks") +// emote_hear = list("brays") + emote_see = list("flaps it's wings") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/seal + name = "seal" + desc = "A beautiful white seal." + icon_state = "seal" + icon_living = "seal" + icon_dead = "seal_dead" + speak = list("Urk?","urk","URK") + speak_emote = list("urks") +// emote_hear = list("brays") + emote_see = list("flops around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + +/mob/living/simple_animal/walrus + name = "walrus" + desc = "A big brown walrus." + icon_state = "walrus" + icon_living = "walrus" + icon_dead = "walrus_dead" + speak = list("Urk?","urk","URK") + speak_emote = list("urks") +// emote_hear = list("brays") + emote_see = list("flops around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + +/obj/item/udder + name = "udder" + +/obj/item/udder/New() + create_reagents(50) + reagents.add_reagent("milk", 20) + . = ..() + +/obj/item/udder/proc/generateMilk() + if(prob(5)) + reagents.add_reagent("milk", rand(5, 10)) + +/obj/item/udder/proc/milkAnimal(obj/O, mob/user) + var/obj/item/reagent_containers/glass/G = O + if(G.reagents.total_volume >= G.volume) + to_chat(user, "[O] is full.") + return + var/transfered = reagents.trans_to(O, rand(5,10)) + if(transfered) + user.visible_message("[user] milks [src] using \the [O].", "You milk [src] using \the [O].") + else + to_chat(user, "The udder is dry. Wait a bit longer...") diff --git a/code/modules/mob/living/simple_animal/friendly/fox.dm b/code/modules/mob/living/simple_animal/friendly/fox.dm index ad77fd547a5..9eccc473e6d 100644 --- a/code/modules/mob/living/simple_animal/friendly/fox.dm +++ b/code/modules/mob/living/simple_animal/friendly/fox.dm @@ -40,4 +40,4 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 melee_damage_lower = 10 - melee_damage_upper = 20 \ No newline at end of file + melee_damage_upper = 20 diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm index 2d7426aa451..96688963b73 100644 --- a/code/modules/mob/living/simple_animal/friendly/lizard.dm +++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm @@ -1,25 +1,25 @@ -/mob/living/simple_animal/lizard - name = "Lizard" - desc = "A cute tiny lizard." - icon = 'icons/mob/critter.dmi' - icon_state = "lizard" - icon_living = "lizard" - icon_dead = "lizard-dead" - speak_emote = list("hisses") - health = 5 - maxHealth = 5 - attacktext = "bites" - obj_damage = 0 - melee_damage_lower = 1 - melee_damage_upper = 2 - response_help = "pets" - response_disarm = "shoos" - response_harm = "stomps on" - ventcrawler = 2 - density = 0 - pass_flags = PASSTABLE | PASSMOB - mob_size = MOB_SIZE_SMALL - can_hide = 1 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN +/mob/living/simple_animal/lizard + name = "Lizard" + desc = "A cute tiny lizard." + icon = 'icons/mob/critter.dmi' + icon_state = "lizard" + icon_living = "lizard" + icon_dead = "lizard-dead" + speak_emote = list("hisses") + health = 5 + maxHealth = 5 + attacktext = "bites" + obj_damage = 0 + melee_damage_lower = 1 + melee_damage_upper = 2 + response_help = "pets" + response_disarm = "shoos" + response_harm = "stomps on" + ventcrawler = 2 + density = 0 + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_SMALL + can_hide = 1 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index d6f38d33268..a48b36cb0c7 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -1,236 +1,236 @@ -/mob/living/simple_animal/mouse - name = "mouse" - real_name = "mouse" - desc = "It's a small, disease-ridden rodent." - icon_state = "mouse_gray" - icon_living = "mouse_gray" - icon_dead = "mouse_gray_dead" - icon_resting = "mouse_gray_sleep" - speak = list("Squeek!","SQUEEK!","Squeek?") - speak_emote = list("squeeks","squeaks","squiks") - emote_hear = list("squeeks","squeaks","squiks") - emote_see = list("runs in a circle", "shakes", "scritches at something") - var/squeak_sound = 'sound/creatures/mousesqueak.ogg' - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - maxHealth = 5 - health = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stamps on" - density = 0 - ventcrawler = 2 - pass_flags = PASSTABLE | PASSGRILLE | PASSMOB - mob_size = MOB_SIZE_TINY - var/mouse_color //brown, gray and white, leave blank for random - layer = MOB_LAYER - atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 223 //Below -50 Degrees Celcius - maxbodytemp = 323 //Above 50 Degrees Celcius - universal_speak = 0 - can_hide = 1 - holder_type = /obj/item/holder/mouse - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - var/chew_probability = 1 - -/mob/living/simple_animal/mouse/Initialize(mapload) - . = ..() - AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueak.ogg' = 1), 100) - -/mob/living/simple_animal/mouse/handle_automated_action() - if(prob(chew_probability) && isturf(loc)) - var/turf/simulated/floor/F = get_turf(src) - if(istype(F) && !F.intact) - var/obj/structure/cable/C = locate() in F - if(C && prob(15)) - if(C.avail()) - visible_message("[src] chews through [C]. It's toast!") - playsound(src, 'sound/effects/sparks2.ogg', 100, 1) - C.deconstruct() - toast() // mmmm toasty. - else - C.deconstruct() - visible_message("[src] chews through [C].") - -/mob/living/simple_animal/mouse/handle_automated_speech() - ..() - if(prob(speak_chance) && !incapacitated()) - playsound(src, squeak_sound, 100, 1) - -/mob/living/simple_animal/mouse/handle_automated_movement() - . = ..() - if(resting) - if(prob(1)) - StopResting() - else if(prob(5)) - custom_emote(2, "snuffles") - else if(prob(0.5)) - StartResting() - -/mob/living/simple_animal/mouse/New() - ..() - if(!mouse_color) - mouse_color = pick( list("brown","gray","white") ) - icon_state = "mouse_[mouse_color]" - icon_living = "mouse_[mouse_color]" - icon_dead = "mouse_[mouse_color]_dead" - icon_resting = "mouse_[mouse_color]_sleep" - desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." - -/mob/living/simple_animal/mouse/proc/splat() - src.health = 0 - src.stat = DEAD - src.icon_dead = "mouse_[mouse_color]_splat" - src.icon_state = "mouse_[mouse_color]_splat" - layer = MOB_LAYER - if(client) - client.time_died_as_mouse = world.time - -/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob) - if(M.a_intent == INTENT_HELP) - get_scooped(M) - ..() - -/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things - to_chat(src, "You are too small to pull anything.") - return - -/mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc) - if(ishuman(AM)) - if(!stat) - var/mob/M = AM - to_chat(M, "[bicon(src)] Squeek!") - ..() - -/mob/living/simple_animal/mouse/proc/toast() - add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY) - desc = "It's toast." - death() - -/mob/living/simple_animal/mouse/death(gibbed) - // Only execute the below if we successfully died - playsound(src, squeak_sound, 40, 1) - . = ..(gibbed) - if(!.) - return FALSE - layer = MOB_LAYER - if(client) - client.time_died_as_mouse = world.time - -/mob/living/simple_animal/mouse/emote(act, m_type = 1, message = null, force) - if(stat != CONSCIOUS) - return - - var/on_CD = 0 - act = lowertext(act) - switch(act) - if("squeak") //Mouse time - on_CD = handle_emote_CD() - else - on_CD = 0 - - if(!force && on_CD == 1) - return - - switch(act) - if("squeak") - message = "\The [src] [pick(emote_hear)]!" - m_type = 2 //audible - playsound(src, squeak_sound, 40, 1) - if("help") - to_chat(src, "scream, squeak") - - ..() - -/* - * Mouse types - */ - -/mob/living/simple_animal/mouse/white - mouse_color = "white" - icon_state = "mouse_white" - -/mob/living/simple_animal/mouse/gray - mouse_color = "gray" - icon_state = "mouse_gray" - -/mob/living/simple_animal/mouse/brown - mouse_color = "brown" - icon_state = "mouse_brown" - -//TOM IS ALIVE! SQUEEEEEEEE~K :) -/mob/living/simple_animal/mouse/brown/Tom - name = "Tom" - desc = "Jerry the cat is not amused." - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "splats" - unique_pet = TRUE - gold_core_spawnable = NO_SPAWN - - -/mob/living/simple_animal/mouse/blobinfected - maxHealth = 100 - health = 100 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - gold_core_spawnable = NO_SPAWN - var/cycles_alive = 0 - var/cycles_limit = 60 - var/has_burst = FALSE - -/mob/living/simple_animal/mouse/blobinfected/Life() - cycles_alive++ - var/timeleft = (cycles_limit - cycles_alive) * 2 - if(ismob(loc)) // if someone ate it, burst immediately - burst(FALSE) - else if(timeleft < 1) // if timer expired, burst. - burst(FALSE) - else if(cycles_alive % 2 == 0) // give the mouse/player a countdown reminder every 2 cycles - to_chat(src, "[timeleft] seconds until you burst, and become a blob...") - return ..() - -/mob/living/simple_animal/mouse/blobinfected/death(gibbed) - burst(gibbed) - return ..(gibbed) - -/mob/living/simple_animal/mouse/blobinfected/proc/burst(gibbed) - if(has_burst) - return FALSE - var/turf/T = get_turf(src) - if(!is_station_level(T.z) || isspaceturf(T)) - to_chat(src, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") - return FALSE - has_burst = TRUE - var/datum/mind/blobmind = mind - var/client/C = client - if(istype(blobmind) && istype(C)) - blobmind.special_role = SPECIAL_ROLE_BLOB - var/obj/structure/blob/core/core = new(T, 200, C, 3) - core.lateblobtimer() - else - new /obj/structure/blob/core(T) // Ghosts will be prompted to control it. - if(ismob(loc)) // in case some taj/etc ate the mouse. - var/mob/M = loc - M.gib() - if(!gibbed) - gib() - -/mob/living/simple_animal/mouse/blobinfected/get_scooped(mob/living/carbon/grabber) - to_chat(grabber, "You try to pick up [src], but they slip out of your grasp!") - to_chat(src, "[src] tries to pick you up, but you wriggle free of their grasp!") - -/mob/living/simple_animal/mouse/fluff/clockwork - name = "Chip" - real_name = "Chip" - mouse_color = "clockwork" - icon_state = "mouse_clockwork" - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stamps on" - gold_core_spawnable = NO_SPAWN - can_collar = 0 - butcher_results = list(/obj/item/stack/sheet/metal = 1) +/mob/living/simple_animal/mouse + name = "mouse" + real_name = "mouse" + desc = "It's a small, disease-ridden rodent." + icon_state = "mouse_gray" + icon_living = "mouse_gray" + icon_dead = "mouse_gray_dead" + icon_resting = "mouse_gray_sleep" + speak = list("Squeek!","SQUEEK!","Squeek?") + speak_emote = list("squeeks","squeaks","squiks") + emote_hear = list("squeeks","squeaks","squiks") + emote_see = list("runs in a circle", "shakes", "scritches at something") + var/squeak_sound = 'sound/creatures/mousesqueak.ogg' + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + maxHealth = 5 + health = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stamps on" + density = 0 + ventcrawler = 2 + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + var/mouse_color //brown, gray and white, leave blank for random + layer = MOB_LAYER + atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 223 //Below -50 Degrees Celcius + maxbodytemp = 323 //Above 50 Degrees Celcius + universal_speak = 0 + can_hide = 1 + holder_type = /obj/item/holder/mouse + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + var/chew_probability = 1 + +/mob/living/simple_animal/mouse/Initialize(mapload) + . = ..() + AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueak.ogg' = 1), 100) + +/mob/living/simple_animal/mouse/handle_automated_action() + if(prob(chew_probability) && isturf(loc)) + var/turf/simulated/floor/F = get_turf(src) + if(istype(F) && !F.intact) + var/obj/structure/cable/C = locate() in F + if(C && prob(15)) + if(C.avail()) + visible_message("[src] chews through [C]. It's toast!") + playsound(src, 'sound/effects/sparks2.ogg', 100, 1) + toast() // mmmm toasty. + else + visible_message("[src] chews through [C].") + investigate_log("was chewed through by a mouse in [get_area(F)]([F.x], [F.y], [F.z] - [ADMIN_JMP(F)])","wires") + C.deconstruct() + +/mob/living/simple_animal/mouse/handle_automated_speech() + ..() + if(prob(speak_chance) && !incapacitated()) + playsound(src, squeak_sound, 100, 1) + +/mob/living/simple_animal/mouse/handle_automated_movement() + . = ..() + if(resting) + if(prob(1)) + StopResting() + else if(prob(5)) + custom_emote(2, "snuffles") + else if(prob(0.5)) + StartResting() + +/mob/living/simple_animal/mouse/New() + ..() + if(!mouse_color) + mouse_color = pick( list("brown","gray","white") ) + icon_state = "mouse_[mouse_color]" + icon_living = "mouse_[mouse_color]" + icon_dead = "mouse_[mouse_color]_dead" + icon_resting = "mouse_[mouse_color]_sleep" + desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." + +/mob/living/simple_animal/mouse/proc/splat() + src.health = 0 + src.stat = DEAD + src.icon_dead = "mouse_[mouse_color]_splat" + src.icon_state = "mouse_[mouse_color]_splat" + layer = MOB_LAYER + if(client) + client.time_died_as_mouse = world.time + +/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob) + if(M.a_intent == INTENT_HELP) + get_scooped(M) + ..() + +/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things + to_chat(src, "You are too small to pull anything.") + return + +/mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc) + if(ishuman(AM)) + if(!stat) + var/mob/M = AM + to_chat(M, "[bicon(src)] Squeek!") + ..() + +/mob/living/simple_animal/mouse/proc/toast() + add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY) + desc = "It's toast." + death() + +/mob/living/simple_animal/mouse/death(gibbed) + // Only execute the below if we successfully died + playsound(src, squeak_sound, 40, 1) + . = ..(gibbed) + if(!.) + return FALSE + layer = MOB_LAYER + if(client) + client.time_died_as_mouse = world.time + +/mob/living/simple_animal/mouse/emote(act, m_type = 1, message = null, force) + if(stat != CONSCIOUS) + return + + var/on_CD = 0 + act = lowertext(act) + switch(act) + if("squeak") //Mouse time + on_CD = handle_emote_CD() + else + on_CD = 0 + + if(!force && on_CD == 1) + return + + switch(act) + if("squeak") + message = "\The [src] [pick(emote_hear)]!" + m_type = 2 //audible + playsound(src, squeak_sound, 40, 1) + if("help") + to_chat(src, "scream, squeak") + + ..() + +/* + * Mouse types + */ + +/mob/living/simple_animal/mouse/white + mouse_color = "white" + icon_state = "mouse_white" + +/mob/living/simple_animal/mouse/gray + mouse_color = "gray" + icon_state = "mouse_gray" + +/mob/living/simple_animal/mouse/brown + mouse_color = "brown" + icon_state = "mouse_brown" + +//TOM IS ALIVE! SQUEEEEEEEE~K :) +/mob/living/simple_animal/mouse/brown/Tom + name = "Tom" + desc = "Jerry the cat is not amused." + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "splats" + unique_pet = TRUE + gold_core_spawnable = NO_SPAWN + + +/mob/living/simple_animal/mouse/blobinfected + maxHealth = 100 + health = 100 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + gold_core_spawnable = NO_SPAWN + var/cycles_alive = 0 + var/cycles_limit = 60 + var/has_burst = FALSE + +/mob/living/simple_animal/mouse/blobinfected/Life() + cycles_alive++ + var/timeleft = (cycles_limit - cycles_alive) * 2 + if(ismob(loc)) // if someone ate it, burst immediately + burst(FALSE) + else if(timeleft < 1) // if timer expired, burst. + burst(FALSE) + else if(cycles_alive % 2 == 0) // give the mouse/player a countdown reminder every 2 cycles + to_chat(src, "[timeleft] seconds until you burst, and become a blob...") + return ..() + +/mob/living/simple_animal/mouse/blobinfected/death(gibbed) + burst(gibbed) + return ..(gibbed) + +/mob/living/simple_animal/mouse/blobinfected/proc/burst(gibbed) + if(has_burst) + return FALSE + var/turf/T = get_turf(src) + if(!is_station_level(T.z) || isspaceturf(T)) + to_chat(src, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") + return FALSE + has_burst = TRUE + var/datum/mind/blobmind = mind + var/client/C = client + if(istype(blobmind) && istype(C)) + blobmind.special_role = SPECIAL_ROLE_BLOB + var/obj/structure/blob/core/core = new(T, 200, C, 3) + core.lateblobtimer() + else + new /obj/structure/blob/core(T) // Ghosts will be prompted to control it. + if(ismob(loc)) // in case some taj/etc ate the mouse. + var/mob/M = loc + M.gib() + if(!gibbed) + gib() + +/mob/living/simple_animal/mouse/blobinfected/get_scooped(mob/living/carbon/grabber) + to_chat(grabber, "You try to pick up [src], but they slip out of your grasp!") + to_chat(src, "[src] tries to pick you up, but you wriggle free of their grasp!") + +/mob/living/simple_animal/mouse/fluff/clockwork + name = "Chip" + real_name = "Chip" + mouse_color = "clockwork" + icon_state = "mouse_clockwork" + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stamps on" + gold_core_spawnable = NO_SPAWN + can_collar = 0 + butcher_results = list(/obj/item/stack/sheet/metal = 1) diff --git a/code/modules/mob/living/simple_animal/friendly/penguin.dm b/code/modules/mob/living/simple_animal/friendly/penguin.dm index d2dba6da1c9..dc00e252661 100644 --- a/code/modules/mob/living/simple_animal/friendly/penguin.dm +++ b/code/modules/mob/living/simple_animal/friendly/penguin.dm @@ -64,4 +64,4 @@ icon_dead = "penguin_baby_dead" density = FALSE pass_flags = PASSMOB - mob_size = MOB_SIZE_SMALL \ No newline at end of file + mob_size = MOB_SIZE_SMALL diff --git a/code/modules/mob/living/simple_animal/friendly/pet.dm b/code/modules/mob/living/simple_animal/friendly/pet.dm index 8a9899b7fa9..ac4f3367002 100644 --- a/code/modules/mob/living/simple_animal/friendly/pet.dm +++ b/code/modules/mob/living/simple_animal/friendly/pet.dm @@ -13,4 +13,4 @@ setDir(i) sleep(1) else - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/simple_animal/friendly/sloth.dm b/code/modules/mob/living/simple_animal/friendly/sloth.dm index ddf311d62d4..d5b47c189ff 100644 --- a/code/modules/mob/living/simple_animal/friendly/sloth.dm +++ b/code/modules/mob/living/simple_animal/friendly/sloth.dm @@ -29,4 +29,4 @@ name = "Paperwork" desc = "Cargo's pet sloth. About as useful as the rest of the techs." unique_pet = TRUE - gold_core_spawnable = NO_SPAWN \ No newline at end of file + gold_core_spawnable = NO_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/snake.dm b/code/modules/mob/living/simple_animal/friendly/snake.dm index 1cd85c1b622..b4a15ba27b4 100644 --- a/code/modules/mob/living/simple_animal/friendly/snake.dm +++ b/code/modules/mob/living/simple_animal/friendly/snake.dm @@ -58,4 +58,4 @@ QDEL_NULL(target) adjustHealth(-2) else - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/simple_animal/friendly/spiderbot.dm b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm index dd248b88a62..7a1227960f4 100644 --- a/code/modules/mob/living/simple_animal/friendly/spiderbot.dm +++ b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm @@ -104,7 +104,7 @@ var/obj/item/pda/pda = O id_card = pda.id - if(access_robotics in id_card.access) + if(ACCESS_ROBOTICS in id_card.access) to_chat(user, "You swipe your access card and pop the brain out of [src].") eject_brain() return 1 diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index b541d55096a..038b3b3d221 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -1,164 +1,164 @@ -/mob/living/simple_animal/hostile/alien - name = "alien hunter" - desc = "Hiss!" - icon = 'icons/mob/alien.dmi' - icon_state = "alienh_running" - icon_living = "alienh_running" - icon_dead = "alienh_dead" - icon_gib = "syndicate_gib" - gender = FEMALE - response_help = "pokes" - response_disarm = "shoves" - response_harm = "hits" - speed = 0 - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 3, /obj/item/stack/sheet/animalhide/xeno = 1) - maxHealth = 125 - health = 125 - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 25 - melee_damage_upper = 25 - attacktext = "slashes" - speak_emote = list("hisses") - a_intent = INTENT_HARM - attack_sound = 'sound/weapons/bladeslice.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - unsuitable_atmos_damage = 15 - heat_damage_per_tick = 20 - faction = list("alien") - status_flags = CANPUSH - minbodytemp = 0 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - gold_core_spawnable = HOSTILE_SPAWN - death_sound = 'sound/voice/hiss6.ogg' - deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..." - - -/mob/living/simple_animal/hostile/alien/drone - name = "alien drone" - icon_state = "aliend_running" - icon_living = "aliend_running" - icon_dead = "aliend_dead" - melee_damage_lower = 15 - melee_damage_upper = 15 - var/plant_cooldown = 30 - var/plants_off = 0 - -/mob/living/simple_animal/hostile/alien/drone/handle_automated_action() - if(!..()) //AIStatus is off - return - plant_cooldown-- - if(AIStatus == AI_IDLE) - if(!plants_off && prob(10) && plant_cooldown<=0) - plant_cooldown = initial(plant_cooldown) - SpreadPlants() - -/mob/living/simple_animal/hostile/alien/sentinel - name = "alien sentinel" - icon_state = "aliens_running" - icon_living = "aliens_running" - icon_dead = "aliens_dead" - health = 150 - maxHealth = 150 - melee_damage_lower = 15 - melee_damage_upper = 15 - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - projectiletype = /obj/item/projectile/neurotox - projectilesound = 'sound/weapons/pierce.ogg' - - -/mob/living/simple_animal/hostile/alien/queen - name = "alien queen" - icon_state = "alienq_running" - icon_living = "alienq_running" - icon_dead = "alienq_d" - health = 250 - maxHealth = 250 - melee_damage_lower = 15 - melee_damage_upper = 15 - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - move_to_delay = 4 - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 4, /obj/item/stack/sheet/animalhide/xeno = 1) - projectiletype = /obj/item/projectile/neurotox - projectilesound = 'sound/weapons/pierce.ogg' - status_flags = 0 - var/sterile = 1 - var/plants_off = 0 - var/egg_cooldown = 30 - var/plant_cooldown = 30 - -/mob/living/simple_animal/hostile/alien/queen/handle_automated_action() - if(!..()) - return - egg_cooldown-- - plant_cooldown-- - if(AIStatus == AI_IDLE) - if(!plants_off && prob(10) && plant_cooldown<=0) - plant_cooldown = initial(plant_cooldown) - SpreadPlants() - if(!sterile && prob(10) && egg_cooldown<=0) - egg_cooldown = initial(egg_cooldown) - LayEggs() - -/mob/living/simple_animal/hostile/alien/proc/SpreadPlants() - if(!isturf(loc) || isspaceturf(loc)) - return - if(locate(/obj/structure/alien/weeds/node) in get_turf(src)) - return - visible_message("[src] has planted some alien weeds!") - new /obj/structure/alien/weeds/node(loc) - -/mob/living/simple_animal/hostile/alien/proc/LayEggs() - if(!isturf(loc) || isspaceturf(loc)) - return - if(locate(/obj/structure/alien/egg) in get_turf(src)) - return - visible_message("[src] has laid an egg!") - new /obj/structure/alien/egg(loc) - -/mob/living/simple_animal/hostile/alien/queen/large - name = "alien empress" - icon = 'icons/mob/alienlarge.dmi' - icon_state = "queen_s" - icon_living = "queen_s" - icon_dead = "queen_dead" - move_to_delay = 4 - maxHealth = 400 - health = 400 - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 10, /obj/item/stack/sheet/animalhide/xeno = 2) - mob_size = MOB_SIZE_LARGE - gold_core_spawnable = NO_SPAWN - -/obj/item/projectile/neurotox - name = "neurotoxin" - damage = 30 - icon_state = "toxin" - -/mob/living/simple_animal/hostile/alien/maid - name = "lusty xenomorph maid" - melee_damage_lower = 0 - melee_damage_upper = 0 - a_intent = INTENT_HELP - friendly = "caresses" - obj_damage = 0 - environment_smash = ENVIRONMENT_SMASH_NONE - gold_core_spawnable = HOSTILE_SPAWN - icon_state = "maid" - icon_living = "maid" - icon_dead = "maid_dead" - -/mob/living/simple_animal/hostile/alien/maid/AttackingTarget() - if(ismovableatom(target)) - if(istype(target, /obj/effect/decal/cleanable)) - visible_message("\The [src] cleans up \the [target].") - qdel(target) - return TRUE - var/atom/movable/M = target - M.clean_blood() - visible_message("\The [src] polishes \the [target].") - return TRUE +/mob/living/simple_animal/hostile/alien + name = "alien hunter" + desc = "Hiss!" + icon = 'icons/mob/alien.dmi' + icon_state = "alienh_running" + icon_living = "alienh_running" + icon_dead = "alienh_dead" + icon_gib = "syndicate_gib" + gender = FEMALE + response_help = "pokes" + response_disarm = "shoves" + response_harm = "hits" + speed = 0 + butcher_results = list(/obj/item/reagent_containers/food/snacks/monstermeat/xenomeat= 3, /obj/item/stack/sheet/animalhide/xeno = 1) + maxHealth = 125 + health = 125 + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "slashes" + speak_emote = list("hisses") + a_intent = INTENT_HARM + attack_sound = 'sound/weapons/bladeslice.ogg' + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + unsuitable_atmos_damage = 15 + heat_damage_per_tick = 20 + faction = list("alien") + status_flags = CANPUSH + minbodytemp = 0 + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + gold_core_spawnable = HOSTILE_SPAWN + death_sound = 'sound/voice/hiss6.ogg' + deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..." + + +/mob/living/simple_animal/hostile/alien/drone + name = "alien drone" + icon_state = "aliend_running" + icon_living = "aliend_running" + icon_dead = "aliend_dead" + melee_damage_lower = 15 + melee_damage_upper = 15 + var/plant_cooldown = 30 + var/plants_off = 0 + +/mob/living/simple_animal/hostile/alien/drone/handle_automated_action() + if(!..()) //AIStatus is off + return + plant_cooldown-- + if(AIStatus == AI_IDLE) + if(!plants_off && prob(10) && plant_cooldown<=0) + plant_cooldown = initial(plant_cooldown) + SpreadPlants() + +/mob/living/simple_animal/hostile/alien/sentinel + name = "alien sentinel" + icon_state = "aliens_running" + icon_living = "aliens_running" + icon_dead = "aliens_dead" + health = 150 + maxHealth = 150 + melee_damage_lower = 15 + melee_damage_upper = 15 + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + projectiletype = /obj/item/projectile/neurotox + projectilesound = 'sound/weapons/pierce.ogg' + + +/mob/living/simple_animal/hostile/alien/queen + name = "alien queen" + icon_state = "alienq_running" + icon_living = "alienq_running" + icon_dead = "alienq_d" + health = 250 + maxHealth = 250 + melee_damage_lower = 15 + melee_damage_upper = 15 + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + move_to_delay = 4 + butcher_results = list(/obj/item/reagent_containers/food/snacks/monstermeat/xenomeat= 4, /obj/item/stack/sheet/animalhide/xeno = 1) + projectiletype = /obj/item/projectile/neurotox + projectilesound = 'sound/weapons/pierce.ogg' + status_flags = 0 + var/sterile = 1 + var/plants_off = 0 + var/egg_cooldown = 30 + var/plant_cooldown = 30 + +/mob/living/simple_animal/hostile/alien/queen/handle_automated_action() + if(!..()) + return + egg_cooldown-- + plant_cooldown-- + if(AIStatus == AI_IDLE) + if(!plants_off && prob(10) && plant_cooldown<=0) + plant_cooldown = initial(plant_cooldown) + SpreadPlants() + if(!sterile && prob(10) && egg_cooldown<=0) + egg_cooldown = initial(egg_cooldown) + LayEggs() + +/mob/living/simple_animal/hostile/alien/proc/SpreadPlants() + if(!isturf(loc) || isspaceturf(loc)) + return + if(locate(/obj/structure/alien/weeds/node) in get_turf(src)) + return + visible_message("[src] has planted some alien weeds!") + new /obj/structure/alien/weeds/node(loc) + +/mob/living/simple_animal/hostile/alien/proc/LayEggs() + if(!isturf(loc) || isspaceturf(loc)) + return + if(locate(/obj/structure/alien/egg) in get_turf(src)) + return + visible_message("[src] has laid an egg!") + new /obj/structure/alien/egg(loc) + +/mob/living/simple_animal/hostile/alien/queen/large + name = "alien empress" + icon = 'icons/mob/alienlarge.dmi' + icon_state = "queen_s" + icon_living = "queen_s" + icon_dead = "queen_dead" + move_to_delay = 4 + maxHealth = 400 + health = 400 + butcher_results = list(/obj/item/reagent_containers/food/snacks/monstermeat/xenomeat= 10, /obj/item/stack/sheet/animalhide/xeno = 2) + mob_size = MOB_SIZE_LARGE + gold_core_spawnable = NO_SPAWN + +/obj/item/projectile/neurotox + name = "neurotoxin" + damage = 30 + icon_state = "toxin" + +/mob/living/simple_animal/hostile/alien/maid + name = "lusty xenomorph maid" + melee_damage_lower = 0 + melee_damage_upper = 0 + a_intent = INTENT_HELP + friendly = "caresses" + obj_damage = 0 + environment_smash = ENVIRONMENT_SMASH_NONE + gold_core_spawnable = HOSTILE_SPAWN + icon_state = "maid" + icon_living = "maid" + icon_dead = "maid_dead" + +/mob/living/simple_animal/hostile/alien/maid/AttackingTarget() + if(ismovableatom(target)) + if(istype(target, /obj/effect/decal/cleanable)) + visible_message("\The [src] cleans up \the [target].") + qdel(target) + return TRUE + var/atom/movable/M = target + M.clean_blood() + visible_message("\The [src] polishes \the [target].") + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm index ae851fe6dcb..be04fc217e9 100644 --- a/code/modules/mob/living/simple_animal/hostile/bat.dm +++ b/code/modules/mob/living/simple_animal/hostile/bat.dm @@ -73,4 +73,4 @@ pass_flags = PASSTABLE universal_speak = 1 universal_understand = 1 - gold_core_spawnable = NO_SPAWN //badmin only \ No newline at end of file + gold_core_spawnable = NO_SPAWN //badmin only diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index cfe673a78f1..d5b7205f09c 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -1,56 +1,56 @@ -//Space bears! -/mob/living/simple_animal/hostile/bear - name = "space bear" - desc = "You don't need to be faster than a space bear, you just need to outrun your crewmates." - icon_state = "bear" - icon_living = "bear" - icon_dead = "bear_dead" - icon_gib = "bear_gib" - speak = list("RAWR!","Rawr!","GRR!","Growl!") - speak_emote = list("growls", "roars") - emote_hear = list("rawrs","grumbles","grawls") - emote_see = list("stares ferociously", "stomps") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/bearmeat = 5, /obj/item/clothing/head/bearpelt = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - stop_automated_movement_when_pulled = 0 - maxHealth = 60 - health = 60 - obj_damage = 60 - melee_damage_lower = 20 - melee_damage_upper = 30 - attacktext = "mauls" - friendly = "bear hugs" - attack_sound = 'sound/weapons/genhit3.ogg' - - //Space bears aren't affected by atmos. - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("russian") - gold_core_spawnable = HOSTILE_SPAWN - -//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! -/mob/living/simple_animal/hostile/bear/Hudson - name = "Hudson" - desc = "Feared outlaw, this guy is one bad news bear." //I'm sorry... - -/mob/living/simple_animal/hostile/bear/Hudson/New() - ..() - var/unbearable_pun = pick("He's unbearably cute.", "It looks like he is a bearer of bad news.", "Sadly, he is bearly able to comprehend puns.") - desc = "That's Hudson. " + unbearable_pun// I am not sorry for this. - -/mob/living/simple_animal/hostile/bear/Move() - ..() - if(stat != DEAD) - if(loc && istype(loc,/turf/space)) - icon_state = "bear" - else - icon_state = "bearfloor" - -/mob/living/simple_animal/hostile/bear/Process_Spacemove(var/movement_dir = 0) - return 1 //No drifting in space for space bears! +//Space bears! +/mob/living/simple_animal/hostile/bear + name = "space bear" + desc = "You don't need to be faster than a space bear, you just need to outrun your crewmates." + icon_state = "bear" + icon_living = "bear" + icon_dead = "bear_dead" + icon_gib = "bear_gib" + speak = list("RAWR!","Rawr!","GRR!","Growl!") + speak_emote = list("growls", "roars") + emote_hear = list("rawrs","grumbles","grawls") + emote_see = list("stares ferociously", "stomps") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/monstermeat/bearmeat= 5, /obj/item/clothing/head/bearpelt = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + stop_automated_movement_when_pulled = 0 + maxHealth = 60 + health = 60 + obj_damage = 60 + melee_damage_lower = 20 + melee_damage_upper = 30 + attacktext = "mauls" + friendly = "bear hugs" + attack_sound = 'sound/weapons/genhit3.ogg' + + //Space bears aren't affected by atmos. + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("russian") + gold_core_spawnable = HOSTILE_SPAWN + +//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! +/mob/living/simple_animal/hostile/bear/Hudson + name = "Hudson" + desc = "Feared outlaw, this guy is one bad news bear." //I'm sorry... + +/mob/living/simple_animal/hostile/bear/Hudson/New() + ..() + var/unbearable_pun = pick("He's unbearably cute.", "It looks like he is a bearer of bad news.", "Sadly, he is bearly able to comprehend puns.") + desc = "That's Hudson. " + unbearable_pun// I am not sorry for this. + +/mob/living/simple_animal/hostile/bear/Move() + ..() + if(stat != DEAD) + if(loc && istype(loc,/turf/space)) + icon_state = "bear" + else + icon_state = "bearfloor" + +/mob/living/simple_animal/hostile/bear/Process_Spacemove(var/movement_dir = 0) + return 1 //No drifting in space for space bears! diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index c6586fefa2f..c1e7548d9a3 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -159,7 +159,7 @@ var/mob/living/L = target if(L.reagents) if(beegent) - beegent.reaction_mob(L, INGEST) + beegent.reaction_mob(L, REAGENT_INGEST) L.reagents.add_reagent(beegent.id, rand(1, 5)) else L.reagents.add_reagent("spidertoxin", 5) @@ -238,7 +238,7 @@ . = ..() if(. && beegent && isliving(target)) var/mob/living/L = target - beegent.reaction_mob(L, TOUCH) + beegent.reaction_mob(L, REAGENT_TOUCH) L.reagents.add_reagent(beegent.id, rand(1, 5)) //PEASENT BEES @@ -352,4 +352,4 @@ if(. && target && isliving(target)) var/mob/living/L = target if(L.stat) - LoseTarget() \ No newline at end of file + LoseTarget() diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index eb2bbddc437..7b0b52f0b4f 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -1,171 +1,167 @@ -#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin for megacarps (ty robustin!) - -/mob/living/simple_animal/hostile/carp - name = "space carp" - desc = "A ferocious, fang-bearing creature that resembles a fish." - icon = 'icons/mob/carp.dmi' - icon_state = "base" - icon_living = "base" - icon_dead = "base_dead" - icon_gib = "carp_gib" - speak_chance = 0 - turns_per_move = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/carpmeat = 2) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - emote_taunt = list("gnashes") - taunt_chance = 30 - speed = 0 - maxHealth = 25 - health = 25 - - harm_intent_damage = 8 - obj_damage = 50 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - speak_emote = list("gnashes") - - //Space carp aren't affected by atmos. - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxbodytemp = 1500 - faction = list("carp") - flying = TRUE - pressure_resistance = 200 - gold_core_spawnable = HOSTILE_SPAWN - - var/random_color = TRUE //if the carp uses random coloring - var/rarechance = 1 //chance for rare color variant - - var/static/list/carp_colors = list(\ - "lightpurple" = "#c3b9f1", \ - "lightpink" = "#da77a8", \ - "green" = "#70ff25", \ - "grape" = "#df0afb", \ - "swamp" = "#e5e75a", \ - "turquoise" = "#04e1ed", \ - "brown" = "#ca805a", \ - "teal" = "#20e28e", \ - "lightblue" = "#4d88cc", \ - "rusty" = "#dd5f34", \ - "beige" = "#bbaeaf", \ - "yellow" = "#f3ca4a", \ - "blue" = "#09bae1", \ - "palegreen" = "#7ef099", \ - ) - var/static/list/carp_colors_rare = list(\ - "silver" = "#fdfbf3", \ - ) - -/mob/living/simple_animal/hostile/carp/Initialize(mapload) - . = ..() - carp_randomify(rarechance) - update_icons() - -/mob/living/simple_animal/hostile/carp/proc/carp_randomify(rarechance) - if(random_color) - var/our_color - if(prob(rarechance)) - our_color = pick(carp_colors_rare) - add_atom_colour(carp_colors_rare[our_color], FIXED_COLOUR_PRIORITY) - else - our_color = pick(carp_colors) - add_atom_colour(carp_colors[our_color], FIXED_COLOUR_PRIORITY) - add_carp_overlay() - -/mob/living/simple_animal/hostile/carp/proc/add_carp_overlay() - if(!random_color) - return - cut_overlays() - var/mutable_appearance/base_overlay = mutable_appearance(icon, "base_mouth") - base_overlay.appearance_flags = RESET_COLOR - add_overlay(base_overlay) - -/mob/living/simple_animal/hostile/carp/proc/add_dead_carp_overlay() - if(!random_color) - return - cut_overlays() - var/mutable_appearance/base_dead_overlay = mutable_appearance(icon, "base_dead_mouth") - base_dead_overlay.appearance_flags = RESET_COLOR - add_overlay(base_dead_overlay) - -/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = 0) - return TRUE //No drifting in space for space carp! //original comments do not steal - -/mob/living/simple_animal/hostile/carp/AttackingTarget() - . = ..() - if(. && ishuman(target)) - var/mob/living/carbon/human/H = target - H.adjustStaminaLoss(8) - -/mob/living/simple_animal/hostile/carp/death(gibbed) - . = ..() - cut_overlays() - if(!random_color || gibbed) - return - add_dead_carp_overlay() - -/mob/living/simple_animal/hostile/carp/revive() - ..() - regenerate_icons() - -/mob/living/simple_animal/hostile/carp/regenerate_icons() - cut_overlays() - if(!random_color) - return - if(stat != DEAD) - add_carp_overlay() - else - add_dead_carp_overlay() - ..() - -/mob/living/simple_animal/hostile/carp/holocarp - icon_state = "holocarp" - icon_living = "holocarp" - maxbodytemp = INFINITY - gold_core_spawnable = NO_SPAWN - del_on_death = 1 - random_color = FALSE - -/mob/living/simple_animal/hostile/carp/megacarp - icon = 'icons/mob/alienqueen.dmi' - name = "Mega Space Carp" - desc = "A ferocious, fang bearing creature that resembles a shark. This one seems especially ticked off." - icon_state = "megacarp" - icon_living = "megacarp" - icon_dead = "megacarp_dead" - icon_gib = "megacarp_gib" - maxHealth = 20 - health = 20 - pixel_x = -16 - mob_size = MOB_SIZE_LARGE - random_color = FALSE - - obj_damage = 80 - melee_damage_lower = 20 - melee_damage_upper = 20 - - var/regen_cooldown = 0 - -/mob/living/simple_animal/hostile/carp/megacarp/Initialize() - . = ..() - name = "[pick(GLOB.megacarp_first_names)] [pick(GLOB.megacarp_last_names)]" - melee_damage_lower += rand(2, 10) - melee_damage_upper += rand(10, 20) - maxHealth += rand(30, 60) - move_to_delay = rand(3, 7) - -/mob/living/simple_animal/hostile/carp/megacarp/adjustHealth(amount, updating_health = TRUE) - . = ..() - if(.) - regen_cooldown = world.time + REGENERATION_DELAY - -/mob/living/simple_animal/hostile/carp/megacarp/Life() - ..() - if(regen_cooldown < world.time) - heal_overall_damage(4) - -#undef REGENERATION_DELAY \ No newline at end of file +#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin for megacarps (ty robustin!) + +/mob/living/simple_animal/hostile/carp + name = "space carp" + desc = "A ferocious, fang-bearing creature that resembles a fish." + icon = 'icons/mob/carp.dmi' + icon_state = "base" + icon_living = "base" + icon_dead = "base_dead" + icon_gib = "carp_gib" + speak_chance = 0 + turns_per_move = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/carpmeat = 2) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + emote_taunt = list("gnashes") + taunt_chance = 30 + speed = 0 + maxHealth = 25 + health = 25 + + harm_intent_damage = 8 + obj_damage = 50 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + speak_emote = list("gnashes") + + //Space carp aren't affected by atmos. + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxbodytemp = 1500 + faction = list("carp") + flying = TRUE + pressure_resistance = 200 + gold_core_spawnable = HOSTILE_SPAWN + + var/random_color = TRUE //if the carp uses random coloring + var/rarechance = 1 //chance for rare color variant + + var/static/list/carp_colors = list(\ + "lightpurple" = "#c3b9f1", \ + "lightpink" = "#da77a8", \ + "green" = "#70ff25", \ + "grape" = "#df0afb", \ + "swamp" = "#e5e75a", \ + "turquoise" = "#04e1ed", \ + "brown" = "#ca805a", \ + "teal" = "#20e28e", \ + "lightblue" = "#4d88cc", \ + "rusty" = "#dd5f34", \ + "beige" = "#bbaeaf", \ + "yellow" = "#f3ca4a", \ + "blue" = "#09bae1", \ + "palegreen" = "#7ef099", \ + ) + var/static/list/carp_colors_rare = list(\ + "silver" = "#fdfbf3", \ + ) + +/mob/living/simple_animal/hostile/carp/Initialize(mapload) + . = ..() + carp_randomify(rarechance) + update_icons() + +/mob/living/simple_animal/hostile/carp/proc/carp_randomify(rarechance) + if(random_color) + var/our_color + if(prob(rarechance)) + our_color = pick(carp_colors_rare) + add_atom_colour(carp_colors_rare[our_color], FIXED_COLOUR_PRIORITY) + else + our_color = pick(carp_colors) + add_atom_colour(carp_colors[our_color], FIXED_COLOUR_PRIORITY) + regenerate_icons() + +/mob/living/simple_animal/hostile/carp/proc/add_carp_overlay() + if(!random_color) + return + var/mutable_appearance/base_overlay = mutable_appearance(icon, "base_mouth") + base_overlay.appearance_flags = RESET_COLOR + add_overlay(base_overlay) + +/mob/living/simple_animal/hostile/carp/proc/add_dead_carp_overlay() + if(!random_color) + return + var/mutable_appearance/base_dead_overlay = mutable_appearance(icon, "base_dead_mouth") + base_dead_overlay.appearance_flags = RESET_COLOR + add_overlay(base_dead_overlay) + +/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = 0) + return TRUE //No drifting in space for space carp! //original comments do not steal + +/mob/living/simple_animal/hostile/carp/AttackingTarget() + . = ..() + if(. && ishuman(target)) + var/mob/living/carbon/human/H = target + H.adjustStaminaLoss(8) + +/mob/living/simple_animal/hostile/carp/death(gibbed) + . = ..() + if(!random_color || gibbed) + return + regenerate_icons() + +/mob/living/simple_animal/hostile/carp/revive() + ..() + regenerate_icons() + +/mob/living/simple_animal/hostile/carp/regenerate_icons() + ..() + if(!random_color) + return + if(stat != DEAD) + add_carp_overlay() + else + add_dead_carp_overlay() + +/mob/living/simple_animal/hostile/carp/holocarp + icon_state = "holocarp" + icon_living = "holocarp" + maxbodytemp = INFINITY + gold_core_spawnable = NO_SPAWN + del_on_death = 1 + random_color = FALSE + +/mob/living/simple_animal/hostile/carp/megacarp + icon = 'icons/mob/alienqueen.dmi' + name = "Mega Space Carp" + desc = "A ferocious, fang bearing creature that resembles a shark. This one seems especially ticked off." + icon_state = "megacarp" + icon_living = "megacarp" + icon_dead = "megacarp_dead" + icon_gib = "megacarp_gib" + maxHealth = 20 + health = 20 + pixel_x = -16 + mob_size = MOB_SIZE_LARGE + random_color = FALSE + + obj_damage = 80 + melee_damage_lower = 20 + melee_damage_upper = 20 + + var/regen_cooldown = 0 + +/mob/living/simple_animal/hostile/carp/megacarp/Initialize() + . = ..() + name = "[pick(GLOB.megacarp_first_names)] [pick(GLOB.megacarp_last_names)]" + melee_damage_lower += rand(2, 10) + melee_damage_upper += rand(10, 20) + maxHealth += rand(30, 60) + move_to_delay = rand(3, 7) + +/mob/living/simple_animal/hostile/carp/megacarp/adjustHealth(amount, updating_health = TRUE) + . = ..() + if(.) + regen_cooldown = world.time + REGENERATION_DELAY + +/mob/living/simple_animal/hostile/carp/megacarp/Life() + ..() + if(regen_cooldown < world.time) + heal_overall_damage(4) + +#undef REGENERATION_DELAY diff --git a/code/modules/mob/living/simple_animal/hostile/creature.dm b/code/modules/mob/living/simple_animal/hostile/creature.dm index 225cbfea5b1..3934a9b1d2f 100644 --- a/code/modules/mob/living/simple_animal/hostile/creature.dm +++ b/code/modules/mob/living/simple_animal/hostile/creature.dm @@ -1,17 +1,17 @@ -/mob/living/simple_animal/hostile/creature - name = "creature" - desc = "A sanity-destroying otherthing." - speak_emote = list("gibbers") - icon_state = "otherthing" - icon_living = "otherthing" - icon_dead = "otherthing-dead" - health = 80 - maxHealth = 80 - obj_damage = 100 - melee_damage_lower = 25 - melee_damage_upper = 50 - attacktext = "chomps" - attack_sound = 'sound/weapons/bite.ogg' - faction = list("creature") - gold_core_spawnable = HOSTILE_SPAWN - +/mob/living/simple_animal/hostile/creature + name = "creature" + desc = "A sanity-destroying otherthing." + speak_emote = list("gibbers") + icon_state = "otherthing" + icon_living = "otherthing" + icon_dead = "otherthing-dead" + health = 80 + maxHealth = 80 + obj_damage = 100 + melee_damage_lower = 25 + melee_damage_upper = 50 + attacktext = "chomps" + attack_sound = 'sound/weapons/bite.ogg' + faction = list("creature") + gold_core_spawnable = HOSTILE_SPAWN + diff --git a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm index 14b66017d41..69f5257d4dd 100644 --- a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm +++ b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm @@ -48,4 +48,4 @@ armour_penetration = 5 melee_damage_lower = 10 melee_damage_upper = 20 - environment_smash = 2 \ No newline at end of file + environment_smash = 2 diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm index 5065ce63545..0ed560f24e9 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithless.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm @@ -1,41 +1,41 @@ -/mob/living/simple_animal/hostile/faithless - name = "Faithless" - desc = "The Wish Granter's faith in humanity, incarnate" - icon_state = "faithless" - icon_living = "faithless" - icon_dead = "faithless_dead" - speak_chance = 0 - turns_per_move = 5 - response_help = "passes through the" - response_disarm = "shoves" - response_harm = "hits the" - speed = 0 - maxHealth = 80 - health = 80 - obj_damage = 50 - harm_intent_damage = 10 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "grips" - attack_sound = 'sound/hallucinations/growl1.ogg' - speak_emote = list("growls") - emote_taunt = list("wails") - taunt_chance = 25 - - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("faithless") - gold_core_spawnable = HOSTILE_SPAWN - -/mob/living/simple_animal/hostile/faithless/Process_Spacemove(var/movement_dir = 0) - return 1 - -/mob/living/simple_animal/hostile/faithless/AttackingTarget() - . = ..() - if(. && iscarbon(target)) - var/mob/living/carbon/C = target - if(prob(12)) - C.Weaken(3) - C.visible_message("\The [src] knocks down \the [C]!", \ - "\The [src] knocks you down!") \ No newline at end of file +/mob/living/simple_animal/hostile/faithless + name = "Faithless" + desc = "The Wish Granter's faith in humanity, incarnate" + icon_state = "faithless" + icon_living = "faithless" + icon_dead = "faithless_dead" + speak_chance = 0 + turns_per_move = 5 + response_help = "passes through the" + response_disarm = "shoves" + response_harm = "hits the" + speed = 0 + maxHealth = 80 + health = 80 + obj_damage = 50 + harm_intent_damage = 10 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "grips" + attack_sound = 'sound/hallucinations/growl1.ogg' + speak_emote = list("growls") + emote_taunt = list("wails") + taunt_chance = 25 + + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("faithless") + gold_core_spawnable = HOSTILE_SPAWN + +/mob/living/simple_animal/hostile/faithless/Process_Spacemove(var/movement_dir = 0) + return 1 + +/mob/living/simple_animal/hostile/faithless/AttackingTarget() + . = ..() + if(. && iscarbon(target)) + var/mob/living/carbon/C = target + if(prob(12)) + C.Weaken(3) + C.visible_message("\The [src] knocks down \the [C]!", \ + "\The [src] knocks you down!") diff --git a/code/modules/mob/living/simple_animal/hostile/feral_cat.dm b/code/modules/mob/living/simple_animal/hostile/feral_cat.dm index 78f5a3ea2b9..4067d552089 100644 --- a/code/modules/mob/living/simple_animal/hostile/feral_cat.dm +++ b/code/modules/mob/living/simple_animal/hostile/feral_cat.dm @@ -25,4 +25,4 @@ faction = list("cat") atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 5 - pass_flags = PASSTABLE \ No newline at end of file + pass_flags = PASSTABLE diff --git a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm index c3939e41395..cf55824542a 100644 --- a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm +++ b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm @@ -78,8 +78,8 @@ pixel_y = 8 if(is_type_in_typecache(get_area(loc), invalid_area_typecache)) - var/area = pick(teleportlocs) - var/area/tp = teleportlocs[area] + var/area = pick(GLOB.teleportlocs) + var/area/tp = GLOB.teleportlocs[area] forceMove(pick(get_area_turfs(tp.type))) if((!current_victim && !admincluwne) || QDELETED(current_victim)) @@ -285,7 +285,7 @@ if(prob(6)) for(var/turf/simulated/floor/O in range(src, 6)) - O.MakeSlippery(TURF_WET_WATER, 10) + O.MakeSlippery(TURF_WET_WATER, 10 SECONDS) playsound(src, 'sound/effects/clownstep1.ogg', 30, 1) if(prob(5)) @@ -323,7 +323,7 @@ if(!eating) addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Grab, H), 70) for(var/turf/simulated/floor/O in range(src, 6)) - O.MakeSlippery(TURF_WET_LUBE, 20) + O.MakeSlippery(TURF_WET_LUBE, 20 SECONDS) playsound(src, 'sound/effects/meteorimpact.ogg', 30, 1) eating = TRUE @@ -426,4 +426,4 @@ #undef STAGE_SPOOK #undef STAGE_TORMENT #undef STAGE_ATTACK -#undef MANIFEST_DELAY \ No newline at end of file +#undef MANIFEST_DELAY diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index 0465d137007..614e814fc25 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -1,246 +1,246 @@ - -#define SPINNING_WEB 1 -#define LAYING_EGGS 2 -#define MOVING_TO_TARGET 3 -#define SPINNING_COCOON 4 - -//basic spider mob, these generally guard nests -/mob/living/simple_animal/hostile/poison/giant_spider - name = "giant spider" - desc = "Furry and black, it makes you shudder to look at it. This one has deep red eyes." - icon_state = "guard" - var/butcher_state = 8 // Icon state for dead spider icons - icon_living = "guard" - icon_dead = "guard_dead" - speak_emote = list("chitters") - emote_hear = list("chitters") - speak_chance = 5 - turns_per_move = 5 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - maxHealth = 200 - health = 200 - obj_damage = 60 - melee_damage_lower = 15 - melee_damage_upper = 20 - heat_damage_per_tick = 20 //amount of damage applied if animal's body temperature is higher than maxbodytemp - cold_damage_per_tick = 20 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - faction = list("spiders") - pass_flags = PASSTABLE - move_to_delay = 6 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - gold_core_spawnable = HOSTILE_SPAWN - var/venom_per_bite = 0 // While the /poison/ type path remains as-is for consistency reasons, we're really talking about venom, not poison. - var/busy = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/AttackingTarget() - // This is placed here, NOT on /poison, because the other subtypes of /poison/ already override AttackingTarget() completely, and as such it would do nothing but confuse people there. - . = ..() - if(. && venom_per_bite > 0 && iscarbon(target) && (!client || a_intent == INTENT_HARM)) - var/mob/living/carbon/C = target - var/inject_target = pick("chest", "head") - if(C.can_inject(null, FALSE, inject_target, FALSE)) - C.reagents.add_reagent("spidertoxin", venom_per_bite) - -//nursemaids - these create webs and eggs -/mob/living/simple_animal/hostile/poison/giant_spider/nurse - desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes." - icon_state = "nurse" - icon_living = "nurse" - icon_dead = "nurse_dead" - butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8, /obj/item/reagent_containers/food/snacks/spidereggs = 4) - - maxHealth = 40 - health = 40 - melee_damage_lower = 5 - melee_damage_upper = 10 - venom_per_bite = 30 - var/atom/cocoon_target - var/fed = 0 - -//hunters have the most poison and move the fastest, so they can find prey -/mob/living/simple_animal/hostile/poison/giant_spider/hunter - desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes." - icon_state = "hunter" - icon_living = "hunter" - icon_dead = "hunter_dead" - maxHealth = 120 - health = 120 - melee_damage_lower = 10 - melee_damage_upper = 20 - venom_per_bite = 10 - move_to_delay = 5 - -/mob/living/simple_animal/hostile/poison/giant_spider/handle_automated_movement() //Hacky and ugly. - . = ..() - if(AIStatus == AI_IDLE) - //1% chance to skitter madly away - if(!busy && prob(1)) - stop_automated_movement = 1 - Goto(pick(urange(20, src, 1)), move_to_delay) - spawn(50) - stop_automated_movement = 0 - walk(src,0) - return 1 - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C) - spawn(100) - if(busy == MOVING_TO_TARGET) - if(cocoon_target == C && get_dist(src,cocoon_target) > 1) - cocoon_target = null - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/handle_automated_movement() //Hacky and ugly. - if(..()) - var/list/can_see = view(src, 10) - if(!busy && prob(30)) //30% chance to stop wandering and do something - //first, check for potential food nearby to cocoon - for(var/mob/living/C in can_see) - if(C.stat && !istype(C, /mob/living/simple_animal/hostile/poison/giant_spider) && !C.anchored) - cocoon_target = C - busy = MOVING_TO_TARGET - Goto(C, move_to_delay) - //give up if we can't reach them after 10 seconds - GiveUp(C) - return - //second, spin a sticky spiderweb on this tile - var/obj/structure/spider/stickyweb/W = locate() in get_turf(src) - if(!W) - Web() - else - //third, lay an egg cluster there - if(fed) - LayEggs() - else - //fourthly, cocoon any nearby items so those pesky pinkskins can't use them - for(var/obj/O in can_see) - if(O.anchored) - continue - - if(isitem(O) || isstructure(O) || ismachinery(O)) - cocoon_target = O - busy = MOVING_TO_TARGET - stop_automated_movement = 1 - Goto(O, move_to_delay) - //give up if we can't reach them after 10 seconds - GiveUp(O) - - else if(busy == MOVING_TO_TARGET && cocoon_target) - if(get_dist(src, cocoon_target) <= 1) - Wrap() - - else - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/verb/Web() - set name = "Lay Web" - set category = "Spider" - set desc = "Spread a sticky web to slow down prey." - - var/T = src.loc - - if(busy != SPINNING_WEB) - busy = SPINNING_WEB - src.visible_message("\the [src] begins to secrete a sticky substance.") - stop_automated_movement = 1 - spawn(40) - if(busy == SPINNING_WEB && src.loc == T) - new /obj/structure/spider/stickyweb(T) - busy = 0 - stop_automated_movement = 0 - - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/Wrap() - set name = "Wrap" - set category = "Spider" - set desc = "Wrap up prey to feast upon and objects for safe keeping." - - if(!cocoon_target) - var/list/choices = list() - for(var/mob/living/L in view(1,src)) - if(L == src) - continue - if(Adjacent(L)) - choices += L - for(var/obj/O in src.loc) - if(Adjacent(O)) - choices += O - cocoon_target = input(src,"What do you wish to cocoon?") in null|choices - - if(cocoon_target && busy != SPINNING_COCOON) - busy = SPINNING_COCOON - src.visible_message("\the [src] begins to secrete a sticky substance around \the [cocoon_target].") - stop_automated_movement = 1 - walk(src,0) - spawn(50) - if(busy == SPINNING_COCOON) - if(cocoon_target && istype(cocoon_target.loc, /turf) && get_dist(src,cocoon_target) <= 1) - var/obj/structure/spider/cocoon/C = new(cocoon_target.loc) - var/large_cocoon = 0 - C.pixel_x = cocoon_target.pixel_x - C.pixel_y = cocoon_target.pixel_y - for(var/obj/item/I in C.loc) - I.loc = C - for(var/obj/structure/S in C.loc) - if(!S.anchored) - S.loc = C - large_cocoon = 1 - for(var/obj/machinery/M in C.loc) - if(!M.anchored) - M.loc = C - large_cocoon = 1 - for(var/mob/living/L in C.loc) - if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider)) - continue - large_cocoon = 1 - L.loc = C - C.pixel_x = L.pixel_x - C.pixel_y = L.pixel_y - fed++ - visible_message("\the [src] sticks a proboscis into \the [L] and sucks a viscous substance out.") - - break - if(large_cocoon) - C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") - cocoon_target = null - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/LayEggs() - set name = "Lay Eggs" - set category = "Spider" - set desc = "Lay a clutch of eggs, but you must wrap a creature for feeding first." - - var/obj/structure/spider/eggcluster/E = locate() in get_turf(src) - if(E) - to_chat(src, "There is already a cluster of eggs here!") - else if(!fed) - to_chat(src, "You are too hungry to do this!") - else if(busy != LAYING_EGGS) - busy = LAYING_EGGS - src.visible_message("\the [src] begins to lay a cluster of eggs.") - stop_automated_movement = 1 - spawn(50) - if(busy == LAYING_EGGS) - E = locate() in get_turf(src) - if(!E) - var/obj/structure/spider/eggcluster/C = new /obj/structure/spider/eggcluster(src.loc) - C.faction = faction.Copy() - C.master_commander = master_commander - if(ckey) - C.player_spiders = 1 - fed-- - busy = 0 - stop_automated_movement = 0 - -#undef SPINNING_WEB -#undef LAYING_EGGS -#undef MOVING_TO_TARGET -#undef SPINNING_COCOON + +#define SPINNING_WEB 1 +#define LAYING_EGGS 2 +#define MOVING_TO_TARGET 3 +#define SPINNING_COCOON 4 + +//basic spider mob, these generally guard nests +/mob/living/simple_animal/hostile/poison/giant_spider + name = "giant spider" + desc = "Furry and black, it makes you shudder to look at it. This one has deep red eyes." + icon_state = "guard" + var/butcher_state = 8 // Icon state for dead spider icons + icon_living = "guard" + icon_dead = "guard_dead" + speak_emote = list("chitters") + emote_hear = list("chitters") + speak_chance = 5 + turns_per_move = 5 + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + butcher_results = list(/obj/item/reagent_containers/food/snacks/monstermeat/spidermeat= 2, /obj/item/reagent_containers/food/snacks/monstermeat/spiderleg= 8) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + maxHealth = 200 + health = 200 + obj_damage = 60 + melee_damage_lower = 15 + melee_damage_upper = 20 + heat_damage_per_tick = 20 //amount of damage applied if animal's body temperature is higher than maxbodytemp + cold_damage_per_tick = 20 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + faction = list("spiders") + pass_flags = PASSTABLE + move_to_delay = 6 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + gold_core_spawnable = HOSTILE_SPAWN + var/venom_per_bite = 0 // While the /poison/ type path remains as-is for consistency reasons, we're really talking about venom, not poison. + var/busy = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/AttackingTarget() + // This is placed here, NOT on /poison, because the other subtypes of /poison/ already override AttackingTarget() completely, and as such it would do nothing but confuse people there. + . = ..() + if(. && venom_per_bite > 0 && iscarbon(target) && (!client || a_intent == INTENT_HARM)) + var/mob/living/carbon/C = target + var/inject_target = pick("chest", "head") + if(C.can_inject(null, FALSE, inject_target, FALSE)) + C.reagents.add_reagent("spidertoxin", venom_per_bite) + +//nursemaids - these create webs and eggs +/mob/living/simple_animal/hostile/poison/giant_spider/nurse + desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes." + icon_state = "nurse" + icon_living = "nurse" + icon_dead = "nurse_dead" + butcher_results = list(/obj/item/reagent_containers/food/snacks/monstermeat/spidermeat= 2, /obj/item/reagent_containers/food/snacks/monstermeat/spiderleg= 8, /obj/item/reagent_containers/food/snacks/monstermeat/spidereggs= 4) + + maxHealth = 40 + health = 40 + melee_damage_lower = 5 + melee_damage_upper = 10 + venom_per_bite = 30 + var/atom/cocoon_target + var/fed = 0 + +//hunters have the most poison and move the fastest, so they can find prey +/mob/living/simple_animal/hostile/poison/giant_spider/hunter + desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes." + icon_state = "hunter" + icon_living = "hunter" + icon_dead = "hunter_dead" + maxHealth = 120 + health = 120 + melee_damage_lower = 10 + melee_damage_upper = 20 + venom_per_bite = 10 + move_to_delay = 5 + +/mob/living/simple_animal/hostile/poison/giant_spider/handle_automated_movement() //Hacky and ugly. + . = ..() + if(AIStatus == AI_IDLE) + //1% chance to skitter madly away + if(!busy && prob(1)) + stop_automated_movement = 1 + Goto(pick(urange(20, src, 1)), move_to_delay) + spawn(50) + stop_automated_movement = 0 + walk(src,0) + return 1 + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C) + spawn(100) + if(busy == MOVING_TO_TARGET) + if(cocoon_target == C && get_dist(src,cocoon_target) > 1) + cocoon_target = null + busy = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/handle_automated_movement() //Hacky and ugly. + if(..()) + var/list/can_see = view(src, 10) + if(!busy && prob(30)) //30% chance to stop wandering and do something + //first, check for potential food nearby to cocoon + for(var/mob/living/C in can_see) + if(C.stat && !istype(C, /mob/living/simple_animal/hostile/poison/giant_spider) && !C.anchored) + cocoon_target = C + busy = MOVING_TO_TARGET + Goto(C, move_to_delay) + //give up if we can't reach them after 10 seconds + GiveUp(C) + return + //second, spin a sticky spiderweb on this tile + var/obj/structure/spider/stickyweb/W = locate() in get_turf(src) + if(!W) + Web() + else + //third, lay an egg cluster there + if(fed) + LayEggs() + else + //fourthly, cocoon any nearby items so those pesky pinkskins can't use them + for(var/obj/O in can_see) + if(O.anchored) + continue + + if(isitem(O) || isstructure(O) || ismachinery(O)) + cocoon_target = O + busy = MOVING_TO_TARGET + stop_automated_movement = 1 + Goto(O, move_to_delay) + //give up if we can't reach them after 10 seconds + GiveUp(O) + + else if(busy == MOVING_TO_TARGET && cocoon_target) + if(get_dist(src, cocoon_target) <= 1) + Wrap() + + else + busy = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/verb/Web() + set name = "Lay Web" + set category = "Spider" + set desc = "Spread a sticky web to slow down prey." + + var/T = src.loc + + if(busy != SPINNING_WEB) + busy = SPINNING_WEB + src.visible_message("\the [src] begins to secrete a sticky substance.") + stop_automated_movement = 1 + spawn(40) + if(busy == SPINNING_WEB && src.loc == T) + new /obj/structure/spider/stickyweb(T) + busy = 0 + stop_automated_movement = 0 + + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/Wrap() + set name = "Wrap" + set category = "Spider" + set desc = "Wrap up prey to feast upon and objects for safe keeping." + + if(!cocoon_target) + var/list/choices = list() + for(var/mob/living/L in view(1,src)) + if(L == src) + continue + if(Adjacent(L)) + choices += L + for(var/obj/O in src.loc) + if(Adjacent(O)) + choices += O + cocoon_target = input(src,"What do you wish to cocoon?") in null|choices + + if(cocoon_target && busy != SPINNING_COCOON) + busy = SPINNING_COCOON + src.visible_message("\the [src] begins to secrete a sticky substance around \the [cocoon_target].") + stop_automated_movement = 1 + walk(src,0) + spawn(50) + if(busy == SPINNING_COCOON) + if(cocoon_target && istype(cocoon_target.loc, /turf) && get_dist(src,cocoon_target) <= 1) + var/obj/structure/spider/cocoon/C = new(cocoon_target.loc) + var/large_cocoon = 0 + C.pixel_x = cocoon_target.pixel_x + C.pixel_y = cocoon_target.pixel_y + for(var/obj/item/I in C.loc) + I.loc = C + for(var/obj/structure/S in C.loc) + if(!S.anchored) + S.loc = C + large_cocoon = 1 + for(var/obj/machinery/M in C.loc) + if(!M.anchored) + M.loc = C + large_cocoon = 1 + for(var/mob/living/L in C.loc) + if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider)) + continue + large_cocoon = 1 + L.loc = C + C.pixel_x = L.pixel_x + C.pixel_y = L.pixel_y + fed++ + visible_message("\the [src] sticks a proboscis into \the [L] and sucks a viscous substance out.") + + break + if(large_cocoon) + C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") + cocoon_target = null + busy = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/LayEggs() + set name = "Lay Eggs" + set category = "Spider" + set desc = "Lay a clutch of eggs, but you must wrap a creature for feeding first." + + var/obj/structure/spider/eggcluster/E = locate() in get_turf(src) + if(E) + to_chat(src, "There is already a cluster of eggs here!") + else if(!fed) + to_chat(src, "You are too hungry to do this!") + else if(busy != LAYING_EGGS) + busy = LAYING_EGGS + src.visible_message("\the [src] begins to lay a cluster of eggs.") + stop_automated_movement = 1 + spawn(50) + if(busy == LAYING_EGGS) + E = locate() in get_turf(src) + if(!E) + var/obj/structure/spider/eggcluster/C = new /obj/structure/spider/eggcluster(src.loc) + C.faction = faction.Copy() + C.master_commander = master_commander + if(ckey) + C.player_spiders = 1 + fed-- + busy = 0 + stop_automated_movement = 0 + +#undef SPINNING_WEB +#undef LAYING_EGGS +#undef MOVING_TO_TARGET +#undef SPINNING_COCOON diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm index ece990c7743..8d2ba52709f 100644 --- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm +++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm @@ -25,23 +25,23 @@ var/list/human_overlays = list() /mob/living/simple_animal/hostile/headcrab/Life(seconds, times_fired) - if(!is_zombie && isturf(src.loc) && stat != DEAD) - for(var/mob/living/carbon/human/H in oview(src, 1)) //Only for corpse right next to/on same tile - if(H.stat == DEAD || (!H.check_death_method() && H.health <= HEALTH_THRESHOLD_DEAD)) - Zombify(H) - break - var/cycles = 4 - if(cycles >= 4) - for(var/mob/living/simple_animal/K in oview(src, 1)) //Only for corpse right next to/on same tile - if(K.stat == DEAD || (!K.check_death_method() && K.health <= HEALTH_THRESHOLD_DEAD)) - visible_message("[src] consumes [target] whole!") - if(health < maxHealth) - health += 10 - qdel(K) - break - cycles = 0 - cycles++ - ..() + if(..()) + if(!is_zombie && isturf(src.loc)) + for(var/mob/living/carbon/human/H in oview(src, 1)) //Only for corpse right next to/on same tile + if(H.stat == DEAD || (!H.check_death_method() && H.health <= HEALTH_THRESHOLD_DEAD)) + Zombify(H) + break + var/cycles = 4 + if(cycles >= 4) + for(var/mob/living/simple_animal/K in oview(src, 1)) //Only for corpse right next to/on same tile + if(K.stat == DEAD || (!K.check_death_method() && K.health <= HEALTH_THRESHOLD_DEAD)) + visible_message("[src] consumes [target] whole!") + if(health < maxHealth) + health += 10 + qdel(K) + break + cycles = 0 + cycles++ /mob/living/simple_animal/hostile/headcrab/OpenFire(atom/A) if(check_friendly_fire) diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm index c94648fe641..d2a97949c8e 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm @@ -1,108 +1,108 @@ -/obj/item/projectile/hivebotbullet - damage = 10 - damage_type = BRUTE - -/mob/living/simple_animal/hostile/hivebot - name = "Hivebot" - desc = "A small robot" - icon = 'icons/mob/hivebot.dmi' - icon_state = "basic" - icon_living = "basic" - icon_dead = "basic" - health = 15 - maxHealth = 15 - melee_damage_lower = 2 - melee_damage_upper = 3 - attacktext = "claws" - attack_sound = 'sound/weapons/bladeslice.ogg' - projectilesound = 'sound/weapons/gunshots/gunshot.ogg' - projectiletype = /obj/item/projectile/hivebotbullet - faction = list("hivebot") - check_friendly_fire = 1 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - speak_emote = list("states") - gold_core_spawnable = HOSTILE_SPAWN - loot = list(/obj/effect/decal/cleanable/blood/gibs/robot) - deathmessage = "blows apart!" - del_on_death = 1 - -/mob/living/simple_animal/hostile/hivebot/range - name = "Hivebot" - desc = "A smallish robot, this one is armed!" - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - -/mob/living/simple_animal/hostile/hivebot/rapid - ranged = 1 - rapid = 3 - retreat_distance = 5 - minimum_distance = 5 - -/mob/living/simple_animal/hostile/hivebot/strong - name = "Strong Hivebot" - desc = "A robot, this one is armed and looks tough!" - health = 80 - maxHealth = 80 - ranged = 1 - -/mob/living/simple_animal/hostile/hivebot/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - do_sparks(3, 1, src) - -/mob/living/simple_animal/hostile/hivebot/tele//this still needs work - name = "Beacon" - desc = "Some odd beacon thing" - icon = 'icons/mob/hivebot.dmi' - icon_state = "def_radar-off" - icon_living = "def_radar-off" - health = 200 - maxHealth = 200 - status_flags = 0 - anchored = 1 - stop_automated_movement = 1 - var/bot_type = "norm" - var/bot_amt = 10 - var/spawn_delay = 600 - var/turn_on = 0 - var/auto_spawn = 1 - proc - warpbots() - - -/mob/living/simple_animal/hostile/hivebot/tele/New() - ..() - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(5, 0, src.loc) - smoke.start() - visible_message("The [src] warps in!") - playsound(src.loc, 'sound/effects/empulse.ogg', 25, 1) - -/mob/living/simple_animal/hostile/hivebot/tele/warpbots() - icon_state = "def_radar" - visible_message("The [src] turns on!") - while(bot_amt > 0) - bot_amt-- - switch(bot_type) - if("norm") - var/mob/living/simple_animal/hostile/hivebot/H = new /mob/living/simple_animal/hostile/hivebot(get_turf(src)) - H.faction = faction - if("range") - var/mob/living/simple_animal/hostile/hivebot/range/R = new /mob/living/simple_animal/hostile/hivebot/range(get_turf(src)) - R.faction = faction - if("rapid") - var/mob/living/simple_animal/hostile/hivebot/rapid/F = new /mob/living/simple_animal/hostile/hivebot/rapid(get_turf(src)) - F.faction = faction - spawn(100) - qdel(src) - return - -/mob/living/simple_animal/hostile/hivebot/tele/handle_automated_action() - if(!..()) - return - if(prob(2))//Might be a bit low, will mess with it likely - warpbots() +/obj/item/projectile/hivebotbullet + damage = 10 + damage_type = BRUTE + +/mob/living/simple_animal/hostile/hivebot + name = "Hivebot" + desc = "A small robot" + icon = 'icons/mob/hivebot.dmi' + icon_state = "basic" + icon_living = "basic" + icon_dead = "basic" + health = 15 + maxHealth = 15 + melee_damage_lower = 2 + melee_damage_upper = 3 + attacktext = "claws" + attack_sound = 'sound/weapons/bladeslice.ogg' + projectilesound = 'sound/weapons/gunshots/gunshot.ogg' + projectiletype = /obj/item/projectile/hivebotbullet + faction = list("hivebot") + check_friendly_fire = 1 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speak_emote = list("states") + gold_core_spawnable = HOSTILE_SPAWN + loot = list(/obj/effect/decal/cleanable/blood/gibs/robot) + deathmessage = "blows apart!" + del_on_death = 1 + +/mob/living/simple_animal/hostile/hivebot/range + name = "Hivebot" + desc = "A smallish robot, this one is armed!" + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + +/mob/living/simple_animal/hostile/hivebot/rapid + ranged = 1 + rapid = 3 + retreat_distance = 5 + minimum_distance = 5 + +/mob/living/simple_animal/hostile/hivebot/strong + name = "Strong Hivebot" + desc = "A robot, this one is armed and looks tough!" + health = 80 + maxHealth = 80 + ranged = 1 + +/mob/living/simple_animal/hostile/hivebot/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + do_sparks(3, 1, src) + +/mob/living/simple_animal/hostile/hivebot/tele//this still needs work + name = "Beacon" + desc = "Some odd beacon thing" + icon = 'icons/mob/hivebot.dmi' + icon_state = "def_radar-off" + icon_living = "def_radar-off" + health = 200 + maxHealth = 200 + status_flags = 0 + anchored = 1 + stop_automated_movement = 1 + var/bot_type = "norm" + var/bot_amt = 10 + var/spawn_delay = 600 + var/turn_on = 0 + var/auto_spawn = 1 + proc + warpbots() + + +/mob/living/simple_animal/hostile/hivebot/tele/New() + ..() + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(5, 0, src.loc) + smoke.start() + visible_message("The [src] warps in!") + playsound(src.loc, 'sound/effects/empulse.ogg', 25, 1) + +/mob/living/simple_animal/hostile/hivebot/tele/warpbots() + icon_state = "def_radar" + visible_message("The [src] turns on!") + while(bot_amt > 0) + bot_amt-- + switch(bot_type) + if("norm") + var/mob/living/simple_animal/hostile/hivebot/H = new /mob/living/simple_animal/hostile/hivebot(get_turf(src)) + H.faction = faction + if("range") + var/mob/living/simple_animal/hostile/hivebot/range/R = new /mob/living/simple_animal/hostile/hivebot/range(get_turf(src)) + R.faction = faction + if("rapid") + var/mob/living/simple_animal/hostile/hivebot/rapid/F = new /mob/living/simple_animal/hostile/hivebot/rapid(get_turf(src)) + F.faction = faction + spawn(100) + qdel(src) + return + +/mob/living/simple_animal/hostile/hivebot/tele/handle_automated_action() + if(!..()) + return + if(prob(2))//Might be a bit low, will mess with it likely + warpbots() diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index c5749e939c4..d539721817a 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -1,568 +1,568 @@ -/mob/living/simple_animal/hostile - faction = list("hostile") - stop_automated_movement_when_pulled = 0 - obj_damage = 40 - environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls - var/atom/target - var/ranged = FALSE - var/rapid = 0 //How many shots per volley. - var/rapid_fire_delay = 2 //Time between rapid fire shots - - var/dodging = FALSE - var/approaching_target = FALSE //We should dodge now - var/in_melee = FALSE //We should sidestep now - var/dodge_prob = 30 - var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee - - var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile - var/projectilesound - var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING - var/move_to_delay = 3 //delay for the automated movement. - var/list/friends = list() - var/list/emote_taunt = list() - var/taunt_chance = 0 - - var/rapid_melee = 1 //Number of melee attacks between each npc pool tick. Spread evenly. - var/melee_queue_distance = 4 //If target is close enough start preparing to hit them if we have rapid_melee enabled - - var/ranged_message = "fires" //Fluff text for ranged mobs - var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time - var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is - var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash - var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting - var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat. - var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance - - -//These vars are related to how mobs locate and target - var/robust_searching = 0 //By default, mobs have a simple searching method, set this to 1 for the more scrutinous searching (stat_attack, stat_exclusive, etc), should be disabled on most mobs - var/vision_range = 9 //How big of an area to search for targets in, a vision of 9 attempts to find targets as soon as they walk into screen view - var/aggro_vision_range = 9 //If a mob is aggro, we search in this radius. Defaults to 9 to keep in line with original simple mob aggro radius - var/search_objects = 0 //If we want to consider objects when searching around, set this to 1. If you want to search for objects while also ignoring mobs until hurt, set it to 2. To completely ignore mobs, even when attacked, set it to 3 - var/search_objects_timer_id //Timer for regaining our old search_objects value after being attacked - var/search_objects_regain_time = 30 //the delay between being attacked and gaining our old search_objects value back - var/list/wanted_objects = list() //A typecache of objects types that will be checked against to attack, should we have search_objects enabled - var/stat_attack = CONSCIOUS //Mobs with stat_attack to UNCONSCIOUS will attempt to attack things that are unconscious, Mobs with stat_attack set to DEAD will attempt to attack the dead. - var/stat_exclusive = FALSE //Mobs with this set to TRUE will exclusively attack things defined by stat_attack, stat_attack DEAD means they will only attack corpses - var/attack_same = 0 //Set us to 1 to allow us to attack our own faction - var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such - var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects. - - var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach - var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever - -/mob/living/simple_animal/hostile/Initialize(mapload) - . = ..() - - if(!targets_from) - targets_from = src - wanted_objects = typecacheof(wanted_objects) - -/mob/living/simple_animal/hostile/Destroy() - targets_from = null - target = null - return ..() - -/mob/living/simple_animal/hostile/Life(seconds, times_fired) - . = ..() - if(!.) - walk(src, 0) - return FALSE - -/mob/living/simple_animal/hostile/handle_automated_action() - if(AIStatus == AI_OFF) - return 0 - var/list/possible_targets = ListTargets() //we look around for potential targets and make it a list for later use. - - if(environment_smash) - EscapeConfinement() - - if(AICanContinue(possible_targets)) - if(!QDELETED(target) && !targets_from.Adjacent(target)) - DestroyPathToTarget() - if(!MoveToTarget(possible_targets)) //if we lose our target - if(AIShouldSleep(possible_targets)) // we try to acquire a new one - toggle_ai(AI_IDLE) // otherwise we go idle - return 1 - -/mob/living/simple_animal/hostile/handle_automated_movement() - . = ..() - if(dodging && target && in_melee && isturf(loc) && isturf(target.loc)) - var/datum/cb = CALLBACK(src,.proc/sidestep) - if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later - var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle - for(var/i in 1 to sidestep_per_cycle) - addtimer(cb, (i - 1) * sidestep_delay) - else //Otherwise randomize it to make the players guessing. - addtimer(cb,rand(1, SSnpcpool.wait)) - -/mob/living/simple_animal/hostile/proc/sidestep() - if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD) - return - var/target_dir = get_dir(src, target) - - var/static/list/cardinal_sidestep_directions = list(-90, -45, 0, 45, 90) - var/static/list/diagonal_sidestep_directions = list(-45, 0, 45) - var/chosen_dir = 0 - if (target_dir & (target_dir - 1)) - chosen_dir = pick(diagonal_sidestep_directions) - else - chosen_dir = pick(cardinal_sidestep_directions) - if(chosen_dir) - chosen_dir = turn(target_dir, chosen_dir) - Move(get_step(src, chosen_dir)) - face_atom(target) //Looks better if they keep looking at you when dodging - -/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) - if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) - FindTarget(list(user), 1) - return ..() - -/mob/living/simple_animal/hostile/bullet_act(obj/item/projectile/P) - if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client) - if(P.firer && get_dist(src, P.firer) <= aggro_vision_range) - FindTarget(list(P.firer), 1) - Goto(P.starting, move_to_delay, 3) - return ..() - -//////////////HOSTILE MOB TARGETTING AND AGGRESSION//////////// - -/mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see - if(!search_objects) - . = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide - - var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) - - for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines)) - if(can_see(targets_from, HM, vision_range)) - . += HM - else - . = oview(vision_range, targets_from) - -/mob/living/simple_animal/hostile/proc/FindTarget(var/list/possible_targets, var/HasTargetsList = 0)//Step 2, filter down possible targets to things we actually care about - . = list() - if(!HasTargetsList) - possible_targets = ListTargets() - for(var/pos_targ in possible_targets) - var/atom/A = pos_targ - if(Found(A))//Just in case people want to override targetting - . = list(A) - break - if(CanAttack(A))//Can we attack it? - . += A - continue - var/Target = PickTarget(.) - GiveTarget(Target) - return Target //We now have a target - -/mob/living/simple_animal/hostile/proc/PossibleThreats() - . = list() - for(var/pos_targ in ListTargets()) - var/atom/A = pos_targ - if(Found(A)) - . = list(A) - break - if(CanAttack(A)) - . += A - continue - -/mob/living/simple_animal/hostile/proc/Found(var/atom/A)//This is here as a potential override to pick a specific target if available - return - -/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets - if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets - for(var/pos_targ in Targets) - var/atom/A = pos_targ - var/target_dist = get_dist(targets_from, target) - var/possible_target_distance = get_dist(targets_from, A) - if(target_dist < possible_target_distance) - Targets -= A - if(!Targets.len)//We didnt find nothin! - return - var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random - return chosen_target - -// Please do not add one-off mob AIs here, but override this function for your mob -/mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target? - if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids - return FALSE - - if(ismob(the_target)) //Target is in godmode, ignore it. - var/mob/M = the_target - if(M.status_flags & GODMODE) - return FALSE - - if(see_invisible < the_target.invisibility) //Target's invisible to us, forget it - return FALSE - if(search_objects < 2) - if(isliving(the_target)) - var/mob/living/L = the_target - var/faction_check = faction_check_mob(L) - if(robust_searching) - if(faction_check && !attack_same) - return FALSE - if(L.stat > stat_attack) - return FALSE - if(L in friends) - return FALSE - else - if((faction_check && !attack_same) || L.stat) - return FALSE - return TRUE - - if(ismecha(the_target)) - var/obj/mecha/M = the_target - if(M.occupant)//Just so we don't attack empty mechs - if(CanAttack(M.occupant)) - return TRUE - - if(isspacepod(the_target)) - var/obj/spacepod/S = the_target - if(S.pilot)//Just so we don't attack empty pods - if(CanAttack(S.pilot)) - return TRUE - - if(istype(the_target, /obj/machinery/porta_turret)) - var/obj/machinery/porta_turret/P = the_target - if(P.faction in faction) - return FALSE - if(!P.raised) //Don't attack invincible turrets - return FALSE - if(P.stat & BROKEN) //Or turrets that are already broken - return FALSE - return TRUE - - if(isobj(the_target)) - if(attack_all_objects || is_type_in_typecache(the_target, wanted_objects)) - return TRUE - - return FALSE - -/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target - target = new_target - LosePatience() - if(target != null) - GainPatience() - Aggro() - return 1 - -//What we do after closing in -/mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE) - if(rapid_melee > 1) - var/datum/callback/cb = CALLBACK(src, .proc/CheckAndAttack) - var/delay = SSnpcpool.wait / rapid_melee - for(var/i in 1 to rapid_melee) - addtimer(cb, (i - 1)*delay) - else - AttackingTarget() - if(patience) - GainPatience() - -/mob/living/simple_animal/hostile/proc/CheckAndAttack() - if(target && targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && !incapacitated()) - AttackingTarget() - -/mob/living/simple_animal/hostile/proc/MoveToTarget(list/possible_targets)//Step 5, handle movement between us and our target - stop_automated_movement = 1 - if(!target || !CanAttack(target)) - LoseTarget() - return 0 - if(target in possible_targets) - var/turf/T = get_turf(src) - if(target.z != T.z) - LoseTarget() - return 0 - var/target_distance = get_dist(targets_from,target) - if(ranged) //We ranged? Shoot at em - if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown - OpenFire(target) - if(!Process_Spacemove()) //Drifting - walk(src,0) - return 1 - if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target - if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run - walk_away(src,target,retreat_distance,move_to_delay) - else - Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them - else - Goto(target,move_to_delay,minimum_distance) - if(target) - if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack - MeleeAction() - else - if(rapid_melee > 1 && target_distance <= melee_queue_distance) - MeleeAction(FALSE) - in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode - return 1 - return 0 - if(environment_smash) - if(target.loc != null && get_dist(targets_from, target.loc) <= vision_range) //We can't see our target, but he's in our vision range still - if(ranged_ignores_vision && ranged_cooldown <= world.time) //we can't see our target... but we can fire at them! - OpenFire(target) - if((environment_smash & ENVIRONMENT_SMASH_WALLS) || (environment_smash & ENVIRONMENT_SMASH_RWALLS)) //If we're capable of smashing through walls, forget about vision completely after finding our target - Goto(target,move_to_delay,minimum_distance) - FindHidden() - return 1 - else - if(FindHidden()) - return 1 - LoseTarget() - return 0 - -/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance) - if(target == src.target) - approaching_target = TRUE - else - approaching_target = FALSE - walk_to(src, target, minimum_distance, delay) - -/mob/living/simple_animal/hostile/adjustHealth(damage, updating_health = TRUE) - . = ..() - if(!ckey && !stat && search_objects < 3 && damage > 0)//Not unconscious, and we don't ignore mobs - if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight - target = null - LoseSearchObjects() - if(AIStatus != AI_ON && AIStatus != AI_OFF) - toggle_ai(AI_ON) - FindTarget() - else if(target != null && prob(40))//No more pulling a mob forever and having a second player attack it, it can switch targets now if it finds a more suitable one - FindTarget() - -/mob/living/simple_animal/hostile/proc/AttackingTarget() - SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target) - in_melee = TRUE - return target.attack_animal(src) - -/mob/living/simple_animal/hostile/proc/Aggro() - vision_range = aggro_vision_range - if(target && emote_taunt.len && prob(taunt_chance)) - emote("me", 1, "[pick(emote_taunt)] at [target].") - taunt_chance = max(taunt_chance-7,2) - -/mob/living/simple_animal/hostile/proc/LoseAggro() - stop_automated_movement = 0 - vision_range = initial(vision_range) - taunt_chance = initial(taunt_chance) - -/mob/living/simple_animal/hostile/proc/LoseTarget() - target = null - approaching_target = FALSE - in_melee = FALSE - walk(src, 0) - LoseAggro() - -//////////////END HOSTILE MOB TARGETTING AND AGGRESSION//////////// - -/mob/living/simple_animal/hostile/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - LoseTarget() - -/mob/living/simple_animal/hostile/proc/summon_backup(distance) - do_alert_animation(src) - playsound(loc, 'sound/machines/chime.ogg', 50, 1, -1) - for(var/mob/living/simple_animal/hostile/M in oview(distance, targets_from)) - if(faction_check_mob(M, TRUE)) - if(M.AIStatus == AI_OFF) - return - else - M.Goto(src,M.move_to_delay,M.minimum_distance) - -/mob/living/simple_animal/hostile/proc/CheckFriendlyFire(atom/A) - if(check_friendly_fire) - for(var/turf/T in getline(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory - for(var/mob/living/L in T) - if(L == src || L == A) - continue - if(faction_check_mob(L) && !attack_same) - return TRUE - -/mob/living/simple_animal/hostile/proc/OpenFire(atom/A) - if(CheckFriendlyFire(A)) - return - visible_message("[src] [ranged_message] at [A]!") - - - if(rapid > 1) - var/datum/callback/cb = CALLBACK(src, .proc/Shoot, A) - for(var/i in 1 to rapid) - addtimer(cb, (i - 1)*rapid_fire_delay) - else - Shoot(A) - ranged_cooldown = world.time + ranged_cooldown_time - -/mob/living/simple_animal/hostile/proc/Shoot(atom/targeted_atom) - if( QDELETED(targeted_atom) || targeted_atom == targets_from.loc || targeted_atom == targets_from ) - return - var/turf/startloc = get_turf(targets_from) - if(casingtype) - var/obj/item/ammo_casing/casing = new casingtype(startloc) - playsound(src, projectilesound, 100, 1) - casing.fire(targeted_atom, src, zone_override = ran_zone()) - else if(projectiletype) - var/obj/item/projectile/P = new projectiletype(startloc) - playsound(src, projectilesound, 100, 1) - P.current = startloc - P.starting = startloc - P.firer = src - 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)) - P.original = targeted_atom - P.preparePixelProjectile(targeted_atom, get_turf(targeted_atom), src) - P.fire() - return P - -/mob/living/simple_animal/hostile/proc/CanSmashTurfs(turf/T) - return iswallturf(T) || ismineralturf(T) - -/mob/living/simple_animal/hostile/Move(atom/newloc, dir , step_x , step_y) - if(dodging && approaching_target && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc)) - return dodge(newloc, dir) - else - return ..() - -/mob/living/simple_animal/hostile/proc/dodge(moving_to,move_direction) - //Assuming we move towards the target we want to swerve toward them to get closer - var/cdir = turn(move_direction, 45) - var/ccdir = turn(move_direction, -45) - dodging = FALSE - . = Move(get_step(loc,pick(cdir,ccdir))) - if(!.)//Can't dodge there so we just carry on - . = Move(moving_to,move_direction) - dodging = TRUE - -/mob/living/simple_animal/hostile/proc/DestroyObjectsInDirection(direction) - var/turf/T = get_step(targets_from, direction) - if(QDELETED(T)) - return - if(T.Adjacent(targets_from)) - if(CanSmashTurfs(T)) - T.attack_animal(src) - return - for(var/obj/O in T.contents) - if(!O.Adjacent(targets_from)) - continue - if((ismachinery(O) || isstructure(O)) && O.density && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !O.IsObscured()) - O.attack_animal(src) - return - -/mob/living/simple_animal/hostile/proc/DestroyPathToTarget() - if(environment_smash) - EscapeConfinement() - var/dir_to_target = get_dir(targets_from, target) - var/dir_list = list() - if(dir_to_target in diagonals) //it's diagonal, so we need two directions to hit - for(var/direction in cardinal) - if(direction & dir_to_target) - dir_list += direction - else - dir_list += dir_to_target - for(var/direction in dir_list) //now we hit all of the directions we got in this fashion, since it's the only directions we should actually need - DestroyObjectsInDirection(direction) - -/mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with megafauna destroying everything around them - if(environment_smash) - EscapeConfinement() - for(var/dir in cardinal) - DestroyObjectsInDirection(dir) - -/mob/living/simple_animal/hostile/proc/EscapeConfinement() - if(buckled) - buckled.attack_animal(src) - if(!isturf(targets_from.loc) && targets_from.loc != null)//Did someone put us in something? - var/atom/A = targets_from.loc - A.attack_animal(src)//Bang on it till we get out - -/mob/living/simple_animal/hostile/proc/FindHidden() - if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper) || istype(target.loc, /obj/machinery/bodyscanner) || istype(target.loc, /obj/machinery/recharge_station)) - var/atom/A = target.loc - Goto(A,move_to_delay,minimum_distance) - if(A.Adjacent(targets_from)) - A.attack_animal(src) - return 1 - -/mob/living/simple_animal/hostile/RangedAttack(atom/A, params) //Player firing - if(ranged && ranged_cooldown <= world.time) - target = A - OpenFire(A) - ..() - - - -////// AI Status /////// -/mob/living/simple_animal/hostile/proc/AICanContinue(var/list/possible_targets) - switch(AIStatus) - if(AI_ON) - . = 1 - if(AI_IDLE) - if(FindTarget(possible_targets, 1)) - . = 1 - toggle_ai(AI_ON) //Wake up for more than one Life() cycle. - else - . = 0 - -/mob/living/simple_animal/hostile/proc/AIShouldSleep(var/list/possible_targets) - return !FindTarget(possible_targets, 1) - - -//These two procs handle losing our target if we've failed to attack them for -//more than lose_patience_timeout deciseconds, which probably means we're stuck -/mob/living/simple_animal/hostile/proc/GainPatience() - if(lose_patience_timeout) - LosePatience() - lose_patience_timer_id = addtimer(CALLBACK(src, .proc/LoseTarget), lose_patience_timeout, TIMER_STOPPABLE) - - -/mob/living/simple_animal/hostile/proc/LosePatience() - deltimer(lose_patience_timer_id) - - -//These two procs handle losing and regaining search_objects when attacked by a mob -/mob/living/simple_animal/hostile/proc/LoseSearchObjects() - search_objects = 0 - deltimer(search_objects_timer_id) - search_objects_timer_id = addtimer(CALLBACK(src, .proc/RegainSearchObjects), search_objects_regain_time, TIMER_STOPPABLE) - - -/mob/living/simple_animal/hostile/proc/RegainSearchObjects(value) - if(!value) - value = initial(search_objects) - search_objects = value - -/mob/living/simple_animal/hostile/consider_wakeup() - ..() - var/list/tlist - var/turf/T = get_turf(src) - - if(!T) - return - - if(!length(SSmobs.clients_by_zlevel[T.z])) // It's fine to use .len here but doesn't compile on 511 - toggle_ai(AI_Z_OFF) - return - - var/cheap_search = isturf(T) && !is_station_level(T.z) - if(cheap_search) - tlist = ListTargetsLazy(T.z) - else - tlist = ListTargets() - - if(AIStatus == AI_IDLE && FindTarget(tlist, 1)) - if(cheap_search) //Try again with full effort - FindTarget() - toggle_ai(AI_ON) - -/mob/living/simple_animal/hostile/proc/ListTargetsLazy(var/_Z)//Step 1, find out what we can see - var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) - . = list() - for(var/I in SSmobs.clients_by_zlevel[_Z]) - var/mob/M = I - if(get_dist(M, src) < vision_range) - if(isturf(M.loc)) - . += M - else if(M.loc.type in hostile_machines) - . += M.loc \ No newline at end of file +/mob/living/simple_animal/hostile + faction = list("hostile") + stop_automated_movement_when_pulled = 0 + obj_damage = 40 + environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls + var/atom/target + var/ranged = FALSE + var/rapid = 0 //How many shots per volley. + var/rapid_fire_delay = 2 //Time between rapid fire shots + + var/dodging = FALSE + var/approaching_target = FALSE //We should dodge now + var/in_melee = FALSE //We should sidestep now + var/dodge_prob = 30 + var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee + + var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile + var/projectilesound + var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING + var/move_to_delay = 3 //delay for the automated movement. + var/list/friends = list() + var/list/emote_taunt = list() + var/taunt_chance = 0 + + var/rapid_melee = 1 //Number of melee attacks between each npc pool tick. Spread evenly. + var/melee_queue_distance = 4 //If target is close enough start preparing to hit them if we have rapid_melee enabled + + var/ranged_message = "fires" //Fluff text for ranged mobs + var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time + var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is + var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash + var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting + var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat. + var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance + + +//These vars are related to how mobs locate and target + var/robust_searching = 0 //By default, mobs have a simple searching method, set this to 1 for the more scrutinous searching (stat_attack, stat_exclusive, etc), should be disabled on most mobs + var/vision_range = 9 //How big of an area to search for targets in, a vision of 9 attempts to find targets as soon as they walk into screen view + var/aggro_vision_range = 9 //If a mob is aggro, we search in this radius. Defaults to 9 to keep in line with original simple mob aggro radius + var/search_objects = 0 //If we want to consider objects when searching around, set this to 1. If you want to search for objects while also ignoring mobs until hurt, set it to 2. To completely ignore mobs, even when attacked, set it to 3 + var/search_objects_timer_id //Timer for regaining our old search_objects value after being attacked + var/search_objects_regain_time = 30 //the delay between being attacked and gaining our old search_objects value back + var/list/wanted_objects = list() //A typecache of objects types that will be checked against to attack, should we have search_objects enabled + var/stat_attack = CONSCIOUS //Mobs with stat_attack to UNCONSCIOUS will attempt to attack things that are unconscious, Mobs with stat_attack set to DEAD will attempt to attack the dead. + var/stat_exclusive = FALSE //Mobs with this set to TRUE will exclusively attack things defined by stat_attack, stat_attack DEAD means they will only attack corpses + var/attack_same = 0 //Set us to 1 to allow us to attack our own faction + var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such + var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects. + + var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach + var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever + +/mob/living/simple_animal/hostile/Initialize(mapload) + . = ..() + + if(!targets_from) + targets_from = src + wanted_objects = typecacheof(wanted_objects) + +/mob/living/simple_animal/hostile/Destroy() + targets_from = null + target = null + return ..() + +/mob/living/simple_animal/hostile/Life(seconds, times_fired) + . = ..() + if(!.) + walk(src, 0) + return FALSE + +/mob/living/simple_animal/hostile/handle_automated_action() + if(AIStatus == AI_OFF) + return 0 + var/list/possible_targets = ListTargets() //we look around for potential targets and make it a list for later use. + + if(environment_smash) + EscapeConfinement() + + if(AICanContinue(possible_targets)) + if(!QDELETED(target) && !targets_from.Adjacent(target)) + DestroyPathToTarget() + if(!MoveToTarget(possible_targets)) //if we lose our target + if(AIShouldSleep(possible_targets)) // we try to acquire a new one + toggle_ai(AI_IDLE) // otherwise we go idle + return 1 + +/mob/living/simple_animal/hostile/handle_automated_movement() + . = ..() + if(dodging && target && in_melee && isturf(loc) && isturf(target.loc)) + var/datum/cb = CALLBACK(src,.proc/sidestep) + if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later + var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle + for(var/i in 1 to sidestep_per_cycle) + addtimer(cb, (i - 1) * sidestep_delay) + else //Otherwise randomize it to make the players guessing. + addtimer(cb,rand(1, SSnpcpool.wait)) + +/mob/living/simple_animal/hostile/proc/sidestep() + if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD) + return + var/target_dir = get_dir(src, target) + + var/static/list/cardinal_sidestep_directions = list(-90, -45, 0, 45, 90) + var/static/list/diagonal_sidestep_directions = list(-45, 0, 45) + var/chosen_dir = 0 + if (target_dir & (target_dir - 1)) + chosen_dir = pick(diagonal_sidestep_directions) + else + chosen_dir = pick(cardinal_sidestep_directions) + if(chosen_dir) + chosen_dir = turn(target_dir, chosen_dir) + Move(get_step(src, chosen_dir)) + face_atom(target) //Looks better if they keep looking at you when dodging + +/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) + if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) + FindTarget(list(user), 1) + return ..() + +/mob/living/simple_animal/hostile/bullet_act(obj/item/projectile/P) + if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client) + if(P.firer && get_dist(src, P.firer) <= aggro_vision_range) + FindTarget(list(P.firer), 1) + Goto(P.starting, move_to_delay, 3) + return ..() + +//////////////HOSTILE MOB TARGETTING AND AGGRESSION//////////// + +/mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see + if(!search_objects) + . = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide + + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) + + for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines)) + if(can_see(targets_from, HM, vision_range)) + . += HM + else + . = oview(vision_range, targets_from) + +/mob/living/simple_animal/hostile/proc/FindTarget(var/list/possible_targets, var/HasTargetsList = 0)//Step 2, filter down possible targets to things we actually care about + . = list() + if(!HasTargetsList) + possible_targets = ListTargets() + for(var/pos_targ in possible_targets) + var/atom/A = pos_targ + if(Found(A))//Just in case people want to override targetting + . = list(A) + break + if(CanAttack(A))//Can we attack it? + . += A + continue + var/Target = PickTarget(.) + GiveTarget(Target) + return Target //We now have a target + +/mob/living/simple_animal/hostile/proc/PossibleThreats() + . = list() + for(var/pos_targ in ListTargets()) + var/atom/A = pos_targ + if(Found(A)) + . = list(A) + break + if(CanAttack(A)) + . += A + continue + +/mob/living/simple_animal/hostile/proc/Found(var/atom/A)//This is here as a potential override to pick a specific target if available + return + +/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets + if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets + for(var/pos_targ in Targets) + var/atom/A = pos_targ + var/target_dist = get_dist(targets_from, target) + var/possible_target_distance = get_dist(targets_from, A) + if(target_dist < possible_target_distance) + Targets -= A + if(!Targets.len)//We didnt find nothin! + return + var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random + return chosen_target + +// Please do not add one-off mob AIs here, but override this function for your mob +/mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target? + if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids + return FALSE + + if(ismob(the_target)) //Target is in godmode, ignore it. + var/mob/M = the_target + if(M.status_flags & GODMODE) + return FALSE + + if(see_invisible < the_target.invisibility) //Target's invisible to us, forget it + return FALSE + if(search_objects < 2) + if(isliving(the_target)) + var/mob/living/L = the_target + var/faction_check = faction_check_mob(L) + if(robust_searching) + if(faction_check && !attack_same) + return FALSE + if(L.stat > stat_attack) + return FALSE + if(L in friends) + return FALSE + else + if((faction_check && !attack_same) || L.stat) + return FALSE + return TRUE + + if(ismecha(the_target)) + var/obj/mecha/M = the_target + if(M.occupant)//Just so we don't attack empty mechs + if(CanAttack(M.occupant)) + return TRUE + + if(isspacepod(the_target)) + var/obj/spacepod/S = the_target + if(S.pilot)//Just so we don't attack empty pods + if(CanAttack(S.pilot)) + return TRUE + + if(istype(the_target, /obj/machinery/porta_turret)) + var/obj/machinery/porta_turret/P = the_target + if(P.faction in faction) + return FALSE + if(!P.raised) //Don't attack invincible turrets + return FALSE + if(P.stat & BROKEN) //Or turrets that are already broken + return FALSE + return TRUE + + if(isobj(the_target)) + if(attack_all_objects || is_type_in_typecache(the_target, wanted_objects)) + return TRUE + + return FALSE + +/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target + target = new_target + LosePatience() + if(target != null) + GainPatience() + Aggro() + return 1 + +//What we do after closing in +/mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE) + if(rapid_melee > 1) + var/datum/callback/cb = CALLBACK(src, .proc/CheckAndAttack) + var/delay = SSnpcpool.wait / rapid_melee + for(var/i in 1 to rapid_melee) + addtimer(cb, (i - 1)*delay) + else + AttackingTarget() + if(patience) + GainPatience() + +/mob/living/simple_animal/hostile/proc/CheckAndAttack() + if(target && targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && !incapacitated()) + AttackingTarget() + +/mob/living/simple_animal/hostile/proc/MoveToTarget(list/possible_targets)//Step 5, handle movement between us and our target + stop_automated_movement = 1 + if(!target || !CanAttack(target)) + LoseTarget() + return 0 + if(target in possible_targets) + var/turf/T = get_turf(src) + if(target.z != T.z) + LoseTarget() + return 0 + var/target_distance = get_dist(targets_from,target) + if(ranged) //We ranged? Shoot at em + if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown + OpenFire(target) + if(!Process_Spacemove()) //Drifting + walk(src,0) + return 1 + if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target + if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run + walk_away(src,target,retreat_distance,move_to_delay) + else + Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them + else + Goto(target,move_to_delay,minimum_distance) + if(target) + if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack + MeleeAction() + else + if(rapid_melee > 1 && target_distance <= melee_queue_distance) + MeleeAction(FALSE) + in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode + return 1 + return 0 + if(environment_smash) + if(target.loc != null && get_dist(targets_from, target.loc) <= vision_range) //We can't see our target, but he's in our vision range still + if(ranged_ignores_vision && ranged_cooldown <= world.time) //we can't see our target... but we can fire at them! + OpenFire(target) + if((environment_smash & ENVIRONMENT_SMASH_WALLS) || (environment_smash & ENVIRONMENT_SMASH_RWALLS)) //If we're capable of smashing through walls, forget about vision completely after finding our target + Goto(target,move_to_delay,minimum_distance) + FindHidden() + return 1 + else + if(FindHidden()) + return 1 + LoseTarget() + return 0 + +/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance) + if(target == src.target) + approaching_target = TRUE + else + approaching_target = FALSE + walk_to(src, target, minimum_distance, delay) + +/mob/living/simple_animal/hostile/adjustHealth(damage, updating_health = TRUE) + . = ..() + if(!ckey && !stat && search_objects < 3 && damage > 0)//Not unconscious, and we don't ignore mobs + if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight + target = null + LoseSearchObjects() + if(AIStatus != AI_ON && AIStatus != AI_OFF) + toggle_ai(AI_ON) + FindTarget() + else if(target != null && prob(40))//No more pulling a mob forever and having a second player attack it, it can switch targets now if it finds a more suitable one + FindTarget() + +/mob/living/simple_animal/hostile/proc/AttackingTarget() + SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target) + in_melee = TRUE + return target.attack_animal(src) + +/mob/living/simple_animal/hostile/proc/Aggro() + vision_range = aggro_vision_range + if(target && emote_taunt.len && prob(taunt_chance)) + emote("me", 1, "[pick(emote_taunt)] at [target].") + taunt_chance = max(taunt_chance-7,2) + +/mob/living/simple_animal/hostile/proc/LoseAggro() + stop_automated_movement = 0 + vision_range = initial(vision_range) + taunt_chance = initial(taunt_chance) + +/mob/living/simple_animal/hostile/proc/LoseTarget() + target = null + approaching_target = FALSE + in_melee = FALSE + walk(src, 0) + LoseAggro() + +//////////////END HOSTILE MOB TARGETTING AND AGGRESSION//////////// + +/mob/living/simple_animal/hostile/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + LoseTarget() + +/mob/living/simple_animal/hostile/proc/summon_backup(distance) + do_alert_animation(src) + playsound(loc, 'sound/machines/chime.ogg', 50, 1, -1) + for(var/mob/living/simple_animal/hostile/M in oview(distance, targets_from)) + if(faction_check_mob(M, TRUE)) + if(M.AIStatus == AI_OFF) + return + else + M.Goto(src,M.move_to_delay,M.minimum_distance) + +/mob/living/simple_animal/hostile/proc/CheckFriendlyFire(atom/A) + if(check_friendly_fire) + for(var/turf/T in getline(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory + for(var/mob/living/L in T) + if(L == src || L == A) + continue + if(faction_check_mob(L) && !attack_same) + return TRUE + +/mob/living/simple_animal/hostile/proc/OpenFire(atom/A) + if(CheckFriendlyFire(A)) + return + visible_message("[src] [ranged_message] at [A]!") + + + if(rapid > 1) + var/datum/callback/cb = CALLBACK(src, .proc/Shoot, A) + for(var/i in 1 to rapid) + addtimer(cb, (i - 1)*rapid_fire_delay) + else + Shoot(A) + ranged_cooldown = world.time + ranged_cooldown_time + +/mob/living/simple_animal/hostile/proc/Shoot(atom/targeted_atom) + if( QDELETED(targeted_atom) || targeted_atom == targets_from.loc || targeted_atom == targets_from ) + return + var/turf/startloc = get_turf(targets_from) + if(casingtype) + var/obj/item/ammo_casing/casing = new casingtype(startloc) + playsound(src, projectilesound, 100, 1) + casing.fire(targeted_atom, src, zone_override = ran_zone()) + else if(projectiletype) + var/obj/item/projectile/P = new projectiletype(startloc) + playsound(src, projectilesound, 100, 1) + P.current = startloc + P.starting = startloc + P.firer = src + 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)) + P.original = targeted_atom + P.preparePixelProjectile(targeted_atom, get_turf(targeted_atom), src) + P.fire() + return P + +/mob/living/simple_animal/hostile/proc/CanSmashTurfs(turf/T) + return iswallturf(T) || ismineralturf(T) + +/mob/living/simple_animal/hostile/Move(atom/newloc, dir , step_x , step_y) + if(dodging && approaching_target && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc)) + return dodge(newloc, dir) + else + return ..() + +/mob/living/simple_animal/hostile/proc/dodge(moving_to,move_direction) + //Assuming we move towards the target we want to swerve toward them to get closer + var/cdir = turn(move_direction, 45) + var/ccdir = turn(move_direction, -45) + dodging = FALSE + . = Move(get_step(loc,pick(cdir,ccdir))) + if(!.)//Can't dodge there so we just carry on + . = Move(moving_to,move_direction) + dodging = TRUE + +/mob/living/simple_animal/hostile/proc/DestroyObjectsInDirection(direction) + var/turf/T = get_step(targets_from, direction) + if(QDELETED(T)) + return + if(T.Adjacent(targets_from)) + if(CanSmashTurfs(T)) + T.attack_animal(src) + return + for(var/obj/O in T.contents) + if(!O.Adjacent(targets_from)) + continue + if((ismachinery(O) || isstructure(O)) && O.density && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !O.IsObscured()) + O.attack_animal(src) + return + +/mob/living/simple_animal/hostile/proc/DestroyPathToTarget() + if(environment_smash) + EscapeConfinement() + var/dir_to_target = get_dir(targets_from, target) + var/dir_list = list() + if(dir_to_target in GLOB.diagonals) //it's diagonal, so we need two directions to hit + for(var/direction in GLOB.cardinal) + if(direction & dir_to_target) + dir_list += direction + else + dir_list += dir_to_target + for(var/direction in dir_list) //now we hit all of the directions we got in this fashion, since it's the only directions we should actually need + DestroyObjectsInDirection(direction) + +/mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with megafauna destroying everything around them + if(environment_smash) + EscapeConfinement() + for(var/dir in GLOB.cardinal) + DestroyObjectsInDirection(dir) + +/mob/living/simple_animal/hostile/proc/EscapeConfinement() + if(buckled) + buckled.attack_animal(src) + if(!isturf(targets_from.loc) && targets_from.loc != null)//Did someone put us in something? + var/atom/A = targets_from.loc + A.attack_animal(src)//Bang on it till we get out + +/mob/living/simple_animal/hostile/proc/FindHidden() + if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper) || istype(target.loc, /obj/machinery/bodyscanner) || istype(target.loc, /obj/machinery/recharge_station)) + var/atom/A = target.loc + Goto(A,move_to_delay,minimum_distance) + if(A.Adjacent(targets_from)) + A.attack_animal(src) + return 1 + +/mob/living/simple_animal/hostile/RangedAttack(atom/A, params) //Player firing + if(ranged && ranged_cooldown <= world.time) + target = A + OpenFire(A) + ..() + + + +////// AI Status /////// +/mob/living/simple_animal/hostile/proc/AICanContinue(var/list/possible_targets) + switch(AIStatus) + if(AI_ON) + . = 1 + if(AI_IDLE) + if(FindTarget(possible_targets, 1)) + . = 1 + toggle_ai(AI_ON) //Wake up for more than one Life() cycle. + else + . = 0 + +/mob/living/simple_animal/hostile/proc/AIShouldSleep(var/list/possible_targets) + return !FindTarget(possible_targets, 1) + + +//These two procs handle losing our target if we've failed to attack them for +//more than lose_patience_timeout deciseconds, which probably means we're stuck +/mob/living/simple_animal/hostile/proc/GainPatience() + if(lose_patience_timeout) + LosePatience() + lose_patience_timer_id = addtimer(CALLBACK(src, .proc/LoseTarget), lose_patience_timeout, TIMER_STOPPABLE) + + +/mob/living/simple_animal/hostile/proc/LosePatience() + deltimer(lose_patience_timer_id) + + +//These two procs handle losing and regaining search_objects when attacked by a mob +/mob/living/simple_animal/hostile/proc/LoseSearchObjects() + search_objects = 0 + deltimer(search_objects_timer_id) + search_objects_timer_id = addtimer(CALLBACK(src, .proc/RegainSearchObjects), search_objects_regain_time, TIMER_STOPPABLE) + + +/mob/living/simple_animal/hostile/proc/RegainSearchObjects(value) + if(!value) + value = initial(search_objects) + search_objects = value + +/mob/living/simple_animal/hostile/consider_wakeup() + ..() + var/list/tlist + var/turf/T = get_turf(src) + + if(!T) + return + + if(!length(SSmobs.clients_by_zlevel[T.z])) // It's fine to use .len here but doesn't compile on 511 + toggle_ai(AI_Z_OFF) + return + + var/cheap_search = isturf(T) && !is_station_level(T.z) + if(cheap_search) + tlist = ListTargetsLazy(T.z) + else + tlist = ListTargets() + + if(AIStatus == AI_IDLE && FindTarget(tlist, 1)) + if(cheap_search) //Try again with full effort + FindTarget() + toggle_ai(AI_ON) + +/mob/living/simple_animal/hostile/proc/ListTargetsLazy(var/_Z)//Step 1, find out what we can see + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) + . = list() + for(var/I in SSmobs.clients_by_zlevel[_Z]) + var/mob/M = I + if(get_dist(M, src) < vision_range) + if(isturf(M.loc)) + . += M + else if(M.loc.type in hostile_machines) + . += M.loc diff --git a/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm b/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm index 79d6c42cb11..934e1312606 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm @@ -42,4 +42,4 @@ if(prob(15) && iscarbon(target)) var/mob/living/carbon/C = target C.Weaken(3) - C.visible_message("\the [src] knocks down \the [C]!") \ No newline at end of file + C.visible_message("\the [src] knocks down \the [C]!") diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 3061c765359..70585b818f5 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -27,7 +27,7 @@ Difficulty: Medium maxHealth = 900 icon_state = "miner" icon_living = "miner" - icon = 'icons/mob/alienqueen.dmi' + icon = 'icons/mob/lavaland/blood_drunk.dmi' light_color = "#E4C7C5" flying = FALSE speak_emote = list("roars") @@ -37,7 +37,7 @@ Difficulty: Medium projectilesound = 'sound/weapons/kenetic_accel.ogg' ranged = TRUE ranged_cooldown_time = 16 - pixel_x = -16 + pixel_x = -7 crusher_loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator, /obj/item/crusher_trophy/miner_eye) loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator) wander = FALSE @@ -290,4 +290,4 @@ Difficulty: Medium if(. && prob(12)) INVOKE_ASYNC(src, .proc/dash) -#undef MINER_DASH_RANGE \ No newline at end of file +#undef MINER_DASH_RANGE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 66799ffc6c6..6cde07d0b8d 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -396,9 +396,9 @@ Difficulty: Hard if(. > 0 && prob(25)) var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc) if(prob(40)) - step(B, pick(cardinal)) + step(B, pick(GLOB.cardinal)) else - B.setDir(pick(cardinal)) + B.setDir(pick(GLOB.cardinal)) /obj/effect/decal/cleanable/blood/gibs/bubblegum name = "thick blood" @@ -548,4 +548,4 @@ Difficulty: Hard return /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/try_bloodattack() - return \ No newline at end of file + return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 8eed750804f..7f1b3942d94 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -103,7 +103,7 @@ Difficulty: Very Hard visible_message("\"You can't dodge.\"") ranged_cooldown = world.time + 30 telegraph() - dir_shots(alldirs) + dir_shots(GLOB.alldirs) move_to_delay = 3 return else @@ -127,13 +127,13 @@ Difficulty: Very Hard /mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() ranged_cooldown = world.time + 40 - dir_shots(diagonals) + dir_shots(GLOB.diagonals) SLEEP_CHECK_DEATH(10) - dir_shots(cardinal) + dir_shots(GLOB.cardinal) SLEEP_CHECK_DEATH(10) - dir_shots(diagonals) + dir_shots(GLOB.diagonals) SLEEP_CHECK_DEATH(10) - dir_shots(cardinal) + dir_shots(GLOB.cardinal) /mob/living/simple_animal/hostile/megafauna/colossus/proc/select_spiral_attack() telegraph() @@ -150,7 +150,7 @@ Difficulty: Very Hard INVOKE_ASYNC(src, .proc/spiral_shoot, TRUE) /mob/living/simple_animal/hostile/megafauna/colossus/proc/spiral_shoot(negative = pick(TRUE, FALSE), counter_start = 8) - var/turf/start_turf = get_step(src, pick(alldirs)) + var/turf/start_turf = get_step(src, pick(GLOB.alldirs)) var/counter = counter_start for(var/i in 1 to 80) if(negative) @@ -198,7 +198,7 @@ Difficulty: Very Hard /mob/living/simple_animal/hostile/megafauna/colossus/proc/dir_shots(list/dirs) if(!islist(dirs)) - dirs = alldirs.Copy() + dirs = GLOB.alldirs.Copy() playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 2) for(var/d in dirs) var/turf/E = get_step(src, d) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 8b6d6fed947..e97b7671b67 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -366,7 +366,7 @@ Difficulty: Medium if(L && !QDELETED(L)) // Some mobs are deleted on death var/throw_dir = get_dir(src, L) if(L.loc == loc) - throw_dir = pick(alldirs) + throw_dir = pick(GLOB.alldirs) var/throwtarget = get_edge_target_turf(src, throw_dir) L.throw_at(throwtarget, 3) visible_message("[L] is thrown clear of [src]!") @@ -669,4 +669,4 @@ obj/effect/temp_visual/fireball ..(targets, user, 3) /mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/AltClickOn(atom/movable/A) - return \ No newline at end of file + return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 088d8631e77..716f5436223 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -179,18 +179,18 @@ Difficulty: Hard if((prob(anger_modifier) || target.Adjacent(src)) && target != src) var/obj/effect/temp_visual/hierophant/chaser/OC = new(loc, src, target, chaser_speed * 1.5, FALSE) OC.moving = 4 - OC.moving_dir = pick(cardinal - C.moving_dir) + OC.moving_dir = pick(GLOB.cardinal - C.moving_dir) else if(prob(10 + (anger_modifier * 0.5)) && get_dist(src, target) > 2) blink(target) else if(prob(70 - anger_modifier)) //a cross blast of some type if(prob(anger_modifier * (2 / target_slowness)) && health < maxHealth * 0.5) //we're super angry do it at all dirs - INVOKE_ASYNC(src, .proc/blasts, target, alldirs) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.alldirs) else if(prob(60)) - INVOKE_ASYNC(src, .proc/blasts, target, cardinal) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.cardinal) else - INVOKE_ASYNC(src, .proc/blasts, target, diagonals) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.diagonals) else //just release a burst of power INVOKE_ASYNC(src, .proc/burst, get_turf(src)) @@ -226,9 +226,9 @@ Difficulty: Hard while(!QDELETED(target) && cross_counter) cross_counter-- if(prob(60)) - INVOKE_ASYNC(src, .proc/blasts, target, cardinal) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.cardinal) else - INVOKE_ASYNC(src, .proc/blasts, target, diagonals) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.diagonals) SLEEP_CHECK_DEATH(6 + target_slowness) animate(src, color = oldcolor, time = 8) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) @@ -244,7 +244,7 @@ Difficulty: Hard animate(src, color = "#660099", time = 6) SLEEP_CHECK_DEATH(6) var/list/targets = ListTargets() - var/list/cardinal_copy = cardinal.Copy() + var/list/cardinal_copy = GLOB.cardinal.Copy() while(targets.len && cardinal_copy.len) var/mob/living/pickedtarget = pick(targets) if(targets.len >= cardinal_copy.len) @@ -263,13 +263,13 @@ Difficulty: Hard SLEEP_CHECK_DEATH(8) blinking = FALSE -/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blasts(mob/victim, var/list/directions = cardinal) //fires cross blasts with a delay +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blasts(mob/victim, var/list/directions = GLOB.cardinal) //fires cross blasts with a delay var/turf/T = get_turf(victim) if(!T) return - if(directions == cardinal) + if(directions == GLOB.cardinal) new /obj/effect/temp_visual/hierophant/telegraph/cardinal(T, src) - else if(directions == diagonals) + else if(directions == GLOB.diagonals) new /obj/effect/temp_visual/hierophant/telegraph/diagonal(T, src) else new /obj/effect/temp_visual/hierophant/telegraph(T, src) @@ -295,7 +295,7 @@ Difficulty: Hard if((istype(get_area(T), /area/ruin/unpowered/hierophant) || istype(get_area(src), /area/ruin/unpowered/hierophant)) && victim != src) return arena_cooldown = world.time + initial(arena_cooldown) - for(var/d in cardinal) + for(var/d in GLOB.cardinal) INVOKE_ASYNC(src, .proc/arena_squares, T, d) for(var/t in RANGE_TURFS(11, T)) if(t && get_dist(t, T) == 11) @@ -565,7 +565,7 @@ Difficulty: Hard /obj/effect/temp_visual/hierophant/chaser/proc/get_target_dir() . = get_cardinal_dir(src, targetturf) if((. != previous_moving_dir && . == more_previouser_moving_dir) || . == 0) //we're alternating, recalculate - var/list/cardinal_copy = cardinal.Copy() + var/list/cardinal_copy = GLOB.cardinal.Copy() cardinal_copy -= more_previouser_moving_dir . = pick(cardinal_copy) @@ -737,4 +737,4 @@ Difficulty: Hard icon_state = null gpstag = "Zealous Signal" desc = "Heed its words." - invisibility = 100 \ No newline at end of file + invisibility = 100 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 81c96135b0e..4ce66ccd1d5 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -91,7 +91,7 @@ else devour(L) -/mob/living/simple_animal/hostile/megafauna/onShuttleMove() +/mob/living/simple_animal/hostile/megafauna/onShuttleMove(turf/oldT, turf/T1, rotation, mob/caller) var/turf/oldloc = loc . = ..() if(!.) @@ -100,7 +100,7 @@ message_admins("Megafauna [src] \ ([ADMIN_FLW(src,"FLW")]) \ moved via shuttle from ([oldloc.x], [oldloc.y], [oldloc.z]) to \ - ([newloc.x], [newloc.y], [newloc.z])") + ([newloc.x], [newloc.y], [newloc.z])[caller ? " called by [ADMIN_LOOKUP(caller)]" : ""]") /mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L) if(!L) @@ -158,4 +158,4 @@ /datum/action/innate/megafauna_attack/Activate() M.chosen_attack = chosen_attack_num - to_chat(M, chosen_message) \ No newline at end of file + to_chat(M, chosen_message) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm index f31b6a8619b..2515da9dff9 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm @@ -66,7 +66,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa /mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/Initialize(mapload) . = ..() swarmer_caps = GLOB.AISwarmerCapsByType //for admin-edits - for(var/ddir in cardinal) + for(var/ddir in GLOB.cardinal) new /obj/structure/swarmer/blockade (get_step(src, ddir)) var/mob/living/simple_animal/hostile/swarmer/ai/resource/R = new(loc) step(R, ddir) //Step the swarmers, instead of spawning them there, incase the turf is solid @@ -285,4 +285,4 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa name = "swarmer catwalk" desc = "A catwalk-like mesh, produced by swarmers to allow them to navigate hostile terrain." icon = 'icons/obj/smooth_structures/swarmer_catwalk.dmi' - icon_state = "swarmer_catwalk" \ No newline at end of file + icon_state = "swarmer_catwalk" diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index 761f44d11bd..848a285abf4 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -1,283 +1,283 @@ -/mob/living/simple_animal/hostile/mimic - name = "crate" - desc = "A rectangular steel crate." - icon = 'icons/obj/crates.dmi' - icon_state = "crate" - icon_living = "crate" - - response_help = "touches the" - response_disarm = "pushes the" - response_harm = "hits the" - speed = 0 - maxHealth = 250 - health = 250 - - harm_intent_damage = 5 - melee_damage_lower = 8 - melee_damage_upper = 12 - attacktext = "attacks" - attack_sound = 'sound/weapons/bite.ogg' - emote_taunt = list("growls") - speak_emote = list("creaks") - taunt_chance = 30 - - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("mimic") - move_to_delay = 9 - - var/is_electronic = 0 - gold_core_spawnable = HOSTILE_SPAWN - del_on_death = 1 - -/mob/living/simple_animal/hostile/mimic/emp_act(severity) - if(is_electronic) - switch(severity) - if(1) - death() - if(2) - adjustBruteLoss(50) - ..(severity) - -// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. -/mob/living/simple_animal/hostile/mimic/crate - attacktext = "bites" - stop_automated_movement = 1 - wander = 0 - var/attempt_open = 0 - -// Pickup loot -/mob/living/simple_animal/hostile/mimic/crate/Initialize() - ..() - for(var/obj/item/I in loc) - I.loc = src - -/mob/living/simple_animal/hostile/mimic/crate/DestroyPathToTarget() - ..() - if(prob(90)) - icon_state = "[initial(icon_state)]open" - else - icon_state = initial(icon_state) - -/mob/living/simple_animal/hostile/mimic/crate/ListTargets() - if(attempt_open) - return ..() - return ..(1) - -/mob/living/simple_animal/hostile/mimic/crate/FindTarget() - . = ..() - if(.) - trigger() - -/mob/living/simple_animal/hostile/mimic/crate/AttackingTarget() - . = ..() - if(.) - icon_state = initial(icon_state) - if(prob(15) && iscarbon(target)) - var/mob/living/carbon/C = target - C.Weaken(2) - C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") - -/mob/living/simple_animal/hostile/mimic/crate/proc/trigger() - if(!attempt_open) - visible_message("[src] starts to move!") - attempt_open = 1 - -/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(amount, updating_health = TRUE) - trigger() - . = ..() - -/mob/living/simple_animal/hostile/mimic/crate/LoseTarget() - ..() - icon_state = initial(icon_state) - -/mob/living/simple_animal/hostile/mimic/crate/death(gibbed) - if(can_die()) - var/obj/structure/closet/crate/C = new(get_turf(src)) - // Put loot in crate - for(var/obj/O in src) - O.forceMove(C) - // due to `del_on_death` - return ..() - -var/global/list/protected_objects = list(/obj/structure/table, /obj/structure/cable, /obj/structure/window) - -/mob/living/simple_animal/hostile/mimic/copy - health = 100 - maxHealth = 100 - var/mob/living/creator = null // the creator - var/destroy_objects = 0 - var/knockdown_people = 0 - var/image/googly_eyes = null - gold_core_spawnable = NO_SPAWN - -/mob/living/simple_animal/hostile/mimic/copy/New(loc, obj/copy, mob/living/creator, destroy_original = 0) - ..(loc) - CopyObject(copy, creator, destroy_original) - -/mob/living/simple_animal/hostile/mimic/copy/Life() - ..() - if(!target && !ckey) //Objects eventually revert to normal if no one is around to terrorize - adjustBruteLoss(1) - for(var/mob/living/M in contents) //a fix for animated statues from the flesh to stone spell - death() - -/mob/living/simple_animal/hostile/mimic/copy/death(gibbed) - if(can_die()) - for(var/atom/movable/M in src) - M.loc = get_turf(src) - // due to `del_on_death` - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/ListTargets() - . = ..() - return . - creator - -/mob/living/simple_animal/hostile/mimic/copy/proc/ChangeOwner(var/mob/owner) - if(owner != creator) - LoseTarget() - creator = owner - faction |= "\ref[owner]" - -/mob/living/simple_animal/hostile/mimic/copy/proc/CheckObject(var/obj/O) - if((istype(O, /obj/item) || istype(O, /obj/structure)) && !is_type_in_list(O, protected_objects)) - return 1 - return 0 - -/mob/living/simple_animal/hostile/mimic/copy/proc/CopyObject(var/obj/O, var/mob/living/user, var/destroy_original = 0) - if(destroy_original || CheckObject(O)) - O.loc = src - name = O.name - desc = O.desc - icon = O.icon - icon_state = O.icon_state - icon_living = icon_state - overlays = O.overlays - googly_eyes = image('icons/mob/mob.dmi',"googly_eyes") - overlays += googly_eyes - if(istype(O, /obj/structure) || istype(O, /obj/machinery)) - health = (anchored * 50) + 50 - destroy_objects = 1 - if(O.density && O.anchored) - knockdown_people = 1 - melee_damage_lower *= 2 - melee_damage_upper *= 2 - if(istype(O, /obj/machinery)) - is_electronic = 1 - else if(istype(O, /obj/item)) - var/obj/item/I = O - health = 15 * I.w_class - melee_damage_lower = 2 + I.force - melee_damage_upper = 2 + I.force - move_to_delay = 2 * I.w_class + 1 - maxHealth = health - if(user) - creator = user - faction += "\ref[creator]" // very unique - if(destroy_original) - qdel(O) - return 1 - -/mob/living/simple_animal/hostile/mimic/copy/DestroySurroundings() - if(destroy_objects) - ..() - -/mob/living/simple_animal/hostile/mimic/copy/AttackingTarget() - . = ..() - if(knockdown_people && . && prob(15) && iscarbon(target)) - var/mob/living/carbon/C = target - C.Weaken(2) - C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") - -/mob/living/simple_animal/hostile/mimic/copy/Aggro() - ..() - googly_eyes.dir = get_dir(src,target) - -/mob/living/simple_animal/hostile/mimic/copy/machine - speak = list("HUMANS ARE IMPERFECT!", "YOU SHALL BE ASSIMILATED!", "YOU ARE HARMING YOURSELF", "You have been deemed hazardous. Will you comply?", \ - "My logic is undeniable.", "One of us.", "FLESH IS WEAK", "THIS ISN'T WAR, THIS IS EXTERMINATION!") - speak_chance = 15 - -/mob/living/simple_animal/hostile/mimic/copy/machine/CanAttack(var/atom/the_target) - if(the_target == creator) // Don't attack our creator AI. - return 0 - if(isrobot(the_target)) - var/mob/living/silicon/robot/R = the_target - if(R.connected_ai == creator) // Only attack robots that aren't synced to our creator AI. - return 0 - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/ranged - var/obj/item/gun/TrueGun = null - var/obj/item/gun/magic/Zapstick - var/obj/item/gun/projectile/Pewgun - var/obj/item/gun/energy/Zapgun - -/mob/living/simple_animal/hostile/mimic/copy/ranged/CopyObject(obj/O, mob/living/creator, destroy_original = 0) - if(..()) - emote_see = list("aims menacingly") - obj_damage = 0 - environment_smash = 0 //needed? seems weird for them to do so - ranged = 1 - retreat_distance = 1 //just enough to shoot - minimum_distance = 6 - var/obj/item/gun/G = O - melee_damage_upper = G.force - melee_damage_lower = G.force - max(0, (G.force / 2)) - move_to_delay = 2 * G.w_class + 1 - projectilesound = G.fire_sound - TrueGun = G - if(istype(G, /obj/item/gun/magic)) - Zapstick = G - var/obj/item/ammo_casing/magic/M = Zapstick.ammo_type - projectiletype = initial(M.projectile_type) - if(istype(G, /obj/item/gun/projectile)) - Pewgun = G - var/obj/item/ammo_box/magazine/M = Pewgun.mag_type - casingtype = initial(M.ammo_type) - if(istype(G, /obj/item/gun/energy)) - Zapgun = G - var/selectfiresetting = Zapgun.select - var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[selectfiresetting] - projectiletype = initial(E.projectile_type) - -/mob/living/simple_animal/hostile/mimic/copy/ranged/OpenFire(the_target) - if(Zapgun) - if(Zapgun.cell) - var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.select] - if(Zapgun.cell.charge >= shot.e_cost) - Zapgun.cell.use(shot.e_cost) - Zapgun.update_icon() - ..() - else if(Zapstick) - if(Zapstick.charges) - Zapstick.charges-- - Zapstick.update_icon() - ..() - else if(Pewgun) - if(Pewgun.chambered) - if(Pewgun.chambered.BB) - qdel(Pewgun.chambered.BB) - Pewgun.chambered.BB = null //because qdel takes too long, ensures icon update - Pewgun.chambered.update_icon() - ..() - else - visible_message("The [src] clears a jam!") - Pewgun.chambered.loc = loc //rip revolver immersions, blame shotgun snowflake procs - Pewgun.chambered = null - if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) - Pewgun.chambered = Pewgun.magazine.get_round(0) - Pewgun.chambered.loc = Pewgun - Pewgun.update_icon() - else if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) //only true for pumpguns i think - Pewgun.chambered = Pewgun.magazine.get_round(0) - Pewgun.chambered.loc = Pewgun - visible_message("The [src] cocks itself!") - else - ranged = 0 //BANZAIIII - retreat_distance = 0 - minimum_distance = 1 - return - icon_state = TrueGun.icon_state - icon_living = TrueGun.icon_state +/mob/living/simple_animal/hostile/mimic + name = "crate" + desc = "A rectangular steel crate." + icon = 'icons/obj/crates.dmi' + icon_state = "crate" + icon_living = "crate" + + response_help = "touches the" + response_disarm = "pushes the" + response_harm = "hits the" + speed = 0 + maxHealth = 250 + health = 250 + + harm_intent_damage = 5 + melee_damage_lower = 8 + melee_damage_upper = 12 + attacktext = "attacks" + attack_sound = 'sound/weapons/bite.ogg' + emote_taunt = list("growls") + speak_emote = list("creaks") + taunt_chance = 30 + + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("mimic") + move_to_delay = 9 + + var/is_electronic = 0 + gold_core_spawnable = HOSTILE_SPAWN + del_on_death = 1 + +/mob/living/simple_animal/hostile/mimic/emp_act(severity) + if(is_electronic) + switch(severity) + if(1) + death() + if(2) + adjustBruteLoss(50) + ..(severity) + +// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. +/mob/living/simple_animal/hostile/mimic/crate + attacktext = "bites" + stop_automated_movement = 1 + wander = 0 + var/attempt_open = 0 + +// Pickup loot +/mob/living/simple_animal/hostile/mimic/crate/Initialize() + ..() + for(var/obj/item/I in loc) + I.loc = src + +/mob/living/simple_animal/hostile/mimic/crate/DestroyPathToTarget() + ..() + if(prob(90)) + icon_state = "[initial(icon_state)]open" + else + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/mimic/crate/ListTargets() + if(attempt_open) + return ..() + return ..(1) + +/mob/living/simple_animal/hostile/mimic/crate/FindTarget() + . = ..() + if(.) + trigger() + +/mob/living/simple_animal/hostile/mimic/crate/AttackingTarget() + . = ..() + if(.) + icon_state = initial(icon_state) + if(prob(15) && iscarbon(target)) + var/mob/living/carbon/C = target + C.Weaken(2) + C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") + +/mob/living/simple_animal/hostile/mimic/crate/proc/trigger() + if(!attempt_open) + visible_message("[src] starts to move!") + attempt_open = 1 + +/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(amount, updating_health = TRUE) + trigger() + . = ..() + +/mob/living/simple_animal/hostile/mimic/crate/LoseTarget() + ..() + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/mimic/crate/death(gibbed) + if(can_die()) + var/obj/structure/closet/crate/C = new(get_turf(src)) + // Put loot in crate + for(var/obj/O in src) + O.forceMove(C) + // due to `del_on_death` + return ..() + +GLOBAL_LIST_INIT(protected_objects, list(/obj/structure/table, /obj/structure/cable, /obj/structure/window)) + +/mob/living/simple_animal/hostile/mimic/copy + health = 100 + maxHealth = 100 + var/mob/living/creator = null // the creator + var/destroy_objects = 0 + var/knockdown_people = 0 + var/image/googly_eyes = null + gold_core_spawnable = NO_SPAWN + +/mob/living/simple_animal/hostile/mimic/copy/New(loc, obj/copy, mob/living/creator, destroy_original = 0) + ..(loc) + CopyObject(copy, creator, destroy_original) + +/mob/living/simple_animal/hostile/mimic/copy/Life() + ..() + if(!target && !ckey) //Objects eventually revert to normal if no one is around to terrorize + adjustBruteLoss(1) + for(var/mob/living/M in contents) //a fix for animated statues from the flesh to stone spell + death() + +/mob/living/simple_animal/hostile/mimic/copy/death(gibbed) + if(can_die()) + for(var/atom/movable/M in src) + M.loc = get_turf(src) + // due to `del_on_death` + return ..() + +/mob/living/simple_animal/hostile/mimic/copy/ListTargets() + . = ..() + return . - creator + +/mob/living/simple_animal/hostile/mimic/copy/proc/ChangeOwner(var/mob/owner) + if(owner != creator) + LoseTarget() + creator = owner + faction |= "\ref[owner]" + +/mob/living/simple_animal/hostile/mimic/copy/proc/CheckObject(var/obj/O) + if((istype(O, /obj/item) || istype(O, /obj/structure)) && !is_type_in_list(O, GLOB.protected_objects)) + return 1 + return 0 + +/mob/living/simple_animal/hostile/mimic/copy/proc/CopyObject(var/obj/O, var/mob/living/user, var/destroy_original = 0) + if(destroy_original || CheckObject(O)) + O.loc = src + name = O.name + desc = O.desc + icon = O.icon + icon_state = O.icon_state + icon_living = icon_state + overlays = O.overlays + googly_eyes = image('icons/mob/mob.dmi',"googly_eyes") + overlays += googly_eyes + if(istype(O, /obj/structure) || istype(O, /obj/machinery)) + health = (anchored * 50) + 50 + destroy_objects = 1 + if(O.density && O.anchored) + knockdown_people = 1 + melee_damage_lower *= 2 + melee_damage_upper *= 2 + if(istype(O, /obj/machinery)) + is_electronic = 1 + else if(istype(O, /obj/item)) + var/obj/item/I = O + health = 15 * I.w_class + melee_damage_lower = 2 + I.force + melee_damage_upper = 2 + I.force + move_to_delay = 2 * I.w_class + 1 + maxHealth = health + if(user) + creator = user + faction += "\ref[creator]" // very unique + if(destroy_original) + qdel(O) + return 1 + +/mob/living/simple_animal/hostile/mimic/copy/DestroySurroundings() + if(destroy_objects) + ..() + +/mob/living/simple_animal/hostile/mimic/copy/AttackingTarget() + . = ..() + if(knockdown_people && . && prob(15) && iscarbon(target)) + var/mob/living/carbon/C = target + C.Weaken(2) + C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") + +/mob/living/simple_animal/hostile/mimic/copy/Aggro() + ..() + googly_eyes.dir = get_dir(src,target) + +/mob/living/simple_animal/hostile/mimic/copy/machine + speak = list("HUMANS ARE IMPERFECT!", "YOU SHALL BE ASSIMILATED!", "YOU ARE HARMING YOURSELF", "You have been deemed hazardous. Will you comply?", \ + "My logic is undeniable.", "One of us.", "FLESH IS WEAK", "THIS ISN'T WAR, THIS IS EXTERMINATION!") + speak_chance = 15 + +/mob/living/simple_animal/hostile/mimic/copy/machine/CanAttack(var/atom/the_target) + if(the_target == creator) // Don't attack our creator AI. + return 0 + if(isrobot(the_target)) + var/mob/living/silicon/robot/R = the_target + if(R.connected_ai == creator) // Only attack robots that aren't synced to our creator AI. + return 0 + return ..() + +/mob/living/simple_animal/hostile/mimic/copy/ranged + var/obj/item/gun/TrueGun = null + var/obj/item/gun/magic/Zapstick + var/obj/item/gun/projectile/Pewgun + var/obj/item/gun/energy/Zapgun + +/mob/living/simple_animal/hostile/mimic/copy/ranged/CopyObject(obj/O, mob/living/creator, destroy_original = 0) + if(..()) + emote_see = list("aims menacingly") + obj_damage = 0 + environment_smash = 0 //needed? seems weird for them to do so + ranged = 1 + retreat_distance = 1 //just enough to shoot + minimum_distance = 6 + var/obj/item/gun/G = O + melee_damage_upper = G.force + melee_damage_lower = G.force - max(0, (G.force / 2)) + move_to_delay = 2 * G.w_class + 1 + projectilesound = G.fire_sound + TrueGun = G + if(istype(G, /obj/item/gun/magic)) + Zapstick = G + var/obj/item/ammo_casing/magic/M = Zapstick.ammo_type + projectiletype = initial(M.projectile_type) + if(istype(G, /obj/item/gun/projectile)) + Pewgun = G + var/obj/item/ammo_box/magazine/M = Pewgun.mag_type + casingtype = initial(M.ammo_type) + if(istype(G, /obj/item/gun/energy)) + Zapgun = G + var/selectfiresetting = Zapgun.select + var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[selectfiresetting] + projectiletype = initial(E.projectile_type) + +/mob/living/simple_animal/hostile/mimic/copy/ranged/OpenFire(the_target) + if(Zapgun) + if(Zapgun.cell) + var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.select] + if(Zapgun.cell.charge >= shot.e_cost) + Zapgun.cell.use(shot.e_cost) + Zapgun.update_icon() + ..() + else if(Zapstick) + if(Zapstick.charges) + Zapstick.charges-- + Zapstick.update_icon() + ..() + else if(Pewgun) + if(Pewgun.chambered) + if(Pewgun.chambered.BB) + qdel(Pewgun.chambered.BB) + Pewgun.chambered.BB = null //because qdel takes too long, ensures icon update + Pewgun.chambered.update_icon() + ..() + else + visible_message("The [src] clears a jam!") + Pewgun.chambered.loc = loc //rip revolver immersions, blame shotgun snowflake procs + Pewgun.chambered = null + if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) + Pewgun.chambered = Pewgun.magazine.get_round(0) + Pewgun.chambered.loc = Pewgun + Pewgun.update_icon() + else if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) //only true for pumpguns i think + Pewgun.chambered = Pewgun.magazine.get_round(0) + Pewgun.chambered.loc = Pewgun + visible_message("The [src] cocks itself!") + else + ranged = 0 //BANZAIIII + retreat_distance = 0 + minimum_distance = 1 + return + icon_state = TrueGun.icon_state + icon_living = TrueGun.icon_state diff --git a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm index ef6e3c540f6..74eaf55b57c 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm @@ -149,4 +149,4 @@ L.apply_status_effect(/datum/status_effect/freon/watcher) /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril - fromtendril = TRUE \ No newline at end of file + fromtendril = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm index 490e056e62f..d052dd9bc23 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm @@ -89,7 +89,7 @@ throw_message = "does nothing to the tough hide of the" pre_attack_icon = "goliath2" crusher_loot = /obj/item/crusher_trophy/goliath_tentacle - butcher_results = list(/obj/item/reagent_containers/food/snacks/goliath = 2, /obj/item/stack/sheet/animalhide/goliath_hide = 1, /obj/item/stack/sheet/bone = 2) + butcher_results = list(/obj/item/reagent_containers/food/snacks/monstermeat/goliath= 2, /obj/item/stack/sheet/animalhide/goliath_hide = 1, /obj/item/stack/sheet/bone = 2) loot = list() stat_attack = UNCONSCIOUS robust_searching = TRUE @@ -113,7 +113,7 @@ pre_attack_icon = "Goliath_preattack" throw_message = "does nothing to the rocky hide of the" loot = list(/obj/item/stack/sheet/animalhide/goliath_hide) //A throwback to the asteroid days - butcher_results = list(/obj/item/reagent_containers/food/snacks/goliath = 2, /obj/item/stack/sheet/bone = 2) + butcher_results = list(/obj/item/reagent_containers/food/snacks/monstermeat/goliath= 2, /obj/item/stack/sheet/bone = 2) crusher_drop_mod = 30 wander = FALSE var/list/cached_tentacle_turfs @@ -164,7 +164,7 @@ /obj/effect/temp_visual/goliath_tentacle/original/Initialize(mapload, new_spawner) . = ..() - var/list/directions = cardinal.Copy() + var/list/directions = GLOB.cardinal.Copy() for(var/i in 1 to 3) var/spawndir = pick_n_take(directions) var/turf/T = get_step(src, spawndir) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm b/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm index 463b6e61e48..fb2c741cb30 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm @@ -50,10 +50,9 @@ return ..() /mob/living/simple_animal/hostile/asteroid/gutlunch/regenerate_icons() - cut_overlays() + ..() if(udder.reagents.total_volume == udder.reagents.maximum_volume) add_overlay("gl_full") - ..() /mob/living/simple_animal/hostile/asteroid/gutlunch/attackby(obj/item/O, mob/user, params) if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) @@ -163,4 +162,4 @@ L.faction = faction.Copy() L.setDir(dir) visible_message("[src] grows up into [L].") - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm index 080623f1b0d..ca9b632e06a 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -141,7 +141,7 @@ volume = min(volume, reagents.total_volume) var/fraction = min(volume/reagents.total_volume, 1) - reagents.reaction(C, INGEST, fraction) + reagents.reaction(C, REAGENT_INGEST, fraction) reagents.trans_to(C, volume) /mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/link_host(mob/living/carbon/C) @@ -327,9 +327,9 @@ /obj/effect/mob_spawn/human/corpse/damaged/legioninfested/dwarf/equip(mob/living/carbon/human/H) . = ..() - H.dna.SetSEState(SMALLSIZEBLOCK, 1, 1) + H.dna.SetSEState(GLOB.smallsizeblock, 1, 1) H.mutations.Add(DWARF) - genemutcheck(H, SMALLSIZEBLOCK, null, MUTCHK_FORCED) + genemutcheck(H, GLOB.smallsizeblock, null, MUTCHK_FORCED) H.update_mutations() /obj/effect/mob_spawn/human/corpse/damaged/legioninfested/Initialize(mapload) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/mining.dm b/code/modules/mob/living/simple_animal/hostile/mining/mining.dm index e1e86536f19..7c7200e8588 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/mining.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/mining.dm @@ -63,4 +63,4 @@ ..(gibbed) /mob/living/simple_animal/hostile/asteroid/proc/spawn_crusher_loot() - butcher_results[crusher_loot] = 1 \ No newline at end of file + butcher_results[crusher_loot] = 1 diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm index 92c01d7d72b..e8ce4027b4e 100644 --- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm +++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm @@ -108,4 +108,4 @@ blank.name = "[M]" blank.desc = "It's [M], but [M.p_their()] flesh has an ashy texture, and [M.p_their()] face is featureless save an eerie smile." visible_message("[M] reemerges from the link!") - qdel(M) \ No newline at end of file + qdel(M) diff --git a/code/modules/mob/living/simple_animal/hostile/pirate.dm b/code/modules/mob/living/simple_animal/hostile/pirate.dm index 4e3f71b30b1..d8d2d55a8ee 100644 --- a/code/modules/mob/living/simple_animal/hostile/pirate.dm +++ b/code/modules/mob/living/simple_animal/hostile/pirate.dm @@ -1,45 +1,45 @@ -/mob/living/simple_animal/hostile/pirate - name = "Pirate" - desc = "Does what he wants cause a pirate is free." - icon = 'icons/mob/simple_human.dmi' - icon_state = "piratemelee" - icon_living = "piratemelee" - icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. - speak_chance = 0 - turns_per_move = 5 - response_help = "pushes the" - response_disarm = "shoves" - response_harm = "hits the" - speed = 0 - maxHealth = 100 - health = 100 - - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 30 - melee_damage_upper = 30 - attacktext = "slashes" - attack_sound = 'sound/weapons/bladeslice.ogg' - - atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - unsuitable_atmos_damage = 15 - speak_emote = list("yarrs") - loot = list(/obj/effect/mob_spawn/human/corpse/pirate, - /obj/item/melee/energy/sword/pirate) - del_on_death = 1 - faction = list("pirate") - sentience_type = SENTIENCE_OTHER - -/mob/living/simple_animal/hostile/pirate/ranged - name = "Pirate Gunner" - icon_state = "pirateranged" - icon_living = "pirateranged" - icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. - projectilesound = 'sound/weapons/laser.ogg' - ranged = 1 - rapid = 2 - retreat_distance = 5 - minimum_distance = 5 - projectiletype = /obj/item/projectile/beam - loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged, - /obj/item/gun/energy/laser) \ No newline at end of file +/mob/living/simple_animal/hostile/pirate + name = "Pirate" + desc = "Does what he wants cause a pirate is free." + icon = 'icons/mob/simple_human.dmi' + icon_state = "piratemelee" + icon_living = "piratemelee" + icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. + speak_chance = 0 + turns_per_move = 5 + response_help = "pushes the" + response_disarm = "shoves" + response_harm = "hits the" + speed = 0 + maxHealth = 100 + health = 100 + + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 30 + melee_damage_upper = 30 + attacktext = "slashes" + attack_sound = 'sound/weapons/bladeslice.ogg' + + atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + unsuitable_atmos_damage = 15 + speak_emote = list("yarrs") + loot = list(/obj/effect/mob_spawn/human/corpse/pirate, + /obj/item/melee/energy/sword/pirate) + del_on_death = 1 + faction = list("pirate") + sentience_type = SENTIENCE_OTHER + +/mob/living/simple_animal/hostile/pirate/ranged + name = "Pirate Gunner" + icon_state = "pirateranged" + icon_living = "pirateranged" + icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. + projectilesound = 'sound/weapons/laser.ogg' + ranged = 1 + rapid = 2 + retreat_distance = 5 + minimum_distance = 5 + projectiletype = /obj/item/projectile/beam + loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged, + /obj/item/gun/energy/laser) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm index 5a278a65b24..f58f7fcfda2 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm @@ -1,53 +1,53 @@ -/mob/living/simple_animal/hostile/retaliate/clown - name = "Clown" - desc = "A denizen of clown planet" - icon = 'icons/mob/simple_human.dmi' - icon_state = "clown" - icon_living = "clown" - icon_dead = "clown_dead" - icon_gib = "clown_gib" - speak_chance = 0 - turns_per_move = 5 - response_help = "pokes the" - response_disarm = "gently pushes aside the" - response_harm = "hits the" - speak = list("HONK", "Honk!", "Welcome to clown planet!") - emote_see = list("honks") - speak_chance = 1 - a_intent = INTENT_HARM - maxHealth = 75 - health = 75 - speed = 0 - harm_intent_damage = 8 - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "attacks" - attack_sound = 'sound/items/bikehorn.ogg' - obj_damage = 0 - environment_smash = 0 - minbodytemp = 270 - maxbodytemp = 370 - heat_damage_per_tick = 15 //amount of damage applied if animal's body temperature is higher than maxbodytemp - cold_damage_per_tick = 10 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - unsuitable_atmos_damage = 10 - - -/mob/living/simple_animal/hostile/retaliate/clown/goblin - icon = 'icons/mob/animal.dmi' - name = "clown goblin" - desc = "A tiny walking mask and clown shoes. You want to honk his nose!" - icon_state = "clowngoblin" - icon_living = "clowngoblin" - icon_dead = null - response_help = "honks the" - speak = list("Honk!") - speak_emote = list("sqeaks") - emote_see = list("honks") - maxHealth = 100 - health = 100 - - speed = -1 - turns_per_move = 1 - - del_on_death = TRUE - loot = list(/obj/item/clothing/mask/gas/clown_hat, /obj/item/clothing/shoes/clown_shoes) +/mob/living/simple_animal/hostile/retaliate/clown + name = "Clown" + desc = "A denizen of clown planet" + icon = 'icons/mob/simple_human.dmi' + icon_state = "clown" + icon_living = "clown" + icon_dead = "clown_dead" + icon_gib = "clown_gib" + speak_chance = 0 + turns_per_move = 5 + response_help = "pokes the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + speak = list("HONK", "Honk!", "Welcome to clown planet!") + emote_see = list("honks") + speak_chance = 1 + a_intent = INTENT_HARM + maxHealth = 75 + health = 75 + speed = 0 + harm_intent_damage = 8 + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "attacks" + attack_sound = 'sound/items/bikehorn.ogg' + obj_damage = 0 + environment_smash = 0 + minbodytemp = 270 + maxbodytemp = 370 + heat_damage_per_tick = 15 //amount of damage applied if animal's body temperature is higher than maxbodytemp + cold_damage_per_tick = 10 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + unsuitable_atmos_damage = 10 + + +/mob/living/simple_animal/hostile/retaliate/clown/goblin + icon = 'icons/mob/animal.dmi' + name = "clown goblin" + desc = "A tiny walking mask and clown shoes. You want to honk his nose!" + icon_state = "clowngoblin" + icon_living = "clowngoblin" + icon_dead = null + response_help = "honks the" + speak = list("Honk!") + speak_emote = list("sqeaks") + emote_see = list("honks") + maxHealth = 100 + health = 100 + + speed = -1 + turns_per_move = 1 + + del_on_death = TRUE + loot = list(/obj/item/clothing/mask/gas/clown_hat, /obj/item/clothing/shoes/clown_shoes) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm index d3bf35671e2..7afaf3679c2 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm @@ -1,56 +1,56 @@ -/mob/living/simple_animal/hostile/retaliate - var/list/enemies = list() - -/mob/living/simple_animal/hostile/retaliate/Found(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(!L.stat) - return L - else - enemies -= L - else if(ismecha(A)) - var/obj/mecha/M = A - if(M.occupant) - return A - else if(isspacepod(A)) - var/obj/spacepod/S = A - if(S.pilot) - return A - -/mob/living/simple_animal/hostile/retaliate/ListTargets() - if(!enemies.len) - return list() - var/list/see = ..() - see &= enemies // Remove all entries that aren't in enemies - return see - -/mob/living/simple_animal/hostile/retaliate/proc/Retaliate() - var/list/around = view(src, vision_range) - - for(var/atom/movable/A in around) - if(A == src) - continue - if(isliving(A)) - var/mob/living/M = A - if(faction_check_mob(M) && attack_same || !faction_check_mob(M)) - enemies |= M - else if(ismecha(A)) - var/obj/mecha/M = A - if(M.occupant) - enemies |= M - enemies |= M.occupant - else if(isspacepod(A)) - var/obj/spacepod/S = A - if(S.pilot) - enemies |= S - enemies |= S.pilot - - for(var/mob/living/simple_animal/hostile/retaliate/H in around) - if(faction_check_mob(H) && !attack_same && !H.attack_same) - H.enemies |= enemies - return 0 - -/mob/living/simple_animal/hostile/retaliate/adjustHealth(amount, updating_health = TRUE) - . = ..() - if(amount > 0 && stat == CONSCIOUS) - Retaliate() \ No newline at end of file +/mob/living/simple_animal/hostile/retaliate + var/list/enemies = list() + +/mob/living/simple_animal/hostile/retaliate/Found(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(!L.stat) + return L + else + enemies -= L + else if(ismecha(A)) + var/obj/mecha/M = A + if(M.occupant) + return A + else if(isspacepod(A)) + var/obj/spacepod/S = A + if(S.pilot) + return A + +/mob/living/simple_animal/hostile/retaliate/ListTargets() + if(!enemies.len) + return list() + var/list/see = ..() + see &= enemies // Remove all entries that aren't in enemies + return see + +/mob/living/simple_animal/hostile/retaliate/proc/Retaliate() + var/list/around = view(src, vision_range) + + for(var/atom/movable/A in around) + if(A == src) + continue + if(isliving(A)) + var/mob/living/M = A + if(faction_check_mob(M) && attack_same || !faction_check_mob(M)) + enemies |= M + else if(ismecha(A)) + var/obj/mecha/M = A + if(M.occupant) + enemies |= M + enemies |= M.occupant + else if(isspacepod(A)) + var/obj/spacepod/S = A + if(S.pilot) + enemies |= S + enemies |= S.pilot + + for(var/mob/living/simple_animal/hostile/retaliate/H in around) + if(faction_check_mob(H) && !attack_same && !H.attack_same) + H.enemies |= enemies + return 0 + +/mob/living/simple_animal/hostile/retaliate/adjustHealth(amount, updating_health = TRUE) + . = ..() + if(amount > 0 && stat == CONSCIOUS) + Retaliate() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm index 6e05b699ea5..a3e32e9d35e 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm @@ -116,4 +116,4 @@ faction = list("undead") loot = list(/obj/effect/decal/cleanable/blood/gibs) - del_on_death = 1 \ No newline at end of file + del_on_death = 1 diff --git a/code/modules/mob/living/simple_animal/hostile/russian.dm b/code/modules/mob/living/simple_animal/hostile/russian.dm index 37ae67cfef5..83c6fa67e1d 100644 --- a/code/modules/mob/living/simple_animal/hostile/russian.dm +++ b/code/modules/mob/living/simple_animal/hostile/russian.dm @@ -1,44 +1,44 @@ -/mob/living/simple_animal/hostile/russian - name = "Russian" - desc = "For the Motherland!" - icon = 'icons/mob/simple_human.dmi' - icon_state = "russianmelee" - icon_living = "russianmelee" - icon_dead = "russianmelee_dead" // Does not actually exist. del_on_death. - icon_gib = "russianmelee_gib" // Does not actually exist. del_on_death. - speak_chance = 0 - turns_per_move = 5 - response_help = "pokes the" - response_disarm = "shoves the" - response_harm = "hits the" - speed = 0 - maxHealth = 100 - health = 100 - harm_intent_damage = 5 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - a_intent = INTENT_HARM - unsuitable_atmos_damage = 15 - faction = list("russian") - status_flags = CANPUSH - loot = list(/obj/effect/mob_spawn/human/corpse/russian, - /obj/item/kitchen/knife) - del_on_death = 1 - sentience_type = SENTIENCE_OTHER - -/mob/living/simple_animal/hostile/russian/ranged - icon_state = "russianranged" - icon_living = "russianranged" - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - projectilesound = 'sound/weapons/gunshots/gunshot.ogg' - casingtype = /obj/item/ammo_casing/a357 - loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, /obj/item/gun/projectile/revolver/mateba) - -/mob/living/simple_animal/hostile/russian/ranged/mosin - loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, - /obj/item/gun/projectile/shotgun/boltaction) - casingtype = /obj/item/ammo_casing/a762 +/mob/living/simple_animal/hostile/russian + name = "Russian" + desc = "For the Motherland!" + icon = 'icons/mob/simple_human.dmi' + icon_state = "russianmelee" + icon_living = "russianmelee" + icon_dead = "russianmelee_dead" // Does not actually exist. del_on_death. + icon_gib = "russianmelee_gib" // Does not actually exist. del_on_death. + speak_chance = 0 + turns_per_move = 5 + response_help = "pokes the" + response_disarm = "shoves the" + response_harm = "hits the" + speed = 0 + maxHealth = 100 + health = 100 + harm_intent_damage = 5 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + a_intent = INTENT_HARM + unsuitable_atmos_damage = 15 + faction = list("russian") + status_flags = CANPUSH + loot = list(/obj/effect/mob_spawn/human/corpse/russian, + /obj/item/kitchen/knife) + del_on_death = 1 + sentience_type = SENTIENCE_OTHER + +/mob/living/simple_animal/hostile/russian/ranged + icon_state = "russianranged" + icon_living = "russianranged" + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + projectilesound = 'sound/weapons/gunshots/gunshot.ogg' + casingtype = /obj/item/ammo_casing/a357 + loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, /obj/item/gun/projectile/revolver/mateba) + +/mob/living/simple_animal/hostile/russian/ranged/mosin + loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, + /obj/item/gun/projectile/shotgun/boltaction) + casingtype = /obj/item/ammo_casing/a762 diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton.dm b/code/modules/mob/living/simple_animal/hostile/skeleton.dm index acfd9050c04..e0e71506bbe 100644 --- a/code/modules/mob/living/simple_animal/hostile/skeleton.dm +++ b/code/modules/mob/living/simple_animal/hostile/skeleton.dm @@ -46,4 +46,4 @@ loot = list(/obj/effect/decal/remains/human, /obj/item/twohanded/spear, /obj/item/clothing/shoes/winterboots, - /obj/item/clothing/suit/hooded/wintercoat) \ No newline at end of file + /obj/item/clothing/suit/hooded/wintercoat) diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm index b32cae64675..95ce5491395 100644 --- a/code/modules/mob/living/simple_animal/hostile/statue.dm +++ b/code/modules/mob/living/simple_animal/hostile/statue.dm @@ -184,7 +184,7 @@ if(L == user) continue var/turf/T = get_turf(L.loc) - if(T && T in targets) + if(T && (T in targets)) L.EyeBlind(4) return diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 513ab39b180..f3389108498 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -1,367 +1,367 @@ -/mob/living/simple_animal/hostile/syndicate - name = "Syndicate Operative" - desc = "Death to Nanotrasen." - icon = 'icons/mob/simple_human.dmi' - icon_state = "syndicate" - icon_living = "syndicate" - icon_dead = "syndicate_dead" // Does not actually exist. del_on_death. - icon_gib = "syndicate_gib" // Does not actually exist. del_on_death. - speak_chance = 0 - turns_per_move = 5 - response_help = "pokes the" - response_disarm = "shoves the" - response_harm = "hits the" - speed = 0 - maxHealth = 100 - health = 100 - harm_intent_damage = 5 - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - a_intent = INTENT_HARM - unsuitable_atmos_damage = 15 - faction = list("syndicate") - check_friendly_fire = 1 - status_flags = CANPUSH - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) - del_on_death = 1 - sentience_type = SENTIENCE_OTHER - -///////////////Sword and shield//////////// - -/mob/living/simple_animal/hostile/syndicate/melee - melee_damage_lower = 20 - melee_damage_upper = 25 - icon_state = "syndicate_sword" - icon_living = "syndicate_sword" - attacktext = "slashes" - attack_sound = 'sound/weapons/bladeslice.ogg' - armour_penetration = 28 - status_flags = 0 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) - var/melee_block_chance = 20 - var/ranged_block_chance = 35 - -/mob/living/simple_animal/hostile/syndicate/melee/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src) - if(O.force) - if(prob(melee_block_chance)) - visible_message("[src] blocks the [O] with its shield! ") - else - var/damage = O.force - if(O.damtype == STAMINA) - damage = 0 - if(force_threshold && damage < force_threshold) - visible_message("[src] is unharmed by [O]!") - return - adjustHealth(damage) - visible_message("[src] has been attacked with the [O] by [user]. ") - playsound(loc, O.hitsound, 25, 1, -1) - else - to_chat(usr, "This weapon is ineffective, it does no damage.") - visible_message("[user] gently taps [src] with the [O]. ") - - -/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(var/obj/item/projectile/Proj) - if(!Proj) - return - if(prob(ranged_block_chance)) - visible_message("[src] blocks [Proj] with its shield!") - else - if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - adjustHealth(Proj.damage) - return 0 - -/mob/living/simple_animal/hostile/syndicate/melee/autogib - loot = list()//no loot, its gonna delete and gib. - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot - name = "Syndicate Operative" - force_threshold = 6 // Prevents people using punches to bypass eshield - robust_searching = 1 // Together with stat_attack, ensures dionae/etc that regen are killed properly - stat_attack = UNCONSCIOUS - universal_speak = 1 - icon_state = "syndicate_swordonly" - icon_living = "syndicate_swordonly" - melee_block_chance = 0 - ranged_block_chance = 0 - del_on_death = 1 - var/area/syndicate_depot/core/depotarea - var/raised_alert = FALSE - var/alert_on_death = FALSE - var/alert_on_timeout = TRUE - var/alert_on_spacing = TRUE - var/alert_on_shield_breach = FALSE - var/seen_enemy = FALSE - var/seen_enemy_name = null - var/seen_revived_enemy = FALSE - var/aggro_cycles = 0 - var/scan_cycles = 0 - var/shield_key = FALSE - var/turf/spawn_turf - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/New() - ..() - name = "[name] [pick(GLOB.last_names)]" - // Do not attempt to move this code to Initialize() or LateInitialize(). Doing so with other objects has caused bugs in the past, because assigning "depotarea" may not work there. - depotarea = areaMaster - spawn_turf = get_turf(src) - - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/ListTargetsLazy() - // The normal ListTargetsLazy ignores walls, which is very bad in the case of depot mobs. So we override it. - return ListTargets() - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/Aggro() - . = ..() - if(!istype(depotarea)) - return - if(target) - if(!seen_enemy) - seen_enemy = TRUE - if(!ranged) - playsound(loc, 'sound/weapons/saberon.ogg', 35, 1) - if(alert_on_shield_breach) - if(depotarea.shield_list.len) - raise_alert("[name] reports that [target] is trying to breach the armory shield!") - alert_on_shield_breach = FALSE - raised_alert = FALSE - alert_on_death = TRUE - if(isliving(target)) - var/mob/living/M = target - depotarea.list_add(M, depotarea.hostile_list) - if(M.mind && M.mind.special_role == SPECIAL_ROLE_TRAITOR) - depotarea.saw_double_agent(M) - depotarea.declare_started() - seen_enemy_name = target.name - if(istype(target, /obj/mecha)) - depotarea.saw_mech(target) - if(istype(target, /obj/spacepod)) - depotarea.saw_pod(target) - if(depotarea.list_includes(target, depotarea.dead_list)) - seen_revived_enemy = TRUE - raise_alert("[name] reports intruder [target] has returned from death!") - depotarea.list_remove(target, depotarea.dead_list) - if(!atoms_share_level(src, target) && prob(20)) - // This prevents someone from aggroing a depot mob, then hiding in a locker, perfectly safe, while the mob stands there getting killed by their friends. - LoseTarget() - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/handle_automated_action() - . = ..() - if(!.) - return - if(!istype(depotarea)) - return - if(seen_enemy) - aggro_cycles++ - if(alert_on_timeout && !raised_alert && aggro_cycles >= 60) - raise_alert("[name] has reported contact with hostile entity: [seen_enemy_name]") - if(scan_cycles >= 15) - scan_cycles = 0 - if(!atoms_share_level(src, spawn_turf)) - if(istype(loc, /obj/structure/closet)) - var/obj/structure/closet/O = loc - forceMove(get_turf(src)) - visible_message("[src] smashes their way out of [O]!") - qdel(O) - raise_alert("[src] reported being trapped in a locker.") - raised_alert = FALSE - return - if(alert_on_spacing) - raise_alert("[src] lost in space.") - death() - return - for(var/mob/living/body in hearers(vision_range, targets_from)) - if(body.stat != DEAD) - continue - if(depotarea.list_includes(body, depotarea.dead_list)) - continue - if(faction_check_mob(body)) - continue - say("Target [body]... terminated.") - depotarea.list_add(body, depotarea.dead_list) - pointed(body) - else - scan_cycles++ - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/AIShouldSleep(var/list/possible_targets) - FindTarget(possible_targets, 1) - return FALSE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/proc/raise_alert(var/reason) - if(istype(depotarea) && (!raised_alert || seen_revived_enemy) && !depotarea.used_self_destruct) - raised_alert = TRUE - say("Intruder!") - depotarea.increase_alert(reason) - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/death() - if(!istype(depotarea)) - return ..() - if(alert_on_death) - if(seen_enemy_name) - raise_alert("[name] has died in combat with [seen_enemy_name].") - else - raise_alert("[name] has died.") - if(shield_key && depotarea) - depotarea.shields_key_check() - if(depotarea) - depotarea.list_remove(src, depotarea.guard_list) - new /obj/effect/gibspawner/human(get_turf(src)) - return ..() - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/CanPass(atom/movable/mover, turf/target, height=0) - if(isliving(mover)) - var/mob/living/blocker = mover - if(faction_check_mob(blocker)) - return 1 - return ..(mover, target, height) - - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer - name = "Syndicate Officer" - icon_state = "syndicate_sword" - icon_living = "syndicate_sword" - melee_block_chance = 20 - ranged_block_chance = 35 - alert_on_death = TRUE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/Initialize(mapload) - . = ..() - if(prob(50)) - // 50% chance of switching to ranged variant. - // Designed to counter players taking cover behind reinforced plasmasglass. - // Does almost no danage in melee, but decent damage at range, and its shots go through glass. - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - ranged = 1 - rapid = 3 - retreat_distance = 3 - minimum_distance = 3 - melee_block_chance = 0 - ranged_block_chance = 0 - icon_state = "syndicate_pistol" - icon_living = "syndicate_pistol" - projectiletype = /obj/item/projectile/beam/laser - projectilesound = 'sound/weapons/laser.ogg' - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory - name = "Syndicate Quartermaster" - icon_state = "syndicate_stormtrooper_sword" - icon_living = "syndicate_stormtrooper_sword" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxHealth = 200 - health = 200 - melee_block_chance = 40 - alert_on_shield_breach = TRUE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload) - ..() - if(prob(50)) - // 50% chance of switching to extremely dangerous ranged variant - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - ranged = 1 - retreat_distance = 3 - minimum_distance = 3 - melee_block_chance = 0 - ranged_block_chance = 0 - icon_state = "syndicate_stormtrooper_shotgun" - icon_living = "syndicate_stormtrooper_shotgun" - projectiletype = /obj/item/projectile/bullet/sniper/penetrator // Ignores cover. - projectilesound = 'sound/weapons/gunshots/gunshot_sniper.ogg' - return INITIALIZE_HINT_LATELOAD - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/LateInitialize() - if(istype(depotarea)) - var/list/key_candidates = list() - for(var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O in GLOB.living_mob_list) - key_candidates += O - if(key_candidates.len) - var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O = pick(key_candidates) - O.shield_key = TRUE - depotarea.shields_up() - - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space - name = "Syndicate Backup" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - icon_state = "syndicate_space_sword" - icon_living = "syndicate_space_sword" - speed = 1 - wander = 0 - alert_on_spacing = FALSE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(var/movement_dir = 0) - return TRUE - - - -/mob/living/simple_animal/hostile/syndicate/melee/space - name = "Syndicate Commando" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - icon_state = "syndicate_space_sword" - icon_living = "syndicate_space_sword" - speed = 1 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) - -/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(var/movement_dir = 0) - return TRUE - - -/mob/living/simple_animal/hostile/syndicate/ranged - ranged = 1 - rapid = 2 - retreat_distance = 5 - minimum_distance = 5 - icon_state = "syndicate_smg" - icon_living = "syndicate_smg" - projectilesound = 'sound/weapons/gunshots/gunshot.ogg' - casingtype = /obj/item/ammo_casing/c45 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/gun/projectile/automatic/c20r) - -/mob/living/simple_animal/hostile/syndicate/ranged/space - icon_state = "syndicate_space_smg" - icon_living = "syndicate_space_smg" - name = "Syndicate Commando" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - speed = 1 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/gun/projectile/automatic/c20r) - -/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(var/movement_dir = 0) - return TRUE - -/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib - loot = list()//gonna gibe, no loot. - -/mob/living/simple_animal/hostile/viscerator - name = "viscerator" - desc = "A small, twin-bladed machine capable of inflicting very deadly lacerations." - icon = 'icons/mob/critter.dmi' - icon_state = "viscerator_attack" - icon_living = "viscerator_attack" - pass_flags = PASSTABLE - health = 15 - maxHealth = 15 - obj_damage = 0 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "cuts" - attack_sound = 'sound/weapons/bladeslice.ogg' - faction = list("syndicate") - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - mob_size = MOB_SIZE_TINY - flying = 1 - gold_core_spawnable = HOSTILE_SPAWN - del_on_death = 1 - deathmessage = "is smashed into pieces!" \ No newline at end of file +/mob/living/simple_animal/hostile/syndicate + name = "Syndicate Operative" + desc = "Death to Nanotrasen." + icon = 'icons/mob/simple_human.dmi' + icon_state = "syndicate" + icon_living = "syndicate" + icon_dead = "syndicate_dead" // Does not actually exist. del_on_death. + icon_gib = "syndicate_gib" // Does not actually exist. del_on_death. + speak_chance = 0 + turns_per_move = 5 + response_help = "pokes the" + response_disarm = "shoves the" + response_harm = "hits the" + speed = 0 + maxHealth = 100 + health = 100 + harm_intent_damage = 5 + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + a_intent = INTENT_HARM + unsuitable_atmos_damage = 15 + faction = list("syndicate") + check_friendly_fire = 1 + status_flags = CANPUSH + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) + del_on_death = 1 + sentience_type = SENTIENCE_OTHER + +///////////////Sword and shield//////////// + +/mob/living/simple_animal/hostile/syndicate/melee + melee_damage_lower = 20 + melee_damage_upper = 25 + icon_state = "syndicate_sword" + icon_living = "syndicate_sword" + attacktext = "slashes" + attack_sound = 'sound/weapons/bladeslice.ogg' + armour_penetration = 28 + status_flags = 0 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) + var/melee_block_chance = 20 + var/ranged_block_chance = 35 + +/mob/living/simple_animal/hostile/syndicate/melee/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src) + if(O.force) + if(prob(melee_block_chance)) + visible_message("[src] blocks the [O] with its shield! ") + else + var/damage = O.force + if(O.damtype == STAMINA) + damage = 0 + if(force_threshold && damage < force_threshold) + visible_message("[src] is unharmed by [O]!") + return + adjustHealth(damage) + visible_message("[src] has been attacked with the [O] by [user]. ") + playsound(loc, O.hitsound, 25, 1, -1) + else + to_chat(usr, "This weapon is ineffective, it does no damage.") + visible_message("[user] gently taps [src] with the [O]. ") + + +/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(var/obj/item/projectile/Proj) + if(!Proj) + return + if(prob(ranged_block_chance)) + visible_message("[src] blocks [Proj] with its shield!") + else + if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + adjustHealth(Proj.damage) + return 0 + +/mob/living/simple_animal/hostile/syndicate/melee/autogib + loot = list()//no loot, its gonna delete and gib. + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot + name = "Syndicate Operative" + force_threshold = 6 // Prevents people using punches to bypass eshield + robust_searching = 1 // Together with stat_attack, ensures dionae/etc that regen are killed properly + stat_attack = UNCONSCIOUS + universal_speak = 1 + icon_state = "syndicate_swordonly" + icon_living = "syndicate_swordonly" + melee_block_chance = 0 + ranged_block_chance = 0 + del_on_death = 1 + var/area/syndicate_depot/core/depotarea + var/raised_alert = FALSE + var/alert_on_death = FALSE + var/alert_on_timeout = TRUE + var/alert_on_spacing = TRUE + var/alert_on_shield_breach = FALSE + var/seen_enemy = FALSE + var/seen_enemy_name = null + var/seen_revived_enemy = FALSE + var/aggro_cycles = 0 + var/scan_cycles = 0 + var/shield_key = FALSE + var/turf/spawn_turf + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/New() + ..() + name = "[name] [pick(GLOB.last_names)]" + // Do not attempt to move this code to Initialize() or LateInitialize(). Doing so with other objects has caused bugs in the past, because assigning "depotarea" may not work there. + depotarea = areaMaster + spawn_turf = get_turf(src) + + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/ListTargetsLazy() + // The normal ListTargetsLazy ignores walls, which is very bad in the case of depot mobs. So we override it. + return ListTargets() + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/Aggro() + . = ..() + if(!istype(depotarea)) + return + if(target) + if(!seen_enemy) + seen_enemy = TRUE + if(!ranged) + playsound(loc, 'sound/weapons/saberon.ogg', 35, 1) + if(alert_on_shield_breach) + if(depotarea.shield_list.len) + raise_alert("[name] reports that [target] is trying to breach the armory shield!") + alert_on_shield_breach = FALSE + raised_alert = FALSE + alert_on_death = TRUE + if(isliving(target)) + var/mob/living/M = target + depotarea.list_add(M, depotarea.hostile_list) + if(M.mind && M.mind.special_role == SPECIAL_ROLE_TRAITOR) + depotarea.saw_double_agent(M) + depotarea.declare_started() + seen_enemy_name = target.name + if(istype(target, /obj/mecha)) + depotarea.saw_mech(target) + if(istype(target, /obj/spacepod)) + depotarea.saw_pod(target) + if(depotarea.list_includes(target, depotarea.dead_list)) + seen_revived_enemy = TRUE + raise_alert("[name] reports intruder [target] has returned from death!") + depotarea.list_remove(target, depotarea.dead_list) + if(!atoms_share_level(src, target) && prob(20)) + // This prevents someone from aggroing a depot mob, then hiding in a locker, perfectly safe, while the mob stands there getting killed by their friends. + LoseTarget() + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/handle_automated_action() + . = ..() + if(!.) + return + if(!istype(depotarea)) + return + if(seen_enemy) + aggro_cycles++ + if(alert_on_timeout && !raised_alert && aggro_cycles >= 60) + raise_alert("[name] has reported contact with hostile entity: [seen_enemy_name]") + if(scan_cycles >= 15) + scan_cycles = 0 + if(!atoms_share_level(src, spawn_turf)) + if(istype(loc, /obj/structure/closet)) + var/obj/structure/closet/O = loc + forceMove(get_turf(src)) + visible_message("[src] smashes their way out of [O]!") + qdel(O) + raise_alert("[src] reported being trapped in a locker.") + raised_alert = FALSE + return + if(alert_on_spacing) + raise_alert("[src] lost in space.") + death() + return + for(var/mob/living/body in hearers(vision_range, targets_from)) + if(body.stat != DEAD) + continue + if(depotarea.list_includes(body, depotarea.dead_list)) + continue + if(faction_check_mob(body)) + continue + say("Target [body]... terminated.") + depotarea.list_add(body, depotarea.dead_list) + pointed(body) + else + scan_cycles++ + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/AIShouldSleep(var/list/possible_targets) + FindTarget(possible_targets, 1) + return FALSE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/proc/raise_alert(var/reason) + if(istype(depotarea) && (!raised_alert || seen_revived_enemy) && !depotarea.used_self_destruct) + raised_alert = TRUE + say("Intruder!") + depotarea.increase_alert(reason) + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/death() + if(!istype(depotarea)) + return ..() + if(alert_on_death) + if(seen_enemy_name) + raise_alert("[name] has died in combat with [seen_enemy_name].") + else + raise_alert("[name] has died.") + if(shield_key && depotarea) + depotarea.shields_key_check() + if(depotarea) + depotarea.list_remove(src, depotarea.guard_list) + new /obj/effect/gibspawner/human(get_turf(src)) + return ..() + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/CanPass(atom/movable/mover, turf/target, height=0) + if(isliving(mover)) + var/mob/living/blocker = mover + if(faction_check_mob(blocker)) + return 1 + return ..(mover, target, height) + + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer + name = "Syndicate Officer" + icon_state = "syndicate_sword" + icon_living = "syndicate_sword" + melee_block_chance = 20 + ranged_block_chance = 35 + alert_on_death = TRUE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/Initialize(mapload) + . = ..() + if(prob(50)) + // 50% chance of switching to ranged variant. + // Designed to counter players taking cover behind reinforced plasmasglass. + // Does almost no danage in melee, but decent damage at range, and its shots go through glass. + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + ranged = 1 + rapid = 3 + retreat_distance = 3 + minimum_distance = 3 + melee_block_chance = 0 + ranged_block_chance = 0 + icon_state = "syndicate_pistol" + icon_living = "syndicate_pistol" + projectiletype = /obj/item/projectile/beam/laser + projectilesound = 'sound/weapons/laser.ogg' + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory + name = "Syndicate Quartermaster" + icon_state = "syndicate_stormtrooper_sword" + icon_living = "syndicate_stormtrooper_sword" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxHealth = 200 + health = 200 + melee_block_chance = 40 + alert_on_shield_breach = TRUE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload) + ..() + if(prob(50)) + // 50% chance of switching to extremely dangerous ranged variant + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + ranged = 1 + retreat_distance = 3 + minimum_distance = 3 + melee_block_chance = 0 + ranged_block_chance = 0 + icon_state = "syndicate_stormtrooper_shotgun" + icon_living = "syndicate_stormtrooper_shotgun" + projectiletype = /obj/item/projectile/bullet/sniper/penetrator // Ignores cover. + projectilesound = 'sound/weapons/gunshots/gunshot_sniper.ogg' + return INITIALIZE_HINT_LATELOAD + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/LateInitialize() + if(istype(depotarea)) + var/list/key_candidates = list() + for(var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O in GLOB.living_mob_list) + key_candidates += O + if(key_candidates.len) + var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O = pick(key_candidates) + O.shield_key = TRUE + depotarea.shields_up() + + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space + name = "Syndicate Backup" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + icon_state = "syndicate_space_sword" + icon_living = "syndicate_space_sword" + speed = 1 + wander = 0 + alert_on_spacing = FALSE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(var/movement_dir = 0) + return TRUE + + + +/mob/living/simple_animal/hostile/syndicate/melee/space + name = "Syndicate Commando" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + icon_state = "syndicate_space_sword" + icon_living = "syndicate_space_sword" + speed = 1 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) + +/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(var/movement_dir = 0) + return TRUE + + +/mob/living/simple_animal/hostile/syndicate/ranged + ranged = 1 + rapid = 2 + retreat_distance = 5 + minimum_distance = 5 + icon_state = "syndicate_smg" + icon_living = "syndicate_smg" + projectilesound = 'sound/weapons/gunshots/gunshot.ogg' + casingtype = /obj/item/ammo_casing/c45 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/gun/projectile/automatic/c20r) + +/mob/living/simple_animal/hostile/syndicate/ranged/space + icon_state = "syndicate_space_smg" + icon_living = "syndicate_space_smg" + name = "Syndicate Commando" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speed = 1 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/gun/projectile/automatic/c20r) + +/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(var/movement_dir = 0) + return TRUE + +/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib + loot = list()//gonna gibe, no loot. + +/mob/living/simple_animal/hostile/viscerator + name = "viscerator" + desc = "A small, twin-bladed machine capable of inflicting very deadly lacerations." + icon = 'icons/mob/critter.dmi' + icon_state = "viscerator_attack" + icon_living = "viscerator_attack" + pass_flags = PASSTABLE + health = 15 + maxHealth = 15 + obj_damage = 0 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "cuts" + attack_sound = 'sound/weapons/bladeslice.ogg' + faction = list("syndicate") + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + mob_size = MOB_SIZE_TINY + flying = 1 + gold_core_spawnable = HOSTILE_SPAWN + del_on_death = 1 + deathmessage = "is smashed into pieces!" diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm index b72ec90cf58..2b15e584922 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm @@ -58,7 +58,7 @@ if(feedings_left > 0) to_chat(user, "You must wrap [feedings_left] more humanoid prey before you can do this!") return - for(var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q in GLOB.ts_spiderlist) if(Q.spider_awaymission == user.spider_awaymission) to_chat(user, "The presence of another Queen in the area is preventing you from maturing.") return @@ -278,4 +278,4 @@ forceMove(C.loc) C.visible_message("[src] smashes the welded cover off [C]!") return - to_chat(src, "There is no welded vent or scrubber close enough to do this.") \ No newline at end of file + to_chat(src, "There is no welded vent or scrubber close enough to do this.") diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm index eae926b88e1..a1bb0fb0ae9 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm @@ -56,4 +56,4 @@ var/inject_target = pick("chest","head") if(C.can_inject(null, FALSE, inject_target, FALSE)) to_chat(C, "[src] slices into you!") - C.reagents.add_reagent("terror_black_toxin", 30) \ No newline at end of file + C.reagents.add_reagent("terror_black_toxin", 30) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm index fd30532a7bc..137d6739466 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm @@ -96,13 +96,13 @@ S.amount_grown = 250 /mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/proc/EraseBrood() - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.spider_tier < spider_tier) T.degenerate = 1 to_chat(T, "Through the hivemind, the raw power of [src] floods into your body, burning it from the inside out!") - for(var/obj/structure/spider/eggcluster/terror_eggcluster/T in ts_egg_list) + for(var/obj/structure/spider/eggcluster/terror_eggcluster/T in GLOB.ts_egg_list) qdel(T) - for(var/obj/structure/spider/spiderling/terror_spiderling/T in ts_spiderling_list) + for(var/obj/structure/spider/spiderling/terror_spiderling/T in GLOB.ts_spiderling_list) qdel(T) to_chat(src, "All Terror Spiders, except yourself, will die off shortly.") @@ -110,4 +110,4 @@ /obj/item/projectile/terrorqueenspit/empress damage_type = BURN damage = 30 - bonus_tox = 0 \ No newline at end of file + bonus_tox = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm index 283cbb6ad0d..daed363b8d9 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm @@ -14,7 +14,7 @@ var/error_on_humanize = "" var/humanize_prompt = "Take direct control of [src]?" humanize_prompt += " Role: [spider_role_summary]" - if(user.ckey in ts_ckey_blacklist) + if(user.ckey in GLOB.ts_ckey_blacklist) error_on_humanize = "You are not able to control any terror spider this round." else if(cannotPossess(user)) error_on_humanize = "You have enabled antag HUD and are unable to re-enter the round." @@ -43,4 +43,4 @@ return key = user.key for(var/mob/dead/observer/G in GLOB.player_list) - G.show_message("A ghost has taken control of [src]. ([ghost_follow_link(src, ghost=G)]).") \ No newline at end of file + G.show_message("A ghost has taken control of [src]. ([ghost_follow_link(src, ghost=G)]).") diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm index c06a7c2fee6..544f7201d24 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm @@ -63,4 +63,4 @@ /obj/structure/spider/terrorweb/gray alpha = 100 name = "transparent web" - desc = "This web is partly transparent, making it harder to see, and easier to get caught by." \ No newline at end of file + desc = "This web is partly transparent, making it harder to see, and easier to get caught by." diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm index d4b1886e49e..245ae95c5e9 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm @@ -4,7 +4,7 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/DoHiveSense() var/hsline = "" to_chat(src, "Your Brood: ") - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.spider_awaymission != spider_awaymission) continue hsline = "* [T] in [get_area(T)], " @@ -20,21 +20,21 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpiders() var/numspiders = 0 - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission) numspiders += 1 return numspiders /mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpidersType(specific_type) var/numspiders = 0 - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission) if(T.type == specific_type) numspiders += 1 - for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in ts_egg_list) + for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in GLOB.ts_egg_list) if(E.spiderling_type == specific_type && E.z == z) numspiders += E.spiderling_number - for(var/obj/structure/spider/spiderling/terror_spiderling/L in ts_spiderling_list) + for(var/obj/structure/spider/spiderling/terror_spiderling/L in GLOB.ts_spiderling_list) if(!L.stillborn && L.grow_as == specific_type && L.z == z) numspiders += 1 - return numspiders \ No newline at end of file + return numspiders diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm index 92c7688e1e1..7e36b411bae 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm @@ -94,4 +94,4 @@ name = "thick web" desc = "This web is so thick, most cannot see beyond it." opacity = 1 - max_integrity = 40 \ No newline at end of file + max_integrity = 40 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm index d7d41912233..241cceb3d2c 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm @@ -88,7 +88,7 @@ if(spider_uo71) UnlockBlastDoors("UO71_Caves") // When a queen dies, so do her player-controlled purple-type guardians. Intended as a motivator for purples to ensure they guard her. - for(var/mob/living/simple_animal/hostile/poison/terror_spider/purple/P in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/purple/P in GLOB.ts_spiderlist) if(ckey) P.visible_message("\The [src] writhes in pain!") to_chat(P,"\The [src] has died. Without her hivemind link, purple terrors like yourself cannot survive more than a few minutes!") @@ -97,7 +97,7 @@ /mob/living/simple_animal/hostile/poison/terror_spider/queen/Retaliate() ..() - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) T.enemies |= enemies /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/ai_nest_is_full() @@ -335,8 +335,8 @@ damage_type = TOX var/bonus_tox = 30 -/obj/item/projectile/terrorqueenspit/on_hit(mob/living/carbon/target) - if(ismob(target)) +/obj/item/projectile/terrorqueenspit/on_hit(mob/living/carbon/target, blocked = 0, hit_zone) + if(ismob(target) && blocked < 100) var/mob/living/L = target if(L.reagents) if(L.can_inject(null, FALSE, "chest", FALSE)) @@ -353,4 +353,4 @@ var/inject_target = pick("chest","head") if(C.can_inject(null, FALSE, inject_target, FALSE)) C.Hallucinate(400) - C.adjustToxLoss(30) \ No newline at end of file + C.adjustToxLoss(30) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm index 4bba74001fd..a889bc6d328 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm @@ -23,12 +23,12 @@ /obj/structure/spider/spiderling/terror_spiderling/New() ..() - ts_spiderling_list += src + GLOB.ts_spiderling_list += src if(is_away_level(z)) spider_awaymission = TRUE /obj/structure/spider/spiderling/terror_spiderling/Destroy() - ts_spiderling_list -= src + GLOB.ts_spiderling_list -= src return ..() /obj/structure/spider/spiderling/terror_spiderling/Bump(obj/O) @@ -205,7 +205,7 @@ /obj/structure/spider/eggcluster/terror_eggcluster/New() ..() - ts_egg_list += src + GLOB.ts_egg_list += src spawn(50) if(spiderling_type == /mob/living/simple_animal/hostile/poison/terror_spider/red) name = "red terror eggs" @@ -227,7 +227,7 @@ name = "queen of terror eggs" /obj/structure/spider/eggcluster/terror_eggcluster/Destroy() - ts_egg_list -= src + GLOB.ts_egg_list -= src return ..() /obj/structure/spider/eggcluster/terror_eggcluster/process() @@ -243,4 +243,4 @@ S.enemies = enemies if(spider_growinstantly) S.amount_grown = 250 - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm index 8d18471b1cb..11e4cac3e4a 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm @@ -103,8 +103,8 @@ return if(!target) var/my_ventcrawl_freq = freq_ventcrawl_idle - if(ts_count_dead > 0) - if(world.time < (ts_death_last + ts_death_window)) + if(GLOB.ts_count_dead > 0) + if(world.time < (GLOB.ts_death_last + GLOB.ts_death_window)) my_ventcrawl_freq = freq_ventcrawl_combat // First, check for general actions that any spider could take. if(path_to_vent) @@ -267,7 +267,7 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/ClearObstacle(turf/target_turf) var/list/valid_obstacles = list(/obj/structure/window, /obj/structure/closet, /obj/structure/table, /obj/structure/grille, /obj/structure/rack, /obj/machinery/door/window) - for(var/dir in cardinal) // North, South, East, West + for(var/dir in GLOB.cardinal) // North, South, East, West var/obj/structure/obstacle = locate(/obj/structure, get_step(src, dir)) if(is_type_in_list(obstacle, valid_obstacles)) obstacle.attack_animal(src) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index d6d64096ff1..520143d71d0 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -1,12 +1,12 @@ -var/global/list/ts_ckey_blacklist = list() -var/global/ts_count_dead = 0 -var/global/ts_count_alive_awaymission = 0 -var/global/ts_count_alive_station = 0 -var/global/ts_death_last = 0 -var/global/ts_death_window = 9000 // 15 minutes -var/global/list/ts_spiderlist = list() -var/global/list/ts_egg_list = list() -var/global/list/ts_spiderling_list = list() +GLOBAL_LIST_EMPTY(ts_ckey_blacklist) +GLOBAL_VAR_INIT(ts_count_dead, 0) +GLOBAL_VAR_INIT(ts_count_alive_awaymission, 0) +GLOBAL_VAR_INIT(ts_count_alive_station, 0) +GLOBAL_VAR_INIT(ts_death_last, 0) +GLOBAL_VAR_INIT(ts_death_window, 9000) // 15 minutes +GLOBAL_LIST_EMPTY(ts_spiderlist) +GLOBAL_LIST_EMPTY(ts_egg_list) +GLOBAL_LIST_EMPTY(ts_spiderling_list) // -------------------------------------------------------------------------------- // --------------------- TERROR SPIDERS: DEFAULTS --------------------------------- @@ -245,7 +245,7 @@ var/global/list/ts_spiderling_list = list() /mob/living/simple_animal/hostile/poison/terror_spider/New() ..() - ts_spiderlist += src + GLOB.ts_spiderlist += src add_language("Spider Hivemind") if(spider_tier >= TS_TIER_2) add_language("Galactic Common") @@ -262,7 +262,7 @@ var/global/list/ts_spiderling_list = list() msg_terrorspiders("[src] has grown in [get_area(src)].") if(is_away_level(z)) spider_awaymission = 1 - ts_count_alive_awaymission++ + GLOB.ts_count_alive_awaymission++ if(spider_tier >= 3) ai_ventcrawls = FALSE // means that pre-spawned bosses on away maps won't ventcrawl. Necessary to keep prince/mother in one place. if(istype(get_area(src), /area/awaymission/UO71)) // if we are playing the away mission with our special spiders... @@ -272,11 +272,11 @@ var/global/list/ts_spiderling_list = list() ai_ventcrawls = FALSE spider_placed = 1 else - ts_count_alive_station++ + GLOB.ts_count_alive_station++ // after 3 seconds, assuming nobody took control of it yet, offer it to ghosts. addtimer(CALLBACK(src, .proc/CheckFaction), 20) addtimer(CALLBACK(src, .proc/announcetoghosts), 30) - var/datum/atom_hud/U = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/U = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] U.add_hud_to(src) /mob/living/simple_animal/hostile/poison/terror_spider/proc/announcetoghosts() @@ -292,7 +292,7 @@ var/global/list/ts_spiderling_list = list() notify_ghosts("[src] has appeared in [get_area(src)].", enter_link = "(Click to control)", source = src, alert_overlay = alert_overlay, action = NOTIFY_ATTACK) /mob/living/simple_animal/hostile/poison/terror_spider/Destroy() - ts_spiderlist -= src + GLOB.ts_spiderlist -= src handle_dying() return ..() @@ -322,12 +322,12 @@ var/global/list/ts_spiderling_list = list() /mob/living/simple_animal/hostile/poison/terror_spider/proc/handle_dying() if(!hasdied) hasdied = 1 - ts_count_dead++ - ts_death_last = world.time + GLOB.ts_count_dead++ + GLOB.ts_death_last = world.time if(spider_awaymission) - ts_count_alive_awaymission-- + GLOB.ts_count_alive_awaymission-- else - ts_count_alive_station-- + GLOB.ts_count_alive_station-- /mob/living/simple_animal/hostile/poison/terror_spider/death(gibbed) if(can_die()) @@ -352,7 +352,7 @@ var/global/list/ts_spiderling_list = list() . = ..() /mob/living/simple_animal/hostile/poison/terror_spider/proc/msg_terrorspiders(msgtext) - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.stat != DEAD) to_chat(T, "TerrorSense: [msgtext]") diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm index f62167792a3..919f91742cc 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm @@ -68,4 +68,4 @@ var/inject_target = pick("chest","head") if(C.can_inject(null, FALSE, inject_target, FALSE)) to_chat(C, "[src] slices into you!") - new /obj/item/organ/internal/body_egg/terror_eggs(C) \ No newline at end of file + new /obj/item/organ/internal/body_egg/terror_eggs(C) diff --git a/code/modules/mob/living/simple_animal/hostile/tree.dm b/code/modules/mob/living/simple_animal/hostile/tree.dm index 510e609756b..6881e3d7b12 100644 --- a/code/modules/mob/living/simple_animal/hostile/tree.dm +++ b/code/modules/mob/living/simple_animal/hostile/tree.dm @@ -1,45 +1,45 @@ -/mob/living/simple_animal/hostile/tree - name = "pine tree" - desc = "A pissed off tree-like alien. It seems annoyed with the festivities..." - icon = 'icons/obj/flora/pinetrees.dmi' - icon_state = "pine_1" - icon_living = "pine_1" - icon_dead = "pine_1" - icon_gib = "pine_1" - speak_chance = 0 - turns_per_move = 5 - response_help = "brushes the" - response_disarm = "pushes the" - response_harm = "hits the" - speed = 1 - maxHealth = 250 - health = 250 - mob_size = MOB_SIZE_LARGE - - pixel_x = -16 - - harm_intent_damage = 5 - melee_damage_lower = 8 - melee_damage_upper = 12 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - speak_emote = list("pines") - emote_taunt = list("growls") - taunt_chance = 20 - - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("hostile", "winter") - loot = list(/obj/item/stack/sheet/wood) - gold_core_spawnable = HOSTILE_SPAWN - deathmessage = "is hacked into pieces!" - del_on_death = 1 - -/mob/living/simple_animal/hostile/tree/AttackingTarget() - . = ..() - if(. && iscarbon(target)) - var/mob/living/carbon/C = target - if(prob(15)) - C.Weaken(3) - C.visible_message("\the [src] knocks down \the [C]!") \ No newline at end of file +/mob/living/simple_animal/hostile/tree + name = "pine tree" + desc = "A pissed off tree-like alien. It seems annoyed with the festivities..." + icon = 'icons/obj/flora/pinetrees.dmi' + icon_state = "pine_1" + icon_living = "pine_1" + icon_dead = "pine_1" + icon_gib = "pine_1" + speak_chance = 0 + turns_per_move = 5 + response_help = "brushes the" + response_disarm = "pushes the" + response_harm = "hits the" + speed = 1 + maxHealth = 250 + health = 250 + mob_size = MOB_SIZE_LARGE + + pixel_x = -16 + + harm_intent_damage = 5 + melee_damage_lower = 8 + melee_damage_upper = 12 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + speak_emote = list("pines") + emote_taunt = list("growls") + taunt_chance = 20 + + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("hostile", "winter") + loot = list(/obj/item/stack/sheet/wood) + gold_core_spawnable = HOSTILE_SPAWN + deathmessage = "is hacked into pieces!" + del_on_death = 1 + +/mob/living/simple_animal/hostile/tree/AttackingTarget() + . = ..() + if(. && iscarbon(target)) + var/mob/living/carbon/C = target + if(prob(15)) + C.Weaken(3) + C.visible_message("\the [src] knocks down \the [C]!") diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 0cc174827ae..251a67a6c4b 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -1,729 +1,729 @@ -/* Parrots! - * Contains - * Defines - * Inventory (headset stuff) - * Attack responces - * AI - * Procs / Verbs (usable by players) - * Sub-types - */ - -/* - * Defines - */ - -//Only a maximum of one action and one intent should be active at any given time. -//Actions -#define PARROT_PERCH 1 //Sitting/sleeping, not moving -#define PARROT_SWOOP 2 //Moving towards or away from a target -#define PARROT_WANDER 4 //Moving without a specific target in mind - -//Intents -#define PARROT_STEAL 8 //Flying towards a target to steal it/from it -#define PARROT_ATTACK 16 //Flying towards a target to attack it -#define PARROT_RETURN 32 //Flying towards its perch -#define PARROT_FLEE 64 //Flying away from its attacker - - -/mob/living/simple_animal/parrot - name = "\improper Parrot" - desc = "The parrot squaks, \"It's a Parrot! BAWWK!\"" - icon = 'icons/mob/animal.dmi' - icon_state = "parrot_fly" - icon_living = "parrot_fly" - icon_dead = "parrot_dead" - pass_flags = PASSTABLE - can_collar = 1 - - var/list/clean_speak = list( - "Hi", - "Hello!", - "Cracker?", - "BAWWWWK george mellons griffing me") - speak_emote = list("squawks","says","yells") - emote_hear = list("squawks","bawks") - emote_see = list("flutters its wings") - - speak_chance = 1//1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s - turns_per_move = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/cracker = 3) - - response_help = "pets the" - response_disarm = "gently moves aside the" - response_harm = "swats the" - stop_automated_movement = 1 - universal_speak = 1 - mob_size = MOB_SIZE_SMALL - - var/parrot_state = PARROT_WANDER //Hunt for a perch when created - var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in process_ai() being run every single tick. - var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down - var/parrot_dam_zone = list("chest", "head", "l_arm", "l_leg", "r_arm", "r_leg") //For humans, select a bodypart to attack - - var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. - var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every process_ai() and at 0 the parrot will return to regular speed. - - var/list/speech_buffer = list() - var/list/available_channels = list() - - //Headset for Poly to yell at engineers :) - var/obj/item/radio/headset/ears = null - - //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, - //mobs it wants to attack or mobs that have attacked it - var/atom/movable/parrot_interest = null - - //Parrots will generally sit on their pertch unless something catches their eye. - //These vars store their preffered perch and if they dont have one, what they can use as a perch - var/obj/parrot_perch = null - var/obj/desired_perches = list(/obj/structure/computerframe, /obj/structure/displaycase, \ - /obj/structure/filingcabinet, /obj/machinery/teleport, \ - /obj/machinery/suit_storage_unit, /obj/machinery/clonepod, \ - /obj/machinery/dna_scannernew, /obj/machinery/telecomms, \ - /obj/machinery/nuclearbomb, /obj/machinery/particle_accelerator, \ - /obj/machinery/recharge_station, /obj/machinery/smartfridge, \ - /obj/machinery/computer) - - //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. - var/obj/item/held_item = null - gold_core_spawnable = FRIENDLY_SPAWN - - -/mob/living/simple_animal/parrot/New() - ..() - GLOB.hear_radio_list += src - if(!ears) - var/headset = pick(/obj/item/radio/headset/headset_sec, \ - /obj/item/radio/headset/headset_eng, \ - /obj/item/radio/headset/headset_med, \ - /obj/item/radio/headset/headset_sci, \ - /obj/item/radio/headset/headset_cargo) - ears = new headset(src) - update_speak() - - parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var - - verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \ - /mob/living/simple_animal/parrot/proc/steal_from_mob, \ - /mob/living/simple_animal/parrot/verb/drop_held_item_player, \ - /mob/living/simple_animal/parrot/proc/perch_player) - -/mob/living/simple_animal/parrot/Destroy() - GLOB.hear_radio_list -= src - return ..() - -/mob/living/simple_animal/parrot/death(gibbed) - if(can_die()) - if(held_item) - held_item.loc = src.loc - held_item = null - walk(src,0) - return ..() - -/mob/living/simple_animal/parrot/Stat() - ..() - stat("Held Item", held_item) - -/* - * Inventory - */ -/mob/living/simple_animal/parrot/show_inv(mob/user as mob) - user.set_machine(src) - - var/dat = {""} - - dat += "" - if(can_collar) - dat += "" - dat += "" - - dat += {"
    Headset:[(ears && !(ears.flags&ABSTRACT)) ? ears : "Empty"]
     
    Collar:[(pcollar && !(pcollar.flags&ABSTRACT)) ? pcollar : "Empty"]
    - Close - "} - - var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 500) - popup.set_content(dat) - popup.open() - -/mob/living/simple_animal/parrot/Topic(href, href_list) - - //Can the usr physically do this? - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - return - - //Is the usr's mob type able to do this? - if(ishuman(usr) || isrobot(usr)) - if(href_list["remove_inv"]) - var/remove_from = href_list["remove_inv"] - switch(remove_from) - if("ears") - if(ears) - if(available_channels.len) - say("[pick(available_channels)]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - else - say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - ears.forceMove(loc) - ears = null - update_speak() - else - to_chat(usr, "There is nothing to remove from its [remove_from].") - return - show_inv(usr) - else if(href_list["add_inv"]) - var/add_to = href_list["add_inv"] - if(!usr.get_active_hand()) - to_chat(usr, "You have nothing in your hand to put on its [add_to].") - return - switch(add_to) - if("ears") - if(ears) - to_chat(usr, "It's already wearing something.") - return - else - var/obj/item/item_to_add = usr.get_active_hand() - if(!item_to_add) - return - - if( !istype(item_to_add, /obj/item/radio/headset) ) - to_chat(usr, "This object won't fit.") - return - - var/obj/item/radio/headset/headset_to_add = item_to_add - - usr.drop_item() - headset_to_add.forceMove(src) - ears = headset_to_add - to_chat(usr, "You fit the headset onto [src].") - - available_channels.Cut() - for(var/ch in headset_to_add.channels) - switch(ch) - if("Engineering") - available_channels.Add(":e") - if("Command") - available_channels.Add(":c") - if("Security") - available_channels.Add(":s") - if("Science") - available_channels.Add(":n") - if("Medical") - available_channels.Add(":m") - if("Mining") - available_channels.Add(":d") - if("Cargo") - available_channels.Add(":q") - - if(headset_to_add.translate_binary) - available_channels.Add(":b") - update_speak() - show_inv(usr) - else - ..() - - -/* - * Attack responces - */ -//Humans, monkeys, aliens -/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob) - ..() - if(client) return - if(!stat && M.a_intent == "hurt") - - icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks - - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - parrot_interest = M - parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction.. - - if(M.health < 50) //Weakened mob? Fight back! - parrot_state |= PARROT_ATTACK - else - parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! - drop_held_item(0) - return - -//Mobs with objects -/mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - ..() - if(!stat && !client && !istype(O, /obj/item/stack/medical)) - if(O.force) - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - parrot_interest = user - parrot_state = PARROT_SWOOP | PARROT_FLEE - icon_state = "parrot_fly" - drop_held_item(0) - return - -//Bullets -/mob/living/simple_animal/parrot/bullet_act(var/obj/item/projectile/Proj) - ..() - if(!stat && !client) - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - parrot_interest = null - parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL! - parrot_been_shot += 5 - icon_state = "parrot_fly" - drop_held_item(0) - return - - -/* - * AI - Not really intelligent, but I'm calling it AI anyway. - */ -/mob/living/simple_animal/parrot/Life(seconds, times_fired) - ..() - - //Sprite and AI update for when a parrot gets pulled - if(pulledby && stat == CONSCIOUS) - icon_state = "parrot_fly" - -/mob/living/simple_animal/parrot/proc/update_speak() - speak.Cut() - - if(available_channels.len && ears) - for(var/possible_phrase in clean_speak) - //50/50 chance to not use the radio at all - speak += "[prob(50) ? pick(available_channels) : ""][possible_phrase]" - - else //If we have no headset or channels to use, dont try to use any! - for(var/possible_phrase in clean_speak) - speak += possible_phrase - -/mob/living/simple_animal/parrot/handle_automated_movement() - if(pulledby) - parrot_state = PARROT_WANDER - return - - if(!isturf(src.loc) || !canmove || buckled) - return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove) - - -//-----SPEECH - /* Parrot speech mimickry! - Phrases that the parrot hears in mob/living/say() get added to speach_buffer. - Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. - Then it clears the buffer to make sure they dont magically remember something from hours ago. */ - if(speech_buffer.len && prob(10)) - if(clean_speak.len) - clean_speak -= pick(clean_speak) - - clean_speak += pick(speech_buffer) - speech_buffer.Cut() - - -//-----SLEEPING - if(parrot_state == PARROT_PERCH) - if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us - if(parrot_perch in view(src)) - parrot_state = PARROT_SWOOP | PARROT_RETURN - icon_state = "parrot_fly" - return - else - parrot_state = PARROT_WANDER - icon_state = "parrot_fly" - return - - if(--parrot_sleep_dur) //Zzz - return - - else - //This way we only call the stuff below once every [sleep_max] ticks. - parrot_sleep_dur = parrot_sleep_max - - //Cycle through message modes for the headset - update_speak() - - //Search for item to steal - parrot_interest = search_for_item() - if(parrot_interest) - custom_emote(1,"looks in [parrot_interest]'s direction and takes flight.") - parrot_state = PARROT_SWOOP | PARROT_STEAL - icon_state = "parrot_fly" - return - -//-----WANDERING - This is basically a 'I dont know what to do yet' state - else if(parrot_state == PARROT_WANDER) - //Stop movement, we'll set it later - walk(src, 0) - parrot_interest = null - - //Wander around aimlessly. This will help keep the loops from searches down - //and possibly move the mob into a new are in view of something they can use - if(prob(90)) - step(src, pick(cardinal)) - return - - if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do. - var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item - if(AM) - if(istype(AM, /obj/item) || isliving(AM)) //If stealable item - parrot_interest = AM - custom_emote(1,"turns and flies towards [parrot_interest]") - parrot_state = PARROT_SWOOP | PARROT_STEAL - return - else //Else it's a perch - parrot_perch = AM - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - return - - if(parrot_interest && parrot_interest in view(src)) - parrot_state = PARROT_SWOOP | PARROT_STEAL - return - - if(parrot_perch && parrot_perch in view(src)) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - else //Have an item but no perch? Find one! - parrot_perch = search_for_perch() - if(parrot_perch) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return -//-----STEALING - else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL)) - walk(src,0) - if(!parrot_interest || held_item) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - if(!(parrot_interest in view(src))) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - if(in_range(src, parrot_interest)) - - if(isliving(parrot_interest)) - steal_from_mob() - - else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch - if(!parrot_perch || parrot_interest.loc != parrot_perch.loc) - held_item = parrot_interest - parrot_interest.loc = src - visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") - - parrot_interest = null - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - walk_to(src, parrot_interest, 1, parrot_speed) - return - -//-----RETURNING TO PERCH - else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN)) - walk(src, 0) - if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else. - parrot_perch = null - parrot_state = PARROT_WANDER - return - - if(in_range(src, parrot_perch)) - src.loc = parrot_perch.loc - drop_held_item() - parrot_state = PARROT_PERCH - icon_state = "parrot_sit" - return - - walk_to(src, parrot_perch, 1, parrot_speed) - return - -//-----FLEEING - else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE)) - walk(src,0) - if(!parrot_interest || !isliving(parrot_interest)) //Sanity - parrot_state = PARROT_WANDER - - walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot) - parrot_been_shot-- - return - -//-----ATTACKING - else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK)) - - //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander - if(!parrot_interest || !isliving(parrot_interest)) - parrot_interest = null - parrot_state = PARROT_WANDER - return - - var/mob/living/L = parrot_interest - - //If the mob is close enough to interact with - if(in_range(src, parrot_interest)) - - //If the mob we've been chasing/attacking dies or falls into crit, check for loot! - if(L.stat) - parrot_interest = null - if(!held_item) - held_item = steal_from_ground() - if(!held_item) - held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances. - if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home - parrot_state = PARROT_SWOOP | PARROT_RETURN - else - parrot_state = PARROT_WANDER - return - - //Time for the hurt to begin! - var/damage = rand(5,10) - - if(ishuman(parrot_interest)) - var/mob/living/carbon/human/H = parrot_interest - var/obj/item/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone))) - - H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp = 1) - custom_emote(1, pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons.")) - - else - L.adjustBruteLoss(damage) - custom_emote(1, pick("pecks at [L].", "claws [L].")) - return - - //Otherwise, fly towards the mob! - else - walk_to(src, parrot_interest, 1, parrot_speed) - return -//-----STATE MISHAP - else //This should not happen. If it does lets reset everything and try again - walk(src,0) - parrot_interest = null - parrot_perch = null - drop_held_item() - parrot_state = PARROT_WANDER - return - -/* - * Procs - */ - -/mob/living/simple_animal/parrot/movement_delay() - if(client && stat == CONSCIOUS && parrot_state != "parrot_fly") - icon_state = "parrot_fly" - //Because the most appropriate place to set icon_state is movement_delay(), clearly - return ..() - -/mob/living/simple_animal/parrot/proc/search_for_item() - for(var/atom/movable/AM in view(src)) - //Skip items we already stole or are wearing or are too big - if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) - continue - - if(istype(AM, /obj/item)) - var/obj/item/I = AM - if(I.w_class < WEIGHT_CLASS_SMALL) - return I - - if(iscarbon(AM)) - var/mob/living/carbon/C = AM - if((C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) || (C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL)) - return C - return null - -/mob/living/simple_animal/parrot/proc/search_for_perch() - for(var/obj/O in view(src)) - for(var/path in desired_perches) - if(istype(O, path)) - return O - return null - -//This proc was made to save on doing two 'in view' loops seperatly -/mob/living/simple_animal/parrot/proc/search_for_perch_and_item() - for(var/atom/movable/AM in view(src)) - for(var/perch_path in desired_perches) - if(istype(AM, perch_path)) - return AM - - //Skip items we already stole or are wearing or are too big - if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) - continue - - if(istype(AM, /obj/item)) - var/obj/item/I = AM - if(I.w_class <= WEIGHT_CLASS_SMALL) - return I - - if(iscarbon(AM)) - var/mob/living/carbon/C = AM - if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL || C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) - return C - return null - - -/* - * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots. - */ -/mob/living/simple_animal/parrot/proc/steal_from_ground() - set name = "Steal from ground" - set category = "Parrot" - set desc = "Grabs a nearby item." - - if(stat) - return -1 - - if(held_item) - to_chat(src, "You are already holding the [held_item]") - return 1 - - for(var/obj/item/I in view(1,src)) - //Make sure we're not already holding it and it's small enough - if(I.loc != src && I.w_class <= WEIGHT_CLASS_SMALL) - - //If we have a perch and the item is sitting on it, continue - if(!client && parrot_perch && I.loc == parrot_perch.loc) - continue - - held_item = I - I.loc = src - visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") - return held_item - - to_chat(src, "There is nothing of interest to take.") - return 0 - -/mob/living/simple_animal/parrot/proc/steal_from_mob() - set name = "Steal from mob" - set category = "Parrot" - set desc = "Steals an item right out of a person's hand!" - - if(stat) - return -1 - - if(held_item) - to_chat(src, "You are already holding the [held_item]") - return 1 - - var/obj/item/stolen_item = null - - for(var/mob/living/carbon/C in view(1,src)) - if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) - stolen_item = C.l_hand - - if(C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) - stolen_item = C.r_hand - - if(stolen_item) - C.unEquip(stolen_item) - held_item = stolen_item - stolen_item.loc = src - visible_message("[src] grabs the [held_item] out of [C]'s hand!", "You snag the [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.") - return held_item - - to_chat(src, "There is nothing of interest to take.") - return 0 - -/mob/living/simple_animal/parrot/verb/drop_held_item_player() - set name = "Drop held item" - set category = "Parrot" - set desc = "Drop the item you're holding." - - if(stat) - return - - src.drop_held_item() - - return - -/mob/living/simple_animal/parrot/proc/drop_held_item(var/drop_gently = 1) - set name = "Drop held item" - set category = "Parrot" - set desc = "Drop the item you're holding." - - if(stat) - return -1 - - if(!held_item) - to_chat(src, "You have nothing to drop!") - return 0 - - if(!drop_gently) - if(istype(held_item, /obj/item/grenade)) - var/obj/item/grenade/G = held_item - G.loc = src.loc - G.prime() - to_chat(src, "You let go of the [held_item]!") - held_item = null - return 1 - - to_chat(src, "You drop the [held_item].") - - held_item.loc = src.loc - held_item = null - return 1 - -/mob/living/simple_animal/parrot/proc/perch_player() - set name = "Sit" - set category = "Parrot" - set desc = "Sit on a nice comfy perch." - - if(stat || !client) - return - - if(icon_state == "parrot_fly") - for(var/atom/movable/AM in view(src,1)) - for(var/perch_path in desired_perches) - if(istype(AM, perch_path)) - src.loc = AM.loc - icon_state = "parrot_sit" - return - to_chat(src, "There is no perch nearby to sit on.") - return - -/* - * Sub-types - */ -/mob/living/simple_animal/parrot/Poly - name = "Poly" - desc = "Poly the Parrot. An expert on quantum cracker theory." - clean_speak = list( - "Poly wanna cracker!", - "Check the singlo, you chucklefucks!", - "Check the tesla, you shits!", - "STOP HOT-WIRING THE ENGINE, FUCKING CHRIST!", - "Wire the solars, you lazy bums!", - "WHO TOOK THE DAMN HARDSUITS?", - "OH GOD ITS FREE CALL THE SHUTTLE", - "Why are there so many atmos alerts?", - "OH GOD WHY WOULD YOU TURN ON THE PA BEFORE CONTAINMENT IS UP?", - "Remember to lock the emitters!", - "Stop goofing off and repair the goddam station!", - "The singularity is not your friend!", - "What were the wires again?", - "Goddam emaggers!" - ) - unique_pet = TRUE - gold_core_spawnable = NO_SPAWN - -/mob/living/simple_animal/parrot/Poly/New() - ears = new /obj/item/radio/headset/headset_eng(src) - available_channels = list(":e") - ..() - -/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) - if(message_mode && istype(ears)) - ears.talk_into(src, message_pieces, message_mode, verb) - used_radios += ears - -/mob/living/simple_animal/parrot/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null) - if(speaker != src && prob(50)) - parrot_hear(html_decode(multilingual_to_message(message_pieces))) - ..() - - - -/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target) - if(speaker != src && prob(50)) - parrot_hear(html_decode(multilingual_to_message(message_pieces))) - ..() - - -/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="") - if(!message || stat) - return - speech_buffer.Add(message) +/* Parrots! + * Contains + * Defines + * Inventory (headset stuff) + * Attack responces + * AI + * Procs / Verbs (usable by players) + * Sub-types + */ + +/* + * Defines + */ + +//Only a maximum of one action and one intent should be active at any given time. +//Actions +#define PARROT_PERCH 1 //Sitting/sleeping, not moving +#define PARROT_SWOOP 2 //Moving towards or away from a target +#define PARROT_WANDER 4 //Moving without a specific target in mind + +//Intents +#define PARROT_STEAL 8 //Flying towards a target to steal it/from it +#define PARROT_ATTACK 16 //Flying towards a target to attack it +#define PARROT_RETURN 32 //Flying towards its perch +#define PARROT_FLEE 64 //Flying away from its attacker + + +/mob/living/simple_animal/parrot + name = "\improper Parrot" + desc = "The parrot squaks, \"It's a Parrot! BAWWK!\"" + icon = 'icons/mob/animal.dmi' + icon_state = "parrot_fly" + icon_living = "parrot_fly" + icon_dead = "parrot_dead" + pass_flags = PASSTABLE + can_collar = 1 + + var/list/clean_speak = list( + "Hi", + "Hello!", + "Cracker?", + "BAWWWWK george mellons griffing me") + speak_emote = list("squawks","says","yells") + emote_hear = list("squawks","bawks") + emote_see = list("flutters its wings") + + speak_chance = 1//1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s + turns_per_move = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/cracker = 3) + + response_help = "pets the" + response_disarm = "gently moves aside the" + response_harm = "swats the" + stop_automated_movement = 1 + universal_speak = 1 + mob_size = MOB_SIZE_SMALL + + var/parrot_state = PARROT_WANDER //Hunt for a perch when created + var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in process_ai() being run every single tick. + var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down + var/parrot_dam_zone = list("chest", "head", "l_arm", "l_leg", "r_arm", "r_leg") //For humans, select a bodypart to attack + + var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. + var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every process_ai() and at 0 the parrot will return to regular speed. + + var/list/speech_buffer = list() + var/list/available_channels = list() + + //Headset for Poly to yell at engineers :) + var/obj/item/radio/headset/ears = null + + //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, + //mobs it wants to attack or mobs that have attacked it + var/atom/movable/parrot_interest = null + + //Parrots will generally sit on their pertch unless something catches their eye. + //These vars store their preffered perch and if they dont have one, what they can use as a perch + var/obj/parrot_perch = null + var/obj/desired_perches = list(/obj/structure/computerframe, /obj/structure/displaycase, \ + /obj/structure/filingcabinet, /obj/machinery/teleport, \ + /obj/machinery/suit_storage_unit, /obj/machinery/clonepod, \ + /obj/machinery/dna_scannernew, /obj/machinery/telecomms, \ + /obj/machinery/nuclearbomb, /obj/machinery/particle_accelerator, \ + /obj/machinery/recharge_station, /obj/machinery/smartfridge, \ + /obj/machinery/computer) + + //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. + var/obj/item/held_item = null + gold_core_spawnable = FRIENDLY_SPAWN + + +/mob/living/simple_animal/parrot/New() + ..() + GLOB.hear_radio_list += src + if(!ears) + var/headset = pick(/obj/item/radio/headset/headset_sec, \ + /obj/item/radio/headset/headset_eng, \ + /obj/item/radio/headset/headset_med, \ + /obj/item/radio/headset/headset_sci, \ + /obj/item/radio/headset/headset_cargo) + ears = new headset(src) + update_speak() + + parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var + + verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \ + /mob/living/simple_animal/parrot/proc/steal_from_mob, \ + /mob/living/simple_animal/parrot/verb/drop_held_item_player, \ + /mob/living/simple_animal/parrot/proc/perch_player) + +/mob/living/simple_animal/parrot/Destroy() + GLOB.hear_radio_list -= src + return ..() + +/mob/living/simple_animal/parrot/death(gibbed) + if(can_die()) + if(held_item) + held_item.loc = src.loc + held_item = null + walk(src,0) + return ..() + +/mob/living/simple_animal/parrot/Stat() + ..() + stat("Held Item", held_item) + +/* + * Inventory + */ +/mob/living/simple_animal/parrot/show_inv(mob/user as mob) + user.set_machine(src) + + var/dat = {""} + + dat += "" + if(can_collar) + dat += "" + dat += "" + + dat += {"
    Headset:[(ears && !(ears.flags&ABSTRACT)) ? ears : "Empty"]
     
    Collar:[(pcollar && !(pcollar.flags&ABSTRACT)) ? pcollar : "Empty"]
    + Close + "} + + var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 500) + popup.set_content(dat) + popup.open() + +/mob/living/simple_animal/parrot/Topic(href, href_list) + + //Can the usr physically do this? + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return + + //Is the usr's mob type able to do this? + if(ishuman(usr) || isrobot(usr)) + if(href_list["remove_inv"]) + var/remove_from = href_list["remove_inv"] + switch(remove_from) + if("ears") + if(ears) + if(available_channels.len) + say("[pick(available_channels)]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + else + say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + ears.forceMove(loc) + ears = null + update_speak() + else + to_chat(usr, "There is nothing to remove from its [remove_from].") + return + show_inv(usr) + else if(href_list["add_inv"]) + var/add_to = href_list["add_inv"] + if(!usr.get_active_hand()) + to_chat(usr, "You have nothing in your hand to put on its [add_to].") + return + switch(add_to) + if("ears") + if(ears) + to_chat(usr, "It's already wearing something.") + return + else + var/obj/item/item_to_add = usr.get_active_hand() + if(!item_to_add) + return + + if( !istype(item_to_add, /obj/item/radio/headset) ) + to_chat(usr, "This object won't fit.") + return + + var/obj/item/radio/headset/headset_to_add = item_to_add + + usr.drop_item() + headset_to_add.forceMove(src) + ears = headset_to_add + to_chat(usr, "You fit the headset onto [src].") + + available_channels.Cut() + for(var/ch in headset_to_add.channels) + switch(ch) + if("Engineering") + available_channels.Add(":e") + if("Command") + available_channels.Add(":c") + if("Security") + available_channels.Add(":s") + if("Science") + available_channels.Add(":n") + if("Medical") + available_channels.Add(":m") + if("Mining") + available_channels.Add(":d") + if("Cargo") + available_channels.Add(":q") + + if(headset_to_add.translate_binary) + available_channels.Add(":b") + update_speak() + show_inv(usr) + else + ..() + + +/* + * Attack responces + */ +//Humans, monkeys, aliens +/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob) + ..() + if(client) return + if(!stat && M.a_intent == "hurt") + + icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks + + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + parrot_interest = M + parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction.. + + if(M.health < 50) //Weakened mob? Fight back! + parrot_state |= PARROT_ATTACK + else + parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! + drop_held_item(0) + return + +//Mobs with objects +/mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + ..() + if(!stat && !client && !istype(O, /obj/item/stack/medical)) + if(O.force) + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + parrot_interest = user + parrot_state = PARROT_SWOOP | PARROT_FLEE + icon_state = "parrot_fly" + drop_held_item(0) + return + +//Bullets +/mob/living/simple_animal/parrot/bullet_act(var/obj/item/projectile/Proj) + ..() + if(!stat && !client) + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + parrot_interest = null + parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL! + parrot_been_shot += 5 + icon_state = "parrot_fly" + drop_held_item(0) + return + + +/* + * AI - Not really intelligent, but I'm calling it AI anyway. + */ +/mob/living/simple_animal/parrot/Life(seconds, times_fired) + ..() + + //Sprite and AI update for when a parrot gets pulled + if(pulledby && stat == CONSCIOUS) + icon_state = "parrot_fly" + +/mob/living/simple_animal/parrot/proc/update_speak() + speak.Cut() + + if(available_channels.len && ears) + for(var/possible_phrase in clean_speak) + //50/50 chance to not use the radio at all + speak += "[prob(50) ? pick(available_channels) : ""][possible_phrase]" + + else //If we have no headset or channels to use, dont try to use any! + for(var/possible_phrase in clean_speak) + speak += possible_phrase + +/mob/living/simple_animal/parrot/handle_automated_movement() + if(pulledby) + parrot_state = PARROT_WANDER + return + + if(!isturf(src.loc) || !canmove || buckled) + return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove) + + +//-----SPEECH + /* Parrot speech mimickry! + Phrases that the parrot hears in mob/living/say() get added to speach_buffer. + Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. + Then it clears the buffer to make sure they dont magically remember something from hours ago. */ + if(speech_buffer.len && prob(10)) + if(clean_speak.len) + clean_speak -= pick(clean_speak) + + clean_speak += pick(speech_buffer) + speech_buffer.Cut() + + +//-----SLEEPING + if(parrot_state == PARROT_PERCH) + if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us + if(parrot_perch in view(src)) + parrot_state = PARROT_SWOOP | PARROT_RETURN + icon_state = "parrot_fly" + return + else + parrot_state = PARROT_WANDER + icon_state = "parrot_fly" + return + + if(--parrot_sleep_dur) //Zzz + return + + else + //This way we only call the stuff below once every [sleep_max] ticks. + parrot_sleep_dur = parrot_sleep_max + + //Cycle through message modes for the headset + update_speak() + + //Search for item to steal + parrot_interest = search_for_item() + if(parrot_interest) + custom_emote(1,"looks in [parrot_interest]'s direction and takes flight.") + parrot_state = PARROT_SWOOP | PARROT_STEAL + icon_state = "parrot_fly" + return + +//-----WANDERING - This is basically a 'I dont know what to do yet' state + else if(parrot_state == PARROT_WANDER) + //Stop movement, we'll set it later + walk(src, 0) + parrot_interest = null + + //Wander around aimlessly. This will help keep the loops from searches down + //and possibly move the mob into a new are in view of something they can use + if(prob(90)) + step(src, pick(GLOB.cardinal)) + return + + if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do. + var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item + if(AM) + if(istype(AM, /obj/item) || isliving(AM)) //If stealable item + parrot_interest = AM + custom_emote(1,"turns and flies towards [parrot_interest]") + parrot_state = PARROT_SWOOP | PARROT_STEAL + return + else //Else it's a perch + parrot_perch = AM + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + return + + if(parrot_interest && (parrot_interest in view(src))) + parrot_state = PARROT_SWOOP | PARROT_STEAL + return + + if(parrot_perch && (parrot_perch in view(src))) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + else //Have an item but no perch? Find one! + parrot_perch = search_for_perch() + if(parrot_perch) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return +//-----STEALING + else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL)) + walk(src,0) + if(!parrot_interest || held_item) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + if(!(parrot_interest in view(src))) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + if(in_range(src, parrot_interest)) + + if(isliving(parrot_interest)) + steal_from_mob() + + else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch + if(!parrot_perch || parrot_interest.loc != parrot_perch.loc) + held_item = parrot_interest + parrot_interest.loc = src + visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") + + parrot_interest = null + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + walk_to(src, parrot_interest, 1, parrot_speed) + return + +//-----RETURNING TO PERCH + else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN)) + walk(src, 0) + if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else. + parrot_perch = null + parrot_state = PARROT_WANDER + return + + if(in_range(src, parrot_perch)) + src.loc = parrot_perch.loc + drop_held_item() + parrot_state = PARROT_PERCH + icon_state = "parrot_sit" + return + + walk_to(src, parrot_perch, 1, parrot_speed) + return + +//-----FLEEING + else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE)) + walk(src,0) + if(!parrot_interest || !isliving(parrot_interest)) //Sanity + parrot_state = PARROT_WANDER + + walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot) + parrot_been_shot-- + return + +//-----ATTACKING + else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK)) + + //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander + if(!parrot_interest || !isliving(parrot_interest)) + parrot_interest = null + parrot_state = PARROT_WANDER + return + + var/mob/living/L = parrot_interest + + //If the mob is close enough to interact with + if(in_range(src, parrot_interest)) + + //If the mob we've been chasing/attacking dies or falls into crit, check for loot! + if(L.stat) + parrot_interest = null + if(!held_item) + held_item = steal_from_ground() + if(!held_item) + held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances. + if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home + parrot_state = PARROT_SWOOP | PARROT_RETURN + else + parrot_state = PARROT_WANDER + return + + //Time for the hurt to begin! + var/damage = rand(5,10) + + if(ishuman(parrot_interest)) + var/mob/living/carbon/human/H = parrot_interest + var/obj/item/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone))) + + H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp = 1) + custom_emote(1, pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons.")) + + else + L.adjustBruteLoss(damage) + custom_emote(1, pick("pecks at [L].", "claws [L].")) + return + + //Otherwise, fly towards the mob! + else + walk_to(src, parrot_interest, 1, parrot_speed) + return +//-----STATE MISHAP + else //This should not happen. If it does lets reset everything and try again + walk(src,0) + parrot_interest = null + parrot_perch = null + drop_held_item() + parrot_state = PARROT_WANDER + return + +/* + * Procs + */ + +/mob/living/simple_animal/parrot/movement_delay() + if(client && stat == CONSCIOUS && parrot_state != "parrot_fly") + icon_state = "parrot_fly" + //Because the most appropriate place to set icon_state is movement_delay(), clearly + return ..() + +/mob/living/simple_animal/parrot/proc/search_for_item() + for(var/atom/movable/AM in view(src)) + //Skip items we already stole or are wearing or are too big + if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) + continue + + if(istype(AM, /obj/item)) + var/obj/item/I = AM + if(I.w_class < WEIGHT_CLASS_SMALL) + return I + + if(iscarbon(AM)) + var/mob/living/carbon/C = AM + if((C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) || (C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL)) + return C + return null + +/mob/living/simple_animal/parrot/proc/search_for_perch() + for(var/obj/O in view(src)) + for(var/path in desired_perches) + if(istype(O, path)) + return O + return null + +//This proc was made to save on doing two 'in view' loops seperatly +/mob/living/simple_animal/parrot/proc/search_for_perch_and_item() + for(var/atom/movable/AM in view(src)) + for(var/perch_path in desired_perches) + if(istype(AM, perch_path)) + return AM + + //Skip items we already stole or are wearing or are too big + if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) + continue + + if(istype(AM, /obj/item)) + var/obj/item/I = AM + if(I.w_class <= WEIGHT_CLASS_SMALL) + return I + + if(iscarbon(AM)) + var/mob/living/carbon/C = AM + if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL || C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) + return C + return null + + +/* + * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots. + */ +/mob/living/simple_animal/parrot/proc/steal_from_ground() + set name = "Steal from ground" + set category = "Parrot" + set desc = "Grabs a nearby item." + + if(stat) + return -1 + + if(held_item) + to_chat(src, "You are already holding the [held_item]") + return 1 + + for(var/obj/item/I in view(1,src)) + //Make sure we're not already holding it and it's small enough + if(I.loc != src && I.w_class <= WEIGHT_CLASS_SMALL) + + //If we have a perch and the item is sitting on it, continue + if(!client && parrot_perch && I.loc == parrot_perch.loc) + continue + + held_item = I + I.loc = src + visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") + return held_item + + to_chat(src, "There is nothing of interest to take.") + return 0 + +/mob/living/simple_animal/parrot/proc/steal_from_mob() + set name = "Steal from mob" + set category = "Parrot" + set desc = "Steals an item right out of a person's hand!" + + if(stat) + return -1 + + if(held_item) + to_chat(src, "You are already holding the [held_item]") + return 1 + + var/obj/item/stolen_item = null + + for(var/mob/living/carbon/C in view(1,src)) + if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) + stolen_item = C.l_hand + + if(C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) + stolen_item = C.r_hand + + if(stolen_item) + C.unEquip(stolen_item) + held_item = stolen_item + stolen_item.loc = src + visible_message("[src] grabs the [held_item] out of [C]'s hand!", "You snag the [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.") + return held_item + + to_chat(src, "There is nothing of interest to take.") + return 0 + +/mob/living/simple_animal/parrot/verb/drop_held_item_player() + set name = "Drop held item" + set category = "Parrot" + set desc = "Drop the item you're holding." + + if(stat) + return + + src.drop_held_item() + + return + +/mob/living/simple_animal/parrot/proc/drop_held_item(var/drop_gently = 1) + set name = "Drop held item" + set category = "Parrot" + set desc = "Drop the item you're holding." + + if(stat) + return -1 + + if(!held_item) + to_chat(src, "You have nothing to drop!") + return 0 + + if(!drop_gently) + if(istype(held_item, /obj/item/grenade)) + var/obj/item/grenade/G = held_item + G.loc = src.loc + G.prime() + to_chat(src, "You let go of the [held_item]!") + held_item = null + return 1 + + to_chat(src, "You drop the [held_item].") + + held_item.loc = src.loc + held_item = null + return 1 + +/mob/living/simple_animal/parrot/proc/perch_player() + set name = "Sit" + set category = "Parrot" + set desc = "Sit on a nice comfy perch." + + if(stat || !client) + return + + if(icon_state == "parrot_fly") + for(var/atom/movable/AM in view(src,1)) + for(var/perch_path in desired_perches) + if(istype(AM, perch_path)) + src.loc = AM.loc + icon_state = "parrot_sit" + return + to_chat(src, "There is no perch nearby to sit on.") + return + +/* + * Sub-types + */ +/mob/living/simple_animal/parrot/Poly + name = "Poly" + desc = "Poly the Parrot. An expert on quantum cracker theory." + clean_speak = list( + "Poly wanna cracker!", + "Check the singlo, you chucklefucks!", + "Check the tesla, you shits!", + "STOP HOT-WIRING THE ENGINE, FUCKING CHRIST!", + "Wire the solars, you lazy bums!", + "WHO TOOK THE DAMN HARDSUITS?", + "OH GOD ITS FREE CALL THE SHUTTLE", + "Why are there so many atmos alerts?", + "OH GOD WHY WOULD YOU TURN ON THE PA BEFORE CONTAINMENT IS UP?", + "Remember to lock the emitters!", + "Stop goofing off and repair the goddam station!", + "The singularity is not your friend!", + "What were the wires again?", + "Goddam emaggers!" + ) + unique_pet = TRUE + gold_core_spawnable = NO_SPAWN + +/mob/living/simple_animal/parrot/Poly/New() + ears = new /obj/item/radio/headset/headset_eng(src) + available_channels = list(":e") + ..() + +/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) + if(message_mode && istype(ears)) + ears.talk_into(src, message_pieces, message_mode, verb) + used_radios += ears + +/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) + if(speaker != src && prob(50)) + parrot_hear(html_decode(multilingual_to_message(message_pieces))) + ..() + + + +/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target) + if(speaker != src && prob(50)) + parrot_hear(html_decode(multilingual_to_message(message_pieces))) + ..() + + +/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="") + if(!message || stat) + return + speech_buffer.Add(message) diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 948c16314c9..663838d3a45 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -1,61 +1,61 @@ -/mob/living/simple_animal/shade - name = "Shade" - real_name = "Shade" - desc = "A bound spirit" - icon = 'icons/mob/mob.dmi' - icon_state = "shade" - icon_living = "shade" - icon_dead = "shade_dead" - maxHealth = 50 - health = 50 - speak_emote = list("hisses") - emote_hear = list("wails","screeches") - response_help = "puts their hand through" - response_disarm = "flails at" - response_harm = "punches the" - melee_damage_lower = 5 - melee_damage_upper = 15 - attacktext = "drains the life from" - minbodytemp = 0 - maxbodytemp = 4000 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - speed = -1 - stop_automated_movement = 1 - status_flags = 0 - faction = list("cult") - status_flags = CANPUSH - loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) - del_on_death = 1 - deathmessage = "lets out a contented sigh as their form unwinds." - -/mob/living/simple_animal/shade/death(gibbed) - . = ..() - SSticker.mode.remove_cultist(src.mind, FALSE) - -/mob/living/simple_animal/shade/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri - if(istype(O, /obj/item/soulstone)) - O.transfer_soul("SHADE", src, user) - else - if(O.force) - var/damage = O.force - if(O.damtype == STAMINA) - damage = 0 - health -= damage - user.visible_message("[src] has been attacked with the [O] by [user]. ") - else - user.visible_message("[user] gently taps [src] with the [O]. ", "This weapon is ineffective, it does no damage.") - return - -/mob/living/simple_animal/shade/cult/Initialize(mapload) - . = ..() - name = SSticker.cultdat?.shade_name - real_name = SSticker.cultdat?.shade_name - icon_state = SSticker.cultdat?.shade_icon_state - -/mob/living/simple_animal/shade/sword - universal_speak = 1 - faction = list("neutral") - -/mob/living/simple_animal/shade/sword/Initialize(mapload) - .=..() - status_flags |= GODMODE \ No newline at end of file +/mob/living/simple_animal/shade + name = "Shade" + real_name = "Shade" + desc = "A bound spirit" + icon = 'icons/mob/mob.dmi' + icon_state = "shade" + icon_living = "shade" + icon_dead = "shade_dead" + maxHealth = 50 + health = 50 + speak_emote = list("hisses") + emote_hear = list("wails","screeches") + response_help = "puts their hand through" + response_disarm = "flails at" + response_harm = "punches the" + melee_damage_lower = 5 + melee_damage_upper = 15 + attacktext = "drains the life from" + minbodytemp = 0 + maxbodytemp = 4000 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + speed = -1 + stop_automated_movement = 1 + status_flags = 0 + faction = list("cult") + status_flags = CANPUSH + loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) + del_on_death = 1 + deathmessage = "lets out a contented sigh as their form unwinds." + +/mob/living/simple_animal/shade/death(gibbed) + . = ..() + SSticker.mode.remove_cultist(src.mind, FALSE) + +/mob/living/simple_animal/shade/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri + if(istype(O, /obj/item/soulstone)) + O.transfer_soul("SHADE", src, user) + else + if(O.force) + var/damage = O.force + if(O.damtype == STAMINA) + damage = 0 + health -= damage + user.visible_message("[src] has been attacked with the [O] by [user]. ") + else + user.visible_message("[user] gently taps [src] with the [O]. ", "This weapon is ineffective, it does no damage.") + return + +/mob/living/simple_animal/shade/cult/Initialize(mapload) + . = ..() + name = SSticker.cultdat?.shade_name + real_name = SSticker.cultdat?.shade_name + icon_state = SSticker.cultdat?.shade_icon_state + +/mob/living/simple_animal/shade/sword + universal_speak = 1 + faction = list("neutral") + +/mob/living/simple_animal/shade/sword/Initialize(mapload) + .=..() + status_flags |= GODMODE diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 01c1f7cfe43..2479f3c0674 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -1,615 +1,615 @@ -/mob/living/simple_animal - name = "animal" - icon = 'icons/mob/animal.dmi' - health = 20 - maxHealth = 20 - gender = PLURAL //placeholder - - universal_understand = 1 - universal_speak = 0 - status_flags = CANPUSH - - var/icon_living = "" - var/icon_dead = "" - var/icon_resting = "" - var/icon_gib = null //We only try to show a gibbing animation if this exists. - var/flip_on_death = FALSE //Flip the sprite upside down on death. Mostly here for things lacking custom dead sprites. - - var/list/speak = list() - var/speak_chance = 0 - var/list/emote_hear = list() //Hearable emotes - var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps - - var/turns_per_move = 1 - var/turns_since_move = 0 - var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. - var/wander = 1 // Does the mob wander around when idle? - var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. - - //Interaction - var/response_help = "pokes" - var/response_disarm = "shoves" - var/response_harm = "hits" - var/harm_intent_damage = 3 - var/force_threshold = 0 //Minimum force required to deal any damage - - //Temperature effect - var/minbodytemp = 250 - var/maxbodytemp = 350 - var/heat_damage_per_tick = 2 //amount of damage applied if animal's body temperature is higher than maxbodytemp - var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - - //Healable by medical stacks? Defaults to yes. - var/healable = 1 - - //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage - var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum - var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above - - //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly - var/melee_damage_lower = 0 - var/melee_damage_upper = 0 - var/obj_damage = 0 //how much damage this simple animal does to objects, if any - var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value - var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. - var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source - var/attacktext = "attacks" - var/attack_sound = null - var/friendly = "nuzzles" //If the mob does no damage with it's attack - var/environment_smash = ENVIRONMENT_SMASH_NONE //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls - - var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster - var/can_hide = 0 - - var/obj/item/clothing/accessory/petcollar/pcollar = null - var/collar_type //if the mob has collar sprites, define them. - var/unique_pet = FALSE // if the mob can be renamed - var/can_collar = FALSE // can add collar to mob or not - - //Hot simple_animal baby making vars - var/list/childtype = null - var/next_scan_time = 0 - var/animal_species //Sorry, no spider+corgi buttbabies. - - var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against - var/gold_core_spawnable = NO_SPAWN //If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood - - var/mob/living/carbon/human/master_commander = null //holding var for determining who own/controls a sentient simple animal (for sentience potions). - - var/datum/component/spawner/nest - - var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions - - var/list/loot = list() //list of things spawned at mob's loc when it dies - var/del_on_death = 0 //causes mob to be deleted on death, useful for mobs that spawn lootable corpses - var/deathmessage = "" - var/death_sound = null //The sound played on death - - var/allow_movement_on_non_turfs = FALSE - - var/attacked_sound = "punch" - - var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) - var/can_have_ai = TRUE //once we have become sentient, we can never go back - - var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. - - //domestication - var/tame = 0 - - var/my_z // I don't want to confuse this with client registered_z - -/mob/living/simple_animal/Initialize(mapload) - . = ..() - GLOB.simple_animals[AIStatus] += src - if(gender == PLURAL) - gender = pick(MALE, FEMALE) - if(!real_name) - real_name = name - if(!loc) - stack_trace("Simple animal being instantiated in nullspace") - verbs -= /mob/verb/observe - if(!can_hide) - verbs -= /mob/living/simple_animal/verb/hide - if(pcollar) - pcollar = new(src) - regenerate_icons() - -/mob/living/simple_animal/Destroy() - QDEL_NULL(pcollar) - master_commander = null - GLOB.simple_animals[AIStatus] -= src - if(SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) - SSnpcpool.currentrun -= src - - if(nest) - nest.spawned_mobs -= src - nest = null - - var/turf/T = get_turf(src) - if (T && AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - - return ..() - -/mob/living/simple_animal/handle_atom_del(atom/A) - if(A == pcollar) - pcollar = null - return ..() - -/mob/living/simple_animal/examine(mob/user) - . = ..() - if(stat == DEAD) - . += "Upon closer examination, [p_they()] appear[p_s()] to be dead." - -/mob/living/simple_animal/updatehealth(reason = "none given") - ..(reason) - health = Clamp(health, 0, maxHealth) - med_hud_set_status() - -/mob/living/simple_animal/StartResting(updating = 1) - ..() - if(icon_resting && stat != DEAD) - icon_state = icon_resting - if(collar_type) - collar_type = "[initial(collar_type)]_rest" - regenerate_icons() - -/mob/living/simple_animal/StopResting(updating = 1) - ..() - if(icon_resting && stat != DEAD) - icon_state = icon_living - if(collar_type) - collar_type = "[initial(collar_type)]" - regenerate_icons() - -/mob/living/simple_animal/update_stat(reason = "none given") - if(status_flags & GODMODE) - return - - ..(reason) - if(stat != DEAD) - if(health < 1) - death() - create_debug_log("died of damage, trigger reason: [reason]") - -/mob/living/simple_animal/proc/handle_automated_action() - set waitfor = FALSE - return - -/mob/living/simple_animal/proc/handle_automated_movement() - set waitfor = FALSE - if(!stop_automated_movement && wander) - if((isturf(loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - if(!(stop_automated_movement_when_pulled && pulledby)) //Soma animals don't move when pulled - var/anydir = pick(cardinal) - if(Process_Spacemove(anydir)) - Move(get_step(src,anydir), anydir) - turns_since_move = 0 - return 1 - -/mob/living/simple_animal/proc/handle_automated_speech(override) - set waitfor = FALSE - if(speak_chance) - if(prob(speak_chance) || override) - if(speak && speak.len) - if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) - var/length = speak.len - if(emote_hear && emote_hear.len) - length += emote_hear.len - if(emote_see && emote_see.len) - length += emote_see.len - var/randomValue = rand(1,length) - if(randomValue <= speak.len) - say(pick(speak)) - else - randomValue -= speak.len - if(emote_see && randomValue <= emote_see.len) - custom_emote(1, pick(emote_see)) - else - custom_emote(2, pick(emote_hear)) - else - say(pick(speak)) - else - if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - custom_emote(1, pick(emote_see)) - if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) - custom_emote(2, pick(emote_hear)) - if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - var/length = emote_hear.len + emote_see.len - var/pick = rand(1,length) - if(pick <= emote_see.len) - custom_emote(1, pick(emote_see)) - else - custom_emote(2,pick(emote_hear)) - - -/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) - var/atmos_suitable = 1 - - var/areatemp = get_temperature(environment) - - if(abs(areatemp - bodytemperature) > 5 && !(BREATHLESS in mutations)) - var/diff = areatemp - bodytemperature - diff = diff / 5 - bodytemperature += diff - - var/tox = environment.toxins - var/oxy = environment.oxygen - var/n2 = environment.nitrogen - var/co2 = environment.carbon_dioxide - - if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) - atmos_suitable = 0 - throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) - else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) - atmos_suitable = 0 - throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy) - else - clear_alert("not_enough_oxy") - clear_alert("too_much_oxy") - - if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) - atmos_suitable = 0 - throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) - else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) - atmos_suitable = 0 - throw_alert("too_much_tox", /obj/screen/alert/too_much_tox) - else - clear_alert("too_much_tox") - clear_alert("not_enough_tox") - - if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) - atmos_suitable = 0 - - if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) - atmos_suitable = 0 - - if(!atmos_suitable) - adjustHealth(unsuitable_atmos_damage) - - handle_temperature_damage() - -/mob/living/simple_animal/proc/handle_temperature_damage() - if(bodytemperature < minbodytemp) - adjustHealth(cold_damage_per_tick) - else if(bodytemperature > maxbodytemp) - adjustHealth(heat_damage_per_tick) - -/mob/living/simple_animal/gib() - if(icon_gib) - flick(icon_gib, src) - if(butcher_results) - var/atom/Tsec = drop_location() - for(var/path in butcher_results) - for(var/i in 1 to butcher_results[path]) - new path(Tsec) - if(pcollar) - pcollar.forceMove(drop_location()) - pcollar = null - ..() - -/mob/living/simple_animal/emote(act, m_type = 1, message = null, force) - if(stat) - return - act = lowertext(act) - switch(act) //IMPORTANT: Emotes MUST NOT CONFLICT anywhere along the chain. - if("scream") - message = "\The [src] whimpers." - m_type = 2 - if("help") - to_chat(src, "scream") - - ..() - -/mob/living/simple_animal/say_quote(message) - var/verb = "says" - - if(speak_emote.len) - verb = pick(speak_emote) - - return verb - -/mob/living/simple_animal/movement_delay() - . = ..() - - . = speed - - . += config.animal_delay - -/mob/living/simple_animal/Stat() - ..() - if(statpanel("Status")) - stat(null, "Health: [round((health / maxHealth) * 100)]%") - return TRUE - -/mob/living/simple_animal/proc/drop_loot() - if(loot.len) - for(var/i in loot) - new i(loc) - -/mob/living/simple_animal/revive() - ..() - density = initial(density) - -/mob/living/simple_animal/death(gibbed) - // Only execute the below if we successfully died - . = ..() - if(!.) - return FALSE - flying = FALSE - if(nest) - nest.spawned_mobs -= src - nest = null - drop_loot() - if(!gibbed) - if(death_sound) - playsound(get_turf(src),death_sound, 200, 1) - if(deathmessage) - visible_message("\The [src] [deathmessage]") - else if(!del_on_death) - visible_message("\The [src] stops moving...") - if(del_on_death) - //Prevent infinite loops if the mob Destroy() is overridden in such - //a manner as to cause a call to death() again - del_on_death = FALSE - ghostize() - qdel(src) - else - health = 0 - icon_state = icon_dead - if(flip_on_death) - transform = transform.Turn(180) - density = 0 - if(collar_type) - collar_type = "[initial(collar_type)]_dead" - regenerate_icons() - -/mob/living/simple_animal/proc/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility) - return FALSE - if(ismob(the_target)) - var/mob/M = the_target - if(M.status_flags & GODMODE) - return FALSE - if(isliving(the_target)) - var/mob/living/L = the_target - if(L.stat != CONSCIOUS) - return FALSE - if(ismecha(the_target)) - var/obj/mecha/M = the_target - if(M.occupant) - return FALSE - if(isspacepod(the_target)) - var/obj/spacepod/S = the_target - if(S.pilot) - return FALSE - return TRUE - -/mob/living/simple_animal/handle_fire() - return TRUE - -/mob/living/simple_animal/IgniteMob() - return FALSE - -/mob/living/simple_animal/ExtinguishMob() - return - -/mob/living/simple_animal/revive() - ..() - health = maxHealth - icon = initial(icon) - icon_state = icon_living - density = initial(density) - update_canmove() - flying = initial(flying) - if(collar_type) - collar_type = "[initial(collar_type)]" - regenerate_icons() - -/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 - if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) - return FALSE - next_scan_time = world.time + 400 - - var/alone = TRUE - var/mob/living/simple_animal/partner - var/children = 0 - - for(var/mob/M in oview(7, src)) - if(M.stat != CONSCIOUS) //Check if it's conscious FIRST. - continue - else if(istype(M, childtype)) //Check for children SECOND. - children++ - else if(istype(M, animal_species)) - if(M.ckey) - continue - else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; - partner = M - else if(isliving(M) && !faction_check_mob(M)) //shyness check. we're not shy in front of things that share a faction with us. - return //we never mate when not alone, so just abort early - - if(alone && partner && children < 3) - var/childspawn = pickweight(childtype) - var/turf/target = get_turf(loc) - if(target) - return new childspawn(target) - -/mob/living/simple_animal/show_inv(mob/user as mob) - if(!can_collar) - return - - user.set_machine(src) - var/dat = "
    Collar:[(pcollar && !(pcollar.flags & ABSTRACT)) ? pcollar : "Empty"]
    " - dat += "Close" - - var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 250) - popup.set_content(dat) - popup.open() - -/mob/living/simple_animal/get_item_by_slot(slot_id) - switch(slot_id) - if(slot_collar) - return pcollar - . = ..() - -/mob/living/simple_animal/can_equip(obj/item/I, slot, disable_warning = 0) - // . = ..() // Do not call parent. We do not want animals using their hand slots. - switch(slot) - if(slot_collar) - if(pcollar) - return FALSE - if(!can_collar) - return FALSE - if(!istype(I, /obj/item/clothing/accessory/petcollar)) - return FALSE - return TRUE - -/mob/living/simple_animal/equip_to_slot(obj/item/W, slot) - if(!istype(W)) - return FALSE - - if(!slot) - return FALSE - - W.layer = ABOVE_HUD_LAYER - W.plane = ABOVE_HUD_PLANE - - switch(slot) - if(slot_collar) - add_collar(W) - -/mob/living/simple_animal/unEquip(obj/item/I, force) - . = ..() - if(!. || !I) - return - - if(I == pcollar) - pcollar = null - regenerate_icons() - -/mob/living/simple_animal/get_access() - . = ..() - if(pcollar) - . |= pcollar.GetAccess() - -/mob/living/simple_animal/update_canmove(delay_action_updates = 0) - if(paralysis || stunned || IsWeakened() || stat || resting) - drop_r_hand() - drop_l_hand() - canmove = 0 - else if(buckled) - canmove = 0 - else - canmove = 1 - if(!canmove) - walk(src, 0) //stop mid walk - - update_transform() - if(!delay_action_updates) - update_action_buttons_icon() - return canmove - -/mob/living/simple_animal/update_transform() - var/matrix/ntransform = matrix(transform) //aka transform.Copy() - var/changed = FALSE - - if(resize != RESIZE_DEFAULT_SIZE) - changed = TRUE - ntransform.Scale(resize) - resize = RESIZE_DEFAULT_SIZE - - if(changed) - animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) - -/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion - toggle_ai(AI_OFF) - can_have_ai = FALSE - -/mob/living/simple_animal/grant_death_vision() - sight |= SEE_TURFS - sight |= SEE_MOBS - sight |= SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_OBSERVER - sync_lighting_plane_alpha() - -/mob/living/simple_animal/update_sight() - if(!client) - return - - if(stat == DEAD) - grant_death_vision() - return - - see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) - sight = initial(sight) - - if(client.eye != src) - var/atom/A = client.eye - if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. - return - - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() - -/mob/living/simple_animal/proc/toggle_ai(togglestatus) - if(!can_have_ai && (togglestatus != AI_OFF)) - return - if(AIStatus != togglestatus) - if(togglestatus > 0 && togglestatus < 5) - if(togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) - var/turf/T = get_turf(src) - if(AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - else - SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src - GLOB.simple_animals[AIStatus] -= src - GLOB.simple_animals[togglestatus] += src - AIStatus = togglestatus - else - stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") - -/mob/living/simple_animal/proc/consider_wakeup() - if(pulledby || shouldwakeup) - toggle_ai(AI_ON) - -/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE) - . = ..() - if(!ckey && !stat)//Not unconscious - if(AIStatus == AI_IDLE) - toggle_ai(AI_ON) - -/mob/living/simple_animal/onTransitZ(old_z, new_z) - ..() - if(AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src - toggle_ai(initial(AIStatus)) - -/mob/living/simple_animal/proc/add_collar(obj/item/clothing/accessory/petcollar/P, mob/user) - if(QDELETED(P) || pcollar) - return - if(user && !user.unEquip(P)) - return - P.forceMove(src) - P.equipped(src) - pcollar = P - regenerate_icons() - if(user) - to_chat(user, "You put [P] around [src]'s neck.") - if(P.tagname && !unique_pet) - name = P.tagname - real_name = P.tagname - -/mob/living/simple_animal/regenerate_icons() - if(pcollar && collar_type) - cut_overlays() - add_overlay("[collar_type]collar") - add_overlay("[collar_type]tag") \ No newline at end of file +/mob/living/simple_animal + name = "animal" + icon = 'icons/mob/animal.dmi' + health = 20 + maxHealth = 20 + gender = PLURAL //placeholder + + universal_understand = 1 + universal_speak = 0 + status_flags = CANPUSH + + var/icon_living = "" + var/icon_dead = "" + var/icon_resting = "" + var/icon_gib = null //We only try to show a gibbing animation if this exists. + var/flip_on_death = FALSE //Flip the sprite upside down on death. Mostly here for things lacking custom dead sprites. + + var/list/speak = list() + var/speak_chance = 0 + var/list/emote_hear = list() //Hearable emotes + var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps + + var/turns_per_move = 1 + var/turns_since_move = 0 + var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. + var/wander = 1 // Does the mob wander around when idle? + var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. + + //Interaction + var/response_help = "pokes" + var/response_disarm = "shoves" + var/response_harm = "hits" + var/harm_intent_damage = 3 + var/force_threshold = 0 //Minimum force required to deal any damage + + //Temperature effect + var/minbodytemp = 250 + var/maxbodytemp = 350 + var/heat_damage_per_tick = 2 //amount of damage applied if animal's body temperature is higher than maxbodytemp + var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + + //Healable by medical stacks? Defaults to yes. + var/healable = 1 + + //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage + var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum + var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above + + //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly + var/melee_damage_lower = 0 + var/melee_damage_upper = 0 + var/obj_damage = 0 //how much damage this simple animal does to objects, if any + var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value + var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. + var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source + var/attacktext = "attacks" + var/attack_sound = null + var/friendly = "nuzzles" //If the mob does no damage with it's attack + var/environment_smash = ENVIRONMENT_SMASH_NONE //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls + + var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster + var/can_hide = 0 + + var/obj/item/clothing/accessory/petcollar/pcollar = null + var/collar_type //if the mob has collar sprites, define them. + var/unique_pet = FALSE // if the mob can be renamed + var/can_collar = FALSE // can add collar to mob or not + + //Hot simple_animal baby making vars + var/list/childtype = null + var/next_scan_time = 0 + var/animal_species //Sorry, no spider+corgi buttbabies. + + var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against + var/gold_core_spawnable = NO_SPAWN //If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood + + var/mob/living/carbon/human/master_commander = null //holding var for determining who own/controls a sentient simple animal (for sentience potions). + + var/datum/component/spawner/nest + + var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions + + var/list/loot = list() //list of things spawned at mob's loc when it dies + var/del_on_death = 0 //causes mob to be deleted on death, useful for mobs that spawn lootable corpses + var/deathmessage = "" + var/death_sound = null //The sound played on death + + var/allow_movement_on_non_turfs = FALSE + + var/attacked_sound = "punch" + + var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) + var/can_have_ai = TRUE //once we have become sentient, we can never go back + + var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. + + //domestication + var/tame = 0 + + var/my_z // I don't want to confuse this with client registered_z + +/mob/living/simple_animal/Initialize(mapload) + . = ..() + GLOB.simple_animals[AIStatus] += src + if(gender == PLURAL) + gender = pick(MALE, FEMALE) + if(!real_name) + real_name = name + if(!loc) + stack_trace("Simple animal being instantiated in nullspace") + verbs -= /mob/verb/observe + if(!can_hide) + verbs -= /mob/living/simple_animal/verb/hide + if(pcollar) + pcollar = new(src) + regenerate_icons() + +/mob/living/simple_animal/Destroy() + QDEL_NULL(pcollar) + master_commander = null + GLOB.simple_animals[AIStatus] -= src + if(SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) + SSnpcpool.currentrun -= src + + if(nest) + nest.spawned_mobs -= src + nest = null + + var/turf/T = get_turf(src) + if (T && AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + + return ..() + +/mob/living/simple_animal/handle_atom_del(atom/A) + if(A == pcollar) + pcollar = null + return ..() + +/mob/living/simple_animal/examine(mob/user) + . = ..() + if(stat == DEAD) + . += "Upon closer examination, [p_they()] appear[p_s()] to be dead." + +/mob/living/simple_animal/updatehealth(reason = "none given") + ..(reason) + health = Clamp(health, 0, maxHealth) + med_hud_set_status() + +/mob/living/simple_animal/StartResting(updating = 1) + ..() + if(icon_resting && stat != DEAD) + icon_state = icon_resting + if(collar_type) + collar_type = "[initial(collar_type)]_rest" + regenerate_icons() + +/mob/living/simple_animal/StopResting(updating = 1) + ..() + if(icon_resting && stat != DEAD) + icon_state = icon_living + if(collar_type) + collar_type = "[initial(collar_type)]" + regenerate_icons() + +/mob/living/simple_animal/update_stat(reason = "none given") + if(status_flags & GODMODE) + return + + ..(reason) + if(stat != DEAD) + if(health < 1) + death() + create_debug_log("died of damage, trigger reason: [reason]") + +/mob/living/simple_animal/proc/handle_automated_action() + set waitfor = FALSE + return + +/mob/living/simple_animal/proc/handle_automated_movement() + set waitfor = FALSE + if(!stop_automated_movement && wander) + if((isturf(loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. + turns_since_move++ + if(turns_since_move >= turns_per_move) + if(!(stop_automated_movement_when_pulled && pulledby)) //Soma animals don't move when pulled + var/anydir = pick(GLOB.cardinal) + if(Process_Spacemove(anydir)) + Move(get_step(src,anydir), anydir) + turns_since_move = 0 + return 1 + +/mob/living/simple_animal/proc/handle_automated_speech(override) + set waitfor = FALSE + if(speak_chance) + if(prob(speak_chance) || override) + if(speak && speak.len) + if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) + var/length = speak.len + if(emote_hear && emote_hear.len) + length += emote_hear.len + if(emote_see && emote_see.len) + length += emote_see.len + var/randomValue = rand(1,length) + if(randomValue <= speak.len) + say(pick(speak)) + else + randomValue -= speak.len + if(emote_see && randomValue <= emote_see.len) + custom_emote(1, pick(emote_see)) + else + custom_emote(2, pick(emote_hear)) + else + say(pick(speak)) + else + if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + custom_emote(1, pick(emote_see)) + if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) + custom_emote(2, pick(emote_hear)) + if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + var/length = emote_hear.len + emote_see.len + var/pick = rand(1,length) + if(pick <= emote_see.len) + custom_emote(1, pick(emote_see)) + else + custom_emote(2,pick(emote_hear)) + + +/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) + var/atmos_suitable = 1 + + var/areatemp = get_temperature(environment) + + if(abs(areatemp - bodytemperature) > 5 && !(BREATHLESS in mutations)) + var/diff = areatemp - bodytemperature + diff = diff / 5 + bodytemperature += diff + + var/tox = environment.toxins + var/oxy = environment.oxygen + var/n2 = environment.nitrogen + var/co2 = environment.carbon_dioxide + + if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) + atmos_suitable = 0 + throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) + else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) + atmos_suitable = 0 + throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy) + else + clear_alert("not_enough_oxy") + clear_alert("too_much_oxy") + + if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) + atmos_suitable = 0 + throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) + else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) + atmos_suitable = 0 + throw_alert("too_much_tox", /obj/screen/alert/too_much_tox) + else + clear_alert("too_much_tox") + clear_alert("not_enough_tox") + + if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) + atmos_suitable = 0 + else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) + atmos_suitable = 0 + + if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) + atmos_suitable = 0 + else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) + atmos_suitable = 0 + + if(!atmos_suitable) + adjustHealth(unsuitable_atmos_damage) + + handle_temperature_damage() + +/mob/living/simple_animal/proc/handle_temperature_damage() + if(bodytemperature < minbodytemp) + adjustHealth(cold_damage_per_tick) + else if(bodytemperature > maxbodytemp) + adjustHealth(heat_damage_per_tick) + +/mob/living/simple_animal/gib() + if(icon_gib) + flick(icon_gib, src) + if(butcher_results) + var/atom/Tsec = drop_location() + for(var/path in butcher_results) + for(var/i in 1 to butcher_results[path]) + new path(Tsec) + if(pcollar) + pcollar.forceMove(drop_location()) + pcollar = null + ..() + +/mob/living/simple_animal/emote(act, m_type = 1, message = null, force) + if(stat) + return + act = lowertext(act) + switch(act) //IMPORTANT: Emotes MUST NOT CONFLICT anywhere along the chain. + if("scream") + message = "\The [src] whimpers." + m_type = 2 + if("help") + to_chat(src, "scream") + + ..() + +/mob/living/simple_animal/say_quote(message) + var/verb = "says" + + if(speak_emote.len) + verb = pick(speak_emote) + + return verb + +/mob/living/simple_animal/movement_delay() + . = ..() + + . = speed + + . += config.animal_delay + +/mob/living/simple_animal/Stat() + ..() + if(statpanel("Status")) + stat(null, "Health: [round((health / maxHealth) * 100)]%") + return TRUE + +/mob/living/simple_animal/proc/drop_loot() + if(loot.len) + for(var/i in loot) + new i(loc) + +/mob/living/simple_animal/revive() + ..() + density = initial(density) + +/mob/living/simple_animal/death(gibbed) + // Only execute the below if we successfully died + . = ..() + if(!.) + return FALSE + flying = FALSE + if(nest) + nest.spawned_mobs -= src + nest = null + drop_loot() + if(!gibbed) + if(death_sound) + playsound(get_turf(src),death_sound, 200, 1) + if(deathmessage) + visible_message("\The [src] [deathmessage]") + else if(!del_on_death) + visible_message("\The [src] stops moving...") + if(del_on_death) + //Prevent infinite loops if the mob Destroy() is overridden in such + //a manner as to cause a call to death() again + del_on_death = FALSE + ghostize() + qdel(src) + else + health = 0 + icon_state = icon_dead + if(flip_on_death) + transform = transform.Turn(180) + density = 0 + if(collar_type) + collar_type = "[initial(collar_type)]_dead" + regenerate_icons() + +/mob/living/simple_animal/proc/CanAttack(atom/the_target) + if(see_invisible < the_target.invisibility) + return FALSE + if(ismob(the_target)) + var/mob/M = the_target + if(M.status_flags & GODMODE) + return FALSE + if(isliving(the_target)) + var/mob/living/L = the_target + if(L.stat != CONSCIOUS) + return FALSE + if(ismecha(the_target)) + var/obj/mecha/M = the_target + if(M.occupant) + return FALSE + if(isspacepod(the_target)) + var/obj/spacepod/S = the_target + if(S.pilot) + return FALSE + return TRUE + +/mob/living/simple_animal/handle_fire() + return TRUE + +/mob/living/simple_animal/IgniteMob() + return FALSE + +/mob/living/simple_animal/ExtinguishMob() + return + +/mob/living/simple_animal/revive() + ..() + health = maxHealth + icon = initial(icon) + icon_state = icon_living + density = initial(density) + update_canmove() + flying = initial(flying) + if(collar_type) + collar_type = "[initial(collar_type)]" + regenerate_icons() + +/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 + if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) + return FALSE + next_scan_time = world.time + 400 + + var/alone = TRUE + var/mob/living/simple_animal/partner + var/children = 0 + + for(var/mob/M in oview(7, src)) + if(M.stat != CONSCIOUS) //Check if it's conscious FIRST. + continue + else if(istype(M, childtype)) //Check for children SECOND. + children++ + else if(istype(M, animal_species)) + if(M.ckey) + continue + else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; + partner = M + else if(isliving(M) && !faction_check_mob(M)) //shyness check. we're not shy in front of things that share a faction with us. + return //we never mate when not alone, so just abort early + + if(alone && partner && children < 3) + var/childspawn = pickweight(childtype) + var/turf/target = get_turf(loc) + if(target) + return new childspawn(target) + +/mob/living/simple_animal/show_inv(mob/user as mob) + if(!can_collar) + return + + user.set_machine(src) + var/dat = "
    Collar:[(pcollar && !(pcollar.flags & ABSTRACT)) ? pcollar : "Empty"]
    " + dat += "Close" + + var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 250) + popup.set_content(dat) + popup.open() + +/mob/living/simple_animal/get_item_by_slot(slot_id) + switch(slot_id) + if(slot_collar) + return pcollar + . = ..() + +/mob/living/simple_animal/can_equip(obj/item/I, slot, disable_warning = 0) + // . = ..() // Do not call parent. We do not want animals using their hand slots. + switch(slot) + if(slot_collar) + if(pcollar) + return FALSE + if(!can_collar) + return FALSE + if(!istype(I, /obj/item/clothing/accessory/petcollar)) + return FALSE + return TRUE + +/mob/living/simple_animal/equip_to_slot(obj/item/W, slot) + if(!istype(W)) + return FALSE + + if(!slot) + return FALSE + + W.layer = ABOVE_HUD_LAYER + W.plane = ABOVE_HUD_PLANE + + switch(slot) + if(slot_collar) + add_collar(W) + +/mob/living/simple_animal/unEquip(obj/item/I, force) + . = ..() + if(!. || !I) + return + + if(I == pcollar) + pcollar = null + regenerate_icons() + +/mob/living/simple_animal/get_access() + . = ..() + if(pcollar) + . |= pcollar.GetAccess() + +/mob/living/simple_animal/update_canmove(delay_action_updates = 0) + if(paralysis || stunned || IsWeakened() || stat || resting) + drop_r_hand() + drop_l_hand() + canmove = 0 + else if(buckled) + canmove = 0 + else + canmove = 1 + if(!canmove) + walk(src, 0) //stop mid walk + + update_transform() + if(!delay_action_updates) + update_action_buttons_icon() + return canmove + +/mob/living/simple_animal/update_transform() + var/matrix/ntransform = matrix(transform) //aka transform.Copy() + var/changed = FALSE + + if(resize != RESIZE_DEFAULT_SIZE) + changed = TRUE + ntransform.Scale(resize) + resize = RESIZE_DEFAULT_SIZE + + if(changed) + animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) + +/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion + toggle_ai(AI_OFF) + can_have_ai = FALSE + +/mob/living/simple_animal/grant_death_vision() + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_OBSERVER + sync_lighting_plane_alpha() + +/mob/living/simple_animal/update_sight() + if(!client) + return + + if(stat == DEAD) + grant_death_vision() + return + + see_invisible = initial(see_invisible) + see_in_dark = initial(see_in_dark) + sight = initial(sight) + + if(client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() + +/mob/living/simple_animal/proc/toggle_ai(togglestatus) + if(!can_have_ai && (togglestatus != AI_OFF)) + return + if(AIStatus != togglestatus) + if(togglestatus > 0 && togglestatus < 5) + if(togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) + var/turf/T = get_turf(src) + if(AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + else + SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src + GLOB.simple_animals[AIStatus] -= src + GLOB.simple_animals[togglestatus] += src + AIStatus = togglestatus + else + stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") + +/mob/living/simple_animal/proc/consider_wakeup() + if(pulledby || shouldwakeup) + toggle_ai(AI_ON) + +/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE) + . = ..() + if(!ckey && !stat)//Not unconscious + if(AIStatus == AI_IDLE) + toggle_ai(AI_ON) + +/mob/living/simple_animal/onTransitZ(old_z, new_z) + ..() + if(AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src + toggle_ai(initial(AIStatus)) + +/mob/living/simple_animal/proc/add_collar(obj/item/clothing/accessory/petcollar/P, mob/user) + if(!istype(P) || QDELETED(P) || pcollar) + return + if(user && !user.unEquip(P)) + return + P.forceMove(src) + P.equipped(src) + pcollar = P + regenerate_icons() + if(user) + to_chat(user, "You put [P] around [src]'s neck.") + if(P.tagname && !unique_pet) + name = P.tagname + real_name = P.tagname + +/mob/living/simple_animal/regenerate_icons() + cut_overlays() + if(pcollar && collar_type) + add_overlay("[collar_type]collar") + add_overlay("[collar_type]tag") diff --git a/code/modules/mob/living/simple_animal/slime/death.dm b/code/modules/mob/living/simple_animal/slime/death.dm index 29a1adbd347..cf72363ac9a 100644 --- a/code/modules/mob/living/simple_animal/slime/death.dm +++ b/code/modules/mob/living/simple_animal/slime/death.dm @@ -29,4 +29,4 @@ /mob/living/simple_animal/slime/gib() death(TRUE) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/slime/emote.dm b/code/modules/mob/living/simple_animal/slime/emote.dm index 7fc7f4c5f87..94806ede9d1 100644 --- a/code/modules/mob/living/simple_animal/slime/emote.dm +++ b/code/modules/mob/living/simple_animal/slime/emote.dm @@ -102,4 +102,4 @@ loc.audible_message(message) if(regenerate_icons) - regenerate_icons() \ No newline at end of file + regenerate_icons() diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm index efd4c19d55c..4e1aee2c822 100644 --- a/code/modules/mob/living/simple_animal/slime/life.dm +++ b/code/modules/mob/living/simple_animal/slime/life.dm @@ -314,7 +314,7 @@ for(var/mob/living/L in view(7,src)) - if(isslime(L) || L.stat == DEAD) // Ignore other slimes and dead mobs + if(L.stat == DEAD) // Ignore dead mobs continue if(L in Friends) // No eating friends! @@ -368,7 +368,7 @@ if (holding_still) holding_still = max(holding_still - hungry, 0) else if(canmove && isturf(loc) && prob(50)) - step(src, pick(cardinal)) + step(src, pick(GLOB.cardinal)) else if(holding_still) @@ -376,7 +376,7 @@ else if (docile && pulledby) holding_still = 10 else if(canmove && isturf(loc) && prob(33)) - step(src, pick(cardinal)) + step(src, pick(GLOB.cardinal)) else if(!AIproc) INVOKE_ASYNC(src, .proc/AIprocess) @@ -614,4 +614,4 @@ return 0 if(holding_still) return 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/mob/living/simple_animal/slime/say.dm b/code/modules/mob/living/simple_animal/slime/say.dm index 01269aab8aa..74241dc676c 100644 --- a/code/modules/mob/living/simple_animal/slime/say.dm +++ b/code/modules/mob/living/simple_animal/slime/say.dm @@ -9,7 +9,7 @@ return verb -/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol) +/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) if(speaker != src && !stat) if(speaker in Friends) speech_buffer = list() diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index f42bc2b6b74..91e6c502869 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -121,7 +121,7 @@ set_colour(pick(slime_colours)) /mob/living/simple_animal/slime/regenerate_icons() - cut_overlays() + ..() var/icon_text = "[colour] [is_adult ? "adult" : "baby"] slime" icon_dead = "[icon_text] dead" if(stat != DEAD) @@ -130,7 +130,6 @@ add_overlay("aslime-[mood]") else icon_state = icon_dead - ..() /mob/living/simple_animal/slime/movement_delay() if(bodytemperature >= 330.23) // 135 F or 57.08 C @@ -262,7 +261,7 @@ Feedon(Food) return ..() -/mob/living/simple_animal/slime/unEquip(obj/item/I) +/mob/living/simple_animal/slime/unEquip(obj/item/I, force) return /mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) @@ -422,7 +421,7 @@ visible_message("The mutated core shudders, and collapses into a puddle, unable to maintain its form.") qdel(src) */ -/mob/living/simple_animal/slime/water_act(volume, temperature, source, method = TOUCH) +/mob/living/simple_animal/slime/water_act(volume, temperature, source, method = REAGENT_TOUCH) . = ..() var/water_damage = rand(10, 15) * volume adjustBruteLoss(water_damage) @@ -507,4 +506,4 @@ if(buckled) to_chat(src, "I can't vent crawl while feeding...") return - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/tribbles.dm b/code/modules/mob/living/simple_animal/tribbles.dm index b916fa801f0..e5ebfb2ff61 100644 --- a/code/modules/mob/living/simple_animal/tribbles.dm +++ b/code/modules/mob/living/simple_animal/tribbles.dm @@ -1,4 +1,4 @@ -var/global/totaltribbles = 0 //global variable so it updates for all tribbles, not just the new one being made. +GLOBAL_VAR_INIT(totaltribbles, 0) //global variable so it updates for all tribbles, not just the new one being made. /mob/living/simple_animal/tribble @@ -35,7 +35,7 @@ var/global/totaltribbles = 0 //global variable so it updates for all tribbles, //random pixel offsets so they cover the floor src.pixel_x = rand(-5.0, 5) src.pixel_y = rand(-5.0, 5) - totaltribbles += 1 + GLOB.totaltribbles += 1 /mob/living/simple_animal/tribble/attack_hand(mob/user as mob) @@ -60,8 +60,7 @@ var/global/totaltribbles = 0 //global variable so it updates for all tribbles, /mob/living/simple_animal/tribble/proc/procreate() - ..() - if(totaltribbles <= maxtribbles) + if(GLOB.totaltribbles <= maxtribbles) for(var/mob/living/simple_animal/tribble/F in src.loc) if(!F || F == src) new /mob/living/simple_animal/tribble(src.loc) @@ -84,7 +83,7 @@ var/global/totaltribbles = 0 //global variable so it updates for all tribbles, . = ..(gibbed) if(!.) return FALSE - totaltribbles -= 1 + GLOB.totaltribbles -= 1 //||Item version of the trible || diff --git a/code/modules/mob/living/stat_states.dm b/code/modules/mob/living/stat_states.dm index 70cced62aab..926bdc5970d 100644 --- a/code/modules/mob/living/stat_states.dm +++ b/code/modules/mob/living/stat_states.dm @@ -7,6 +7,7 @@ else if(stat == UNCONSCIOUS) return 0 create_attack_log("Fallen unconscious at [atom_loc_line(get_turf(src))]") + add_attack_logs(src, null, "Fallen unconscious", ATKLOG_ALL) log_game("[key_name(src)] fell unconscious at [atom_loc_line(get_turf(src))]") stat = UNCONSCIOUS if(updating) @@ -22,6 +23,7 @@ else if(stat == CONSCIOUS) return 0 create_attack_log("Woken up at [atom_loc_line(get_turf(src))]") + add_attack_logs(src, null, "Woken up", ATKLOG_ALL) log_game("[key_name(src)] woke up at [atom_loc_line(get_turf(src))]") stat = CONSCIOUS if(updating) @@ -45,6 +47,7 @@ if(!can_be_revived()) return 0 create_attack_log("Came back to life at [atom_loc_line(get_turf(src))]") + add_attack_logs(src, null, "Came back to life", ATKLOG_ALL) log_game("[key_name(src)] came back to life at [atom_loc_line(get_turf(src))]") stat = CONSCIOUS GLOB.dead_mob_list -= src @@ -73,4 +76,4 @@ return 1 /mob/living/proc/check_death_method() - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 72f136304d7..b2871038155 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -478,7 +478,7 @@ . = val_change ? STATUS_UPDATE_BLIND : STATUS_UPDATE_NONE disabilities &= ~BLIND if(val_change && updating) - CureIfHasDisability(BLINDBLOCK) + CureIfHasDisability(GLOB.blindblock) update_blind_effects() // Coughing @@ -488,7 +488,7 @@ /mob/living/proc/CureCoughing() disabilities &= ~COUGHING - CureIfHasDisability(COUGHBLOCK) + CureIfHasDisability(GLOB.coughblock) // Deaf @@ -497,7 +497,7 @@ /mob/living/proc/CureDeaf() disabilities &= ~DEAF - CureIfHasDisability(DEAFBLOCK) + CureIfHasDisability(GLOB.deafblock) // Epilepsy @@ -506,7 +506,7 @@ /mob/living/proc/CureEpilepsy() disabilities &= ~EPILEPSY - CureIfHasDisability(EPILEPSYBLOCK) + CureIfHasDisability(GLOB.epilepsyblock) // Mute @@ -515,7 +515,7 @@ /mob/living/proc/CureMute() disabilities &= ~MUTE - CureIfHasDisability(MUTEBLOCK) + CureIfHasDisability(GLOB.muteblock) // Nearsighted @@ -531,7 +531,7 @@ . = val_change ? STATUS_UPDATE_NEARSIGHTED : STATUS_UPDATE_NONE disabilities &= ~NEARSIGHTED if(val_change && updating) - CureIfHasDisability(GLASSESBLOCK) + CureIfHasDisability(GLOB.glassesblock) update_nearsighted_effects() // Nervous @@ -541,7 +541,7 @@ /mob/living/proc/CureNervous() disabilities &= ~NERVOUS - CureIfHasDisability(NERVOUSBLOCK) + CureIfHasDisability(GLOB.nervousblock) // Tourettes @@ -550,7 +550,7 @@ /mob/living/proc/CureTourettes() disabilities &= ~TOURETTES - CureIfHasDisability(TWITCHBLOCK) + CureIfHasDisability(GLOB.twitchblock) /mob/living/proc/CureIfHasDisability(block) if(dna && dna.GetSEState(block)) @@ -598,4 +598,4 @@ else if(priority_absorb_key["self_message"]) to_chat(src, "[priority_absorb_key["self_message"]]") priority_absorb_key["stuns_absorbed"] += amount - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/mob/living/taste.dm b/code/modules/mob/living/taste.dm index a403aa39244..03b0ff5752b 100644 --- a/code/modules/mob/living/taste.dm +++ b/code/modules/mob/living/taste.dm @@ -27,4 +27,4 @@ // "something indescribable" -> too many tastes, not enough flavor. last_taste_time = world.time - last_taste_text = text_output \ No newline at end of file + last_taste_text = text_output diff --git a/code/modules/mob/living/update_status.dm b/code/modules/mob/living/update_status.dm index 20cc969119f..d24301b0dd0 100644 --- a/code/modules/mob/living/update_status.dm +++ b/code/modules/mob/living/update_status.dm @@ -65,8 +65,14 @@ return !(IsWeakened() || paralysis || stat || (status_flags & FAKEDEATH)) // Whether the mob is capable of actions or not -/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE) - if(stat || paralysis || stunned || IsWeakened() || (!ignore_restraints && restrained()) || (!ignore_lying && lying)) +/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE, list/extra_checks = list(), use_default_checks = TRUE) + // By default, checks for weakness and stunned get added to the extra_checks list. + // Setting `use_default_checks` to FALSE means that you don't want it checking for these statuses or you are supplying your own checks. + if(use_default_checks) + extra_checks += CALLBACK(src, /mob.proc/IsWeakened) + extra_checks += CALLBACK(src, /mob.proc/IsStunned) + + if(stat || paralysis || (!ignore_restraints && restrained()) || (!ignore_lying && lying) || check_for_true_callbacks(extra_checks)) return TRUE // wonderful proc names, I know - used to check whether the blur overlay diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 66b4f2f9a16..5345bda5b94 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -1,70 +1,71 @@ -//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying -/mob/proc/update_Login_details() - //Multikey checks and logging - lastKnownIP = client.address - computer_id = client.computer_id - log_access_in(client) - create_attack_log("Logged in at [atom_loc_line(get_turf(src))]") - if(config.log_access) - for(var/mob/M in GLOB.player_list) - if(M == src) continue - if( M.key && (M.key != key) ) - var/matches - if( (M.lastKnownIP == client.address) ) - matches += "IP ([client.address])" - if( (M.computer_id == client.computer_id) ) - if(matches) matches += " and " - matches += "ID ([client.computer_id])" - if(!config.disable_cid_warn_popup) - spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!") - if(matches) - if(M.client) - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") - else - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") - -/mob/Login() - GLOB.player_list |= src - update_Login_details() - world.update_status() - - client.images = null //remove the images such as AIs being unable to see runes - client.screen = list() //remove hud items just in case - if(client.click_intercept) - client.click_intercept.quit() // Let's not keep any old click_intercepts - - if(!hud_used) - create_mob_hud() - if(hud_used) - hud_used.show_hud(hud_used.hud_version) - - next_move = 1 - sight |= SEE_SELF - ..() - - reset_perspective(loc) - - - if(ckey in GLOB.deadmins) - verbs += /client/proc/readmin - - //Clear ability list and update from mob. - client.verbs -= ability_verbs - - if(abilities) - client.verbs |= abilities - - //HUD updates (antag hud, etc) - //readd this mob's HUDs (antag, med, etc) - reload_huds() - - add_click_catcher() - - if(viewing_alternate_appearances && viewing_alternate_appearances.len) - for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) - AA.display_to(list(src)) - - update_client_colour(0) - update_morgue() +//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying +/mob/proc/update_Login_details() + //Multikey checks and logging + lastKnownIP = client.address + computer_id = client.computer_id + log_access_in(client) + create_attack_log("Logged in at [atom_loc_line(get_turf(src))]") + create_log(MISC_LOG, "Logged in") + if(config.log_access) + for(var/mob/M in GLOB.player_list) + if(M == src) continue + if( M.key && (M.key != key) ) + var/matches + if( (M.lastKnownIP == client.address) ) + matches += "IP ([client.address])" + if( (M.computer_id == client.computer_id) ) + if(matches) matches += " and " + matches += "ID ([client.computer_id])" + if(!config.disable_cid_warn_popup) + spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!") + if(matches) + if(M.client) + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") + else + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") + +/mob/Login() + GLOB.player_list |= src + update_Login_details() + world.update_status() + + client.images = null //remove the images such as AIs being unable to see runes + client.screen = list() //remove hud items just in case + if(client.click_intercept) + client.click_intercept.quit() // Let's not keep any old click_intercepts + + if(!hud_used) + create_mob_hud() + if(hud_used) + hud_used.show_hud(hud_used.hud_version) + + next_move = 1 + sight |= SEE_SELF + ..() + + reset_perspective(loc) + + + if(ckey in GLOB.deadmins) + verbs += /client/proc/readmin + + //Clear ability list and update from mob. + client.verbs -= GLOB.ability_verbs + + if(abilities) + client.verbs |= abilities + + //HUD updates (antag hud, etc) + //readd this mob's HUDs (antag, med, etc) + reload_huds() + + add_click_catcher() + + if(viewing_alternate_appearances && viewing_alternate_appearances.len) + for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) + AA.display_to(list(src)) + + update_client_colour(0) + update_morgue() diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 376b8d46ea5..8bdb0d89332 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,20 +1,21 @@ -/mob/Logout() - SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs - unset_machine() - GLOB.player_list -= src - log_access_out(src) - create_attack_log("Logged out at [atom_loc_line(get_turf(src))]") - // `holder` is nil'd out by now, so we check the `admin_datums` array directly - //Only report this stuff if we are currently playing. - if(admin_datums[ckey] && SSticker && SSticker.current_state == GAME_STATE_PLAYING) - var/datum/admins/temp_admin = admin_datums[ckey] - // Triggers on people with banhammer power only - no mentors tripping the alarm - if(temp_admin.rights & R_BAN) - message_admins("Admin logout: [key_name_admin(src)]") - var/list/admincounter = staff_countup(R_BAN) - if(admincounter[1] == 0) // No active admins - send2irc(config.admin_notify_irc, "[key_name(src)] logged out - No active admins, [admincounter[2]] non-admin staff, [admincounter[3]] inactive staff.") - - ..() - update_morgue() - return 1 +/mob/Logout() + SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs + unset_machine() + GLOB.player_list -= src + log_access_out(src) + create_attack_log("Logged out at [atom_loc_line(get_turf(src))]") + create_log(MISC_LOG, "Logged out") + // `holder` is nil'd out by now, so we check the `admin_datums` array directly + //Only report this stuff if we are currently playing. + if(GLOB.admin_datums[ckey] && SSticker && SSticker.current_state == GAME_STATE_PLAYING) + var/datum/admins/temp_admin = GLOB.admin_datums[ckey] + // Triggers on people with banhammer power only - no mentors tripping the alarm + if(temp_admin.rights & R_BAN) + message_admins("Admin logout: [key_name_admin(src)]") + var/list/admincounter = staff_countup(R_BAN) + if(admincounter[1] == 0) // No active admins + send2irc(config.admin_notify_irc, "[key_name(src)] logged out - No active admins, [admincounter[2]] non-admin staff, [admincounter[3]] inactive staff.") + + ..() + update_morgue() + return 1 diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 609940bc2a6..6123c8429c5 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -22,6 +22,7 @@ for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) AA.viewers -= src viewing_alternate_appearances = null + logs.Cut() ..() return QDEL_HINT_HARDDEL @@ -261,7 +262,7 @@ //The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot. -var/list/slot_equipment_priority = list( \ +GLOBAL_LIST_INIT(slot_equipment_priority, list( \ slot_back,\ slot_wear_pda,\ slot_wear_id,\ @@ -279,14 +280,14 @@ var/list/slot_equipment_priority = list( \ slot_tie,\ slot_l_store,\ slot_r_store\ - ) + )) //puts the item "W" into an appropriate slot in a human's inventory //returns 0 if it cannot, 1 if successful /mob/proc/equip_to_appropriate_slot(obj/item/W) if(!istype(W)) return 0 - for(var/slot in slot_equipment_priority) + for(var/slot in GLOB.slot_equipment_priority) if(istype(W,/obj/item/storage/) && slot == slot_head) // Storage items should be put on the belt before the head continue if(equip_to_slot_if_possible(W, slot, 0, 1, 1)) //del_on_fail = 0; disable_warning = 0; redraw_mob = 1 @@ -297,7 +298,7 @@ var/list/slot_equipment_priority = list( \ /mob/proc/check_for_open_slot(obj/item/W) if(!istype(W)) return 0 var/openslot = 0 - for(var/slot in slot_equipment_priority) + for(var/slot in GLOB.slot_equipment_priority) if(W.mob_check_equip(src, slot, 1) == 1) openslot = 1 break @@ -413,8 +414,6 @@ var/list/slot_equipment_priority = list( \ if(slot_w_uniform) if( !(slot_flags & SLOT_ICLOTHING) ) return 0 - if((FAT in H.mutations) && !(flags_size & ONESIZEFITSALL)) - return 0 if(H.w_uniform) if(!(H.w_uniform.flags & NODROP)) return 2 @@ -741,7 +740,7 @@ var/list/slot_equipment_priority = list( \ set name = "Respawn" set category = "OOC" - if(!abandon_allowed) + if(!GLOB.abandon_allowed) to_chat(usr, "Respawning is disabled.") return @@ -963,8 +962,8 @@ var/list/slot_equipment_priority = list( \ for(var/obj/effect/proc_holder/spell/S in mind.spell_list) add_spell_to_statpanel(S) - - if(is_admin(src)) + // Allow admins + PR reviewers to VIEW the panel. Doesnt mean they can click things. + if(is_admin(src) || check_rights(R_VIEWRUNTIMES, FALSE)) if(statpanel("MC")) //looking at that panel var/turf/T = get_turf(client.eye) stat("Location:", COORD(T)) @@ -1118,10 +1117,10 @@ var/list/slot_equipment_priority = list( \ /mob/proc/become_mouse() var/timedifference = world.time - client.time_died_as_mouse - if(client.time_died_as_mouse && timedifference <= mouse_respawn_time * 600) + if(client.time_died_as_mouse && timedifference <= GLOB.mouse_respawn_time * 600) var/timedifference_text - timedifference_text = time2text(mouse_respawn_time * 600 - timedifference,"mm:ss") - to_chat(src, "You may only spawn again as a mouse more than [mouse_respawn_time] minutes after your death. You have [timedifference_text] left.") + timedifference_text = time2text(GLOB.mouse_respawn_time * 600 - timedifference,"mm:ss") + to_chat(src, "You may only spawn again as a mouse more than [GLOB.mouse_respawn_time] minutes after your death. You have [timedifference_text] left.") return //find a viable mouse candidate @@ -1257,14 +1256,20 @@ var/list/slot_equipment_priority = list( \ return list() //must return list or IGNORE_ACCESS /mob/proc/create_attack_log(text, collapse = TRUE) - LAZYINITLIST(attack_log) - create_log_in_list(attack_log, text, collapse, last_log) + LAZYINITLIST(attack_log_old) + create_log_in_list(attack_log_old, text, collapse, last_log) last_log = world.timeofday /mob/proc/create_debug_log(text, collapse = TRUE) LAZYINITLIST(debug_log) create_log_in_list(debug_log, text, collapse, world.timeofday) +/mob/proc/create_log(log_type, what, target = null, turf/where = get_turf(src)) + LAZYINITLIST(logs[log_type]) + var/list/log_list = logs[log_type] + var/datum/log_record/record = new(log_type, src, what, target, where, world.time) + log_list.Add(record) + /proc/create_log_in_list(list/target, text, collapse = TRUE, last_log)//forgive me code gods for this shitcode proc //this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar." //That makes the logs easier to read, but because all of this is stored in strings, weird things have to be used to get it all out. @@ -1406,4 +1411,4 @@ var/list/slot_equipment_priority = list( \ ///Force set the mob nutrition /mob/proc/set_nutrition(change) - nutrition = max(0, change) \ No newline at end of file + nutrition = max(0, change) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 4091b8df9d7..ee514930c95 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -33,15 +33,17 @@ var/computer_id = null var/lastattacker = null var/lastattacked = null - var/list/attack_log = list( ) + var/list/attack_log_old = list( ) var/list/debug_log = null + + var/list/logs = list() // Logs for each log type defined in __DEFINES/logs.dm + var/last_log = 0 var/obj/machinery/machine = null var/other_mobs = null var/memory = "" var/next_move = null var/notransform = null //Carbon - var/other = 0.0 var/hand = null var/real_name = null var/flavor_text = "" @@ -60,12 +62,10 @@ var/name_archive //For admin things like possession var/timeofdeath = 0.0//Living - var/cpr_time = 1.0//Carbon var/bodytemperature = 310.055 //98.7 F var/flying = 0 - var/charges = 0.0 var/nutrition = NUTRITION_LEVEL_FED + 50 //Carbon var/satiety = 0 //Carbon var/hunger_drain = HUNGER_FACTOR // how quickly the mob gets hungry; largely utilized by species. @@ -88,8 +88,6 @@ var/obj/item/storage/s_active = null//Carbon var/obj/item/clothing/mask/wear_mask = null//Carbon - var/seer = 0 //for cult//Carbon, probably Human - var/datum/hud/hud_used = null hud_possible = list(SPECIALROLE_HUD) @@ -165,7 +163,6 @@ var/remote_view = 0 // Set to 1 to prevent view resets on Life var/obj/control_object //Used by admins to possess objects. All mobs should have this var - var/datum/visibility_interface/visibility_interface = null // used by the visibility system to provide an interface for the visibility networks //Whether or not mobs can understand other mobtypes. These stay in /mob so that ghosts can hear everything. var/universal_speak = 0 // Set to 1 to enable the mob to speak to everyone -- TLE @@ -208,4 +205,4 @@ var/list/tkgrabbed_objects = list() // Assoc list of items to TK grabs var/forced_look = null // This can either be a numerical direction or a soft object reference (UID). It makes the mob always face towards the selected thing. - var/registered_z \ No newline at end of file + var/registered_z diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 329a01907e2..f595f6b4839 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -1,452 +1,452 @@ -#define UPGRADE_COOLDOWN 40 -#define UPGRADE_KILL_TIMER 100 - -//times it takes for a mob to eat -#define EAT_TIME_XENO 30 -#define EAT_TIME_FAT 100 - -//time it takes for a mob to be eaten (in deciseconds) (overrides mob eat time) -#define EAT_TIME_ANIMAL 30 - -/obj/item/grab - name = "grab" - flags = NOBLUDGEON | ABSTRACT | DROPDEL - var/obj/screen/grab/hud = null - var/mob/living/affecting = null - var/mob/living/assailant = null - var/state = GRAB_PASSIVE - - var/allow_upgrade = 1 - var/last_upgrade = 0 - var/last_hit_zone = 0 -// var/force_down //determines if the affecting mob will be pinned to the ground //disabled due to balance, kept for an example for any new things. - var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position - - layer = 21 - plane = HUD_PLANE - item_state = "nothing" - icon = 'icons/mob/screen_gen.dmi' - w_class = WEIGHT_CLASS_BULKY - - -/obj/item/grab/New(var/mob/user, var/mob/victim) - ..() - - //Okay, first off, some fucking sanity checking. No user, or no victim, or they are not mobs, no grab. - if(!istype(user) || !istype(victim)) - return - - loc = user - assailant = user - affecting = victim - - if(affecting.anchored) - qdel(src) - return - - affecting.grabbed_by += src - - hud = new /obj/screen/grab(src) - hud.icon_state = "reinforce" - icon_state = "grabbed" - hud.name = "reinforce grab" - hud.master = src - - //check if assailant is grabbed by victim as well - if(assailant.grabbed_by) - for(var/obj/item/grab/G in assailant.grabbed_by) - if(G.assailant == affecting && G.affecting == assailant) - G.dancing = 1 - G.adjust_position() - dancing = 1 - - clean_grabbed_by(assailant, affecting) - adjust_position() - -/obj/item/grab/proc/clean_grabbed_by(var/mob/user, var/mob/victim) //Cleans up any nulls in the grabbed_by list. - if(istype(user)) - - for(var/entry in user.grabbed_by) - if(isnull(entry) || !istype(entry, /obj/item/grab)) //the isnull() here is probably redundant- but it might outperform istype, who knows. Better safe than sorry. - user.grabbed_by -= entry - - if(istype(victim)) - - for(var/entry in victim.grabbed_by) - if(isnull(entry) || !istype(entry, /obj/item/grab)) - victim.grabbed_by -= entry - - return 1 - -//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. -/obj/item/grab/proc/get_mob_if_throwable() - if(affecting && assailant.Adjacent(affecting)) - if(affecting.buckled) - return null - if(state >= GRAB_AGGRESSIVE) - return affecting - return null - -//This makes sure that the grab screen object is displayed in the correct hand. -/obj/item/grab/proc/synch() - if(affecting) - if(assailant.r_hand == src) - hud.screen_loc = ui_rhand - else - hud.screen_loc = ui_lhand - assailant.client.screen += hud - -/obj/item/grab/process() - if(!confirm()) - return //If the confirm fails, the grab is about to be deleted. That means it shouldn't continue processing. - - if(assailant.client) - if(!hud) - return //this somehow can runtime under the right circumstances - assailant.client.screen -= hud - assailant.client.screen += hud - - var/hit_zone = assailant.zone_selected - last_hit_zone = hit_zone - - if(assailant.pulling == affecting) - assailant.stop_pulling() - - if(state <= GRAB_AGGRESSIVE) - allow_upgrade = 1 - //disallow upgrading if we're grabbing more than one person - if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/grab))) - var/obj/item/grab/G = assailant.l_hand - if(G.affecting != affecting) - allow_upgrade = 0 - if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/grab))) - var/obj/item/grab/G = assailant.r_hand - if(G.affecting != affecting) - allow_upgrade = 0 - - //disallow upgrading past aggressive if we're being grabbed aggressively - for(var/obj/item/grab/G in affecting.grabbed_by) - if(G == src) continue - if(G.state >= GRAB_AGGRESSIVE) - allow_upgrade = 0 - - if(allow_upgrade) - if(state < GRAB_AGGRESSIVE) - hud.icon_state = "reinforce" - else - hud.icon_state = "reinforce1" - else - hud.icon_state = "!reinforce" - - if(state >= GRAB_AGGRESSIVE) - if(!HAS_TRAIT(assailant, TRAIT_PACIFISM)) - affecting.drop_r_hand() - affecting.drop_l_hand() - - - //var/announce = 0 - //(hit_zone != last_hit_zone) - //announce = 1 - /* if(ishuman(affecting)) - switch(hit_zone) - /*if("mouth") - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s mouth!") - if(affecting.silent < 3) - affecting.silent = 3 - if("eyes") - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s eyes!") - if(affecting.eye_blind < 3) - affecting.eye_blind = 3*///These are being left in the code as an example for adding new hit-zone based things. - - if(force_down) - if(affecting.loc != assailant.loc) - force_down = 0 - else - affecting.Weaken(3) //This is being left in the code as an example of adding a new variable to do something in grab code. - -*/ - - var/breathing_tube = affecting.get_organ_slot("breathing_tube") - - if(state >= GRAB_NECK) - affecting.Stun(5) //It will hamper your voice, being choked and all. - if(isliving(affecting) && !breathing_tube) - var/mob/living/L = affecting - L.adjustOxyLoss(1) - - if(state >= GRAB_KILL) - //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. - affecting.Stuttering(5) //It will hamper your voice, being choked and all. - affecting.Weaken(5) //Should keep you down unless you get help. - if(!breathing_tube) - affecting.AdjustLoseBreath(2, bound_lower = 0, bound_upper = 3) - - adjust_position() - - -/obj/item/grab/attack_self(mob/user) - s_click(hud) - -//Updating pixelshift, position and direction -//Gets called on process, when the grab gets upgraded or the assailant moves -/obj/item/grab/proc/adjust_position() - if(affecting.buckled) - return - if(affecting.lying && state != GRAB_KILL) - animate(affecting, pixel_x = 0, pixel_y = 0, 5, 1, LINEAR_EASING) - return //KJK - /* if(force_down) //THIS GOES ABOVE THE RETURN LABELED KJK - affecting.setDir(SOUTH)*///This shows how you can apply special directions based on a variable. //face up - - var/shift = 0 - var/adir = get_dir(assailant, affecting) - affecting.layer = 4 - switch(state) - if(GRAB_PASSIVE) - shift = 8 - if(dancing) //look at partner - shift = 10 - assailant.setDir(get_dir(assailant, affecting)) - if(GRAB_AGGRESSIVE) - shift = 12 - if(GRAB_NECK, GRAB_UPGRADING) - shift = -10 - adir = assailant.dir - affecting.setDir(assailant.dir) - affecting.forceMove(assailant.loc) - if(GRAB_KILL) - shift = 0 - adir = 1 - affecting.setDir(SOUTH)//face up - affecting.forceMove(assailant.loc) - - switch(adir) - if(NORTH) - animate(affecting, pixel_x = 0, pixel_y =-shift, 5, 1, LINEAR_EASING) - affecting.layer = 3.9 - if(SOUTH) - animate(affecting, pixel_x = 0, pixel_y = shift, 5, 1, LINEAR_EASING) - if(WEST) - animate(affecting, pixel_x = shift, pixel_y = 0, 5, 1, LINEAR_EASING) - if(EAST) - animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING) - -/obj/item/grab/proc/s_click(obj/screen/S) - if(!affecting) - return - if(state >= GRAB_AGGRESSIVE && HAS_TRAIT(assailant, TRAIT_PACIFISM)) - to_chat(assailant, "You don't want to risk hurting [affecting]!") - return - if(state == GRAB_UPGRADING) - return - if(assailant.next_move > world.time) - return - if(world.time < (last_upgrade + UPGRADE_COOLDOWN)) - return - if(!assailant.canmove || assailant.lying) - qdel(src) - return - - last_upgrade = world.time - - if(state < GRAB_AGGRESSIVE) - if(!allow_upgrade) - return - //if(!affecting.lying) - assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") - /* else - assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") - force_down = 1 - affecting.Weaken(3) - step_to(assailant, affecting) - assailant.setDir(EAST) //face the victim - affecting.setDir(SOUTH) //face up //This is an example of a new feature based on the context of the location of the victim. - */ //It means that upgrading while someone is lying on the ground would cause you to go into pin mode. - state = GRAB_AGGRESSIVE - icon_state = "grabbed1" - hud.icon_state = "reinforce1" - add_attack_logs(assailant, affecting, "Aggressively grabbed", ATKLOG_ALL) - else if(state < GRAB_NECK) - if(isslime(affecting)) - to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") - return - - assailant.visible_message("[assailant] has reinforced [assailant.p_their()] grip on [affecting] (now neck)!") - state = GRAB_NECK - icon_state = "grabbed+1" - assailant.setDir(get_dir(assailant, affecting)) - add_attack_logs(assailant, affecting, "Neck grabbed", ATKLOG_ALL) - if(!iscarbon(assailant)) - affecting.LAssailant = null - else - affecting.LAssailant = assailant - hud.icon_state = "kill" - hud.name = "kill" - affecting.Stun(10) //10 ticks of ensured grab - else if(state < GRAB_UPGRADING) - assailant.visible_message("[assailant] starts to tighten [assailant.p_their()] grip on [affecting]'s neck!") - hud.icon_state = "kill1" - - state = GRAB_KILL - assailant.visible_message("[assailant] has tightened [assailant.p_their()] grip on [affecting]'s neck!") - add_attack_logs(assailant, affecting, "Strangled") - - assailant.next_move = world.time + 10 - if(!affecting.get_organ_slot("breathing_tube")) - affecting.AdjustLoseBreath(1) - affecting.setDir(WEST) - adjust_position() - -//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. -/obj/item/grab/proc/confirm() - if(!assailant || !affecting) - qdel(src) - return 0 - - if(affecting) - if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) - qdel(src) - return 0 - return 1 - - -/obj/item/grab/attack(mob/living/M, mob/living/carbon/user) - if(!affecting) - return - - if(M == affecting) - if(ishuman(M) && ishuman(assailant)) - var/mob/living/carbon/human/affected = affecting - var/mob/living/carbon/human/attacker = assailant - switch(assailant.a_intent) - if(INTENT_HELP) - /*if(force_down) - to_chat(assailant, "You no longer pin [affecting] to the ground.") - force_down = 0 - return*///This is a very basic demonstration of a new feature based on attacking someone with the grab, based on intent. - //This specific example would allow you to stop pinning people to the floor without moving away from them. - return - - if(INTENT_GRAB) - return - - if(INTENT_HARM) //This checks that the user is on harm intent. - if(last_hit_zone == "head") //This checks the hitzone the user has selected. In this specific case, they have the head selected. - if(affecting.lying) - return - assailant.visible_message("[assailant] thrusts [assailant.p_their()] head into [affecting]'s skull!") //A visible message for what is going on. - var/damage = 5 - var/obj/item/clothing/hat = attacker.head - if(istype(hat)) - damage += hat.force * 3 - affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", affected.run_armor_check(affecting, "melee")) - playsound(assailant.loc, "swing_hit", 25, 1, -1) - add_attack_logs(assailant, affecting, "Headbutted") - return - - /*if(last_hit_zone == "eyes") - if(state < GRAB_NECK) - to_chat(assailant, "You require a better grab to do this.") - return - if((affected.head && affected.head.flags_cover & HEADCOVERSEYES) || \ - (affected.wear_mask && affected.wear_mask.flags_cover & MASKCOVERSEYES) || \ - (affected.glasses && affected.glasses.flags_cover & GLASSESCOVERSEYES)) - to_chat(assailant, "You're going to need to remove the eye covering first.") - return - if(!affected.internal_bodyparts_by_name["eyes"]) - to_chat(assailant, "You cannot locate any eyes on [affecting]!") - return - assailant.visible_message("[assailant] presses [assailant.p_their()] fingers into [affecting]'s eyes!") - to_chat(affecting, "You feel immense pain as digits are being pressed into your eyes!") - add_attack_logs(assailant, affecting, "Eye-fucked with their fingers") - var/obj/item/organ/internal/eyes/eyes = affected.get_int_organ(/obj/item/organ/internal/eyes) - eyes.damage += rand(3,4) - if(eyes.damage >= eyes.min_broken_damage) - if(M.stat != 2) - to_chat(M, "You go blind!")*///This is a demonstration of adding a new damaging type based on intent as well as hitzone. - - //This specific example would allow you to squish people's eyes with a GRAB_NECK. - - if(INTENT_DISARM) //This checks that the user is on disarm intent. - /* if(state < GRAB_AGGRESSIVE) - to_chat(assailant, "You require a better grab to do this.") - return - if(!force_down) - assailant.visible_message("[user] is forcing [affecting] to the ground!") - force_down = 1 - affecting.Weaken(3) - affecting.lying = 1 - step_to(assailant, affecting) - assailant.setDir(EAST) //face the victim - affecting.setDir(SOUTH) //face up - return - else - to_chat(assailant, "You are already pinning [affecting] to the ground.") - return*///This is an example of something being done with an agressive grab + disarm intent. - return - - - if(M == assailant && state >= GRAB_AGGRESSIVE) //no eatin unless you have an agressive grab - if(checkvalid(user, affecting)) //wut - var/mob/living/carbon/attacker = user - - if(affecting.buckled) - to_chat(user, "[affecting] is buckled!") - return - - user.visible_message("[user] is attempting to devour \the [affecting]!") - - if(!do_after(user, checktime(user, affecting), target = affecting)) return - - if(affecting.buckled) - to_chat(user, "[affecting] is buckled!") - return - - user.visible_message("[user] devours \the [affecting]!") - if(affecting.mind) - add_attack_logs(attacker, affecting, "Devoured") - - affecting.forceMove(user) - attacker.stomach_contents.Add(affecting) - qdel(src) - -/obj/item/grab/proc/checkvalid(var/mob/attacker, var/mob/prey) //does all the checking for the attack proc to see if a mob can eat another with the grab - if(isalien(attacker) && iscarbon(prey)) //Xenomorphs eating carbon mobs - return 1 - - var/mob/living/carbon/human/H = attacker - if(ishuman(H) && is_type_in_list(prey, H.dna.species.allowed_consumed_mobs)) //species eating of other mobs - return 1 - - return 0 - -/obj/item/grab/proc/checktime(var/mob/attacker, var/mob/prey) //Returns the time the attacker has to wait before they eat the prey - if(isalien(attacker)) - return EAT_TIME_XENO //xenos get a speed boost - - if(istype(prey,/mob/living/simple_animal)) //simple animals get eaten at xeno-eating-speed regardless - return EAT_TIME_ANIMAL - - return EAT_TIME_FAT //if it doesn't fit into the above, it's probably a fat guy, take EAT_TIME_FAT to do it - -/obj/item/grab/Destroy() - if(affecting) - affecting.pixel_x = 0 - affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing - affecting.layer = initial(affecting.layer) - affecting.grabbed_by -= src - affecting = null - if(assailant) - if(assailant.client) - assailant.client.screen -= hud - assailant = null - QDEL_NULL(hud) - return ..() - - -#undef EAT_TIME_XENO -#undef EAT_TIME_FAT - -#undef EAT_TIME_ANIMAL +#define UPGRADE_COOLDOWN 40 +#define UPGRADE_KILL_TIMER 100 + +//times it takes for a mob to eat +#define EAT_TIME_XENO 30 +#define EAT_TIME_FAT 100 + +//time it takes for a mob to be eaten (in deciseconds) (overrides mob eat time) +#define EAT_TIME_ANIMAL 30 + +/obj/item/grab + name = "grab" + flags = NOBLUDGEON | ABSTRACT | DROPDEL + var/obj/screen/grab/hud = null + var/mob/living/affecting = null + var/mob/living/assailant = null + var/state = GRAB_PASSIVE + + var/allow_upgrade = 1 + var/last_upgrade = 0 + var/last_hit_zone = 0 +// var/force_down //determines if the affecting mob will be pinned to the ground //disabled due to balance, kept for an example for any new things. + var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position + + layer = 21 + plane = HUD_PLANE + item_state = "nothing" + icon = 'icons/mob/screen_gen.dmi' + w_class = WEIGHT_CLASS_BULKY + + +/obj/item/grab/New(var/mob/user, var/mob/victim) + ..() + + //Okay, first off, some fucking sanity checking. No user, or no victim, or they are not mobs, no grab. + if(!istype(user) || !istype(victim)) + return + + loc = user + assailant = user + affecting = victim + + if(affecting.anchored) + qdel(src) + return + + affecting.grabbed_by += src + + hud = new /obj/screen/grab(src) + hud.icon_state = "reinforce" + icon_state = "grabbed" + hud.name = "reinforce grab" + hud.master = src + + //check if assailant is grabbed by victim as well + if(assailant.grabbed_by) + for(var/obj/item/grab/G in assailant.grabbed_by) + if(G.assailant == affecting && G.affecting == assailant) + G.dancing = 1 + G.adjust_position() + dancing = 1 + + clean_grabbed_by(assailant, affecting) + adjust_position() + +/obj/item/grab/proc/clean_grabbed_by(var/mob/user, var/mob/victim) //Cleans up any nulls in the grabbed_by list. + if(istype(user)) + + for(var/entry in user.grabbed_by) + if(isnull(entry) || !istype(entry, /obj/item/grab)) //the isnull() here is probably redundant- but it might outperform istype, who knows. Better safe than sorry. + user.grabbed_by -= entry + + if(istype(victim)) + + for(var/entry in victim.grabbed_by) + if(isnull(entry) || !istype(entry, /obj/item/grab)) + victim.grabbed_by -= entry + + return 1 + +//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. +/obj/item/grab/proc/get_mob_if_throwable() + if(affecting && assailant.Adjacent(affecting)) + if(affecting.buckled) + return null + if(state >= GRAB_AGGRESSIVE) + return affecting + return null + +//This makes sure that the grab screen object is displayed in the correct hand. +/obj/item/grab/proc/synch() + if(affecting) + if(assailant.r_hand == src) + hud.screen_loc = ui_rhand + else + hud.screen_loc = ui_lhand + assailant.client.screen += hud + +/obj/item/grab/process() + if(!confirm()) + return //If the confirm fails, the grab is about to be deleted. That means it shouldn't continue processing. + + if(assailant.client) + if(!hud) + return //this somehow can runtime under the right circumstances + assailant.client.screen -= hud + assailant.client.screen += hud + + var/hit_zone = assailant.zone_selected + last_hit_zone = hit_zone + + if(assailant.pulling == affecting) + assailant.stop_pulling() + + if(state <= GRAB_AGGRESSIVE) + allow_upgrade = 1 + //disallow upgrading if we're grabbing more than one person + if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/grab))) + var/obj/item/grab/G = assailant.l_hand + if(G.affecting != affecting) + allow_upgrade = 0 + if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/grab))) + var/obj/item/grab/G = assailant.r_hand + if(G.affecting != affecting) + allow_upgrade = 0 + + //disallow upgrading past aggressive if we're being grabbed aggressively + for(var/obj/item/grab/G in affecting.grabbed_by) + if(G == src) continue + if(G.state >= GRAB_AGGRESSIVE) + allow_upgrade = 0 + + if(allow_upgrade) + if(state < GRAB_AGGRESSIVE) + hud.icon_state = "reinforce" + else + hud.icon_state = "reinforce1" + else + hud.icon_state = "!reinforce" + + if(state >= GRAB_AGGRESSIVE) + if(!HAS_TRAIT(assailant, TRAIT_PACIFISM)) + affecting.drop_r_hand() + affecting.drop_l_hand() + + + //var/announce = 0 + //(hit_zone != last_hit_zone) + //announce = 1 + /* if(ishuman(affecting)) + switch(hit_zone) + /*if("mouth") + if(announce) + assailant.visible_message("[assailant] covers [affecting]'s mouth!") + if(affecting.silent < 3) + affecting.silent = 3 + if("eyes") + if(announce) + assailant.visible_message("[assailant] covers [affecting]'s eyes!") + if(affecting.eye_blind < 3) + affecting.eye_blind = 3*///These are being left in the code as an example for adding new hit-zone based things. + + if(force_down) + if(affecting.loc != assailant.loc) + force_down = 0 + else + affecting.Weaken(3) //This is being left in the code as an example of adding a new variable to do something in grab code. + +*/ + + var/breathing_tube = affecting.get_organ_slot("breathing_tube") + + if(state >= GRAB_NECK) + affecting.Stun(5) //It will hamper your voice, being choked and all. + if(isliving(affecting) && !breathing_tube) + var/mob/living/L = affecting + L.adjustOxyLoss(1) + + if(state >= GRAB_KILL) + //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. + affecting.Stuttering(5) //It will hamper your voice, being choked and all. + affecting.Weaken(5) //Should keep you down unless you get help. + if(!breathing_tube) + affecting.AdjustLoseBreath(2, bound_lower = 0, bound_upper = 3) + + adjust_position() + + +/obj/item/grab/attack_self(mob/user) + s_click(hud) + +//Updating pixelshift, position and direction +//Gets called on process, when the grab gets upgraded or the assailant moves +/obj/item/grab/proc/adjust_position() + if(affecting.buckled) + return + if(affecting.lying && state != GRAB_KILL) + animate(affecting, pixel_x = 0, pixel_y = 0, 5, 1, LINEAR_EASING) + return //KJK + /* if(force_down) //THIS GOES ABOVE THE RETURN LABELED KJK + affecting.setDir(SOUTH)*///This shows how you can apply special directions based on a variable. //face up + + var/shift = 0 + var/adir = get_dir(assailant, affecting) + affecting.layer = 4 + switch(state) + if(GRAB_PASSIVE) + shift = 8 + if(dancing) //look at partner + shift = 10 + assailant.setDir(get_dir(assailant, affecting)) + if(GRAB_AGGRESSIVE) + shift = 12 + if(GRAB_NECK, GRAB_UPGRADING) + shift = -10 + adir = assailant.dir + affecting.setDir(assailant.dir) + affecting.forceMove(assailant.loc) + if(GRAB_KILL) + shift = 0 + adir = 1 + affecting.setDir(SOUTH)//face up + affecting.forceMove(assailant.loc) + + switch(adir) + if(NORTH) + animate(affecting, pixel_x = 0, pixel_y =-shift, 5, 1, LINEAR_EASING) + affecting.layer = 3.9 + if(SOUTH) + animate(affecting, pixel_x = 0, pixel_y = shift, 5, 1, LINEAR_EASING) + if(WEST) + animate(affecting, pixel_x = shift, pixel_y = 0, 5, 1, LINEAR_EASING) + if(EAST) + animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING) + +/obj/item/grab/proc/s_click(obj/screen/S) + if(!affecting) + return + if(state >= GRAB_AGGRESSIVE && HAS_TRAIT(assailant, TRAIT_PACIFISM)) + to_chat(assailant, "You don't want to risk hurting [affecting]!") + return + if(state == GRAB_UPGRADING) + return + if(assailant.next_move > world.time) + return + if(world.time < (last_upgrade + UPGRADE_COOLDOWN)) + return + if(!assailant.canmove || assailant.lying) + qdel(src) + return + + last_upgrade = world.time + + if(state < GRAB_AGGRESSIVE) + if(!allow_upgrade) + return + //if(!affecting.lying) + assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") + /* else + assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") + force_down = 1 + affecting.Weaken(3) + step_to(assailant, affecting) + assailant.setDir(EAST) //face the victim + affecting.setDir(SOUTH) //face up //This is an example of a new feature based on the context of the location of the victim. + */ //It means that upgrading while someone is lying on the ground would cause you to go into pin mode. + state = GRAB_AGGRESSIVE + icon_state = "grabbed1" + hud.icon_state = "reinforce1" + add_attack_logs(assailant, affecting, "Aggressively grabbed", ATKLOG_ALL) + else if(state < GRAB_NECK) + if(isslime(affecting)) + to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") + return + + assailant.visible_message("[assailant] has reinforced [assailant.p_their()] grip on [affecting] (now neck)!") + state = GRAB_NECK + icon_state = "grabbed+1" + assailant.setDir(get_dir(assailant, affecting)) + add_attack_logs(assailant, affecting, "Neck grabbed", ATKLOG_ALL) + if(!iscarbon(assailant)) + affecting.LAssailant = null + else + affecting.LAssailant = assailant + hud.icon_state = "kill" + hud.name = "kill" + affecting.Stun(10) //10 ticks of ensured grab + else if(state < GRAB_UPGRADING) + assailant.visible_message("[assailant] starts to tighten [assailant.p_their()] grip on [affecting]'s neck!") + hud.icon_state = "kill1" + + state = GRAB_KILL + assailant.visible_message("[assailant] has tightened [assailant.p_their()] grip on [affecting]'s neck!") + add_attack_logs(assailant, affecting, "Strangled") + + assailant.next_move = world.time + 10 + if(!affecting.get_organ_slot("breathing_tube")) + affecting.AdjustLoseBreath(1) + affecting.setDir(WEST) + adjust_position() + +//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. +/obj/item/grab/proc/confirm() + if(!assailant || !affecting) + qdel(src) + return 0 + + if(affecting) + if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) + qdel(src) + return 0 + return 1 + + +/obj/item/grab/attack(mob/living/M, mob/living/carbon/user) + if(!affecting) + return + + if(M == affecting) + if(ishuman(M) && ishuman(assailant)) + var/mob/living/carbon/human/affected = affecting + var/mob/living/carbon/human/attacker = assailant + switch(assailant.a_intent) + if(INTENT_HELP) + /*if(force_down) + to_chat(assailant, "You no longer pin [affecting] to the ground.") + force_down = 0 + return*///This is a very basic demonstration of a new feature based on attacking someone with the grab, based on intent. + //This specific example would allow you to stop pinning people to the floor without moving away from them. + return + + if(INTENT_GRAB) + return + + if(INTENT_HARM) //This checks that the user is on harm intent. + if(last_hit_zone == "head") //This checks the hitzone the user has selected. In this specific case, they have the head selected. + if(affecting.lying) + return + assailant.visible_message("[assailant] thrusts [assailant.p_their()] head into [affecting]'s skull!") //A visible message for what is going on. + var/damage = 5 + var/obj/item/clothing/hat = attacker.head + if(istype(hat)) + damage += hat.force * 3 + affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", affected.run_armor_check(affecting, "melee")) + playsound(assailant.loc, "swing_hit", 25, 1, -1) + add_attack_logs(assailant, affecting, "Headbutted") + return + + /*if(last_hit_zone == "eyes") + if(state < GRAB_NECK) + to_chat(assailant, "You require a better grab to do this.") + return + if((affected.head && affected.head.flags_cover & HEADCOVERSEYES) || \ + (affected.wear_mask && affected.wear_mask.flags_cover & MASKCOVERSEYES) || \ + (affected.glasses && affected.glasses.flags_cover & GLASSESCOVERSEYES)) + to_chat(assailant, "You're going to need to remove the eye covering first.") + return + if(!affected.internal_bodyparts_by_name["eyes"]) + to_chat(assailant, "You cannot locate any eyes on [affecting]!") + return + assailant.visible_message("[assailant] presses [assailant.p_their()] fingers into [affecting]'s eyes!") + to_chat(affecting, "You feel immense pain as digits are being pressed into your eyes!") + add_attack_logs(assailant, affecting, "Eye-fucked with their fingers") + var/obj/item/organ/internal/eyes/eyes = affected.get_int_organ(/obj/item/organ/internal/eyes) + eyes.damage += rand(3,4) + if(eyes.damage >= eyes.min_broken_damage) + if(M.stat != 2) + to_chat(M, "You go blind!")*///This is a demonstration of adding a new damaging type based on intent as well as hitzone. + + //This specific example would allow you to squish people's eyes with a GRAB_NECK. + + if(INTENT_DISARM) //This checks that the user is on disarm intent. + /* if(state < GRAB_AGGRESSIVE) + to_chat(assailant, "You require a better grab to do this.") + return + if(!force_down) + assailant.visible_message("[user] is forcing [affecting] to the ground!") + force_down = 1 + affecting.Weaken(3) + affecting.lying = 1 + step_to(assailant, affecting) + assailant.setDir(EAST) //face the victim + affecting.setDir(SOUTH) //face up + return + else + to_chat(assailant, "You are already pinning [affecting] to the ground.") + return*///This is an example of something being done with an agressive grab + disarm intent. + return + + + if(M == assailant && state >= GRAB_AGGRESSIVE) //no eatin unless you have an agressive grab + if(checkvalid(user, affecting)) //wut + var/mob/living/carbon/attacker = user + + if(affecting.buckled) + to_chat(user, "[affecting] is buckled!") + return + + user.visible_message("[user] is attempting to devour \the [affecting]!") + + if(!do_after(user, checktime(user, affecting), target = affecting)) return + + if(affecting.buckled) + to_chat(user, "[affecting] is buckled!") + return + + user.visible_message("[user] devours \the [affecting]!") + if(affecting.mind) + add_attack_logs(attacker, affecting, "Devoured") + + affecting.forceMove(user) + attacker.stomach_contents.Add(affecting) + qdel(src) + +/obj/item/grab/proc/checkvalid(var/mob/attacker, var/mob/prey) //does all the checking for the attack proc to see if a mob can eat another with the grab + if(isalien(attacker) && iscarbon(prey)) //Xenomorphs eating carbon mobs + return 1 + + var/mob/living/carbon/human/H = attacker + if(ishuman(H) && is_type_in_list(prey, H.dna.species.allowed_consumed_mobs)) //species eating of other mobs + return 1 + + return 0 + +/obj/item/grab/proc/checktime(var/mob/attacker, var/mob/prey) //Returns the time the attacker has to wait before they eat the prey + if(isalien(attacker)) + return EAT_TIME_XENO //xenos get a speed boost + + if(istype(prey,/mob/living/simple_animal)) //simple animals get eaten at xeno-eating-speed regardless + return EAT_TIME_ANIMAL + + return EAT_TIME_FAT //if it doesn't fit into the above, it's probably a fat guy, take EAT_TIME_FAT to do it + +/obj/item/grab/Destroy() + if(affecting) + affecting.pixel_x = 0 + affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing + affecting.layer = initial(affecting.layer) + affecting.grabbed_by -= src + affecting = null + if(assailant) + if(assailant.client) + assailant.client.screen -= hud + assailant = null + QDEL_NULL(hud) + return ..() + + +#undef EAT_TIME_XENO +#undef EAT_TIME_FAT + +#undef EAT_TIME_ANIMAL diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index a96a832fef4..1e0f31ac6fe 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -1,683 +1,686 @@ -/proc/issmall(A) - if(A && istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = A - if(H.dna.species && H.dna.species.is_small) - return 1 - return 0 - -/proc/ispet(A) - if(istype(A, /mob/living/simple_animal)) - var/mob/living/simple_animal/SA = A - if(SA.can_collar) - return 1 - return 0 - -/mob/proc/isSynthetic() - return 0 - -/mob/living/carbon/human/isSynthetic() - if(ismachine(src)) - return TRUE - return FALSE - -/mob/proc/get_screen_colour() - -/mob/proc/update_client_colour(var/time = 10) //Update the mob's client.color with an animation the specified time in length. - if(!client) //No client_colour without client. If the player logs back in they'll be back through here anyway. - return - client.colour_transition(get_screen_colour(), time = time) //Get the colour matrix we're going to transition to depending on relevance (magic glasses first, eyes second). - -/mob/living/carbon/human/get_screen_colour() //Fetch the colour matrix from wherever (e.g. eyes) so it can be compared to client.color. - . = ..() - if(.) - return . - - var/obj/item/clothing/glasses/worn_glasses = glasses - var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) - if(istype(worn_glasses) && worn_glasses.color_view) //Check to see if they got those magic glasses and they're augmenting the colour of what the wearer sees. If they're not, color_view should be null. - return worn_glasses.color_view - else if(eyes) //If they're not, check to see if their eyes got one of them there colour matrices. Will be null if eyes are robotic/the mob isn't colourblind and they have no default colour matrix. - return eyes.get_colourmatrix() - -/proc/ismindshielded(A) //Checks to see if the person contains a mindshield implant, then checks that the implant is actually inside of them - for(var/obj/item/implant/mindshield/L in A) - if(L && L.implanted) - return 1 - return 0 - -/proc/ismindslave(A) //Checks to see if the person contains a mindslave implant, then checks that the implant is actually inside of them - for(var/obj/item/implant/traitor/T in A) - if(T && T.implanted) - return 1 - return 0 - -/proc/isLivingSSD(mob/M) - return istype(M) && M.player_logged && M.stat != DEAD - -/proc/isAntag(A) - if(istype(A, /mob/living/carbon)) - var/mob/living/carbon/C = A - if(C.mind && C.mind.special_role) - return 1 - return 0 - -/proc/isNonCrewAntag(A) - if(!isAntag(A)) - return 0 - - var/mob/living/carbon/C = A - var/special_role = C.mind.special_role - var/list/crew_roles = list( - SPECIAL_ROLE_BLOB, - SPECIAL_ROLE_CULTIST, - SPECIAL_ROLE_CHANGELING, - SPECIAL_ROLE_ERT, - SPECIAL_ROLE_HEAD_REV, - SPECIAL_ROLE_REV, - SPECIAL_ROLE_SHADOWLING, - SPECIAL_ROLE_SHADOWLING_THRALL, - SPECIAL_ROLE_TRAITOR, - SPECIAL_ROLE_VAMPIRE, - SPECIAL_ROLE_VAMPIRE_THRALL - ) - if(special_role in crew_roles) - return 0 - - return 1 - -/proc/cannotPossess(A) - var/mob/dead/observer/G = A - if(G.has_enabled_antagHUD == 1 && config.antag_hud_restricted) - return 1 - return 0 - - -/proc/iscuffed(A) - if(istype(A, /mob/living/carbon)) - var/mob/living/carbon/C = A - if(C.handcuffed) - return 1 - return 0 - -/proc/hassensorlevel(A, var/level) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode >= level - return 0 - -/proc/getsensorlevel(A) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode - return SUIT_SENSOR_OFF - -/proc/offer_control(mob/M) - to_chat(M, "Control of your mob has been offered to dead players.") - log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.") - var/minhours = input(usr, "Minimum hours required to play [M]?", "Set Min Hrs", 10) as num - message_admins("[key_name_admin(usr)] has offered control of ([key_name_admin(M)]) to ghosts with [minhours] hrs playtime") - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [M.real_name ? M.real_name : M.name]?", poll_time = 100, min_hours = minhours) - var/mob/dead/observer/theghost = null - - if(LAZYLEN(candidates)) - theghost = pick(candidates) - to_chat(M, "Your mob has been taken over by a ghost!") - message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])") - M.ghostize() - M.key = theghost.key - else - to_chat(M, "There were no ghosts willing to take control.") - message_admins("No ghosts were willing to take control of [key_name_admin(M)])") - -/proc/check_zone(zone) - if(!zone) return "chest" - switch(zone) - if("eyes") - zone = "head" - if("mouth") - zone = "head" - return zone - -// Returns zone with a certain probability. -// If the probability misses, returns "chest" instead. -// If "chest" was passed in as zone, then on a "miss" will return "head", "l_arm", or "r_arm" -// Do not use this if someone is intentionally trying to hit a specific body part. -// Use get_zone_with_miss_chance() for that. -/proc/ran_zone(zone, probability = 80) - - zone = check_zone(zone) - - if(prob(probability)) - return zone - - var/t = rand(1, 18) // randomly pick a different zone, or maybe the same one - switch(t) - if(1) return "head" - if(2) return "chest" - if(3 to 4) return "l_arm" - if(5 to 6) return "l_hand" - if(7 to 8) return "r_arm" - if(9 to 10) return "r_hand" - if(11 to 12) return "l_leg" - if(13 to 14) return "l_foot" - if(15 to 16) return "r_leg" - if(17 to 18) return "r_foot" - - return zone - -/proc/above_neck(zone) - var/list/zones = list("head", "mouth", "eyes") - if(zones.Find(zone)) - return 1 - else - return 0 - -/proc/stars(n, pr) - if(pr == null) - pr = 25 - if(pr <= 0) - return null - else - if(pr >= 100) - return n - var/te = n - var/t = "" - n = length(n) - var/p = null - p = 1 - while(p <= n) - if((copytext(te, p, p + 1) == " " || prob(pr))) - t = text("[][]", t, copytext(te, p, p + 1)) - else - t = text("[]*", t) - p++ - return t - -/proc/stars_all(list/message_pieces, pr) - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = stars(S.message, pr) - -/proc/slur(phrase, var/list/slurletters = ("'"))//use a different list as an input if you want to make robots slur with $#@%! characters - phrase = html_decode(phrase) - var/leng=length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(rand(1,3)==3) - if(lowertext(newletter)=="o") newletter="u" - if(lowertext(newletter)=="s") newletter="ch" - if(lowertext(newletter)=="a") newletter="ah" - if(lowertext(newletter)=="c") newletter="k" - switch(rand(1,15)) - if(1,3,5,8) newletter="[lowertext(newletter)]" - if(2,4,6,15) newletter="[uppertext(newletter)]" - if(7) newletter+=pick(slurletters) - //if(9,10) newletter="[newletter]" - //if(11,12) newletter="[newletter]" - //if(13) newletter="[newletter]" - newphrase+="[newletter]" - counter-=1 - return newphrase - -/proc/stutter(n) - var/te = html_decode(n) - var/t = ""//placed before the message. Not really sure what it's for. - n = length(n)//length of the entire word - var/p = null - p = 1//1 is the start of any word - while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. - var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. - if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) - if(prob(10)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. - else - if(prob(20)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]") - else - if(prob(5)) - n_letter = null - else - n_letter = text("[n_letter]-[n_letter]") - t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. - p++//for each letter p is increased to find where the next letter will be. - return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - -/proc/robostutter(n) //for robutts - var/te = html_decode(n) - var/t = ""//placed before the message. Not really sure what it's for. - n = length(n)//length of the entire word - var/p = null - p = 1//1 is the start of any word - while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. - var/robotletter = pick("@", "!", "#", "$", "%", "&", "?") //for beep boop - var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. - if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) - if(prob(10)) - n_letter = text("[n_letter]-[robotletter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. - else - if(prob(20)) - n_letter = text("[n_letter]-[robotletter]-[n_letter]") - else - if(prob(5)) - n_letter = robotletter - else - n_letter = text("[n_letter]-[n_letter]") - t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. - p++//for each letter p is increased to find where the next letter will be. - return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - - -/proc/Gibberish(t, p)//t is the inputted message, and any value higher than 70 for p will cause letters to be replaced instead of added - /* Turn text into complete gibberish! */ - var/returntext = "" - for(var/i = 1, i <= length(t), i++) - - var/letter = copytext(t, i, i+1) - if(prob(50)) - if(p >= 70) - letter = "" - - for(var/j = 1, j <= rand(0, 2), j++) - letter += pick("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*") - - returntext += letter - - return returntext - -/proc/Gibberish_all(list/message_pieces, p) - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = Gibberish(S.message, p) - - -proc/muffledspeech(phrase) - phrase = html_decode(phrase) - var/leng=length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(newletter in list(" ", "!", "?", ".", ",")) - //do nothing - else if(lowertext(newletter) in list("a", "e", "i", "o", "u", "y")) - newletter = "ph" - else - newletter = "m" - newphrase+="[newletter]" - counter-=1 - return newphrase - -/proc/muffledspeech_all(list/message_pieces) - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = muffledspeech(S.message) - - -/proc/shake_camera(mob/M, duration, strength=1) - if(!M || !M.client || M.shakecamera) - return - M.shakecamera = 1 - spawn(1) - - var/atom/oldeye=M.client.eye - var/aiEyeFlag = 0 - if(istype(oldeye, /mob/camera/aiEye)) - aiEyeFlag = 1 - - var/x - for(x=0; x
    ") - return - else - if(alert(src, "You sure you want to sleep for a while?", "Sleep", "Yes", "No") == "Yes") - SetSleeping(20) //Short nap - -/mob/living/verb/lay_down() - set name = "Rest" - set category = "IC" - - if(!resting) - client.move_delay = world.time + 20 - to_chat(src, "You are now resting.") - StartResting() - else if(resting) - to_chat(src, "You are now getting up.") - StopResting() - -/proc/get_multitool(mob/user as mob) - // Get tool - var/obj/item/multitool/P - if(isrobot(user) || ishuman(user)) - P = user.get_active_hand() - else if(isAI(user)) - var/mob/living/silicon/ai/AI=user - P = AI.aiMulti - - if(!istype(P)) - return null - return P - -/proc/get_both_hands(mob/living/carbon/M) - return list(M.l_hand, M.r_hand) - - -//Direct dead say used both by emote and say -//It is somewhat messy. I don't know what to do. -//I know you can't see the change, but I rewrote the name code. It is significantly less messy now -/proc/say_dead_direct(var/message, var/mob/subject = null) - var/name - var/keyname - if(subject && subject.client) - var/client/C = subject.client - keyname = (C.holder && C.holder.fakekey) ? C.holder.fakekey : C.key - if(C.mob) //Most of the time this is the dead/observer mob; we can totally use him if there is no better name - var/mindname - var/realname = C.mob.real_name - if(C.mob.mind) - mindname = C.mob.mind.name - if(C.mob.mind.original && C.mob.mind.original.real_name) - realname = C.mob.mind.original.real_name - if(mindname && mindname != realname) - name = "[realname] died as [mindname]" - else - name = realname - - for(var/mob/M in GLOB.player_list) - if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(CHAT_DEAD)) - var/follow - var/lname - if(subject) - if(subject != M) - follow = "([ghost_follow_link(subject, ghost=M)]) " - if(M.stat != DEAD && check_rights(R_ADMIN|R_MOD,0,M)) - follow = "([admin_jump_link(subject)]) " - var/mob/dead/observer/DM - if(istype(subject, /mob/dead/observer)) - DM = subject - if(check_rights(R_ADMIN|R_MOD,0,M)) // What admins see - lname = "[keyname][(DM && DM.client && DM.client.prefs.ghost_anonsay) ? "*" : (DM ? "" : "^")] ([name])" - else - if(DM && DM.client && DM.client.prefs.ghost_anonsay) // If the person is actually observer they have the option to be anonymous - lname = "Ghost of [name]" - else if(DM) // Non-anons - lname = "[keyname] ([name])" - else // Everyone else (dead people who didn't ghost yet, etc.) - lname = name - lname = "[lname] " - to_chat(M, "[lname][follow][message]") - -/proc/notify_ghosts(message, ghost_sound = null, enter_link = null, title = null, atom/source = null, image/alert_overlay = null, flashwindow = TRUE, var/action = NOTIFY_JUMP) //Easy notification of ghosts. - for(var/mob/dead/observer/O in GLOB.player_list) - if(O.client) - to_chat(O, "[message][(enter_link) ? " [enter_link]" : ""]") - if(ghost_sound) - O << sound(ghost_sound) - if(flashwindow) - window_flash(O.client) - if(source) - var/obj/screen/alert/notify_action/A = O.throw_alert("\ref[source]_notify_action", /obj/screen/alert/notify_action) - if(A) - if(O.client.prefs && O.client.prefs.UI_style) - A.icon = ui_style2icon(O.client.prefs.UI_style) - if(title) - A.name = title - A.desc = message - A.action = action - A.target = source - if(!alert_overlay) - var/old_layer = source.layer - var/old_plane = source.plane - source.layer = FLOAT_LAYER - source.plane = FLOAT_PLANE - A.overlays += source - source.layer = old_layer - source.plane = old_plane - else - alert_overlay.layer = FLOAT_LAYER - alert_overlay.plane = FLOAT_PLANE - A.overlays += alert_overlay - -/mob/proc/switch_to_camera(var/obj/machinery/camera/C) - if(!C.can_use() || stat || (get_dist(C, src) > 1 || machine != src || !has_vision() || !canmove)) - return 0 - check_eye(src) - return 1 - -/mob/proc/rename_character(oldname, newname) - if(!newname) - return 0 - real_name = newname - name = newname - if(mind) - mind.name = newname - if(dna) - dna.real_name = real_name - - if(oldname) - //update the datacore records! This is goig to be a bit costly. - for(var/list/L in list(data_core.general,data_core.medical,data_core.security,data_core.locked)) - for(var/datum/data/record/R in L) - if(R.fields["name"] == oldname) - R.fields["name"] = newname - break - - //update our pda and id if we have them on our person - var/list/searching = GetAllContents(searchDepth = 3) - var/search_id = 1 - var/search_pda = 1 - - for(var/A in searching) - if( search_id && istype(A,/obj/item/card/id) ) - var/obj/item/card/id/ID = A - if(ID.registered_name == oldname) - ID.registered_name = newname - ID.name = "[newname]'s ID Card ([ID.assignment])" - ID.RebuildHTML() - if(!search_pda) break - search_id = 0 - - else if( search_pda && istype(A,/obj/item/pda) ) - var/obj/item/pda/PDA = A - if(PDA.owner == oldname) - PDA.owner = newname - PDA.name = "PDA-[newname] ([PDA.ownjob])" - if(!search_id) break - search_pda = 0 - - //Fixes renames not being reflected in objective text - var/length - var/pos - for(var/datum/objective/objective in GLOB.all_objectives) - if(!mind || objective.target != mind) - continue - length = length(oldname) - pos = findtextEx(objective.explanation_text, oldname) - objective.explanation_text = copytext(objective.explanation_text, 1, pos)+newname+copytext(objective.explanation_text, pos+length) - return 1 - -/mob/proc/rename_self(var/role, var/allow_numbers = FALSE, var/force = FALSE) - spawn(0) - var/oldname = real_name - - var/time_passed = world.time - var/newname - - for(var/i=1,i<=3,i++) //we get 3 attempts to pick a suitable name. - if(force) - newname = clean_input("Pick a new name.", "Name Change", oldname, src) - else - newname = clean_input("You are a [role]. Would you like to change your name to something else? (You have 3 minutes to select a new name.)", "Name Change", oldname, src) - if(((world.time - time_passed) > 1800) && !force) - alert(src, "Unfortunately, more than 3 minutes have passed for selecting your name. If you are a robot, use the Namepick verb; otherwise, adminhelp.", "Name Change") - return //took too long - newname = reject_bad_name(newname,allow_numbers) //returns null if the name doesn't meet some basic requirements. Tidies up a few other things like bad-characters. - - for(var/mob/living/M in GLOB.player_list) - if(M == src) - continue - if(!newname || M.real_name == newname) - newname = null - break - if(newname) - break //That's a suitable name! - to_chat(src, "Sorry, that [role]-name wasn't appropriate, please try another. It's possibly too long/short, has bad characters or is already taken.") - - if(!newname) //we'll stick with the oldname then - return - - rename_character(oldname, newname) - -/proc/cultslur(n) // Inflicted on victims of a stun talisman - var/phrase = html_decode(n) - var/leng = length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(rand(1,2)==2) - if(lowertext(newletter)=="o") - newletter="u" - if(lowertext(newletter)=="t") - newletter="ch" - if(lowertext(newletter)=="a") - newletter="ah" - if(lowertext(newletter)=="u") - newletter="oo" - if(lowertext(newletter)=="c") - newletter=" NAR " - if(lowertext(newletter)=="s") - newletter=" SIE " - if(rand(1,4)==4) - if(newletter==" ") - newletter=" no hope... " - if(newletter=="H") - newletter=" IT COMES... " - - switch(rand(1,15)) - if(1) - newletter="'" - if(2) - newletter+="agn" - if(3) - newletter="fth" - if(4) - newletter="nglu" - if(5) - newletter="glor" - newphrase+="[newletter]";counter-=1 - return newphrase - -/mob/proc/get_preference(toggleflag) - if(!client) - return FALSE - if(!client.prefs) - log_runtime(EXCEPTION("Mob '[src]', ckey '[ckey]' is missing a prefs datum on the client!")) - return FALSE - // Cast to 1/0 - return !!(client.prefs.toggles & toggleflag) - -// Used to make sure that a player has a valid job preference setup, used to knock players out of eligibility for anything if their prefs don't make sense. -// A "valid job preference setup" in this situation means at least having one job set to low, or not having "return to lobby" enabled -// Prevents "antag rolling" by setting antag prefs on, all jobs to never, and "return to lobby if preferences not availible" -// Doing so would previously allow you to roll for antag, then send you back to lobby if you didn't get an antag role -// This also does some admin notification and logging as well -/mob/proc/has_valid_preferences() - if(!client) - return FALSE //Not sure how this would get run without the mob having a client, but let's just be safe. - if(client.prefs.alternate_option != RETURN_TO_LOBBY) - return TRUE - // If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so. - var/has_antags = FALSE - if(client.prefs.be_special.len > 0) - has_antags = TRUE - if(!client.prefs.check_any_job()) - to_chat(src, "You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.") - if(has_antags) - log_admin("[src.ckey] just got booted back to lobby with no jobs, but antags enabled.") - message_admins("[src.ckey] just got booted back to lobby with no jobs enabled, but antag rolling enabled. Likely antag rolling abuse.") - return FALSE //This is the only case someone should actually be completely blocked from antag rolling as well - return TRUE - -#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider)) +/proc/issmall(A) + if(A && istype(A, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = A + if(H.dna.species && H.dna.species.is_small) + return 1 + return 0 + +/proc/ispet(A) + if(istype(A, /mob/living/simple_animal)) + var/mob/living/simple_animal/SA = A + if(SA.can_collar) + return 1 + return 0 + +/mob/proc/isSynthetic() + return 0 + +/mob/living/carbon/human/isSynthetic() + if(ismachine(src)) + return TRUE + return FALSE + +/mob/proc/get_screen_colour() + +/mob/proc/update_client_colour(var/time = 10) //Update the mob's client.color with an animation the specified time in length. + if(!client) //No client_colour without client. If the player logs back in they'll be back through here anyway. + return + client.colour_transition(get_screen_colour(), time = time) //Get the colour matrix we're going to transition to depending on relevance (magic glasses first, eyes second). + +/mob/living/carbon/human/get_screen_colour() //Fetch the colour matrix from wherever (e.g. eyes) so it can be compared to client.color. + . = ..() + if(.) + return . + + var/obj/item/clothing/glasses/worn_glasses = glasses + var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) + if(istype(worn_glasses) && worn_glasses.color_view) //Check to see if they got those magic glasses and they're augmenting the colour of what the wearer sees. If they're not, color_view should be null. + return worn_glasses.color_view + else if(eyes) //If they're not, check to see if their eyes got one of them there colour matrices. Will be null if eyes are robotic/the mob isn't colourblind and they have no default colour matrix. + return eyes.get_colourmatrix() + +/proc/ismindshielded(A) //Checks to see if the person contains a mindshield implant, then checks that the implant is actually inside of them + for(var/obj/item/implant/mindshield/L in A) + if(L && L.implanted) + return 1 + return 0 + +/proc/ismindslave(A) //Checks to see if the person contains a mindslave implant, then checks that the implant is actually inside of them + for(var/obj/item/implant/traitor/T in A) + if(T && T.implanted) + return 1 + return 0 + +/proc/isLivingSSD(mob/M) + return istype(M) && M.player_logged && M.stat != DEAD + +/proc/isAntag(A) + if(istype(A, /mob/living/carbon)) + var/mob/living/carbon/C = A + if(C.mind && C.mind.special_role) + return 1 + return 0 + +/proc/isNonCrewAntag(A) + if(!isAntag(A)) + return 0 + + var/mob/living/carbon/C = A + var/special_role = C.mind.special_role + var/list/crew_roles = list( + SPECIAL_ROLE_BLOB, + SPECIAL_ROLE_CULTIST, + SPECIAL_ROLE_CHANGELING, + SPECIAL_ROLE_ERT, + SPECIAL_ROLE_HEAD_REV, + SPECIAL_ROLE_REV, + SPECIAL_ROLE_SHADOWLING, + SPECIAL_ROLE_SHADOWLING_THRALL, + SPECIAL_ROLE_TRAITOR, + SPECIAL_ROLE_VAMPIRE, + SPECIAL_ROLE_VAMPIRE_THRALL + ) + if(special_role in crew_roles) + return 0 + + return 1 + +/proc/cannotPossess(A) + var/mob/dead/observer/G = A + if(G.has_enabled_antagHUD == 1 && config.antag_hud_restricted) + return 1 + return 0 + + +/proc/iscuffed(A) + if(istype(A, /mob/living/carbon)) + var/mob/living/carbon/C = A + if(C.handcuffed) + return 1 + return 0 + +/proc/hassensorlevel(A, var/level) + var/mob/living/carbon/human/H = A + if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + return U.sensor_mode >= level + return 0 + +/proc/getsensorlevel(A) + var/mob/living/carbon/human/H = A + if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + return U.sensor_mode + return SUIT_SENSOR_OFF + +/proc/offer_control(mob/M) + to_chat(M, "Control of your mob has been offered to dead players.") + log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.") + var/minhours = input(usr, "Minimum hours required to play [M]?", "Set Min Hrs", 10) as num + message_admins("[key_name_admin(usr)] has offered control of ([key_name_admin(M)]) to ghosts with [minhours] hrs playtime") + var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]" + if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes") + question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]" + var/list/mob/dead/observer/candidates = pollCandidates("[question]?", poll_time = 100, min_hours = minhours) + var/mob/dead/observer/theghost = null + + if(LAZYLEN(candidates)) + theghost = pick(candidates) + to_chat(M, "Your mob has been taken over by a ghost!") + message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])") + M.ghostize() + M.key = theghost.key + else + to_chat(M, "There were no ghosts willing to take control.") + message_admins("No ghosts were willing to take control of [key_name_admin(M)])") + +/proc/check_zone(zone) + if(!zone) return "chest" + switch(zone) + if("eyes") + zone = "head" + if("mouth") + zone = "head" + return zone + +// Returns zone with a certain probability. +// If the probability misses, returns "chest" instead. +// If "chest" was passed in as zone, then on a "miss" will return "head", "l_arm", or "r_arm" +// Do not use this if someone is intentionally trying to hit a specific body part. +// Use get_zone_with_miss_chance() for that. +/proc/ran_zone(zone, probability = 80) + + zone = check_zone(zone) + + if(prob(probability)) + return zone + + var/t = rand(1, 18) // randomly pick a different zone, or maybe the same one + switch(t) + if(1) return "head" + if(2) return "chest" + if(3 to 4) return "l_arm" + if(5 to 6) return "l_hand" + if(7 to 8) return "r_arm" + if(9 to 10) return "r_hand" + if(11 to 12) return "l_leg" + if(13 to 14) return "l_foot" + if(15 to 16) return "r_leg" + if(17 to 18) return "r_foot" + + return zone + +/proc/above_neck(zone) + var/list/zones = list("head", "mouth", "eyes") + if(zones.Find(zone)) + return 1 + else + return 0 + +/proc/stars(n, pr) + if(pr == null) + pr = 25 + if(pr <= 0) + return null + else + if(pr >= 100) + return n + var/te = n + var/t = "" + n = length(n) + var/p = null + p = 1 + while(p <= n) + if((copytext(te, p, p + 1) == " " || prob(pr))) + t = text("[][]", t, copytext(te, p, p + 1)) + else + t = text("[]*", t) + p++ + return t + +/proc/stars_all(list/message_pieces, pr) + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = stars(S.message, pr) + +/proc/slur(phrase, var/list/slurletters = ("'"))//use a different list as an input if you want to make robots slur with $#@%! characters + phrase = html_decode(phrase) + var/leng=length(phrase) + var/counter=length(phrase) + var/newphrase="" + var/newletter="" + while(counter>=1) + newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) + if(rand(1,3)==3) + if(lowertext(newletter)=="o") newletter="u" + if(lowertext(newletter)=="s") newletter="ch" + if(lowertext(newletter)=="a") newletter="ah" + if(lowertext(newletter)=="c") newletter="k" + switch(rand(1,15)) + if(1,3,5,8) newletter="[lowertext(newletter)]" + if(2,4,6,15) newletter="[uppertext(newletter)]" + if(7) newletter+=pick(slurletters) + //if(9,10) newletter="[newletter]" + //if(11,12) newletter="[newletter]" + //if(13) newletter="[newletter]" + newphrase+="[newletter]" + counter-=1 + return newphrase + +/proc/stutter(n) + var/te = html_decode(n) + var/t = ""//placed before the message. Not really sure what it's for. + n = length(n)//length of the entire word + var/p = null + p = 1//1 is the start of any word + while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. + var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. + if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) + if(prob(10)) + n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. + else + if(prob(20)) + n_letter = text("[n_letter]-[n_letter]-[n_letter]") + else + if(prob(5)) + n_letter = null + else + n_letter = text("[n_letter]-[n_letter]") + t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. + p++//for each letter p is increased to find where the next letter will be. + return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + +/proc/robostutter(n) //for robutts + var/te = html_decode(n) + var/t = ""//placed before the message. Not really sure what it's for. + n = length(n)//length of the entire word + var/p = null + p = 1//1 is the start of any word + while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. + var/robotletter = pick("@", "!", "#", "$", "%", "&", "?") //for beep boop + var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. + if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) + if(prob(10)) + n_letter = text("[n_letter]-[robotletter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. + else + if(prob(20)) + n_letter = text("[n_letter]-[robotletter]-[n_letter]") + else + if(prob(5)) + n_letter = robotletter + else + n_letter = text("[n_letter]-[n_letter]") + t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. + p++//for each letter p is increased to find where the next letter will be. + return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + + +/proc/Gibberish(t, p)//t is the inputted message, and any value higher than 70 for p will cause letters to be replaced instead of added + /* Turn text into complete gibberish! */ + var/returntext = "" + for(var/i = 1, i <= length(t), i++) + + var/letter = copytext(t, i, i+1) + if(prob(50)) + if(p >= 70) + letter = "" + + for(var/j = 1, j <= rand(0, 2), j++) + letter += pick("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*") + + returntext += letter + + return returntext + +/proc/Gibberish_all(list/message_pieces, p) + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = Gibberish(S.message, p) + + +proc/muffledspeech(phrase) + phrase = html_decode(phrase) + var/leng=length(phrase) + var/counter=length(phrase) + var/newphrase="" + var/newletter="" + while(counter>=1) + newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) + if(newletter in list(" ", "!", "?", ".", ",")) + //do nothing + else if(lowertext(newletter) in list("a", "e", "i", "o", "u", "y")) + newletter = "ph" + else + newletter = "m" + newphrase+="[newletter]" + counter-=1 + return newphrase + +/proc/muffledspeech_all(list/message_pieces) + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = muffledspeech(S.message) + + +/proc/shake_camera(mob/M, duration, strength=1) + if(!M || !M.client || M.shakecamera) + return + M.shakecamera = 1 + spawn(1) + + var/atom/oldeye=M.client.eye + var/aiEyeFlag = 0 + if(istype(oldeye, /mob/camera/aiEye)) + aiEyeFlag = 1 + + var/x + for(x=0; x