diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0aadbbdb6d8c..28ec31372b6d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,6 +4,9 @@ # In the event that multiple org members are to be informed of changes # to the same file or dir, add them to the end under Multiple Owners +# bobbahbrown +/code/datums/chatmessage.dm @bobbahbrown + # ChangelingRain /code/__DEFINES/clockcult.dm @ChangelingRain diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e9fe49241a92..faf566c8d053 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -408,6 +408,7 @@ Do not add any of the following in a Pull Request or risk getting the PR closed: * National Socialist Party of Germany content, National Socialist Party of Germany related content, or National Socialist Party of Germany references * Code where one line of code is split across mutiple lines (except for multiple, separate strings and comments; in those cases, existing longer lines must not be split up) * Code adding, removing, or updating the availability of alien races/species/human mutants without prior approval. Pull requests attempting to add or remove features from said races/species/mutants require prior approval as well. +* Code which violates GitHub's [terms of service](https://github.com/site/terms). Just because something isn't on this list doesn't mean that it's acceptable. Use common sense above all else. diff --git a/.github/POLICYCONFIG.md b/.github/POLICYCONFIG.md new file mode 100644 index 000000000000..0e5e46260baf --- /dev/null +++ b/.github/POLICYCONFIG.md @@ -0,0 +1,34 @@ +Welcome to this short guide to the POLICY config mechanism. + +You are probably reading this guide because you have been informed your antagonist or ghost role needs to support policy configuration. + +## Requirements +It is a requirement of /tg/station development that all ghost roles, antags, minor antags and event mobs of any kind must support the policy system when implemented. + +## What is policy configuration +Policy configuration is a json file that the administrators of a server can edit, which contains a dictionary of keywords -> string message. + +The policy text for a specific keyword should be displayed when relevant and appropriate, to allow server administrators to define the broad strokes of policy for some feature or mob. + +It is okay to provide a default text when the config is not set, but you are required to provide the config in all cases of a ghost role or an antagonist, or minor event. + +If you're in doubt about needing to support policy config, I suggest doing it anyway. This should replace all flavour text, ghost spawn messages and so forth that the player (i.e client) sees upon entering the role, mob, or feature that is meant to dictate how they are permitted to be played as/with. + +## What does this mean? + +Concretely, it means you need to display to the client taking control of the mob or ghost role a string of text, pulled via keyword from the policy config system. + +You can access the string of text through the `get_policy(keyword)` proc, this takes a single keyword argument, which should be a text string unique to your feature. + +This will return a configured string of text, or blank/null if no policy string is set. + +This is also accessible to the user if they use `/client/verb/policy()` which will display to them a list of all the policy texts for keywords applicable to the mob, you can add/modify the list of keywords by modifying the `get_policy_keywords()` proc of a mob type where that is relevant. + +### Example +Here is a simple example taken from the slime pyroclastic event +``` +var/policy = get_policy(ROLE_PYROCLASTIC_SLIME) +if (policy) + to_chat(S, policy) +``` +It's recommended to use a define for your policy keyword to make it easily changeable by a developer diff --git a/.github/workflows/round_id_linker.yml b/.github/workflows/round_id_linker.yml new file mode 100644 index 000000000000..cfb850527885 --- /dev/null +++ b/.github/workflows/round_id_linker.yml @@ -0,0 +1,12 @@ +name: "Round ID Linker" +on: + issues: + types: [opened] + +jobs: + link_rounds: + runs-on: ubuntu-latest + steps: + - uses: tgstation/round_linker@master + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index d1aa9b4b6705..0778251cdf2f 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,8 @@ jobs: - uses: actions/stale@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-pr-message: "This PR has been inactive for long enough to be automatically marked as stale. This means it is at risk of being closed by a maintainer if it is not updated or reviews are not addressed. If your PR is closed as stale, feel free to open a new one after dealing with the issues. This may also be an indication that the maintainers do not have interest in this change, you can try to convince them otherwise, or persist in the doomed world you have created." + stale-pr-message: "This PR has been inactive for long enough to be automatically marked as stale. This means it is at risk of being auto closed in ~ 7 days, please address any outstanding review items and ensure your PR is finished, if these are all true and you are auto-staled anyway, you need to actively ask maintainers if your PR will be merged. Once you have done any of the previous actions then you should request a maintainer remove the stale label on your PR, to reset the stale timer. If you feel no maintainer will respond in that time, you may wish to close this PR youself, while you seek maintainer comment, as you will then be able to reopen the PR yourself" days-before-stale: 7 + days-before-close: 7 stale-pr-label: 'Stale' exempt-pr-label: 'RED LABEL' diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000000..11d27b55d347 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,11 @@ +build: + stage: build + rules: + - if: $CI_MERGE_REQUEST_IID || $CI_COMMIT_REF_NAME == "master" + changes: + - tgui/**/*.js + - tgui/**/*.scss + when: always + image: node:lts + script: + - tgui/bin/tgui --ci diff --git a/.vscode/settings.json b/.vscode/settings.json index 0ca4d58b4c69..67e90f1d388c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,12 @@ { "eslint.workingDirectories": [ - "./tgui" - ] + "./tgui" + ], + + "workbench.editorAssociations": [ + { + "filenamePattern": "*.dmi", + "viewType": "imagePreview.previewEditor" + } + ] } diff --git a/README.md b/README.md index cbd92e35b712..b9fd02b02c6c 100644 --- a/README.md +++ b/README.md @@ -141,8 +141,12 @@ Web delivery of game resources makes it quicker for players to join and reduces ## CONTRIBUTING +[/tg/station HACKMD account](https://hackmd.io/@tgstation) - Design documentation here + [Documenting your code](.github/AUTODOC_GUIDE.md) +[Policy configuration system](.github/POLICYCONFIG.md) + ## CODEBASE CREDITS * /tg/, for the codebase @@ -164,9 +168,6 @@ All code before [commit 333c566b88108de218d882840e61928a9b759d8f on 2014/31/12 a See LICENSE and GPLv3.txt for more details. -tgui clientside is licensed as a subproject under the MIT license. -Font Awesome font files, used by tgui, are licensed under the SIL Open Font License v1.1 -tgui assets are licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/). The TGS3 API is licensed as a subproject under the MIT license. See the footers of code/\_\_DEFINES/server\_tools.dm, code/modules/server\_tools/st\_commands.dm, and code/modules/server\_tools/st\_inteface.dm for the MIT license. diff --git a/SQL/database_changelog.txt b/SQL/database_changelog.txt index a18e4ef3a6c2..56b040b42ede 100644 --- a/SQL/database_changelog.txt +++ b/SQL/database_changelog.txt @@ -10,14 +10,62 @@ In any query remember to add a prefix to the table names if you use one. ----------------------------------------------------- -Version 5.9 27 March 2020 by MarkSuckerberg +Version 5.9, 19 April 2020, by Jordie0608 +Updates and improvements to poll handling. +Added the `deleted` column to tables 'poll_option', 'poll_textreply' and 'poll_vote' and the columns `created_datetime`, `subtitle`, `allow_revoting` and `deleted` to 'poll_question'. +Changes table 'poll_question' column `createdby_ckey` to be NOT NULL and index `idx_pquest_time_admin` to be `idx_pquest_time_deleted_id` and 'poll_textreply' column `adminrank` to have no default. +Added procedure `set_poll_deleted` that's called when deleting a poll to set deleted to true on each poll table where rows matching a poll_id argument. + +ALTER TABLE `poll_option` + ADD COLUMN `deleted` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `default_percentage_calc`; + +ALTER TABLE `poll_question` + CHANGE COLUMN `createdby_ckey` `createdby_ckey` VARCHAR(32) NOT NULL AFTER `multiplechoiceoptions`, + ADD COLUMN `created_datetime` datetime NOT NULL AFTER `polltype`, + ADD COLUMN `subtitle` VARCHAR(255) NULL DEFAULT NULL AFTER `question`, + ADD COLUMN `allow_revoting` TINYINT(1) UNSIGNED NOT NULL AFTER `dontshow`, + ADD COLUMN `deleted` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `allow_revoting`, + DROP INDEX `idx_pquest_time_admin`, + ADD INDEX `idx_pquest_time_deleted_id` (`starttime`, `endtime`, `deleted`, `id`); + +ALTER TABLE `poll_textreply` + CHANGE COLUMN `adminrank` `adminrank` varchar(32) NOT NULL AFTER `replytext`, + ADD COLUMN `deleted` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `adminrank`; + +ALTER TABLE `poll_vote` + ADD COLUMN `deleted` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `rating`; + +DELIMITER $$ +CREATE PROCEDURE `set_poll_deleted`( + IN `poll_id` INT +) +SQL SECURITY INVOKER +BEGIN +UPDATE `poll_question` SET deleted = 1 WHERE id = poll_id; +UPDATE `poll_option` SET deleted = 1 WHERE pollid = poll_id; +UPDATE `poll_vote` SET deleted = 1 WHERE pollid = poll_id; +UPDATE `poll_textreply` SET deleted = 1 WHERE pollid = poll_id; +END +$$ +DELIMITER ; + +----------------------------------------------------- + +Version 5.8, 7 April 2020, by Jordie0608 +Modified table `messages`, adding column `deleted_ckey` to record who deleted a message. + +ALTER TABLE `messages` ADD COLUMN `deleted_ckey` VARCHAR(32) NULL DEFAULT NULL AFTER `deleted`; + +----------------------------------------------------- + +Version 5.72 27 March 2020 by MarkSuckerberg Added Metacoin and Antag token support. ALTER TABLE `player` ADD COLUMN (`antag_tokens` tinyint(4) unsigned DEFAULT '0', `metacoins` int(10) unsigned NOT NULL DEFAULT '0'); ----------------------------------------------------- -Version 5.8 16 Febuary 2020 by MarkSuckerberg +Version 5.71 16 Febuary 2020 by MarkSuckerberg Added Mentor table and Mentor Memo table from Oracle. DROP TABLE IF EXISTS `mentor`; @@ -37,7 +85,6 @@ CREATE TABLE `mentor_memo` ( PRIMARY KEY (`ckey`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; - ----------------------------------------------------- Version 5.7, 10 January 2020 by Atlanta-Ned diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql index 6630dfdebbbb..713099e62e67 100644 --- a/SQL/tgstation_schema.sql +++ b/SQL/tgstation_schema.sql @@ -258,6 +258,7 @@ CREATE TABLE `messages` ( `lasteditor` varchar(32) DEFAULT NULL, `edits` text, `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', + `deleted_ckey` VARCHAR(32) NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_msg_ckey_time` (`targetckey`,`timestamp`, `deleted`), KEY `idx_msg_type_ckeys_time` (`type`,`targetckey`,`adminckey`,`timestamp`, `deleted`), @@ -347,6 +348,7 @@ CREATE TABLE `poll_option` ( `descmid` varchar(32) DEFAULT NULL, `descmax` varchar(32) DEFAULT NULL, `default_percentage_calc` tinyint(1) unsigned NOT NULL DEFAULT '1', + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_pop_pollid` (`pollid`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; @@ -362,17 +364,21 @@ DROP TABLE IF EXISTS `poll_question`; CREATE TABLE `poll_question` ( `id` int(11) NOT NULL AUTO_INCREMENT, `polltype` enum('OPTION','TEXT','NUMVAL','MULTICHOICE','IRV') NOT NULL, + `created_datetime` datetime NOT NULL, `starttime` datetime NOT NULL, `endtime` datetime NOT NULL, `question` varchar(255) NOT NULL, + `subtitle` varchar(255) DEFAULT NULL, `adminonly` tinyint(1) unsigned NOT NULL, `multiplechoiceoptions` int(2) DEFAULT NULL, - `createdby_ckey` varchar(32) DEFAULT NULL, + `createdby_ckey` varchar(32) NOT NULL, `createdby_ip` int(10) unsigned NOT NULL, `dontshow` tinyint(1) unsigned NOT NULL, + `allow_revoting` tinyint(1) unsigned NOT NULL, + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_pquest_question_time_ckey` (`question`,`starttime`,`endtime`,`createdby_ckey`,`createdby_ip`), - KEY `idx_pquest_time_admin` (`starttime`,`endtime`,`adminonly`), + KEY `idx_pquest_time_deleted_id` (`starttime`,`endtime`, `deleted`, `id`), KEY `idx_pquest_id_time_type_admin` (`id`,`starttime`,`endtime`,`polltype`,`adminonly`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; @@ -391,7 +397,8 @@ CREATE TABLE `poll_textreply` ( `ckey` varchar(32) NOT NULL, `ip` int(10) unsigned NOT NULL, `replytext` varchar(2048) NOT NULL, - `adminrank` varchar(32) NOT NULL DEFAULT 'Player', + `adminrank` varchar(32) NOT NULL, + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_ptext_pollid_ckey` (`pollid`,`ckey`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; @@ -413,6 +420,7 @@ CREATE TABLE `poll_vote` ( `ip` int(10) unsigned NOT NULL, `adminrank` varchar(32) NOT NULL, `rating` int(2) DEFAULT NULL, + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_pvote_pollid_ckey` (`pollid`,`ckey`), KEY `idx_pvote_optionid_ckey` (`optionid`,`ckey`) @@ -456,18 +464,6 @@ CREATE TABLE `schema_revision` ( PRIMARY KEY (`major`, `minor`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -DELIMITER $$ -CREATE TRIGGER `role_timeTlogupdate` AFTER UPDATE ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (NEW.CKEY, NEW.job, NEW.minutes-OLD.minutes); -END -$$ -CREATE TRIGGER `role_timeTloginsert` AFTER INSERT ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (NEW.ckey, NEW.job, NEW.minutes); -END -$$ -CREATE TRIGGER `role_timeTlogdelete` AFTER DELETE ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (OLD.ckey, OLD.job, 0-OLD.minutes); -END -$$ -DELIMITER ; - -- -- Table structure for table `stickyban` -- @@ -557,6 +553,29 @@ CREATE TABLE `ticket` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +DELIMITER $$ +CREATE PROCEDURE `set_poll_deleted`( + IN `poll_id` INT +) +SQL SECURITY INVOKER +BEGIN +UPDATE `poll_question` SET deleted = 1 WHERE id = poll_id; +UPDATE `poll_option` SET deleted = 1 WHERE pollid = poll_id; +UPDATE `poll_vote` SET deleted = 1 WHERE pollid = poll_id; +UPDATE `poll_textreply` SET deleted = 1 WHERE pollid = poll_id; +END +$$ +CREATE TRIGGER `role_timeTlogupdate` AFTER UPDATE ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (NEW.CKEY, NEW.job, NEW.minutes-OLD.minutes); +END +$$ +CREATE TRIGGER `role_timeTloginsert` AFTER INSERT ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (NEW.ckey, NEW.job, NEW.minutes); +END +$$ +CREATE TRIGGER `role_timeTlogdelete` AFTER DELETE ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (OLD.ckey, OLD.job, 0-OLD.minutes); +END +$$ +DELIMITER ; + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; diff --git a/SQL/tgstation_schema_prefixed.sql b/SQL/tgstation_schema_prefixed.sql index 2088156816c9..4c42ea7189cb 100644 --- a/SQL/tgstation_schema_prefixed.sql +++ b/SQL/tgstation_schema_prefixed.sql @@ -258,6 +258,7 @@ CREATE TABLE `SS13_messages` ( `lasteditor` varchar(32) DEFAULT NULL, `edits` text, `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', + `deleted_ckey` VARCHAR(32) NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_msg_ckey_time` (`targetckey`,`timestamp`, `deleted`), KEY `idx_msg_type_ckeys_time` (`type`,`targetckey`,`adminckey`,`timestamp`, `deleted`), @@ -347,6 +348,7 @@ CREATE TABLE `SS13_poll_option` ( `descmid` varchar(32) DEFAULT NULL, `descmax` varchar(32) DEFAULT NULL, `default_percentage_calc` tinyint(1) unsigned NOT NULL DEFAULT '1', + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_pop_pollid` (`pollid`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; @@ -362,17 +364,21 @@ DROP TABLE IF EXISTS `SS13_poll_question`; CREATE TABLE `SS13_poll_question` ( `id` int(11) NOT NULL AUTO_INCREMENT, `polltype` enum('OPTION','TEXT','NUMVAL','MULTICHOICE','IRV') NOT NULL, + `created_datetime` datetime NOT NULL, `starttime` datetime NOT NULL, `endtime` datetime NOT NULL, `question` varchar(255) NOT NULL, + `subtitle` varchar(255) DEFAULT NULL, `adminonly` tinyint(1) unsigned NOT NULL, `multiplechoiceoptions` int(2) DEFAULT NULL, - `createdby_ckey` varchar(32) DEFAULT NULL, + `createdby_ckey` varchar(32) NOT NULL, `createdby_ip` int(10) unsigned NOT NULL, `dontshow` tinyint(1) unsigned NOT NULL, + `allow_revoting` tinyint(1) unsigned NOT NULL, + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_pquest_question_time_ckey` (`question`,`starttime`,`endtime`,`createdby_ckey`,`createdby_ip`), - KEY `idx_pquest_time_admin` (`starttime`,`endtime`,`adminonly`), + KEY `idx_pquest_time_deleted_id` (`starttime`,`endtime`, `deleted`, `id`), KEY `idx_pquest_id_time_type_admin` (`id`,`starttime`,`endtime`,`polltype`,`adminonly`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; @@ -391,7 +397,8 @@ CREATE TABLE `SS13_poll_textreply` ( `ckey` varchar(32) NOT NULL, `ip` int(10) unsigned NOT NULL, `replytext` varchar(2048) NOT NULL, - `adminrank` varchar(32) NOT NULL DEFAULT 'Player', + `adminrank` varchar(32) NOT NULL, + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_ptext_pollid_ckey` (`pollid`,`ckey`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; @@ -413,6 +420,7 @@ CREATE TABLE `SS13_poll_vote` ( `ip` int(10) unsigned NOT NULL, `adminrank` varchar(32) NOT NULL, `rating` int(2) DEFAULT NULL, + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_pvote_pollid_ckey` (`pollid`,`ckey`), KEY `idx_pvote_optionid_ckey` (`optionid`,`ckey`) @@ -456,18 +464,6 @@ CREATE TABLE `SS13_schema_revision` ( PRIMARY KEY (`major`,`minor`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -DELIMITER $$ -CREATE TRIGGER `SS13_role_timeTlogupdate` AFTER UPDATE ON `SS13_role_time` FOR EACH ROW BEGIN INSERT into SS13_role_time_log (ckey, job, delta) VALUES (NEW.CKEY, NEW.job, NEW.minutes-OLD.minutes); -END -$$ -CREATE TRIGGER `SS13_role_timeTloginsert` AFTER INSERT ON `SS13_role_time` FOR EACH ROW BEGIN INSERT into SS13_role_time_log (ckey, job, delta) VALUES (NEW.ckey, NEW.job, NEW.minutes); -END -$$ -CREATE TRIGGER `SS13_role_timeTlogdelete` AFTER DELETE ON `SS13_role_time` FOR EACH ROW BEGIN INSERT into SS13_role_time_log (ckey, job, delta) VALUES (OLD.ckey, OLD.job, 0-OLD.minutes); -END -$$ -DELIMITER ; - -- -- Table structure for table `SS13_stickyban` -- @@ -557,6 +553,29 @@ CREATE TABLE `SS13_ticket` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +DELIMITER $$ +CREATE PROCEDURE `set_poll_deleted`( + IN `poll_id` INT +) +SQL SECURITY INVOKER +BEGIN +UPDATE `SS13_poll_question` SET deleted = 1 WHERE id = poll_id; +UPDATE `SS13_poll_option` SET deleted = 1 WHERE pollid = poll_id; +UPDATE `SS13_poll_vote` SET deleted = 1 WHERE pollid = poll_id; +UPDATE `SS13_poll_textreply` SET deleted = 1 WHERE pollid = poll_id; +END +$$ +CREATE TRIGGER `SS13_role_timeTlogupdate` AFTER UPDATE ON `SS13_role_time` FOR EACH ROW BEGIN INSERT into SS13_role_time_log (ckey, job, delta) VALUES (NEW.CKEY, NEW.job, NEW.minutes-OLD.minutes); +END +$$ +CREATE TRIGGER `SS13_role_timeTloginsert` AFTER INSERT ON `SS13_role_time` FOR EACH ROW BEGIN INSERT into SS13_role_time_log (ckey, job, delta) VALUES (NEW.ckey, NEW.job, NEW.minutes); +END +$$ +CREATE TRIGGER `SS13_role_timeTlogdelete` AFTER DELETE ON `SS13_role_time` FOR EACH ROW BEGIN INSERT into SS13_role_time_log (ckey, job, delta) VALUES (OLD.ckey, OLD.job, 0-OLD.minutes); +END +$$ +DELIMITER ; + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; diff --git a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm index dc0bffe8060f..0c0e5362d26c 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm @@ -1438,7 +1438,7 @@ /area/ruin/powered/beach) "Ut" = ( /obj/effect/turf_decal/sand, -/obj/structure/sign/poster/contraband/sun_kist{ +/obj/structure/sign/poster/contraband/starkist{ pixel_y = 32 }, /turf/open/floor/plating/beach/sand, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm index 08b9c6facd50..f5c859b0ebe7 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm @@ -65,7 +65,7 @@ /obj/item/melee/cultblade/dagger, /obj/effect/step_trigger/sound_effect{ happens_once = 1; - name = "a grave mistake"; + name = "\proper a grave mistake"; sound = 'sound/hallucinations/i_see_you1.ogg'; triggerer_only = 1 }, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm index a3c1094586dd..80ea56799225 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm @@ -290,13 +290,6 @@ }, /turf/open/floor/plasteel/dark, /area/ruin/powered/seedvault) -"aP" = ( -/obj/machinery/plantgenes/seedvault{ - pixel_y = 6 - }, -/obj/structure/table/wood, -/turf/open/floor/vault, -/area/ruin/powered/seedvault) "aQ" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel/freezer, @@ -700,6 +693,13 @@ }, /turf/open/floor/plasteel/freezer, /area/ruin/powered/seedvault) +"QJ" = ( +/obj/machinery/plantgenes/seedvault{ + pixel_y = 6 + }, +/obj/structure/table/wood, +/turf/open/floor/vault, +/area/ruin/powered/seedvault) "Uz" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 1 @@ -1075,7 +1075,7 @@ aX aE aA aA -aP +QJ ac bm br diff --git a/_maps/RandomRuins/SpaceRuins/bus.dmm b/_maps/RandomRuins/SpaceRuins/bus.dmm index 75fd103c73a2..1d94954d348a 100644 --- a/_maps/RandomRuins/SpaceRuins/bus.dmm +++ b/_maps/RandomRuins/SpaceRuins/bus.dmm @@ -62,7 +62,7 @@ /area/ruin/unpowered/no_grav) "an" = ( /obj/structure/fluff/bus/passable/seat, -/obj/item/reagent_containers/food/snacks/faggot, +/obj/item/reagent_containers/food/snacks/meatball, /turf/open/floor/plasteel/airless/dark{ icon_state = "bus" }, @@ -83,7 +83,7 @@ /area/ruin/unpowered/no_grav) "aq" = ( /obj/structure/fluff/bus/passable/seat, -/obj/item/reagent_containers/food/snacks/faggot, +/obj/item/reagent_containers/food/snacks/meatball, /obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel/airless/dark{ icon_state = "bus" @@ -146,9 +146,9 @@ "aA" = ( /obj/structure/fluff/bus/passable, /obj/structure/closet/crate/freezer, -/obj/item/reagent_containers/food/snacks/faggot, -/obj/item/reagent_containers/food/snacks/faggot, -/obj/item/reagent_containers/food/snacks/faggot, +/obj/item/reagent_containers/food/snacks/meatball, +/obj/item/reagent_containers/food/snacks/meatball, +/obj/item/reagent_containers/food/snacks/meatball, /turf/open/floor/plasteel/airless/dark{ icon_state = "bus" }, @@ -170,7 +170,7 @@ /area/ruin/unpowered/no_grav) "aD" = ( /obj/structure/fluff/bus/passable, -/obj/item/reagent_containers/food/snacks/faggot, +/obj/item/reagent_containers/food/snacks/meatball, /turf/open/floor/plasteel/airless/dark{ icon_state = "bus" }, @@ -242,7 +242,7 @@ /turf/open/floor/plating/asteroid/airless, /area/ruin/unpowered/no_grav) "aP" = ( -/obj/item/reagent_containers/food/snacks/faggot, +/obj/item/reagent_containers/food/snacks/meatball, /turf/open/floor/plating/asteroid/airless, /area/ruin/unpowered/no_grav) "aQ" = ( @@ -266,7 +266,7 @@ "aU" = ( /obj/structure/fluff/bus/passable/seat, /obj/effect/decal/cleanable/dirt, -/obj/item/reagent_containers/food/snacks/faggot, +/obj/item/reagent_containers/food/snacks/meatball, /turf/open/floor/plasteel/airless/dark{ icon_state = "bus" }, @@ -297,7 +297,7 @@ "aZ" = ( /obj/structure/fluff/bus/passable, /obj/effect/decal/cleanable/dirt, -/obj/item/reagent_containers/food/snacks/faggot, +/obj/item/reagent_containers/food/snacks/meatball, /turf/open/floor/plasteel/airless/dark{ icon_state = "bus" }, diff --git a/_maps/RandomRuins/SpaceRuins/forgottenship.dmm b/_maps/RandomRuins/SpaceRuins/forgottenship.dmm index 671747103b88..84dbc9b5bd16 100644 --- a/_maps/RandomRuins/SpaceRuins/forgottenship.dmm +++ b/_maps/RandomRuins/SpaceRuins/forgottenship.dmm @@ -418,7 +418,6 @@ /obj/machinery/mineral/ore_redemption{ name = "Syndicate ore redemption machine"; ore_multiplier = 4; - ore_pickup_rate = 20; req_access = list(150) }, /turf/open/floor/mineral/plastitanium, diff --git a/_maps/RandomRuins/SpaceRuins/spacegym.dmm b/_maps/RandomRuins/SpaceRuins/spacegym.dmm index 99d092e8d070..419dcefccd42 100644 --- a/_maps/RandomRuins/SpaceRuins/spacegym.dmm +++ b/_maps/RandomRuins/SpaceRuins/spacegym.dmm @@ -46,7 +46,7 @@ }, /area/ruin/space/has_grav/spacegym) "m" = ( -/obj/item/reagent_containers/food/snacks/faggot, +/obj/item/reagent_containers/food/snacks/meatball, /turf/open/floor/plasteel/airless, /area/ruin/space/has_grav/spacegym) "n" = ( diff --git a/_maps/RandomZLevels/SnowCabin.dmm b/_maps/RandomZLevels/SnowCabin.dmm index 0db6bdac2c8e..1d957c8b78a3 100644 --- a/_maps/RandomZLevels/SnowCabin.dmm +++ b/_maps/RandomZLevels/SnowCabin.dmm @@ -1634,7 +1634,7 @@ id = "TheRealSecretPlace"; name = "\improper Tear In The Fabric of Reality" }, -/obj/structure/sign/plaques/golden{ +/obj/structure/plaque/static_plaque/golden{ desc = "Holding the record for about 500 years now."; name = "The Most Annoying Organization Ever"; pixel_y = 32 @@ -3378,14 +3378,6 @@ name = "soviet floor" }, /area/awaymission/cabin/caves/sovietcave) -"jL" = ( -/obj/structure/table/wood, -/obj/machinery/plantgenes{ - name = "tree DNA manipulator"; - pixel_y = 5 - }, -/turf/open/floor/wood/cold, -/area/awaymission/cabin/lumbermill) "jM" = ( /obj/machinery/space_heater, /turf/open/floor/wood/cold, @@ -3595,7 +3587,7 @@ /turf/open/floor/wood, /area/awaymission/cabin/caves) "kn" = ( -/obj/structure/sign/basic{ +/obj/structure/sign{ pixel_x = 32 }, /turf/open/floor/plating/asteroid/snow{ @@ -5459,6 +5451,14 @@ }, /turf/open/floor/plating, /area/awaymission/cabin) +"WB" = ( +/obj/structure/table/wood, +/obj/machinery/plantgenes{ + name = "tree DNA manipulator"; + pixel_y = 5 + }, +/turf/open/floor/wood/cold, +/area/awaymission/cabin/lumbermill) (1,1,1) = {" ab @@ -38986,7 +38986,7 @@ ad af af ak -jL +WB af kd kc diff --git a/_maps/RandomZLevels/caves.dmm b/_maps/RandomZLevels/caves.dmm index 8172b4435d5d..a001d8df7541 100644 --- a/_maps/RandomZLevels/caves.dmm +++ b/_maps/RandomZLevels/caves.dmm @@ -187,7 +187,7 @@ }, /obj/item/coin/antagtoken, /obj/item/book/granter/spell/summonitem{ - name = "an extremely flamboyant book" + name = "\proper an extremely flamboyant book" }, /turf/open/floor/engine/cult{ initial_gas_mix = "n2=23;o2=14" @@ -635,13 +635,8 @@ /obj/structure/closet/crate/miningcar{ name = "Mining cart" }, -/obj/item/pickaxe{ - attack_verb = list("ineffectively hit"); - desc = "A pickaxe thats been left to rust."; - force = 1; - name = "rusty pickaxe"; - pixel_x = 5; - throwforce = 1 +/obj/item/pickaxe/rusted{ + pixel_x = 5 }, /turf/open/floor/plating/asteroid/basalt{ initial_gas_mix = "n2=23;o2=14" @@ -853,8 +848,8 @@ /area/awaymission/caves/BMP_asteroid/level_two) "cK" = ( /obj/structure/sign/warning/securearea{ - desc = "A warning sign which reads 'HOLY SHIT NIGGA WHAT ARE YOU DOING'."; - name = "\improper HOLY SHIT NIGGA WHAT ARE YOU DOING" + desc = "A warning sign which reads 'HOLY SHIT SWAGMAN WHAT ARE YOU DOING'."; + name = "\improper HOLY SHIT SWAGMAN WHAT ARE YOU DOING" }, /turf/closed/wall, /area/awaymission/caves/BMP_asteroid/level_two) @@ -895,13 +890,8 @@ }, /area/awaymission/caves/research) "cQ" = ( -/obj/item/pickaxe{ - attack_verb = list("ineffectively hit"); - desc = "A pickaxe thats been left to rust."; - force = 1; - name = "rusty pickaxe"; - pixel_x = 5; - throwforce = 1 +/obj/item/pickaxe/rusted{ + pixel_x = 5 }, /turf/open/floor/plating/asteroid/basalt{ initial_gas_mix = "n2=23;o2=14" @@ -1097,13 +1087,8 @@ /area/awaymission/caves/BMP_asteroid/level_two) "ds" = ( /obj/structure/closet/secure_closet/personal, -/obj/item/pickaxe{ - attack_verb = list("ineffectively hit"); - desc = "A pickaxe thats been left to rust."; - force = 1; - name = "rusty pickaxe"; - pixel_x = 5; - throwforce = 1 +/obj/item/pickaxe/rusted{ + pixel_x = 5 }, /turf/open/floor/plasteel, /area/awaymission/caves/BMP_asteroid/level_two) @@ -1492,21 +1477,11 @@ /area/awaymission/caves/listeningpost) "eP" = ( /obj/structure/table, -/obj/item/pickaxe{ - attack_verb = list("ineffectively hit"); - desc = "A pickaxe thats been left to rust."; - force = 1; - name = "rusty pickaxe"; - pixel_x = 5; - throwforce = 1 +/obj/item/pickaxe/rusted{ + pixel_x = 5 }, -/obj/item/pickaxe{ - attack_verb = list("ineffectively hit"); - desc = "A pickaxe thats been left to rust."; - force = 1; - name = "rusty pickaxe"; - pixel_x = 5; - throwforce = 1 +/obj/item/pickaxe/rusted{ + pixel_x = 5 }, /turf/open/floor/plasteel, /area/awaymission/caves/listeningpost) @@ -1872,13 +1847,8 @@ /obj/structure/closet/crate/miningcar{ name = "Mining cart" }, -/obj/item/pickaxe{ - attack_verb = list("ineffectively hit"); - desc = "A pickaxe thats been left to rust."; - force = 1; - name = "rusty pickaxe"; - pixel_x = 5; - throwforce = 1 +/obj/item/pickaxe/rusted{ + pixel_x = 5 }, /obj/item/stack/sheet/mineral/adamantine{ amount = 15 diff --git a/_maps/RandomZLevels/research.dmm b/_maps/RandomZLevels/research.dmm index f4abee683826..ee01c748c4a3 100644 --- a/_maps/RandomZLevels/research.dmm +++ b/_maps/RandomZLevels/research.dmm @@ -1140,7 +1140,7 @@ /obj/structure/closet/crate, /obj/item/disk/data{ desc = "A data disk used to store cloning and genetic records. The name on the label appears to be scratched off."; - fields = list("label" = "Buffer1:Kr-$$@##", "UI" = "f8f603857000f930127c4", "SE" = "414401462231053131010241514651403453121613263463440351136366", "UE" = "340008485c321e542aed4df7032ac04d", "name" = "Krystal Symers", "blood_type" = "A+"); + genetic_makeup_buffer = list("label" = "Buffer1:Kr-$$@##", "UI" = "f8f603857000f930127c4", "SE" = "414401462231053131010241514651403453121613263463440351136366", "UE" = "340008485c321e542aed4df7032ac04d", "name" = "Krystal Symers", "blood_type" = "A+"); name = "dusty genetics data disk"; read_only = 1 }, @@ -1500,7 +1500,7 @@ /obj/structure/closet/crate, /obj/item/disk/data{ desc = "A data disk used to store cloning and genetic records. The name on the label appears to be scratched off with the words 'DO NOT CLONE' hastily written over it."; - fields = list("label" = "Buffer1:George Melons", "UI" = "3c300f11b5421ca7014d8", "SE" = "430431205660551642142504334461413202111310233445620533134255", "UE" = "6893e6a0b0076a41897776b10cc2b324", "name" = "George Melons", "blood_type" = "B+"); + genetic_makeup_buffer = list("label" = "Buffer1:George Melons", "UI" = "3c300f11b5421ca7014d8", "SE" = "430431205660551642142504334461413202111310233445620533134255", "UE" = "6893e6a0b0076a41897776b10cc2b324", "name" = "George Melons", "blood_type" = "B+"); name = "old genetics data disk" }, /obj/item/disk/data{ @@ -2202,7 +2202,7 @@ /turf/open/floor/plasteel/white, /area/awaymission/research/interior/security) "fv" = ( -/obj/structure/sign/plaques/golden{ +/obj/structure/plaque/static_plaque/golden{ pixel_x = 32 }, /obj/effect/turf_decal/tile/red{ diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index d5291fecdbbc..3595cf923a02 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -3325,7 +3325,7 @@ /turf/open/floor/plasteel, /area/security/brig) "ajd" = ( -/obj/structure/sign/plaques/golden{ +/obj/structure/plaque/static_plaque/golden{ pixel_y = 32 }, /obj/effect/turf_decal/tile/red{ @@ -40713,7 +40713,7 @@ /area/engine/break_room) "iYt" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/structure/sign/plaques/atmos{ +/obj/structure/plaque/static_plaque/atmos{ pixel_y = -32 }, /turf/open/floor/plasteel, diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 8832459d2122..b7a5c980646f 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -19676,7 +19676,7 @@ /turf/open/floor/plasteel, /area/engine/atmos) "beE" = ( -/obj/structure/sign/plaques/atmos, +/obj/structure/plaque/static_plaque/atmos, /turf/closed/wall, /area/engine/atmos) "beK" = ( @@ -25966,7 +25966,7 @@ /turf/open/floor/plasteel, /area/security/main) "bwh" = ( -/obj/structure/sign/plaques/golden{ +/obj/structure/plaque/static_plaque/golden{ pixel_y = -32 }, /obj/effect/turf_decal/tile/red, @@ -28231,7 +28231,7 @@ /obj/item/book{ desc = "An undeniably handy book."; icon_state = "bookknock"; - name = "A Simpleton's Guide to Safe-cracking with Stethoscopes" + name = "\improper A Simpleton's Guide to Safe-cracking with Stethoscopes" }, /obj/item/stack/sheet/mineral/diamond, /obj/item/stack/spacecash/c1000, @@ -34578,7 +34578,7 @@ /turf/open/floor/wood, /area/crew_quarters/heads/captain) "bQW" = ( -/obj/structure/sign/plaques/golden/captain{ +/obj/structure/plaque/static_plaque/golden/captain{ pixel_x = 32 }, /turf/open/floor/wood, diff --git a/_maps/map_files/Donutstation/Donutstation.dmm b/_maps/map_files/Donutstation/Donutstation.dmm index e9eb1db96631..0f384d3624ff 100644 --- a/_maps/map_files/Donutstation/Donutstation.dmm +++ b/_maps/map_files/Donutstation/Donutstation.dmm @@ -22255,7 +22255,7 @@ /turf/open/floor/plasteel/dark, /area/ai_monitored/storage/eva) "bje" = ( -/obj/structure/sign/plaques/atmos{ +/obj/structure/plaque/static_plaque/atmos{ pixel_y = 32 }, /turf/open/floor/plasteel, @@ -22266,7 +22266,7 @@ }, /obj/effect/turf_decal/tile/red, /obj/effect/landmark/start/security_officer, -/obj/structure/sign/plaques/golden{ +/obj/structure/plaque/static_plaque/golden{ pixel_y = -32 }, /turf/open/floor/plasteel, @@ -47668,7 +47668,7 @@ /turf/open/floor/carpet, /area/chapel/main) "rhZ" = ( -/obj/structure/sign/plaques/golden/captain{ +/obj/structure/plaque/static_plaque/golden/captain{ pixel_y = 32 }, /obj/machinery/camera{ diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index be2529130393..e35be9aec59b 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -4569,7 +4569,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 1 }, -/obj/structure/sign/plaques/golden/captain{ +/obj/structure/plaque/static_plaque/golden/captain{ pixel_y = -32 }, /obj/effect/turf_decal/bot, @@ -7433,7 +7433,7 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{ dir = 4 }, -/obj/structure/sign/plaques/atmos, +/obj/structure/plaque/static_plaque/atmos, /turf/closed/wall/rust, /area/engine/atmos) "aCV" = ( @@ -23008,7 +23008,7 @@ /turf/open/floor/plasteel/dark, /area/lawoffice) "bxm" = ( -/obj/structure/sign/plaques/atmos, +/obj/structure/plaque/static_plaque/atmos, /turf/closed/wall/rust, /area/engine/atmos) "bxn" = ( @@ -74949,7 +74949,7 @@ /obj/machinery/light{ dir = 8 }, -/obj/structure/sign/plaques/golden{ +/obj/structure/plaque/static_plaque/golden{ pixel_x = -32 }, /obj/structure/cable{ diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 47b27e50177b..c738c76ec8ca 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -8348,7 +8348,7 @@ /obj/item/book{ desc = "An undeniably handy book."; icon_state = "bookknock"; - name = "A Simpleton's Guide to Safe-cracking with Stethoscopes" + name = "\improper A Simpleton's Guide to Safe-cracking with Stethoscopes" }, /obj/effect/turf_decal/bot_white/left, /obj/effect/turf_decal/tile/neutral{ @@ -76178,7 +76178,7 @@ dir = 4 }, /obj/machinery/computer/atmos_control, -/obj/structure/sign/plaques/atmos{ +/obj/structure/plaque/static_plaque/atmos{ pixel_y = 32 }, /obj/machinery/atmospherics/pipe/simple/orange/hidden{ diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index d9ee3771995c..d9575abc2762 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -2495,7 +2495,7 @@ /area/security/main) "ajn" = ( /obj/structure/table, -/obj/structure/sign/plaques/golden{ +/obj/structure/plaque/static_plaque/golden{ pixel_y = 32 }, /obj/machinery/light{ @@ -30106,7 +30106,7 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ dir = 4 }, -/obj/structure/sign/plaques/atmos{ +/obj/structure/plaque/static_plaque/atmos{ pixel_y = 32 }, /obj/machinery/light{ diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index d9bc43bcc366..f812baa30f1f 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -6890,20 +6890,6 @@ }, /turf/open/floor/plasteel, /area/centcom/supplypod/loading/ert) -"pE" = ( -/obj/machinery/plantgenes/seedvault, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/obj/effect/turf_decal/tile/green, -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/turf/open/floor/plasteel/white, -/area/centcom/holding) "pF" = ( /obj/machinery/door/airlock/centcom{ name = "Auxillary Dock"; @@ -7260,7 +7246,7 @@ /area/centcom/ferry) "ql" = ( /obj/structure/dresser, -/obj/structure/sign/plaques/golden/captain{ +/obj/structure/plaque/static_plaque/golden/captain{ pixel_x = 32 }, /obj/effect/turf_decal/tile/neutral{ @@ -14425,7 +14411,7 @@ /area/tdome/tdomeobserve) "GL" = ( /obj/structure/table/wood, -/obj/structure/sign/plaques/golden{ +/obj/structure/plaque/static_plaque/golden{ pixel_y = 32 }, /obj/item/clothing/accessory/lawyers_badge{ @@ -14449,7 +14435,7 @@ /area/tdome/tdomeobserve) "GO" = ( /obj/structure/table/wood, -/obj/structure/sign/plaques/golden{ +/obj/structure/plaque/static_plaque/golden{ pixel_y = 32 }, /obj/item/clothing/accessory/medal/silver{ @@ -14725,7 +14711,7 @@ /area/tdome/tdomeobserve) "Hr" = ( /obj/structure/table/wood, -/obj/structure/sign/plaques/thunderdome{ +/obj/structure/plaque/static_plaque/thunderdome{ pixel_y = -32 }, /obj/item/clothing/accessory/medal/gold{ @@ -14751,7 +14737,7 @@ /area/tdome/tdomeobserve) "Ht" = ( /obj/structure/table/wood, -/obj/structure/sign/plaques/thunderdome{ +/obj/structure/plaque/static_plaque/thunderdome{ pixel_y = -32 }, /obj/item/clothing/accessory/medal{ @@ -18258,6 +18244,20 @@ }, /turf/open/floor/plasteel/freezer, /area/syndicate_mothership/control) +"Wl" = ( +/obj/machinery/plantgenes/seedvault, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/obj/effect/turf_decal/tile/green, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/centcom/holding) "Wm" = ( /obj/structure/window/reinforced{ dir = 1 @@ -44268,7 +44268,7 @@ aa aa aa Nd -pE +Wl NJ SW PY diff --git a/_maps/shuttles/emergency_cruise.dmm b/_maps/shuttles/emergency_cruise.dmm new file mode 100644 index 000000000000..d75f1b1b75c8 --- /dev/null +++ b/_maps/shuttles/emergency_cruise.dmm @@ -0,0 +1,2666 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"ac" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"aY" = ( +/obj/machinery/door/airlock/shuttle, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"ca" = ( +/obj/machinery/vending/cola, +/turf/open/floor/wood, +/area/shuttle/escape) +"cf" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"cm" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/storage/fancy/donut_box{ + pixel_y = -7 + }, +/obj/item/reagent_containers/food/snacks/donut{ + pixel_x = 5; + pixel_y = 10 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"cu" = ( +/obj/machinery/vending/cola/red, +/turf/open/floor/wood, +/area/shuttle/escape) +"cz" = ( +/obj/structure/table/wood/fancy, +/obj/item/reagent_containers/food/snacks/nachos, +/obj/item/reagent_containers/food/snacks/honeybun{ + pixel_x = 3; + pixel_y = 13 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"cZ" = ( +/obj/item/toy/plush/lizardplushie{ + name = "Hides-On-Shuttle" + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"dd" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/structure/table/reinforced, +/obj/item/clipboard, +/obj/item/folder/white, +/obj/item/pen/blue, +/obj/machinery/light{ + dir = 1 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"dR" = ( +/obj/structure/shuttle/engine/heater, +/turf/closed/wall/mineral/titanium/nodiagonal, +/area/shuttle/escape) +"eb" = ( +/obj/effect/turf_decal/tile/yellow, +/obj/effect/turf_decal/tile/yellow{ + dir = 8 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/computer/station_alert{ + desc = "Used to access the shuttle's automated alert system."; + dir = 1; + name = "ship alert console" + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"en" = ( +/obj/effect/spawner/structure/window/shuttle, +/turf/open/floor/plating, +/area/shuttle/escape) +"fB" = ( +/obj/structure/table/wood, +/obj/machinery/chem_dispenser/drinks{ + dir = 8 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"fI" = ( +/obj/machinery/vending/boozeomat/all_access, +/turf/open/floor/wood, +/area/shuttle/escape) +"gv" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"gQ" = ( +/obj/structure/table/wood/fancy, +/turf/open/floor/wood, +/area/shuttle/escape) +"hB" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/camera{ + pixel_y = 3 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"jt" = ( +/turf/closed/wall/mineral/titanium, +/area/shuttle/escape) +"ju" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"jP" = ( +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/obj/structure/table/reinforced, +/obj/item/clipboard, +/obj/item/folder/red, +/obj/item/pen/red, +/obj/machinery/light{ + dir = 1 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"jT" = ( +/obj/item/kirbyplants/photosynthetic, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"jW" = ( +/obj/structure/statue/diamond/ai2{ + anchored = 1; + name = "statue of an AI core." + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"kd" = ( +/turf/template_noop, +/area/template_noop) +"kJ" = ( +/obj/structure/table/wood/fancy, +/obj/item/reagent_containers/food/snacks/burger/baconburger{ + pixel_x = 3 + }, +/obj/item/reagent_containers/food/snacks/cracker{ + pixel_x = -13; + pixel_y = 9 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"kN" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/chair/comfy/shuttle, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"kQ" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/computer/secure_data{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"kZ" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/reagent_containers/food/drinks/britcup{ + pixel_x = 8; + pixel_y = -1 + }, +/obj/item/storage/firstaid/regular{ + pixel_x = -9; + pixel_y = 3 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"lh" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/light{ + dir = 1 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"mv" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"mw" = ( +/mob/living/simple_animal/hostile/alien/maid/barmaid, +/turf/open/floor/wood, +/area/shuttle/escape) +"ni" = ( +/turf/open/floor/light/colour_cycle{ + name = "dancefloor" + }, +/area/shuttle/escape) +"nU" = ( +/obj/structure/chair/comfy/brown{ + dir = 4 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"oo" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"oH" = ( +/obj/machinery/light, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"oZ" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/obj/machinery/computer/crew, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"pk" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/machinery/light{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"qd" = ( +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/machinery/computer/warrant, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"qr" = ( +/obj/effect/turf_decal/tile/yellow, +/obj/effect/turf_decal/tile/yellow{ + dir = 8 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/structure/table/reinforced, +/obj/item/clothing/head/collectable/welding{ + pixel_y = 5 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"qE" = ( +/obj/machinery/door/airlock/shuttle, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"qI" = ( +/obj/structure/table/wood, +/obj/machinery/chem_dispenser/drinks/beer{ + dir = 8 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"qX" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/door/airlock/shuttle, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"rs" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/obj/machinery/light{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"rt" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"rX" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"si" = ( +/obj/structure/table/wood/fancy, +/obj/machinery/light{ + dir = 4 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"sv" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/machinery/computer/med_data{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"sW" = ( +/obj/structure/shuttle/engine/huge, +/turf/open/floor/plating/airless, +/area/shuttle/escape) +"tB" = ( +/obj/structure/statue/diamond/ai1{ + anchored = 1; + name = "statue of an AI hologram" + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"uh" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/light, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"ut" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 1; + name = "Shuttle Captain seat" + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"uV" = ( +/turf/closed/wall/mineral/titanium/nodiagonal, +/area/shuttle/escape) +"uX" = ( +/obj/effect/turf_decal/tile/yellow, +/obj/effect/turf_decal/tile/yellow{ + dir = 1 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/machinery/computer/atmos_alert{ + desc = "Used to monitor the shuttle's air alarms."; + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"vi" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/computer/emergency_shuttle, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"vS" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/camera_film{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/camera_film{ + pixel_x = -13; + pixel_y = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"wf" = ( +/obj/structure/table/wood/fancy, +/obj/item/reagent_containers/food/snacks/salad/fruit{ + pixel_y = 4 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"wF" = ( +/obj/machinery/door/window/westleft{ + name = "bar door" + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"xb" = ( +/mob/living/simple_animal/drone/snowflake/bardrone, +/turf/open/floor/wood, +/area/shuttle/escape) +"xf" = ( +/obj/structure/table/wood/fancy, +/obj/item/reagent_containers/food/snacks/donkpocket/warm{ + pixel_x = -5; + pixel_y = 14 + }, +/obj/item/reagent_containers/food/snacks/fries{ + pixel_x = 16; + pixel_y = 11 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"xj" = ( +/turf/open/floor/plating/airless, +/area/shuttle/escape) +"yn" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"yq" = ( +/obj/machinery/vending/snack/green, +/turf/open/floor/wood, +/area/shuttle/escape) +"yH" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/effect/turf_decal/tile/purple, +/obj/effect/turf_decal/tile/purple{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/obj/item/reactive_armour_shell, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Aq" = ( +/obj/structure/table/wood/fancy, +/obj/item/reagent_containers/food/snacks/cakeslice/chocolate{ + pixel_x = 16; + pixel_y = -8 + }, +/obj/item/reagent_containers/food/snacks/burrito{ + pixel_x = -6; + pixel_y = -4 + }, +/obj/item/reagent_containers/food/snacks/baguette{ + pixel_x = 3; + pixel_y = 10 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"Bp" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/machinery/modular_computer/console/preset/research{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple, +/obj/effect/turf_decal/tile/purple{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"BB" = ( +/obj/item/kirbyplants/photosynthetic, +/obj/machinery/light{ + dir = 1 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Cw" = ( +/obj/machinery/vending/snack/orange, +/turf/open/floor/wood, +/area/shuttle/escape) +"CK" = ( +/obj/item/kirbyplants/photosynthetic, +/obj/machinery/light, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"CR" = ( +/obj/machinery/door/airlock/shuttle, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/docking_port/mobile/emergency{ + height = 22; + name = "The NTSS Independence"; + width = 44 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"Do" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/machinery/light{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Dq" = ( +/obj/structure/table/wood/fancy, +/obj/item/reagent_containers/food/snacks/salad/jungle{ + pixel_y = 4 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"En" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/light{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"EC" = ( +/obj/structure/chair/comfy/brown, +/turf/open/floor/wood, +/area/shuttle/escape) +"EH" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/obj/machinery/light{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"FR" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/obj/machinery/computer/aifixer{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Hi" = ( +/obj/structure/table/wood/fancy, +/obj/item/candle/infinite{ + desc = "A synthetic candle that won't melt down."; + name = "crimson red candle"; + pixel_x = 1; + pixel_y = 6 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"Ho" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 1; + name = "shuttle CMO seat" + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Hz" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/clothing/mask/cigarette/robust{ + pixel_x = -4; + pixel_y = 15 + }, +/obj/item/clothing/mask/cigarette/robust{ + pixel_x = 3; + pixel_y = 7 + }, +/obj/item/clothing/mask/cigarette/robust{ + pixel_x = 7; + pixel_y = 12 + }, +/obj/item/storage/fancy/cigarettes/cigpack_robust{ + pixel_x = -13; + pixel_y = 2 + }, +/obj/item/lighter/greyscale{ + pixel_x = 6; + pixel_y = -10 + }, +/obj/item/cigbutt{ + pixel_x = -8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"HA" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Iu" = ( +/obj/machinery/computer/slot_machine, +/turf/open/floor/wood, +/area/shuttle/escape) +"IL" = ( +/obj/effect/turf_decal/tile/yellow{ + dir = 8 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 1 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/obj/structure/chair/comfy/shuttle{ + dir = 4; + name = "Shuttle CE seat" + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"IX" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Js" = ( +/obj/structure/showcase/mecha/marauder{ + desc = "A stand with an empty old Nanotrasen Corporation combat mech bolted to it. The seraph is described as the most valuable unit in defending the VIPs of Nanotrasen."; + dir = 8; + icon_state = "seraph"; + max_integrity = 2500; + name = "seraph mech exhibit" + }, +/obj/machinery/light{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Kc" = ( +/obj/item/kirbyplants/photosynthetic, +/obj/machinery/light{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Ki" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"KB" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 1; + name = "Shuttle HoS seat" + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"KD" = ( +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"KM" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Lo" = ( +/obj/structure/table/wood/fancy, +/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass{ + pixel_x = -3; + pixel_y = 6 + }, +/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass{ + pixel_x = 6; + pixel_y = 14 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"Ls" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/computer/communications, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Me" = ( +/obj/structure/chair/comfy/brown{ + dir = 1 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"MU" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/shuttle_manipulator{ + desc = "A holographic display of the cruise shuttle we're on right now."; + layer = 2.7; + max_integrity = 99999; + name = "shuttle holographic display" + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"NH" = ( +/obj/structure/chair/comfy/brown{ + dir = 8 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"Oh" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/obj/machinery/light{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"On" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure{ + density = 1; + icon = 'icons/obj/machines/shuttle_manipulator.dmi'; + icon_state = "hologram_on"; + layer = 2.8; + light_color = "#2cb2e8"; + light_range = 2; + max_integrity = 7500; + mouse_opacity = 0; + name = "holographic projection"; + pixel_x = -32; + pixel_y = 17 + }, +/obj/structure{ + desc = "This is the shuttle we're on. It's amazing what Nanotrasen can do once they actually put more than ten seconds of effort into their projects."; + icon = 'icons/obj/machines/shuttle_manipulator.dmi'; + icon_state = "hologram_whiteship"; + layer = 4; + light_color = "#2cb2e8"; + light_range = 7; + max_integrity = 75000; + name = "Cruise Shuttle Hologram"; + pixel_x = -32; + pixel_y = 27 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Ov" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/clothing/glasses/heat{ + pixel_y = -5 + }, +/obj/item/clothing/glasses/heat, +/obj/item/clothing/glasses/heat{ + pixel_y = 5 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"OJ" = ( +/turf/open/floor/light/colour_cycle/dancefloor_a, +/area/shuttle/escape) +"Pb" = ( +/obj/structure/showcase/mecha/marauder{ + desc = "A stand with an empty old Nanotrasen Corporation combat mech bolted to it. The marauder is described as the premier unit used to defend corporate interests and employees."; + dir = 4; + max_integrity = 2500; + name = "marauder mech exhibit" + }, +/obj/machinery/light{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Pr" = ( +/obj/machinery/light{ + dir = 1 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"So" = ( +/obj/item/storage/cans/sixbeer, +/turf/open/floor/wood, +/area/shuttle/escape) +"Sp" = ( +/obj/structure{ + desc = "A jukebox for playing music. Seems like it ran out of charge."; + icon = 'icons/obj/stationobjs.dmi'; + icon_state = "jukebox"; + name = "jukebox" + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"Sx" = ( +/obj/structure/table/wood, +/obj/item/coin/iron{ + pixel_x = 4; + pixel_y = -2 + }, +/obj/item/coin/iron{ + pixel_x = -8; + pixel_y = 6 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"SQ" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/storage/book/bible/booze{ + pixel_x = -3; + pixel_y = 4 + }, +/obj/item/taperecorder{ + pixel_x = 15; + pixel_y = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"TH" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/phone{ + pixel_x = 1; + pixel_y = 3 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"TI" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Ud" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 8; + name = "Shuttle RD seat" + }, +/obj/effect/turf_decal/tile/purple, +/obj/effect/turf_decal/tile/purple{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"UF" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"UJ" = ( +/obj/machinery/light, +/turf/open/floor/wood, +/area/shuttle/escape) +"UN" = ( +/obj/structure/table/wood/fancy, +/obj/item/reagent_containers/glass/rag{ + pixel_y = 7 + }, +/obj/effect/fun_balloon/sentience/emergency_shuttle{ + group_name = "bar staff on the NTSS Independence" + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"Vk" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"Vm" = ( +/turf/closed/wall/mineral/titanium/interior, +/area/shuttle/escape) +"Vv" = ( +/obj/item/kirbyplants/photosynthetic, +/obj/machinery/light{ + dir = 8 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"VQ" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 1; + name = "Shuttle HoP seat" + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"VV" = ( +/obj/machinery/vending/coffee, +/turf/open/floor/wood, +/area/shuttle/escape) +"Wh" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 1 + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"WT" = ( +/turf/open/floor/wood, +/area/shuttle/escape) +"Xa" = ( +/obj/machinery/light{ + dir = 1 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"Xd" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/toy/figure{ + desc = "A model spaceship, it looks like those used back in the day when travelling to Orion!"; + icon_state = "ship"; + name = "Orion Settler Ship figure"; + pixel_y = -5; + toysay = "Oxygen offline. Weapons charging. All systems nominal."; + toysound = 'sound/mecha/nominal.ogg' + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"YA" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/door/airlock/command/glass{ + name = "Cockpit"; + req_access_txt = "19" + }, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) +"YZ" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/open/floor/wood, +/area/shuttle/escape) +"ZL" = ( +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/paper_bin, +/obj/item/pen/fourcolor, +/turf/open/floor/plasteel/white, +/area/shuttle/escape) + +(1,1,1) = {" +kd +kd +kd +kd +kd +kd +kd +kd +kd +jt +qE +CR +jt +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +jt +qE +qE +jt +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +"} +(2,1,1) = {" +kd +kd +kd +kd +kd +kd +en +en +jt +jt +WT +WT +jt +jt +jt +jt +kd +kd +kd +kd +kd +kd +jt +en +en +en +jt +WT +WT +jt +en +en +en +jt +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +"} +(3,1,1) = {" +kd +kd +kd +kd +kd +en +en +nU +YZ +jt +aY +aY +jt +yq +cu +jt +jt +en +en +en +en +jt +jt +EC +Hi +Me +jt +aY +aY +jt +WT +nU +WT +jt +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +"} +(4,1,1) = {" +kd +kd +kd +kd +kd +en +EC +Hi +WT +WT +WT +WT +WT +WT +WT +WT +WT +WT +WT +WT +WT +YZ +WT +WT +WT +WT +WT +WT +WT +WT +EC +Hi +Me +jt +jt +en +en +en +en +uV +uV +kd +kd +kd +"} +(5,1,1) = {" +kd +kd +kd +kd +kd +jt +WT +WT +WT +WT +oo +WT +WT +WT +WT +WT +oo +WT +WT +WT +WT +WT +WT +WT +WT +WT +oo +WT +WT +WT +WT +NH +WT +WT +YZ +nU +nU +nU +WT +VV +uV +jt +jt +kd +"} +(6,1,1) = {" +kd +kd +en +en +jt +jt +Vm +Vm +qX +Vm +Vm +Vm +qX +Vm +Vm +Vm +Vm +en +en +en +en +Vm +Vm +Vm +Vm +qX +Vm +qX +Vm +Vm +oo +WT +WT +WT +EC +Aq +Dq +xf +Me +uV +uV +jt +jt +jt +"} +(7,1,1) = {" +jt +jt +en +KM +FR +yH +Vm +jT +KM +TI +EH +TI +KM +jT +Vm +TI +rs +TI +TI +TI +TI +En +TI +Vm +jT +KM +Pb +KM +jT +Vm +Vm +Vm +WT +WT +EC +kJ +wf +cz +Me +uV +dR +xj +xj +sW +"} +(8,1,1) = {" +jt +jP +kQ +KM +Ud +Bp +Vm +Pr +KM +KM +KM +KM +KM +oH +Vm +HA +KM +KM +KM +KM +KM +KM +yn +Vm +KD +KM +KM +KM +cf +KD +Vv +Vm +Vm +Xa +WT +NH +NH +NH +UJ +uV +dR +xj +xj +xj +"} +(9,1,1) = {" +en +qd +KB +KM +uh +uV +Vm +KD +KM +KM +KM +KM +KM +KD +Vm +Vm +KM +KM +KM +KM +KM +KM +Vm +Vm +KD +KM +ac +ac +ac +KM +KM +KM +qX +WT +WT +WT +WT +WT +WT +uV +dR +xj +xj +xj +"} +(10,1,1) = {" +en +KM +KM +KM +KM +Vm +BB +KD +KM +Ki +Ki +Ki +KM +KD +CK +Vm +lh +Ki +Ki +Ki +Ki +uh +Vm +BB +Vk +kN +cm +ZL +hB +Wh +KM +KM +qX +WT +WT +OJ +OJ +ni +WT +uV +uV +uV +uV +uV +"} +(11,1,1) = {" +en +vi +ut +KM +KM +YA +KM +KM +KM +IX +IX +IX +KM +KM +KM +qX +KM +IX +IX +IX +IX +KM +qX +KM +KM +kN +SQ +MU +vS +Wh +UF +jW +Vm +Iu +WT +OJ +OJ +ni +WT +Sx +uV +cZ +uV +kd +"} +(12,1,1) = {" +en +Ls +VQ +KM +KM +YA +KM +KM +KM +rt +rt +rt +KM +KM +KM +qX +KM +rt +rt +rt +rt +KM +qX +KM +KM +kN +TH +On +kZ +Wh +cf +tB +Vm +Iu +WT +ni +OJ +OJ +WT +Sp +uV +So +uV +kd +"} +(13,1,1) = {" +en +KM +KM +KM +KM +Vm +BB +KD +KM +TI +TI +TI +KM +KD +CK +Vm +lh +TI +TI +TI +TI +uh +Vm +BB +ju +kN +Hz +Xd +Ov +Wh +KM +KM +qX +WT +WT +ni +OJ +OJ +WT +uV +uV +uV +uV +uV +"} +(14,1,1) = {" +en +oZ +Ho +KM +uh +uV +Vm +KD +KM +KM +KM +KM +KM +KD +Vm +Vm +KM +KM +KM +KM +KM +KM +Vm +Vm +KD +KM +gv +gv +gv +KM +KM +KM +qX +WT +WT +WT +WT +WT +WT +uV +dR +xj +xj +sW +"} +(15,1,1) = {" +jt +dd +sv +KM +IL +eb +Vm +Pr +KM +KM +KM +KM +KM +oH +Vm +rX +KM +KM +KM +KM +KM +KM +mv +Vm +KD +KM +KM +KM +UF +KD +Kc +Vm +Vm +Xa +WT +nU +nU +nU +UJ +uV +dR +xj +xj +xj +"} +(16,1,1) = {" +jt +jt +en +KM +uX +qr +Vm +jT +KM +Ki +Oh +Ki +KM +jT +Vm +Ki +Do +Ki +Ki +Ki +Ki +pk +Ki +Vm +jT +KM +Js +KM +jT +Vm +Vm +Vm +WT +WT +gQ +Lo +UN +gQ +wF +uV +dR +xj +xj +xj +"} +(17,1,1) = {" +kd +kd +en +en +jt +jt +Vm +Vm +qX +Vm +Vm +Vm +qX +Vm +Vm +Vm +Vm +en +en +en +en +Vm +Vm +Vm +Vm +qX +Vm +qX +Vm +Vm +YZ +WT +WT +EC +gQ +WT +mw +xb +WT +uV +uV +jt +jt +jt +"} +(18,1,1) = {" +kd +kd +kd +kd +kd +jt +WT +WT +WT +WT +YZ +WT +WT +WT +WT +WT +YZ +WT +WT +WT +WT +WT +WT +WT +WT +WT +YZ +WT +WT +WT +WT +nU +WT +EC +si +WT +fB +qI +WT +fI +uV +jt +jt +kd +"} +(19,1,1) = {" +kd +kd +kd +kd +kd +en +EC +Hi +WT +WT +WT +WT +WT +WT +WT +WT +WT +WT +WT +WT +WT +oo +WT +WT +WT +WT +WT +WT +WT +WT +EC +Hi +Me +jt +jt +en +en +en +en +uV +uV +kd +kd +kd +"} +(20,1,1) = {" +kd +kd +kd +kd +kd +en +en +NH +oo +jt +qE +qE +jt +Cw +ca +jt +jt +en +en +en +en +jt +jt +EC +Hi +Me +jt +qE +qE +jt +WT +NH +WT +jt +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +"} +(21,1,1) = {" +kd +kd +kd +kd +kd +kd +en +en +jt +jt +WT +WT +jt +jt +jt +jt +kd +kd +kd +kd +kd +kd +jt +en +en +en +jt +WT +WT +jt +en +en +en +jt +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +"} +(22,1,1) = {" +kd +kd +kd +kd +kd +kd +kd +kd +kd +jt +aY +aY +jt +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +jt +aY +aY +jt +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +kd +"} diff --git a/_maps/shuttles/emergency_narnar.dmm b/_maps/shuttles/emergency_narnar.dmm index c55b84de088b..19b6f4e527bc 100644 --- a/_maps/shuttles/emergency_narnar.dmm +++ b/_maps/shuttles/emergency_narnar.dmm @@ -120,7 +120,7 @@ /turf/open/floor/plasteel/cult, /area/shuttle/escape) "w" = ( -/mob/living/simple_animal/hostile/construct/builder, +/mob/living/simple_animal/hostile/construct/artificer, /turf/open/floor/plasteel/cult, /area/shuttle/escape) "x" = ( @@ -148,7 +148,7 @@ /turf/open/floor/plasteel/cult, /area/shuttle/escape) "C" = ( -/mob/living/simple_animal/hostile/construct/armored, +/mob/living/simple_animal/hostile/construct/juggernaut, /turf/open/floor/plasteel/cult, /area/shuttle/escape) "D" = ( diff --git a/_maps/shuttles/whiteship_kilo.dmm b/_maps/shuttles/whiteship_kilo.dmm index 2b38f09db6ab..e881e56aabb6 100644 --- a/_maps/shuttles/whiteship_kilo.dmm +++ b/_maps/shuttles/whiteship_kilo.dmm @@ -1158,7 +1158,7 @@ /obj/machinery/status_display/evac{ pixel_y = 32 }, -/obj/structure/sign/plaques/golden/captain{ +/obj/structure/plaque/static_plaque/golden/captain{ pixel_x = 32 }, /obj/item/gun/energy/laser/retro, diff --git a/byond-extools.dll b/byond-extools.dll new file mode 100644 index 000000000000..a83ea1e5434a Binary files /dev/null and b/byond-extools.dll differ diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 237b58ec51b6..b04bd674f4af 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -23,7 +23,6 @@ #define CHAMELEON /datum/mutation/human/chameleon #define WACKY /datum/mutation/human/wacky #define MUT_MUTE /datum/mutation/human/mute -#define SMILE /datum/mutation/human/smile #define STONER /datum/mutation/human/stoner #define UNINTELLIGIBLE /datum/mutation/human/unintelligible #define SWEDISH /datum/mutation/human/swedish diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm index a89ec9229d91..fc6147c43041 100644 --- a/code/__DEFINES/_tick.dm +++ b/code/__DEFINES/_tick.dm @@ -1,11 +1,16 @@ +/// Percentage of tick to leave for master controller to run +#define MAPTICK_MC_MIN_RESERVE 70 +/// internal_tick_usage is updated every tick by extools +#define MAPTICK_LAST_INTERNAL_TICK_USAGE ((GLOB.internal_tick_usage / world.tick_lag) * 100) /// Tick limit while running normally -#define TICK_LIMIT_RUNNING 80 +#define TICK_BYOND_RESERVE 2 +#define TICK_LIMIT_RUNNING (max(100 - TICK_BYOND_RESERVE - MAPTICK_LAST_INTERNAL_TICK_USAGE, MAPTICK_MC_MIN_RESERVE)) /// Tick limit used to resume things in stoplag #define TICK_LIMIT_TO_RUN 70 /// Tick limit for MC while running #define TICK_LIMIT_MC 70 /// Tick limit while initializing -#define TICK_LIMIT_MC_INIT_DEFAULT 98 +#define TICK_LIMIT_MC_INIT_DEFAULT (100 - TICK_BYOND_RESERVE) /// for general usage of tick_usage #define TICK_USAGE world.tick_usage diff --git a/code/__DEFINES/achievements.dm b/code/__DEFINES/achievements.dm index 65bced18233a..68e033420b11 100644 --- a/code/__DEFINES/achievements.dm +++ b/code/__DEFINES/achievements.dm @@ -22,6 +22,8 @@ #define MEDAL_RULE8 "Rule 8" #define MEDAL_LONGSHIFT "longshift" #define MEDAL_SNAIL "KKKiiilll mmmeee" +#define MEDAL_LOOKOUTSIR "Look Out, Sir!" +#define MEDAL_GOTTEM "GOTTEM" //Skill medal hub IDs #define MEDAL_LEGENDARY_MINER "Legendary Miner" diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index b9d2fe49de7a..dfdeb26bcf5e 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -114,3 +114,7 @@ //How many things you can spawn at once with spawn verb/create panel #define ADMIN_SPAWN_CAP 100 + +// LOG BROWSE TYPES +#define BROWSE_ROOT_ALL_LOGS 1 +#define BROWSE_ROOT_CURRENT_LOGS 2 diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm index 323ba823af24..1655dfc627db 100644 --- a/code/__DEFINES/atom_hud.dm +++ b/code/__DEFINES/atom_hud.dm @@ -44,6 +44,8 @@ #define DIAG_LAUNCHPAD_HUD "22" //for antag huds. these are used at the /mob level #define ANTAG_HUD "23" +// for fans to identify pins +#define FAN_HUD "24" //by default everything in the hud_list of an atom is an image //a value in hud_list with one of these will change that behavior @@ -60,25 +62,26 @@ #define DATA_HUD_ABDUCTOR 7 #define DATA_HUD_SENTIENT_DISEASE 8 #define DATA_HUD_AI_DETECT 9 +#define DATA_HUD_FAN 10 //antag HUD defines -#define ANTAG_HUD_CULT 10 -#define ANTAG_HUD_REV 11 -#define ANTAG_HUD_OPS 12 -#define ANTAG_HUD_WIZ 13 -#define ANTAG_HUD_SHADOW 14 -#define ANTAG_HUD_TRAITOR 15 -#define ANTAG_HUD_NINJA 16 -#define ANTAG_HUD_CHANGELING 17 -#define ANTAG_HUD_ABDUCTOR 18 -#define ANTAG_HUD_DEVIL 19 -#define ANTAG_HUD_SINTOUCHED 20 -#define ANTAG_HUD_SOULLESS 21 -#define ANTAG_HUD_BROTHER 22 -#define ANTAG_HUD_OBSESSED 23 -#define ANTAG_HUD_FUGITIVE 24 -#define ANTAG_HUD_GANGSTER 25 -#define ANTAG_HUD_SPACECOP 26 +#define ANTAG_HUD_CULT 11 +#define ANTAG_HUD_REV 12 +#define ANTAG_HUD_OPS 13 +#define ANTAG_HUD_WIZ 14 +#define ANTAG_HUD_SHADOW 15 +#define ANTAG_HUD_TRAITOR 16 +#define ANTAG_HUD_NINJA 17 +#define ANTAG_HUD_CHANGELING 18 +#define ANTAG_HUD_ABDUCTOR 19 +#define ANTAG_HUD_DEVIL 20 +#define ANTAG_HUD_SINTOUCHED 21 +#define ANTAG_HUD_SOULLESS 22 +#define ANTAG_HUD_BROTHER 23 +#define ANTAG_HUD_OBSESSED 24 +#define ANTAG_HUD_FUGITIVE 25 +#define ANTAG_HUD_GANGSTER 26 +#define ANTAG_HUD_SPACECOP 27 // Notification action types diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index fcd819098e15..19288eccdf76 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -138,6 +138,7 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define EMBEDDED_JOSTLE_CHANCE 5 //Chance for embedded objects to cause pain every time they move (jostle) #define EMBEDDED_JOSTLE_PAIN_MULTIPLIER 1 //Coefficient of multiplication for the damage the item does while #define EMBEDDED_PAIN_STAM_PCT 0.0 //This percentage of all pain will be dealt as stam damage rather than brute (0-1) +#define EMBED_CHANCE_TURF_MOD -15 //You are this many percentage points less likely to embed into a turf (good for things glass shards and spears vs walls) #define EMBED_HARMLESS list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE) #define EMBED_HARMLESS_SUPERIOR list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE, "embed_chance" = 100, "fall_chance" = 0.1) @@ -179,6 +180,10 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( ///ammo box will have a different state for full and empty; -max_ammo and -0 #define AMMO_BOX_FULL_EMPTY 2 +#define SUPPRESSED_NONE 0 +#define SUPPRESSED_QUIET 1 ///standard suppressed +#define SUPPRESSED_VERY 2 /// no message + //Projectile Reflect #define REFLECT_NORMAL (1<<0) #define REFLECT_FAKEPROJECTILE (1<<1) @@ -235,3 +240,5 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting. #define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default. #define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs. + +#define NICE_SHOT_RICOCHET_BONUS 10 //if the shooter has the NICE_SHOT trait and they fire a ricocheting projectile, add this to the ricochet chance and auto aim angle diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index cc225cd39e54..42eb127d94d1 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -1,426 +1,455 @@ -// All signals. Format: -// When the signal is called: (signal arguments) -// All signals send the source datum of the signal as the first argument - -// global signals -// These are signals which can be listened to by any component on any parent -// start global signals with "!", this used to be necessary but now it's just a formatting choice -/// from base of datum/controller/subsystem/mapping/proc/add_new_zlevel(): (list/args) -#define COMSIG_GLOB_NEW_Z "!new_z" -/// called after a successful var edit somewhere in the world: (list/args) -#define COMSIG_GLOB_VAR_EDIT "!var_edit" -/// called after an explosion happened : (epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) -#define COMSIG_GLOB_EXPLOSION "!explosion" -/// mob was created somewhere : (mob) -#define COMSIG_GLOB_MOB_CREATED "!mob_created" -/// mob died somewhere : (mob , gibbed) -#define COMSIG_GLOB_MOB_DEATH "!mob_death" -/// global living say plug - use sparingly: (mob/speaker , message) -#define COMSIG_GLOB_LIVING_SAY_SPECIAL "!say_special" -/// called by datum/cinematic/play() : (datum/cinematic/new_cinematic) -#define COMSIG_GLOB_PLAY_CINEMATIC "!play_cinematic" - #define COMPONENT_GLOB_BLOCK_CINEMATIC 1 -/// ingame button pressed (/obj/machinery/button/button) -#define COMSIG_GLOB_BUTTON_PRESSED "!button_pressed" - -// signals from globally accessible objects -/// from SSsun when the sun changes position : (azimuth) -#define COMSIG_SUN_MOVED "sun_moved" - -////////////////////////////////////////////////////////////////// - -// /datum signals -/// when a component is added to a datum: (/datum/component) -#define COMSIG_COMPONENT_ADDED "component_added" -/// before a component is removed from a datum because of RemoveComponent: (/datum/component) -#define COMSIG_COMPONENT_REMOVING "component_removing" -/// before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation -#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted" -/// just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called -#define COMSIG_PARENT_QDELETING "parent_qdeleting" -/// generic topic handler (usr, href_list) -#define COMSIG_TOPIC "handle_topic" - -/// fires on the target datum when an element is attached to it (/datum/element) -#define COMSIG_ELEMENT_ATTACH "element_attach" -/// fires on the target datum when an element is attached to it (/datum/element) -#define COMSIG_ELEMENT_DETACH "element_detach" - -// /atom signals -#define COMSIG_PARENT_ATTACKBY "atom_attackby" ///from base of atom/attackby(): (/obj/item, /mob/living, params) - #define COMPONENT_NO_AFTERATTACK 1 //Return this in response if you don't want afterattack to be called -#define COMSIG_ATOM_HULK_ATTACK "hulk_attack" ///from base of atom/attack_hulk(): (/mob/living/carbon/human) -#define COMSIG_ATOM_ATTACK_ANIMAL "attack_animal" ///from base of atom/animal_attack(): (/mob/user) -#define COMSIG_PARENT_EXAMINE "atom_examine" ///from base of atom/examine(): (/mob) -#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" ///from base of atom/get_examine_name(): (/mob, list/overrides) - //Positions for overrides list - #define EXAMINE_POSITION_ARTICLE 1 - #define EXAMINE_POSITION_BEFORE 2 - //End positions - #define COMPONENT_EXNAME_CHANGED 1 -#define COMSIG_ATOM_UPDATE_ICON "atom_update_icon" ///from base of atom/update_icon(): () - #define COMSIG_ATOM_NO_UPDATE_ICON_STATE 1 - #define COMSIG_ATOM_NO_UPDATE_OVERLAYS 2 -#define COMSIG_ATOM_UPDATE_OVERLAYS "atom_update_overlays" ///from base of atom/update_overlays(): (list/new_overlays) -#define COMSIG_ATOM_UPDATED_ICON "atom_updated_icon" ///from base of atom/update_icon(): (signalOut, did_anything) -#define COMSIG_ATOM_ENTERED "atom_entered" ///from base of atom/Entered(): (atom/movable/entering, /atom) -#define COMSIG_ATOM_EXIT "atom_exit" ///from base of atom/Exit(): (/atom/movable/exiting, /atom/newloc) - #define COMPONENT_ATOM_BLOCK_EXIT 1 -#define COMSIG_ATOM_EXITED "atom_exited" ///from base of atom/Exited(): (atom/movable/exiting, atom/newloc) -#define COMSIG_ATOM_BUMPED "atom_bumped" ///from base of atom/Bumped(): (/atom/movable) -#define COMSIG_ATOM_EX_ACT "atom_ex_act" ///from base of atom/ex_act(): (severity, target) -#define COMSIG_ATOM_EMP_ACT "atom_emp_act" ///from base of atom/emp_act(): (severity) -#define COMSIG_ATOM_FIRE_ACT "atom_fire_act" ///from base of atom/fire_act(): (exposed_temperature, exposed_volume) -#define COMSIG_ATOM_BULLET_ACT "atom_bullet_act" ///from base of atom/bullet_act(): (/obj/projectile, def_zone) -#define COMSIG_ATOM_BLOB_ACT "atom_blob_act" ///from base of atom/blob_act(): (/obj/structure/blob) -#define COMSIG_ATOM_ACID_ACT "atom_acid_act" ///from base of atom/acid_act(): (acidpwr, acid_volume) -#define COMSIG_ATOM_EMAG_ACT "atom_emag_act" ///from base of atom/emag_act(): (/mob/user) -#define COMSIG_ATOM_RAD_ACT "atom_rad_act" ///from base of atom/rad_act(intensity) -#define COMSIG_ATOM_NARSIE_ACT "atom_narsie_act" ///from base of atom/narsie_act(): () -#define COMSIG_ATOM_RCD_ACT "atom_rcd_act" ///from base of atom/rcd_act(): (/mob, /obj/item/construction/rcd, passed_mode) -#define COMSIG_ATOM_SING_PULL "atom_sing_pull" ///from base of atom/singularity_pull(): (S, current_size) -#define COMSIG_ATOM_BSA_BEAM "atom_bsa_beam_pass" ///from obj/machinery/bsa/full/proc/fire(): () - #define COMSIG_ATOM_BLOCKS_BSA_BEAM 1 -#define COMSIG_ATOM_SET_LIGHT "atom_set_light" ///from base of atom/set_light(): (l_range, l_power, l_color) -#define COMSIG_ATOM_DIR_CHANGE "atom_dir_change" ///from base of atom/setDir(): (old_dir, new_dir) -#define COMSIG_ATOM_CONTENTS_DEL "atom_contents_del" ///from base of atom/handle_atom_del(): (atom/deleted) -#define COMSIG_ATOM_HAS_GRAVITY "atom_has_gravity" ///from base of atom/has_gravity(): (turf/location, list/forced_gravities) -#define COMSIG_ATOM_RAD_PROBE "atom_rad_probe" ///from proc/get_rad_contents(): () - #define COMPONENT_BLOCK_RADIATION 1 -#define COMSIG_ATOM_RAD_CONTAMINATING "atom_rad_contam" ///from base of datum/radiation_wave/radiate(): (strength) - #define COMPONENT_BLOCK_CONTAMINATION 1 -#define COMSIG_ATOM_RAD_WAVE_PASSING "atom_rad_wave_pass" ///from base of datum/radiation_wave/check_obstructions(): (datum/radiation_wave, width) - #define COMPONENT_RAD_WAVE_HANDLED 1 -#define COMSIG_ATOM_CANREACH "atom_can_reach" ///from internal loop in atom/movable/proc/CanReach(): (list/next) - #define COMPONENT_BLOCK_REACH 1 -#define COMSIG_ATOM_SCREWDRIVER_ACT "atom_screwdriver_act" ///from base of atom/screwdriver_act(): (mob/living/user, obj/item/I) -#define COMSIG_ATOM_WRENCH_ACT "atom_wrench_act" ///from base of atom/wrench_act(): (mob/living/user, obj/item/I) -#define COMSIG_ATOM_MULTITOOL_ACT "atom_multitool_act" ///from base of atom/multitool_act(): (mob/living/user, obj/item/I) -#define COMSIG_ATOM_WELDER_ACT "atom_welder_act" ///from base of atom/welder_act(): (mob/living/user, obj/item/I) -#define COMSIG_ATOM_WIRECUTTER_ACT "atom_wirecutter_act" ///from base of atom/wirecutter_act(): (mob/living/user, obj/item/I) -#define COMSIG_ATOM_CROWBAR_ACT "atom_crowbar_act" ///from base of atom/crowbar_act(): (mob/living/user, obj/item/I) -#define COMSIG_ATOM_ANALYSER_ACT "atom_analyser_act" ///from base of atom/analyser_act(): (mob/living/user, obj/item/I) - #define COMPONENT_BLOCK_TOOL_ATTACK 1 -#define COMSIG_ATOM_INTERCEPT_TELEPORT "intercept_teleport" ///called when teleporting into a protected turf: (channel, turf/origin) - #define COMPONENT_BLOCK_TELEPORT 1 -#define COMSIG_ATOM_HEARER_IN_VIEW "atom_hearer_in_view" ///called when an atom is added to the hearers on get_hearers_in_view(): (list/processing_list, list/hearers) -#define COMSIG_ATOM_ORBIT_BEGIN "atom_orbit_begin" ///called when an atom starts orbiting another atom: (atom) -#define COMSIG_ATOM_ORBIT_STOP "atom_orbit_stop" ///called when an atom stops orbiting another atom: (atom) -///////////////// -#define COMSIG_ATOM_ATTACK_GHOST "atom_attack_ghost" ///from base of atom/attack_ghost(): (mob/dead/observer/ghost) -#define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand" ///from base of atom/attack_hand(): (mob/user) -#define COMSIG_ATOM_ATTACK_PAW "atom_attack_paw" ///from base of atom/attack_paw(): (mob/user) - #define COMPONENT_NO_ATTACK_HAND 1 //works on all 3. -//This signal return value bitflags can be found in __DEFINES/misc.dm -#define COMSIG_ATOM_INTERCEPT_Z_FALL "movable_intercept_z_impact" ///called for each movable in a turf contents on /turf/zImpact(): (atom/movable/A, levels) -#define COMSIG_ATOM_START_PULL "movable_start_pull" ///called on a movable (NOT living) when someone starts pulling it (atom/movable/puller, state, force) -#define COMSIG_LIVING_START_PULL "living_start_pull" ///called on /living when someone starts pulling it (atom/movable/puller, state, force) - -///////////////// - -#define COMSIG_ENTER_AREA "enter_area" //from base of area/Entered(): (/area) -#define COMSIG_EXIT_AREA "exit_area" //from base of area/Exited(): (/area) - -#define COMSIG_CLICK "atom_click" //from base of atom/Click(): (location, control, params, mob/user) -#define COMSIG_CLICK_SHIFT "shift_click" //from base of atom/ShiftClick(): (/mob) - #define COMPONENT_ALLOW_EXAMINATE 1 //Allows the user to examinate regardless of client.eye. -#define COMSIG_CLICK_CTRL "ctrl_click" //from base of atom/CtrlClickOn(): (/mob) -#define COMSIG_CLICK_ALT "alt_click" //from base of atom/AltClick(): (/mob) -#define COMSIG_CLICK_CTRL_SHIFT "ctrl_shift_click" //from base of atom/CtrlShiftClick(/mob) -#define COMSIG_MOUSEDROP_ONTO "mousedrop_onto" //from base of atom/MouseDrop(): (/atom/over, /mob/user) - #define COMPONENT_NO_MOUSEDROP 1 -#define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" //from base of atom/MouseDrop_T: (/atom/from, /mob/user) - -// /area signals -#define COMSIG_AREA_ENTERED "area_entered" //from base of area/Entered(): (atom/movable/M) -#define COMSIG_AREA_EXITED "area_exited" //from base of area/Exited(): (atom/movable/M) - -// /turf signals -#define COMSIG_TURF_CHANGE "turf_change" //from base of turf/ChangeTurf(): (path, list/new_baseturfs, flags, list/transferring_comps) -#define COMSIG_TURF_HAS_GRAVITY "turf_has_gravity" ///from base of atom/has_gravity(): (atom/asker, list/forced_gravities) -#define COMSIG_TURF_MULTIZ_NEW "turf_multiz_new" //from base of turf/New(): (turf/source, direction) - -// /atom/movable signals -#define COMSIG_MOVABLE_PRE_MOVE "movable_pre_move" ///from base of atom/movable/Moved(): (/atom) - #define COMPONENT_MOVABLE_BLOCK_PRE_MOVE 1 -#define COMSIG_MOVABLE_MOVED "movable_moved" ///from base of atom/movable/Moved(): (/atom, dir) -#define COMSIG_MOVABLE_CROSS "movable_cross" ///from base of atom/movable/Cross(): (/atom/movable) -#define COMSIG_MOVABLE_CROSSED "movable_crossed" ///from base of atom/movable/Crossed(): (/atom/movable) -#define COMSIG_MOVABLE_UNCROSS "movable_uncross" ///from base of atom/movable/Uncross(): (/atom/movable) - #define COMPONENT_MOVABLE_BLOCK_UNCROSS 1 -#define COMSIG_MOVABLE_UNCROSSED "movable_uncrossed" ///from base of atom/movable/Uncrossed(): (/atom/movable) -#define COMSIG_MOVABLE_BUMP "movable_bump" ///from base of atom/movable/Bump(): (/atom) -#define COMSIG_MOVABLE_IMPACT "movable_impact" ///from base of atom/movable/throw_impact(): (/atom/hit_atom, /datum/thrownthing/throwingdatum) - #define COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH 1 ///if true, flip if the impact will push what it hits - #define COMPONENT_MOVABLE_IMPACT_NEVERMIND 2 ///return true if you destroyed whatever it was you're impacting and there won't be anything for hitby() to run on -#define COMSIG_MOVABLE_IMPACT_ZONE "item_impact_zone" ///from base of mob/living/hitby(): (mob/living/target, hit_zone) -#define COMSIG_MOVABLE_BUCKLE "buckle" ///from base of atom/movable/buckle_mob(): (mob, force) -#define COMSIG_MOVABLE_UNBUCKLE "unbuckle" ///from base of atom/movable/unbuckle_mob(): (mob, force) -#define COMSIG_MOVABLE_PRE_THROW "movable_pre_throw" ///from base of atom/movable/throw_at(): (list/args) - #define COMPONENT_CANCEL_THROW 1 -#define COMSIG_MOVABLE_POST_THROW "movable_post_throw" ///from base of atom/movable/throw_at(): (datum/thrownthing, spin) -#define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit" ///from base of atom/movable/onTransitZ(): (old_z, new_z) -#define COMSIG_MOVABLE_SECLUDED_LOCATION "movable_secluded" ///called when the movable is placed in an unaccessible area, used for stationloving: () -#define COMSIG_MOVABLE_HEAR "movable_hear" ///from base of atom/movable/Hear(): (proc args list(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)) - #define HEARING_MESSAGE 1 - #define HEARING_SPEAKER 2 -// #define HEARING_LANGUAGE 3 - #define HEARING_RAW_MESSAGE 4 - /* #define HEARING_RADIO_FREQ 5 - #define HEARING_SPANS 6 - #define HEARING_MESSAGE_MODE 7 */ -#define COMSIG_MOVABLE_DISPOSING "movable_disposing" //called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source) - -// /mob signals -#define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed) -#define COMSIG_MOB_STATCHANGE "mob_statchange" //from base of mob/set_stat(): (new_stat) -#define COMSIG_MOB_CLICKON "mob_clickon" //from base of mob/clickon(): (atom/A, params) -#define COMSIG_MOB_MIDDLECLICKON "mob_middleclickon" //from base of mob/MiddleClickOn(): (atom/A) -#define COMSIG_MOB_ALTCLICKON "mob_altclickon" //from base of mob/AltClickOn(): (atom/A) - #define COMSIG_MOB_CANCEL_CLICKON 1 - -#define COMSIG_MOB_ALLOWED "mob_allowed" //from base of obj/allowed(mob/M): (/obj) returns bool, if TRUE the mob has id access to the obj -#define COMSIG_MOB_RECEIVE_MAGIC "mob_receive_magic" //from base of mob/anti_magic_check(): (mob/user, magic, holy, tinfoil, chargecost, self, protection_sources) - #define COMPONENT_BLOCK_MAGIC 1 -#define COMSIG_MOB_HUD_CREATED "mob_hud_created" //from base of mob/create_mob_hud(): () -#define COMSIG_MOB_ATTACK_HAND "mob_attack_hand" //from base of -#define COMSIG_MOB_ITEM_ATTACK "mob_item_attack" //from base of /obj/item/attack(): (mob/M, mob/user) - #define COMPONENT_ITEM_NO_ATTACK 1 -#define COMSIG_MOB_APPLY_DAMGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone) -#define COMSIG_MOB_ITEM_AFTERATTACK "mob_item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, proximity_flag, click_parameters) -#define COMSIG_MOB_ITEM_ATTACK_QDELETED "mob_item_attack_qdeleted" //from base of obj/item/attack_qdeleted(): (atom/target, mob/user, proxiumity_flag, click_parameters) -#define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged" //from base of mob/RangedAttack(): (atom/A, params) -#define COMSIG_MOB_THROW "mob_throw" //from base of /mob/throw_item(): (atom/target) -#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/target) -#define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight" //from base of /mob/update_sight(): () -#define COMSIG_MOB_SAY "mob_say" // from /mob/living/say(): () - #define COMPONENT_UPPERCASE_SPEECH 1 - // used to access COMSIG_MOB_SAY argslist - #define SPEECH_MESSAGE 1 - // #define SPEECH_BUBBLE_TYPE 2 - #define SPEECH_SPANS 3 - /* #define SPEECH_SANITIZE 4 - #define SPEECH_LANGUAGE 5 - #define SPEECH_IGNORE_SPAM 6 - #define SPEECH_FORCED 7 */ -#define COMSIG_MOB_DEADSAY "mob_deadsay" // from /mob/say_dead(): (mob/speaker, message) - #define MOB_DEADSAY_SIGNAL_INTERCEPT 1 -#define COMSIG_MOB_EMOTE "mob_emote" // from /mob/living/emote(): () -#define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" //from base of mob/swap_hand(): (obj/item) - #define COMPONENT_BLOCK_SWAP 1 - -// /mob/living signals -#define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living) -#define COMSIG_LIVING_IGNITED "living_ignite" //from base of mob/living/IgniteMob() (/mob/living) -#define COMSIG_LIVING_EXTINGUISHED "living_extinguished" //from base of mob/living/ExtinguishMob() (/mob/living) -#define COMSIG_LIVING_ELECTROCUTE_ACT "living_electrocute_act" //from base of mob/living/electrocute_act(): (shock_damage, source, siemens_coeff, flags) -#define COMSIG_LIVING_SHOCK_PREVENTED "living_shock_prevented" //sent when items with siemen coeff. of 0 block a shock: (power_source, source, siemens_coeff, dist_check) -#define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //sent by stuff like stunbatons and tasers: () -#define COMSIG_LIVING_REVIVE "living_revive" //from base of mob/living/revive() (full_heal, admin_revive) -#define COMSIG_LIVING_REGENERATE_LIMBS "living_regen_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs) -#define COMSIG_LIVING_ATTACH_LIMB "living_attach_limb" //from base of /obj/item/bodypart/proc/attach_limb(): (new_limb, special) allows you to fail limb attachment - #define COMPONENT_NO_ATTACH 1 -#define COMSIG_PROCESS_BORGCHARGER_OCCUPANT "living_charge" //sent from borg recharge stations: (amount, repairs) -#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" //sent when a mob/login() finishes: (client) -#define COMSIG_BORG_SAFE_DECONSTRUCT "borg_safe_decon" //sent from borg mobs to itself, for tools to catch an upcoming destroy() due to safe decon (rather than detonation) - -//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS! -#define COMSIG_LIVING_STATUS_STUN "living_stun" //from base of mob/living/Stun() (amount, update, ignore) -#define COMSIG_LIVING_STATUS_KNOCKDOWN "living_knockdown" //from base of mob/living/Knockdown() (amount, update, ignore) -#define COMSIG_LIVING_STATUS_PARALYZE "living_paralyze" //from base of mob/living/Paralyze() (amount, update, ignore) -#define COMSIG_LIVING_STATUS_IMMOBILIZE "living_immobilize" //from base of mob/living/Immobilize() (amount, update, ignore) -#define COMSIG_LIVING_STATUS_UNCONSCIOUS "living_unconscious" //from base of mob/living/Unconscious() (amount, update, ignore) -#define COMSIG_LIVING_STATUS_SLEEP "living_sleeping" //from base of mob/living/Sleeping() (amount, update, ignore) - #define COMPONENT_NO_STUN 1 //For all of them -#define COMSIG_LIVING_CAN_TRACK "mob_cantrack" //from base of /mob/living/can_track(): (mob/user) - #define COMPONENT_CANT_TRACK 1 - -// /mob/living/carbon signals -#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity)) -#define COMSIG_CARBON_GAIN_ORGAN "carbon_gain_organ" //from /item/organ/proc/Insert() (/obj/item/organ/) -#define COMSIG_CARBON_LOSE_ORGAN "carbon_lose_organ" //from /item/organ/proc/Remove() (/obj/item/organ/) -#define COMSIG_CARBON_EQUIP_HAT "carbon_equip_hat" //from /mob/living/carbon/doUnEquip(obj/item/I, force, newloc, no_move, invdrop, silent) -#define COMSIG_CARBON_UNEQUIP_HAT "carbon_unequip_hat" //from /mob/living/carbon/doUnEquip(obj/item/I, force, newloc, no_move, invdrop, silent) - -// /mob/living/simple_animal/hostile signals -#define COMSIG_HOSTILE_ATTACKINGTARGET "hostile_attackingtarget" - #define COMPONENT_HOSTILE_NO_ATTACK 1 - -// /obj signals -#define COMSIG_OBJ_DECONSTRUCT "obj_deconstruct" //from base of obj/deconstruct(): (disassembled) -#define COMSIG_OBJ_SETANCHORED "obj_setanchored" //called in /obj/structure/setAnchored(): (value) -#define COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH "obj_default_unfasten_wrench" //from base of code/game/machinery -#define COMSIG_OBJ_HIDE "obj_hide" //from base of /turf/proc/levelupdate(). (intact) true to hide and false to unhide - -// /obj/machinery signals -#define COMSIG_MACHINERY_BROKEN "machinery_broken" //from /obj/machinery/obj_break(damage_flag): (damage_flag) -#define COMSIG_MACHINERY_POWER_LOST "machinery_power_lost" //from base power_change() when power is lost -#define COMSIG_MACHINERY_POWER_RESTORED "machinery_power_restored" //from base power_change() when power is restored - -// /obj/item signals -#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user) -#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob) - #define COMPONENT_NO_INTERACT 1 -#define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob) - #define COMPONENT_NO_ATTACK_OBJ 1 -#define COMSIG_ITEM_PRE_ATTACK "item_pre_attack" //from base of obj/item/pre_attack(): (atom/target, mob/user, params) - #define COMPONENT_NO_ATTACK 1 -#define COMSIG_ITEM_AFTERATTACK "item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, params) -#define COMSIG_ITEM_ATTACK_QDELETED "item_attack_qdeleted" //from base of obj/item/attack_qdeleted(): (atom/target, mob/user, params) -#define COMSIG_ITEM_EQUIPPED "item_equip" //from base of obj/item/equipped(): (/mob/equipper, slot) -#define COMSIG_ITEM_DROPPED "item_drop" //from base of obj/item/dropped(): (mob/user) -#define COMSIG_ITEM_PICKUP "item_pickup" //from base of obj/item/pickup(): (/mob/taker) -#define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone" //from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone) -#define COMSIG_ITEM_IMBUE_SOUL "item_imbue_soul" //return a truthy value to prevent ensouling, checked in /obj/effect/proc_holder/spell/targeted/lichdom/cast(): (mob/user) -#define COMSIG_ITEM_MARK_RETRIEVAL "item_mark_retrieval" //called before marking an object for retrieval, checked in /obj/effect/proc_holder/spell/targeted/summonitem/cast() : (mob/user) - #define COMPONENT_BLOCK_MARK_RETRIEVAL 1 -#define COMSIG_ITEM_HIT_REACT "item_hit_react" //from base of obj/item/hit_reaction(): (list/args) -#define COMSIG_ITEM_WEARERCROSSED "wearer_crossed" //called on item when crossed by something (): (/atom/movable, mob/living/crossed) -#define COMSIG_ITEM_MICROWAVE_ACT "microwave_act" //called on item when microwaved (): (obj/machinery/microwave/M) -#define COMSIG_ITEM_SHARPEN_ACT "sharpen_act" //from base of item/sharpener/attackby(): (amount, max) - #define COMPONENT_BLOCK_SHARPEN_APPLIED 1 - #define COMPONENT_BLOCK_SHARPEN_BLOCKED 2 - #define COMPONENT_BLOCK_SHARPEN_ALREADY 4 - #define COMPONENT_BLOCK_SHARPEN_MAXED 8 -#define COMSIG_TOOL_IN_USE "tool_in_use" ///from base of [/obj/item/proc/tool_check_callback]: (mob/living/user) -#define COMSIG_TOOL_START_USE "tool_start_use" ///from base of [/obj/item/proc/tool_start_check]: (mob/living/user) - -// /obj/item signals for economy -#define COMSIG_ITEM_SOLD "item_sold" //called when an item is sold by the exports subsystem -#define COMSIG_STRUCTURE_UNWRAPPED "structure_unwrapped" //called when a wrapped up structure is opened by hand -#define COMSIG_ITEM_UNWRAPPED "item_unwrapped" //called when a wrapped up item is opened by hand - #define COMSIG_ITEM_SPLIT_VALUE 1 -#define COMSIG_ITEM_SPLIT_PROFIT "item_split_profits" //Called when getting the item's exact ratio for cargo's profit. - - -// /obj/item/clothing signals -#define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): () - -// /obj/item/implant signals -#define COMSIG_IMPLANT_ACTIVATED "implant_activated" //from base of /obj/item/implant/proc/activate(): () -#define COMSIG_IMPLANT_IMPLANTING "implant_implanting" //from base of /obj/item/implant/proc/implant(): (list/args) - #define COMPONENT_STOP_IMPLANTING 1 -#define COMSIG_IMPLANT_OTHER "implant_other" //called on already installed implants when a new one is being added in /obj/item/implant/proc/implant(): (list/args, obj/item/implant/new_implant) - //#define COMPONENT_STOP_IMPLANTING 1 //The name makes sense for both - #define COMPONENT_DELETE_NEW_IMPLANT 2 - #define COMPONENT_DELETE_OLD_IMPLANT 4 -#define COMSIG_IMPLANT_EXISTING_UPLINK "implant_uplink_exists" //called on implants being implanted into someone with an uplink implant: (datum/component/uplink) - //This uses all return values of COMSIG_IMPLANT_OTHER - -// /obj/item/pda signals -#define COMSIG_PDA_CHANGE_RINGTONE "pda_change_ringtone" //called on pda when the user changes the ringtone: (mob/living/user, new_ringtone) - #define COMPONENT_STOP_RINGTONE_CHANGE 1 -#define COMSIG_PDA_CHECK_DETONATE "pda_check_detonate" - #define COMPONENT_PDA_NO_DETONATE 1 - -// /obj/item/radio signals -#define COMSIG_RADIO_NEW_FREQUENCY "radio_new_frequency" //called from base of /obj/item/radio/proc/set_frequency(): (list/args) - -// /obj/item/pen signals -#define COMSIG_PEN_ROTATED "pen_rotated" //called after rotation in /obj/item/pen/attack_self(): (rotation, mob/living/carbon/user) - -// /obj/item/gun signals -#define COMSIG_MOB_FIRED_GUN "mob_fired_gun" //called in /obj/item/gun/process_fire (user, target, params, zone_override) - -// /obj/projectile signals (sent to the firer) -#define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit" // from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle) -#define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire" // from base of /obj/projectile/proc/fire(): (obj/projectile, atom/original_target) -#define COMSIG_PROJECTILE_FIRE "projectile_fire" // from the base of /obj/projectile/proc/fire(): () -#define COMSIG_PROJECTILE_PREHIT "com_proj_prehit" // sent to targets during the process_hit proc of projectiles - -// /obj/mecha signals -#define COMSIG_MECHA_ACTION_ACTIVATE "mecha_action_activate" //sent from mecha action buttons to the mecha they're linked to - -// /mob/living/carbon/human signals -#define COMSIG_HUMAN_EARLY_UNARMED_ATTACK "human_early_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity) -#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity) -#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker) -#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted) -#define COMSIG_JOB_RECEIVED "job_received" //Whenever EquipRanked is called, called after job is set -#define COMSIG_HUMAN_EMBED_RIP "item_embed_removing" // called on human when said human tries to rip out this embedded item (mob/living/carbon/human/target, /obj/item, /obj/item/bodypart/L) -#define COMSIG_HUMAN_EMBED_REMOVAL "item_embed_remove_surgery" // called on human from /datum/surgery_step/remove_object/success (mob/living/carbon/human/target, /obj/item, /obj/item/bodypart/L) - -// /datum/species signals -#define COMSIG_SPECIES_GAIN "species_gain" //from datum/species/on_species_gain(): (datum/species/new_species, datum/species/old_species) -#define COMSIG_SPECIES_LOSS "species_loss" //from datum/species/on_species_loss(): (datum/species/lost_species) - -// /datum/song signals -#define COMSIG_SONG_START "song_start" //sent to the instrument when a song starts playing -#define COMSIG_SONG_END "song_end" //sent to the instrument when a song stops playing - -/*******Component Specific Signals*******/ -//Janitor -#define COMSIG_TURF_IS_WET "check_turf_wet" //(): Returns bitflags of wet values. -#define COMSIG_TURF_MAKE_DRY "make_turf_try" //(max_strength, immediate, duration_decrease = INFINITY): Returns bool. -#define COMSIG_COMPONENT_CLEAN_ACT "clean_act" //called on an object to clean it of cleanables. Usualy with soap: (num/strength) - -//Creamed -#define COMSIG_COMPONENT_CLEAN_FACE_ACT "clean_face_act" //called when you wash your face at a sink: (num/strength) - -//Food -#define COMSIG_FOOD_EATEN "food_eaten" //from base of obj/item/reagent_containers/food/snacks/attack(): (mob/living/eater, mob/feeder) - -//Gibs -#define COMSIG_GIBS_STREAK "gibs_streak" // from base of /obj/effect/decal/cleanable/blood/gibs/streak(): (list/directions, list/diseases) - -//Mood -#define COMSIG_ADD_MOOD_EVENT "add_mood" //Called when you send a mood event from anywhere in the code. -#define COMSIG_ADD_MOOD_EVENT_RND "RND_add_mood" //Mood event that only RnD members listen for -#define COMSIG_CLEAR_MOOD_EVENT "clear_mood" //Called when you clear a mood event from anywhere in the code. - -//NTnet -#define COMSIG_COMPONENT_NTNET_RECEIVE "ntnet_receive" //called on an object by its NTNET connection component on receive. (sending_id(number), sending_netname(text), data(datum/netdata)) - -//Nanites -#define COMSIG_HAS_NANITES "has_nanites" //() returns TRUE if nanites are found -#define COMSIG_NANITE_IS_STEALTHY "nanite_is_stealthy" //() returns TRUE if nanites have stealth -#define COMSIG_NANITE_DELETE "nanite_delete" //() deletes the nanite component -#define COMSIG_NANITE_GET_PROGRAMS "nanite_get_programs" //(list/nanite_programs) - makes the input list a copy the nanites' program list -#define COMSIG_NANITE_GET_VOLUME "nanite_get_volume" //(amount) Returns nanite amount -#define COMSIG_NANITE_SET_VOLUME "nanite_set_volume" //(amount) Sets current nanite volume to the given amount -#define COMSIG_NANITE_ADJUST_VOLUME "nanite_adjust" //(amount) Adjusts nanite volume by the given amount -#define COMSIG_NANITE_SET_MAX_VOLUME "nanite_set_max_volume" //(amount) Sets maximum nanite volume to the given amount -#define COMSIG_NANITE_SET_CLOUD "nanite_set_cloud" //(amount(0-100)) Sets cloud ID to the given amount -#define COMSIG_NANITE_SET_CLOUD_SYNC "nanite_set_cloud_sync" //(method) Modify cloud sync status. Method can be toggle, enable or disable -#define COMSIG_NANITE_SET_SAFETY "nanite_set_safety" //(amount) Sets safety threshold to the given amount -#define COMSIG_NANITE_SET_REGEN "nanite_set_regen" //(amount) Sets regeneration rate to the given amount -#define COMSIG_NANITE_SIGNAL "nanite_signal" //(code(1-9999)) Called when sending a nanite signal to a mob. -#define COMSIG_NANITE_COMM_SIGNAL "nanite_comm_signal" //(comm_code(1-9999), comm_message) Called when sending a nanite comm signal to a mob. -#define COMSIG_NANITE_SCAN "nanite_scan" //(mob/user, full_scan) - sends to chat a scan of the nanites to the user, returns TRUE if nanites are detected -#define COMSIG_NANITE_UI_DATA "nanite_ui_data" //(list/data, scan_level) - adds nanite data to the given data list - made for ui_data procs -#define COMSIG_NANITE_ADD_PROGRAM "nanite_add_program" //(datum/nanite_program/new_program, datum/nanite_program/source_program) Called when adding a program to a nanite component - #define COMPONENT_PROGRAM_INSTALLED 1 //Installation successful - #define COMPONENT_PROGRAM_NOT_INSTALLED 2 //Installation failed, but there are still nanites -#define COMSIG_NANITE_SYNC "nanite_sync" //(datum/component/nanites, full_overwrite, copy_activation) Called to sync the target's nanites to a given nanite component - -// /datum/component/storage signals -#define COMSIG_CONTAINS_STORAGE "is_storage" //() - returns bool. -#define COMSIG_TRY_STORAGE_INSERT "storage_try_insert" //(obj/item/inserting, mob/user, silent, force) - returns bool -#define COMSIG_TRY_STORAGE_SHOW "storage_show_to" //(mob/show_to, force) - returns bool. -#define COMSIG_TRY_STORAGE_HIDE_FROM "storage_hide_from" //(mob/hide_from) - returns bool -#define COMSIG_TRY_STORAGE_HIDE_ALL "storage_hide_all" //returns bool -#define COMSIG_TRY_STORAGE_SET_LOCKSTATE "storage_lock_set_state" //(newstate) -#define COMSIG_IS_STORAGE_LOCKED "storage_get_lockstate" //() - returns bool. MUST CHECK IF STORAGE IS THERE FIRST! -#define COMSIG_TRY_STORAGE_TAKE_TYPE "storage_take_type" //(type, atom/destination, amount = INFINITY, check_adjacent, force, mob/user, list/inserted) - returns bool - type can be a list of types. -#define COMSIG_TRY_STORAGE_FILL_TYPE "storage_fill_type" //(type, amount = INFINITY, force = FALSE) //don't fuck this up. Force will ignore max_items, and amount is normally clamped to max_items. -#define COMSIG_TRY_STORAGE_TAKE "storage_take_obj" //(obj, new_loc, force = FALSE) - returns bool -#define COMSIG_TRY_STORAGE_QUICK_EMPTY "storage_quick_empty" //(loc) - returns bool - if loc is null it will dump at parent location. -#define COMSIG_TRY_STORAGE_RETURN_INVENTORY "storage_return_inventory" //(list/list_to_inject_results_into, recursively_search_inside_storages = TRUE) -#define COMSIG_TRY_STORAGE_CAN_INSERT "storage_can_equip" //(obj/item/insertion_candidate, mob/user, silent) - returns bool - -// /datum/component/two_handed signals -#define COMSIG_TWOHANDED_WIELD "twohanded_wield" //from base of datum/component/two_handed/proc/wield(mob/living/carbon/user): (/mob/user) - #define COMPONENT_TWOHANDED_BLOCK_WIELD 1 -#define COMSIG_TWOHANDED_UNWIELD "twohanded_unwield" //from base of datum/component/two_handed/proc/unwield(mob/living/carbon/user): (/mob/user) - -// /datum/action signals -#define COMSIG_ACTION_TRIGGER "action_trigger" //from base of datum/action/proc/Trigger(): (datum/action) - #define COMPONENT_ACTION_BLOCK_TRIGGER 1 - -//Xenobio hotkeys -#define COMSIG_XENO_SLIME_CLICK_CTRL "xeno_slime_click_ctrl" //from slime CtrlClickOn(): (/mob) -#define COMSIG_XENO_SLIME_CLICK_ALT "xeno_slime_click_alt" //from slime AltClickOn(): (/mob) -#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) +// All signals. Format: +// When the signal is called: (signal arguments) +// All signals send the source datum of the signal as the first argument + +// global signals +// These are signals which can be listened to by any component on any parent +// start global signals with "!", this used to be necessary but now it's just a formatting choice +/// from base of datum/controller/subsystem/mapping/proc/add_new_zlevel(): (list/args) +#define COMSIG_GLOB_NEW_Z "!new_z" +/// called after a successful var edit somewhere in the world: (list/args) +#define COMSIG_GLOB_VAR_EDIT "!var_edit" +/// called after an explosion happened : (epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) +#define COMSIG_GLOB_EXPLOSION "!explosion" +/// mob was created somewhere : (mob) +#define COMSIG_GLOB_MOB_CREATED "!mob_created" +/// mob died somewhere : (mob , gibbed) +#define COMSIG_GLOB_MOB_DEATH "!mob_death" +/// global living say plug - use sparingly: (mob/speaker , message) +#define COMSIG_GLOB_LIVING_SAY_SPECIAL "!say_special" +/// called by datum/cinematic/play() : (datum/cinematic/new_cinematic) +#define COMSIG_GLOB_PLAY_CINEMATIC "!play_cinematic" + #define COMPONENT_GLOB_BLOCK_CINEMATIC 1 +/// ingame button pressed (/obj/machinery/button/button) +#define COMSIG_GLOB_BUTTON_PRESSED "!button_pressed" + +// signals from globally accessible objects +/// from SSsun when the sun changes position : (azimuth) +#define COMSIG_SUN_MOVED "sun_moved" + +////////////////////////////////////////////////////////////////// + +// /datum signals +/// when a component is added to a datum: (/datum/component) +#define COMSIG_COMPONENT_ADDED "component_added" +/// before a component is removed from a datum because of RemoveComponent: (/datum/component) +#define COMSIG_COMPONENT_REMOVING "component_removing" +/// before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation +#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted" +/// just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called +#define COMSIG_PARENT_QDELETING "parent_qdeleting" +/// generic topic handler (usr, href_list) +#define COMSIG_TOPIC "handle_topic" + +/// fires on the target datum when an element is attached to it (/datum/element) +#define COMSIG_ELEMENT_ATTACH "element_attach" +/// fires on the target datum when an element is attached to it (/datum/element) +#define COMSIG_ELEMENT_DETACH "element_detach" + +// /atom signals +#define COMSIG_ATOM_CREATED "atom_created" ///from base of atom/proc/Initialize(): sent any time a new atom is created +#define COMSIG_PARENT_ATTACKBY "atom_attackby" ///from base of atom/attackby(): (/obj/item, /mob/living, params) + #define COMPONENT_NO_AFTERATTACK 1 //Return this in response if you don't want afterattack to be called +#define COMSIG_ATOM_HULK_ATTACK "hulk_attack" ///from base of atom/attack_hulk(): (/mob/living/carbon/human) +#define COMSIG_ATOM_ATTACK_ANIMAL "attack_animal" ///from base of atom/animal_attack(): (/mob/user) +#define COMSIG_PARENT_EXAMINE "atom_examine" ///from base of atom/examine(): (/mob) +#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" ///from base of atom/get_examine_name(): (/mob, list/overrides) +#define COMSIG_PARENT_EXAMINE_MORE "atom_examine_more" ///from base of atom/examine_more(): (/mob) + //Positions for overrides list + #define EXAMINE_POSITION_ARTICLE 1 + #define EXAMINE_POSITION_BEFORE 2 + //End positions + #define COMPONENT_EXNAME_CHANGED 1 +#define COMSIG_ATOM_UPDATE_ICON "atom_update_icon" ///from base of atom/update_icon(): () + #define COMSIG_ATOM_NO_UPDATE_ICON_STATE 1 + #define COMSIG_ATOM_NO_UPDATE_OVERLAYS 2 +#define COMSIG_ATOM_UPDATE_OVERLAYS "atom_update_overlays" ///from base of atom/update_overlays(): (list/new_overlays) +#define COMSIG_ATOM_UPDATED_ICON "atom_updated_icon" ///from base of atom/update_icon(): (signalOut, did_anything) +#define COMSIG_ATOM_ENTERED "atom_entered" ///from base of atom/Entered(): (atom/movable/entering, /atom) +#define COMSIG_ATOM_EXIT "atom_exit" ///from base of atom/Exit(): (/atom/movable/exiting, /atom/newloc) + #define COMPONENT_ATOM_BLOCK_EXIT 1 +#define COMSIG_ATOM_EXITED "atom_exited" ///from base of atom/Exited(): (atom/movable/exiting, atom/newloc) +#define COMSIG_ATOM_BUMPED "atom_bumped" ///from base of atom/Bumped(): (/atom/movable) +#define COMSIG_ATOM_EX_ACT "atom_ex_act" ///from base of atom/ex_act(): (severity, target) +#define COMSIG_ATOM_EMP_ACT "atom_emp_act" ///from base of atom/emp_act(): (severity) +#define COMSIG_ATOM_FIRE_ACT "atom_fire_act" ///from base of atom/fire_act(): (exposed_temperature, exposed_volume) +#define COMSIG_ATOM_BULLET_ACT "atom_bullet_act" ///from base of atom/bullet_act(): (/obj/projectile, def_zone) +#define COMSIG_ATOM_BLOB_ACT "atom_blob_act" ///from base of atom/blob_act(): (/obj/structure/blob) +#define COMSIG_ATOM_ACID_ACT "atom_acid_act" ///from base of atom/acid_act(): (acidpwr, acid_volume) +#define COMSIG_ATOM_EMAG_ACT "atom_emag_act" ///from base of atom/emag_act(): (/mob/user) +#define COMSIG_ATOM_RAD_ACT "atom_rad_act" ///from base of atom/rad_act(intensity) +#define COMSIG_ATOM_NARSIE_ACT "atom_narsie_act" ///from base of atom/narsie_act(): () +#define COMSIG_ATOM_RCD_ACT "atom_rcd_act" ///from base of atom/rcd_act(): (/mob, /obj/item/construction/rcd, passed_mode) +#define COMSIG_ATOM_SING_PULL "atom_sing_pull" ///from base of atom/singularity_pull(): (S, current_size) +#define COMSIG_ATOM_BSA_BEAM "atom_bsa_beam_pass" ///from obj/machinery/bsa/full/proc/fire(): () + #define COMSIG_ATOM_BLOCKS_BSA_BEAM 1 +#define COMSIG_ATOM_SET_LIGHT "atom_set_light" ///from base of atom/set_light(): (l_range, l_power, l_color) +#define COMSIG_ATOM_DIR_CHANGE "atom_dir_change" ///from base of atom/setDir(): (old_dir, new_dir) +#define COMSIG_ATOM_CONTENTS_DEL "atom_contents_del" ///from base of atom/handle_atom_del(): (atom/deleted) +#define COMSIG_ATOM_HAS_GRAVITY "atom_has_gravity" ///from base of atom/has_gravity(): (turf/location, list/forced_gravities) +#define COMSIG_ATOM_RAD_PROBE "atom_rad_probe" ///from proc/get_rad_contents(): () + #define COMPONENT_BLOCK_RADIATION 1 +#define COMSIG_ATOM_RAD_CONTAMINATING "atom_rad_contam" ///from base of datum/radiation_wave/radiate(): (strength) + #define COMPONENT_BLOCK_CONTAMINATION 1 +#define COMSIG_ATOM_RAD_WAVE_PASSING "atom_rad_wave_pass" ///from base of datum/radiation_wave/check_obstructions(): (datum/radiation_wave, width) + #define COMPONENT_RAD_WAVE_HANDLED 1 +#define COMSIG_ATOM_CANREACH "atom_can_reach" ///from internal loop in atom/movable/proc/CanReach(): (list/next) + #define COMPONENT_BLOCK_REACH 1 +#define COMSIG_ATOM_SCREWDRIVER_ACT "atom_screwdriver_act" ///from base of atom/screwdriver_act(): (mob/living/user, obj/item/I) +#define COMSIG_ATOM_WRENCH_ACT "atom_wrench_act" ///from base of atom/wrench_act(): (mob/living/user, obj/item/I) +#define COMSIG_ATOM_MULTITOOL_ACT "atom_multitool_act" ///from base of atom/multitool_act(): (mob/living/user, obj/item/I) +#define COMSIG_ATOM_WELDER_ACT "atom_welder_act" ///from base of atom/welder_act(): (mob/living/user, obj/item/I) +#define COMSIG_ATOM_WIRECUTTER_ACT "atom_wirecutter_act" ///from base of atom/wirecutter_act(): (mob/living/user, obj/item/I) +#define COMSIG_ATOM_CROWBAR_ACT "atom_crowbar_act" ///from base of atom/crowbar_act(): (mob/living/user, obj/item/I) +#define COMSIG_ATOM_ANALYSER_ACT "atom_analyser_act" ///from base of atom/analyser_act(): (mob/living/user, obj/item/I) + #define COMPONENT_BLOCK_TOOL_ATTACK 1 +#define COMSIG_ATOM_INTERCEPT_TELEPORT "intercept_teleport" ///called when teleporting into a protected turf: (channel, turf/origin) + #define COMPONENT_BLOCK_TELEPORT 1 +#define COMSIG_ATOM_HEARER_IN_VIEW "atom_hearer_in_view" ///called when an atom is added to the hearers on get_hearers_in_view(): (list/processing_list, list/hearers) +#define COMSIG_ATOM_ORBIT_BEGIN "atom_orbit_begin" ///called when an atom starts orbiting another atom: (atom) +#define COMSIG_ATOM_ORBIT_STOP "atom_orbit_stop" ///called when an atom stops orbiting another atom: (atom) +///////////////// +#define COMSIG_ATOM_ATTACK_GHOST "atom_attack_ghost" ///from base of atom/attack_ghost(): (mob/dead/observer/ghost) +#define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand" ///from base of atom/attack_hand(): (mob/user) +#define COMSIG_ATOM_ATTACK_PAW "atom_attack_paw" ///from base of atom/attack_paw(): (mob/user) + #define COMPONENT_NO_ATTACK_HAND 1 //works on all 3. +//This signal return value bitflags can be found in __DEFINES/misc.dm + +///from base of atom/expose_reagents(): +#define COMSIG_ATOM_EXPOSE_REAGENTS "atom_expose_reagents" + /// Prevents the atom from being exposed to reagents if returned on [COMPONENT_ATOM_EXPOSE_REAGENTS] + #define COMPONENT_NO_EXPOSE_REAGENTS (1<<0) + +///called for each movable in a turf contents on /turf/zImpact(): (atom/movable/A, levels) +#define COMSIG_ATOM_INTERCEPT_Z_FALL "movable_intercept_z_impact" +///called on a movable (NOT living) when someone starts pulling it (atom/movable/puller, state, force) +#define COMSIG_ATOM_START_PULL "movable_start_pull" +///called on /living when someone starts pulling it (atom/movable/puller, state, force) +#define COMSIG_LIVING_START_PULL "living_start_pull" + +///////////////// + +#define COMSIG_ENTER_AREA "enter_area" //from base of area/Entered(): (/area) +#define COMSIG_EXIT_AREA "exit_area" //from base of area/Exited(): (/area) + +#define COMSIG_CLICK "atom_click" //from base of atom/Click(): (location, control, params, mob/user) +#define COMSIG_CLICK_SHIFT "shift_click" //from base of atom/ShiftClick(): (/mob) + #define COMPONENT_ALLOW_EXAMINATE 1 //Allows the user to examinate regardless of client.eye. +#define COMSIG_CLICK_CTRL "ctrl_click" //from base of atom/CtrlClickOn(): (/mob) +#define COMSIG_CLICK_ALT "alt_click" //from base of atom/AltClick(): (/mob) +#define COMSIG_CLICK_CTRL_SHIFT "ctrl_shift_click" //from base of atom/CtrlShiftClick(/mob) +#define COMSIG_MOUSEDROP_ONTO "mousedrop_onto" //from base of atom/MouseDrop(): (/atom/over, /mob/user) + #define COMPONENT_NO_MOUSEDROP 1 +#define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" //from base of atom/MouseDrop_T: (/atom/from, /mob/user) + +///from base of area/proc/power_change(): () +#define COMSIG_AREA_POWER_CHANGE "area_power_change" +///from base of area/Entered(): (atom/movable/M) +#define COMSIG_AREA_ENTERED "area_entered" +///from base of area/Exited(): (atom/movable/M) +#define COMSIG_AREA_EXITED "area_exited" + +// /turf signals +#define COMSIG_TURF_CHANGE "turf_change" //from base of turf/ChangeTurf(): (path, list/new_baseturfs, flags, list/transferring_comps) +#define COMSIG_TURF_HAS_GRAVITY "turf_has_gravity" ///from base of atom/has_gravity(): (atom/asker, list/forced_gravities) +#define COMSIG_TURF_MULTIZ_NEW "turf_multiz_new" //from base of turf/New(): (turf/source, direction) + +// /atom/movable signals +#define COMSIG_MOVABLE_PRE_MOVE "movable_pre_move" ///from base of atom/movable/Moved(): (/atom) + #define COMPONENT_MOVABLE_BLOCK_PRE_MOVE 1 +#define COMSIG_MOVABLE_MOVED "movable_moved" ///from base of atom/movable/Moved(): (/atom, dir) +#define COMSIG_MOVABLE_CROSS "movable_cross" ///from base of atom/movable/Cross(): (/atom/movable) +#define COMSIG_MOVABLE_CROSSED "movable_crossed" ///from base of atom/movable/Crossed(): (/atom/movable) +#define COMSIG_MOVABLE_UNCROSS "movable_uncross" ///from base of atom/movable/Uncross(): (/atom/movable) + #define COMPONENT_MOVABLE_BLOCK_UNCROSS 1 +#define COMSIG_MOVABLE_UNCROSSED "movable_uncrossed" ///from base of atom/movable/Uncrossed(): (/atom/movable) +#define COMSIG_MOVABLE_BUMP "movable_bump" ///from base of atom/movable/Bump(): (/atom) +#define COMSIG_MOVABLE_IMPACT "movable_impact" ///from base of atom/movable/throw_impact(): (/atom/hit_atom, /datum/thrownthing/throwingdatum) + #define COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH 1 ///if true, flip if the impact will push what it hits + #define COMPONENT_MOVABLE_IMPACT_NEVERMIND 2 ///return true if you destroyed whatever it was you're impacting and there won't be anything for hitby() to run on +#define COMSIG_MOVABLE_IMPACT_ZONE "item_impact_zone" ///from base of mob/living/hitby(): (mob/living/target, hit_zone) +#define COMSIG_MOVABLE_BUCKLE "buckle" ///from base of atom/movable/buckle_mob(): (mob, force) +#define COMSIG_MOVABLE_UNBUCKLE "unbuckle" ///from base of atom/movable/unbuckle_mob(): (mob, force) +#define COMSIG_MOVABLE_PRE_THROW "movable_pre_throw" ///from base of atom/movable/throw_at(): (list/args) + #define COMPONENT_CANCEL_THROW 1 +#define COMSIG_MOVABLE_POST_THROW "movable_post_throw" ///from base of atom/movable/throw_at(): (datum/thrownthing, spin) +#define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit" ///from base of atom/movable/onTransitZ(): (old_z, new_z) +#define COMSIG_MOVABLE_SECLUDED_LOCATION "movable_secluded" ///called when the movable is placed in an unaccessible area, used for stationloving: () +#define COMSIG_MOVABLE_HEAR "movable_hear" ///from base of atom/movable/Hear(): (proc args list(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)) + #define HEARING_MESSAGE 1 + #define HEARING_SPEAKER 2 +// #define HEARING_LANGUAGE 3 + #define HEARING_RAW_MESSAGE 4 + /* #define HEARING_RADIO_FREQ 5 + #define HEARING_SPANS 6 + #define HEARING_MESSAGE_MODE 7 */ +#define COMSIG_MOVABLE_DISPOSING "movable_disposing" //called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source) + +// /mob signals +#define COMSIG_MOB_LOGIN "mob_login" //from base of /mob/Login(): () +#define COMSIG_MOB_LOGOUT "mob_logout" //from base of /mob/Logout(): () +#define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed) +#define COMSIG_MOB_STATCHANGE "mob_statchange" //from base of mob/set_stat(): (new_stat) +#define COMSIG_MOB_CLICKON "mob_clickon" //from base of mob/clickon(): (atom/A, params) +#define COMSIG_MOB_MIDDLECLICKON "mob_middleclickon" //from base of mob/MiddleClickOn(): (atom/A) +#define COMSIG_MOB_ALTCLICKON "mob_altclickon" //from base of mob/AltClickOn(): (atom/A) + #define COMSIG_MOB_CANCEL_CLICKON 1 + +#define COMSIG_MOB_ALLOWED "mob_allowed" //from base of obj/allowed(mob/M): (/obj) returns bool, if TRUE the mob has id access to the obj +#define COMSIG_MOB_RECEIVE_MAGIC "mob_receive_magic" //from base of mob/anti_magic_check(): (mob/user, magic, holy, tinfoil, chargecost, self, protection_sources) + #define COMPONENT_BLOCK_MAGIC 1 +#define COMSIG_MOB_HUD_CREATED "mob_hud_created" //from base of mob/create_mob_hud(): () +#define COMSIG_MOB_ATTACK_HAND "mob_attack_hand" //from base of +#define COMSIG_MOB_ITEM_ATTACK "mob_item_attack" //from base of /obj/item/attack(): (mob/M, mob/user) + #define COMPONENT_ITEM_NO_ATTACK 1 +#define COMSIG_MOB_APPLY_DAMGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone) +#define COMSIG_MOB_ITEM_AFTERATTACK "mob_item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, proximity_flag, click_parameters) +#define COMSIG_MOB_ITEM_ATTACK_QDELETED "mob_item_attack_qdeleted" //from base of obj/item/attack_qdeleted(): (atom/target, mob/user, proxiumity_flag, click_parameters) +#define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged" //from base of mob/RangedAttack(): (atom/A, params) +#define COMSIG_MOB_THROW "mob_throw" //from base of /mob/throw_item(): (atom/target) +#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/target) +#define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight" //from base of /mob/update_sight(): () +#define COMSIG_MOB_SAY "mob_say" // from /mob/living/say(): () + #define COMPONENT_UPPERCASE_SPEECH 1 + // used to access COMSIG_MOB_SAY argslist + #define SPEECH_MESSAGE 1 + // #define SPEECH_BUBBLE_TYPE 2 + #define SPEECH_SPANS 3 + /* #define SPEECH_SANITIZE 4 + #define SPEECH_LANGUAGE 5 + #define SPEECH_IGNORE_SPAM 6 + #define SPEECH_FORCED 7 */ +#define COMSIG_MOB_DEADSAY "mob_deadsay" // from /mob/say_dead(): (mob/speaker, message) + #define MOB_DEADSAY_SIGNAL_INTERCEPT 1 +#define COMSIG_MOB_EMOTE "mob_emote" // from /mob/living/emote(): () +#define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" //from base of mob/swap_hand(): (obj/item) + #define COMPONENT_BLOCK_SWAP 1 + +// /mob/living signals +#define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living) +#define COMSIG_LIVING_IGNITED "living_ignite" //from base of mob/living/IgniteMob() (/mob/living) +#define COMSIG_LIVING_EXTINGUISHED "living_extinguished" //from base of mob/living/ExtinguishMob() (/mob/living) +#define COMSIG_LIVING_ELECTROCUTE_ACT "living_electrocute_act" //from base of mob/living/electrocute_act(): (shock_damage, source, siemens_coeff, flags) +#define COMSIG_LIVING_SHOCK_PREVENTED "living_shock_prevented" //sent when items with siemen coeff. of 0 block a shock: (power_source, source, siemens_coeff, dist_check) +#define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //sent by stuff like stunbatons and tasers: () +#define COMSIG_LIVING_REVIVE "living_revive" //from base of mob/living/revive() (full_heal, admin_revive) +#define COMSIG_LIVING_REGENERATE_LIMBS "living_regen_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs) +#define COMSIG_LIVING_ATTACH_LIMB "living_attach_limb" //from base of /obj/item/bodypart/proc/attach_limb(): (new_limb, special) allows you to fail limb attachment + #define COMPONENT_NO_ATTACH 1 +#define COMSIG_PROCESS_BORGCHARGER_OCCUPANT "living_charge" //sent from borg recharge stations: (amount, repairs) +#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" //sent when a mob/login() finishes: (client) +#define COMSIG_BORG_SAFE_DECONSTRUCT "borg_safe_decon" //sent from borg mobs to itself, for tools to catch an upcoming destroy() due to safe decon (rather than detonation) + +//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS! +#define COMSIG_LIVING_STATUS_STUN "living_stun" //from base of mob/living/Stun() (amount, update, ignore) +#define COMSIG_LIVING_STATUS_KNOCKDOWN "living_knockdown" //from base of mob/living/Knockdown() (amount, update, ignore) +#define COMSIG_LIVING_STATUS_PARALYZE "living_paralyze" //from base of mob/living/Paralyze() (amount, update, ignore) +#define COMSIG_LIVING_STATUS_IMMOBILIZE "living_immobilize" //from base of mob/living/Immobilize() (amount, update, ignore) +#define COMSIG_LIVING_STATUS_UNCONSCIOUS "living_unconscious" //from base of mob/living/Unconscious() (amount, update, ignore) +#define COMSIG_LIVING_STATUS_SLEEP "living_sleeping" //from base of mob/living/Sleeping() (amount, update, ignore) + #define COMPONENT_NO_STUN 1 //For all of them +#define COMSIG_LIVING_CAN_TRACK "mob_cantrack" //from base of /mob/living/can_track(): (mob/user) + #define COMPONENT_CANT_TRACK 1 + +// /mob/living/carbon signals +#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity)) +#define COMSIG_CARBON_GAIN_ORGAN "carbon_gain_organ" //from /item/organ/proc/Insert() (/obj/item/organ/) +#define COMSIG_CARBON_LOSE_ORGAN "carbon_lose_organ" //from /item/organ/proc/Remove() (/obj/item/organ/) +#define COMSIG_CARBON_EQUIP_HAT "carbon_equip_hat" //from /mob/living/carbon/doUnEquip(obj/item/I, force, newloc, no_move, invdrop, silent) +#define COMSIG_CARBON_UNEQUIP_HAT "carbon_unequip_hat" //from /mob/living/carbon/doUnEquip(obj/item/I, force, newloc, no_move, invdrop, silent) +#define COMSIG_CARBON_EMBED_RIP "item_embed_start_rip" // defined twice, in carbon and human's topics, fired when interacting with a valid embedded_object to pull it out (mob/living/carbon/target, /obj/item, /obj/item/bodypart/L) +#define COMSIG_CARBON_EMBED_REMOVAL "item_embed_remove_safe" // called when removing a given item from a mob, from mob/living/carbon/remove_embedded_object(mob/living/carbon/target, /obj/item) + +// /mob/living/simple_animal/hostile signals +#define COMSIG_HOSTILE_ATTACKINGTARGET "hostile_attackingtarget" + #define COMPONENT_HOSTILE_NO_ATTACK 1 + +// /obj signals +#define COMSIG_OBJ_DECONSTRUCT "obj_deconstruct" //from base of obj/deconstruct(): (disassembled) +#define COMSIG_OBJ_SETANCHORED "obj_setanchored" //called in /obj/structure/setAnchored(): (value) +#define COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH "obj_default_unfasten_wrench" //from base of code/game/machinery +#define COMSIG_OBJ_HIDE "obj_hide" //from base of /turf/proc/levelupdate(). (intact) true to hide and false to unhide + +// /obj/machinery signals +#define COMSIG_MACHINERY_BROKEN "machinery_broken" //from /obj/machinery/obj_break(damage_flag): (damage_flag) +#define COMSIG_MACHINERY_POWER_LOST "machinery_power_lost" //from base power_change() when power is lost +#define COMSIG_MACHINERY_POWER_RESTORED "machinery_power_restored" //from base power_change() when power is restored + +// /obj/item signals +#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user) +#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob) + #define COMPONENT_NO_INTERACT 1 +#define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob) + #define COMPONENT_NO_ATTACK_OBJ 1 +#define COMSIG_ITEM_PRE_ATTACK "item_pre_attack" //from base of obj/item/pre_attack(): (atom/target, mob/user, params) + #define COMPONENT_NO_ATTACK 1 +#define COMSIG_ITEM_AFTERATTACK "item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, params) +#define COMSIG_ITEM_ATTACK_QDELETED "item_attack_qdeleted" //from base of obj/item/attack_qdeleted(): (atom/target, mob/user, params) +#define COMSIG_ITEM_EQUIPPED "item_equip" //from base of obj/item/equipped(): (/mob/equipper, slot) +#define COMSIG_ITEM_DROPPED "item_drop" //from base of obj/item/dropped(): (mob/user) +#define COMSIG_ITEM_PICKUP "item_pickup" //from base of obj/item/pickup(): (/mob/taker) +#define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone" //from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone) +#define COMSIG_ITEM_IMBUE_SOUL "item_imbue_soul" //return a truthy value to prevent ensouling, checked in /obj/effect/proc_holder/spell/targeted/lichdom/cast(): (mob/user) +#define COMSIG_ITEM_MARK_RETRIEVAL "item_mark_retrieval" //called before marking an object for retrieval, checked in /obj/effect/proc_holder/spell/targeted/summonitem/cast() : (mob/user) + #define COMPONENT_BLOCK_MARK_RETRIEVAL 1 +#define COMSIG_ITEM_HIT_REACT "item_hit_react" //from base of obj/item/hit_reaction(): (list/args) +#define COMSIG_ITEM_WEARERCROSSED "wearer_crossed" //called on item when crossed by something (): (/atom/movable, mob/living/crossed) +#define COMSIG_ITEM_MICROWAVE_ACT "microwave_act" //called on item when microwaved (): (obj/machinery/microwave/M) +#define COMSIG_ITEM_SHARPEN_ACT "sharpen_act" //from base of item/sharpener/attackby(): (amount, max) + #define COMPONENT_BLOCK_SHARPEN_APPLIED 1 + #define COMPONENT_BLOCK_SHARPEN_BLOCKED 2 + #define COMPONENT_BLOCK_SHARPEN_ALREADY 4 + #define COMPONENT_BLOCK_SHARPEN_MAXED 8 +#define COMSIG_TOOL_IN_USE "tool_in_use" ///from base of [/obj/item/proc/tool_check_callback]: (mob/living/user) +#define COMSIG_TOOL_START_USE "tool_start_use" ///from base of [/obj/item/proc/tool_start_check]: (mob/living/user) +#define COMSIG_ITEM_DISABLE_EMBED "item_disable_embed" ///from [/obj/item/proc/disableEmbedding]: +#define COMSIG_MINE_TRIGGERED "minegoboom" ///from [/obj/effect/mine/proc/triggermine]: + +// /obj/item signals for economy +#define COMSIG_ITEM_SOLD "item_sold" //called when an item is sold by the exports subsystem +#define COMSIG_STRUCTURE_UNWRAPPED "structure_unwrapped" //called when a wrapped up structure is opened by hand +#define COMSIG_ITEM_UNWRAPPED "item_unwrapped" //called when a wrapped up item is opened by hand + #define COMSIG_ITEM_SPLIT_VALUE 1 +#define COMSIG_ITEM_SPLIT_PROFIT "item_split_profits" //Called when getting the item's exact ratio for cargo's profit. +#define COMSIG_ITEM_SPLIT_PROFIT_DRY "item_split_profits_dry" //Called when getting the item's exact ratio for cargo's profit, without selling the item. + + +// /obj/item/clothing signals +#define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): () +#define COMSIG_SUIT_SPACE_TOGGLE "suit_space_toggle" //from base of /obj/item/clothing/suit/space/proc/toggle_spacesuit(): (obj/item/clothing/suit/space/suit) + +// /obj/item/implant signals +#define COMSIG_IMPLANT_ACTIVATED "implant_activated" //from base of /obj/item/implant/proc/activate(): () +#define COMSIG_IMPLANT_IMPLANTING "implant_implanting" //from base of /obj/item/implant/proc/implant(): (list/args) + #define COMPONENT_STOP_IMPLANTING 1 +#define COMSIG_IMPLANT_OTHER "implant_other" //called on already installed implants when a new one is being added in /obj/item/implant/proc/implant(): (list/args, obj/item/implant/new_implant) + //#define COMPONENT_STOP_IMPLANTING 1 //The name makes sense for both + #define COMPONENT_DELETE_NEW_IMPLANT 2 + #define COMPONENT_DELETE_OLD_IMPLANT 4 +#define COMSIG_IMPLANT_EXISTING_UPLINK "implant_uplink_exists" //called on implants being implanted into someone with an uplink implant: (datum/component/uplink) + //This uses all return values of COMSIG_IMPLANT_OTHER + +// /obj/item/pda signals +#define COMSIG_PDA_CHANGE_RINGTONE "pda_change_ringtone" //called on pda when the user changes the ringtone: (mob/living/user, new_ringtone) + #define COMPONENT_STOP_RINGTONE_CHANGE 1 +#define COMSIG_PDA_CHECK_DETONATE "pda_check_detonate" + #define COMPONENT_PDA_NO_DETONATE 1 + +// /obj/item/radio signals +#define COMSIG_RADIO_NEW_FREQUENCY "radio_new_frequency" //called from base of /obj/item/radio/proc/set_frequency(): (list/args) + +// /obj/item/pen signals +#define COMSIG_PEN_ROTATED "pen_rotated" //called after rotation in /obj/item/pen/attack_self(): (rotation, mob/living/carbon/user) + +// /obj/item/gun signals +#define COMSIG_MOB_FIRED_GUN "mob_fired_gun" //called in /obj/item/gun/process_fire (user, target, params, zone_override) + +// /obj/item/grenade signals +#define COMSIG_GRENADE_PRIME "grenade_prime" //called in /obj/item/gun/process_fire (user, target, params, zone_override) +#define COMSIG_GRENADE_ARMED "grenade_armed" //called in /obj/item/gun/process_fire (user, target, params, zone_override) + +// /obj/projectile signals (sent to the firer) +#define COMSIG_PROJECTILE_SELF_ON_HIT "projectile_self_on_hit" // from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle) +#define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit" // from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle) +#define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire" // from base of /obj/projectile/proc/fire(): (obj/projectile, atom/original_target) +#define COMSIG_PROJECTILE_FIRE "projectile_fire" // from the base of /obj/projectile/proc/fire(): () +#define COMSIG_PROJECTILE_PREHIT "com_proj_prehit" // sent to targets during the process_hit proc of projectiles +#define COMSIG_PROJECTILE_RANGE_OUT "projectile_range_out" // sent to targets during the process_hit proc of projectiles +#define COMSIG_EMBED_TRY_FORCE "item_try_embed" // sent when trying to force an embed (mainly for projectiles, only used in the embed element) + +#define COMSIG_PELLET_CLOUD_INIT "pellet_cloud_init" // sent to targets during the process_hit proc of projectiles + +// /obj/mecha signals +#define COMSIG_MECHA_ACTION_ACTIVATE "mecha_action_activate" //sent from mecha action buttons to the mecha they're linked to + +// /mob/living/carbon/human signals +#define COMSIG_HUMAN_EARLY_UNARMED_ATTACK "human_early_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity) +#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity) +#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker) +#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted) +#define COMSIG_JOB_RECEIVED "job_received" //Whenever EquipRanked is called, called after job is set + +// /datum/species signals +#define COMSIG_SPECIES_GAIN "species_gain" //from datum/species/on_species_gain(): (datum/species/new_species, datum/species/old_species) +#define COMSIG_SPECIES_LOSS "species_loss" //from datum/species/on_species_loss(): (datum/species/lost_species) + +// /datum/song signals +#define COMSIG_SONG_START "song_start" //sent to the instrument when a song starts playing +#define COMSIG_SONG_END "song_end" //sent to the instrument when a song stops playing + +/*******Component Specific Signals*******/ +//Janitor +#define COMSIG_TURF_IS_WET "check_turf_wet" //(): Returns bitflags of wet values. +#define COMSIG_TURF_MAKE_DRY "make_turf_try" //(max_strength, immediate, duration_decrease = INFINITY): Returns bool. +#define COMSIG_COMPONENT_CLEAN_ACT "clean_act" //called on an object to clean it of cleanables. Usualy with soap: (num/strength) + +//Creamed +#define COMSIG_COMPONENT_CLEAN_FACE_ACT "clean_face_act" //called when you wash your face at a sink: (num/strength) + +//Food +#define COMSIG_FOOD_EATEN "food_eaten" //from base of obj/item/reagent_containers/food/snacks/attack(): (mob/living/eater, mob/feeder) + +//Gibs +#define COMSIG_GIBS_STREAK "gibs_streak" // from base of /obj/effect/decal/cleanable/blood/gibs/streak(): (list/directions, list/diseases) + +//Mood +#define COMSIG_ADD_MOOD_EVENT "add_mood" //Called when you send a mood event from anywhere in the code. +#define COMSIG_ADD_MOOD_EVENT_RND "RND_add_mood" //Mood event that only RnD members listen for +#define COMSIG_CLEAR_MOOD_EVENT "clear_mood" //Called when you clear a mood event from anywhere in the code. + +//NTnet +#define COMSIG_COMPONENT_NTNET_RECEIVE "ntnet_receive" //called on an object by its NTNET connection component on receive. (sending_id(number), sending_netname(text), data(datum/netdata)) + +//Nanites +#define COMSIG_HAS_NANITES "has_nanites" //() returns TRUE if nanites are found +#define COMSIG_NANITE_IS_STEALTHY "nanite_is_stealthy" //() returns TRUE if nanites have stealth +#define COMSIG_NANITE_DELETE "nanite_delete" //() deletes the nanite component +#define COMSIG_NANITE_GET_PROGRAMS "nanite_get_programs" //(list/nanite_programs) - makes the input list a copy the nanites' program list +#define COMSIG_NANITE_GET_VOLUME "nanite_get_volume" //(amount) Returns nanite amount +#define COMSIG_NANITE_SET_VOLUME "nanite_set_volume" //(amount) Sets current nanite volume to the given amount +#define COMSIG_NANITE_ADJUST_VOLUME "nanite_adjust" //(amount) Adjusts nanite volume by the given amount +#define COMSIG_NANITE_SET_MAX_VOLUME "nanite_set_max_volume" //(amount) Sets maximum nanite volume to the given amount +#define COMSIG_NANITE_SET_CLOUD "nanite_set_cloud" //(amount(0-100)) Sets cloud ID to the given amount +#define COMSIG_NANITE_SET_CLOUD_SYNC "nanite_set_cloud_sync" //(method) Modify cloud sync status. Method can be toggle, enable or disable +#define COMSIG_NANITE_SET_SAFETY "nanite_set_safety" //(amount) Sets safety threshold to the given amount +#define COMSIG_NANITE_SET_REGEN "nanite_set_regen" //(amount) Sets regeneration rate to the given amount +#define COMSIG_NANITE_SIGNAL "nanite_signal" //(code(1-9999)) Called when sending a nanite signal to a mob. +#define COMSIG_NANITE_COMM_SIGNAL "nanite_comm_signal" //(comm_code(1-9999), comm_message) Called when sending a nanite comm signal to a mob. +#define COMSIG_NANITE_SCAN "nanite_scan" //(mob/user, full_scan) - sends to chat a scan of the nanites to the user, returns TRUE if nanites are detected +#define COMSIG_NANITE_UI_DATA "nanite_ui_data" //(list/data, scan_level) - adds nanite data to the given data list - made for ui_data procs +#define COMSIG_NANITE_ADD_PROGRAM "nanite_add_program" //(datum/nanite_program/new_program, datum/nanite_program/source_program) Called when adding a program to a nanite component + #define COMPONENT_PROGRAM_INSTALLED 1 //Installation successful + #define COMPONENT_PROGRAM_NOT_INSTALLED 2 //Installation failed, but there are still nanites +#define COMSIG_NANITE_SYNC "nanite_sync" //(datum/component/nanites, full_overwrite, copy_activation) Called to sync the target's nanites to a given nanite component + +// /datum/component/storage signals +#define COMSIG_CONTAINS_STORAGE "is_storage" //() - returns bool. +#define COMSIG_TRY_STORAGE_INSERT "storage_try_insert" //(obj/item/inserting, mob/user, silent, force) - returns bool +#define COMSIG_TRY_STORAGE_SHOW "storage_show_to" //(mob/show_to, force) - returns bool. +#define COMSIG_TRY_STORAGE_HIDE_FROM "storage_hide_from" //(mob/hide_from) - returns bool +#define COMSIG_TRY_STORAGE_HIDE_ALL "storage_hide_all" //returns bool +#define COMSIG_TRY_STORAGE_SET_LOCKSTATE "storage_lock_set_state" //(newstate) +#define COMSIG_IS_STORAGE_LOCKED "storage_get_lockstate" //() - returns bool. MUST CHECK IF STORAGE IS THERE FIRST! +#define COMSIG_TRY_STORAGE_TAKE_TYPE "storage_take_type" //(type, atom/destination, amount = INFINITY, check_adjacent, force, mob/user, list/inserted) - returns bool - type can be a list of types. +#define COMSIG_TRY_STORAGE_FILL_TYPE "storage_fill_type" //(type, amount = INFINITY, force = FALSE) //don't fuck this up. Force will ignore max_items, and amount is normally clamped to max_items. +#define COMSIG_TRY_STORAGE_TAKE "storage_take_obj" //(obj, new_loc, force = FALSE) - returns bool +#define COMSIG_TRY_STORAGE_QUICK_EMPTY "storage_quick_empty" //(loc) - returns bool - if loc is null it will dump at parent location. +#define COMSIG_TRY_STORAGE_RETURN_INVENTORY "storage_return_inventory" //(list/list_to_inject_results_into, recursively_search_inside_storages = TRUE) +#define COMSIG_TRY_STORAGE_CAN_INSERT "storage_can_equip" //(obj/item/insertion_candidate, mob/user, silent) - returns bool + +// /datum/component/two_handed signals +#define COMSIG_TWOHANDED_WIELD "twohanded_wield" //from base of datum/component/two_handed/proc/wield(mob/living/carbon/user): (/mob/user) + #define COMPONENT_TWOHANDED_BLOCK_WIELD 1 +#define COMSIG_TWOHANDED_UNWIELD "twohanded_unwield" //from base of datum/component/two_handed/proc/unwield(mob/living/carbon/user): (/mob/user) + +// /datum/action signals +#define COMSIG_ACTION_TRIGGER "action_trigger" //from base of datum/action/proc/Trigger(): (datum/action) + #define COMPONENT_ACTION_BLOCK_TRIGGER 1 + +//Xenobio hotkeys +#define COMSIG_XENO_SLIME_CLICK_CTRL "xeno_slime_click_ctrl" //from slime CtrlClickOn(): (/mob) +#define COMSIG_XENO_SLIME_CLICK_ALT "xeno_slime_click_alt" //from slime AltClickOn(): (/mob) +#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) diff --git a/code/__DEFINES/dye_keys.dm b/code/__DEFINES/dye_keys.dm index da4430bfc701..742291d308eb 100644 --- a/code/__DEFINES/dye_keys.dm +++ b/code/__DEFINES/dye_keys.dm @@ -28,3 +28,5 @@ #define DYE_CMO "cmo" #define DYE_REDCOAT "redcoat" #define DYE_CLOWN "clown" +#define DYE_CHAP "chap" +#define DYE_CENTCOM "centcom" diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 739e03bfbdae..d793d9c1b564 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -15,8 +15,6 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 /// This flag is what recursive_hear_check() uses to determine wether to add an item to the hearer list or not. #define HEAR_1 (1<<3) -/// Projectiels will check ricochet on things impacted that have this. -#define CHECK_RICOCHET_1 (1<<4) /// conducts electricity (metal etc.) #define CONDUCT_1 (1<<5) /// For machines and structures that should not break into parts, eg, holodeck stuff @@ -38,6 +36,11 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define PREVENT_CONTENTS_EXPLOSION_1 (1<<16) +/// If the thing can reflect light (lasers/energy) +#define RICOCHET_SHINY (1<<0) +/// If the thing can reflect matter (bullets/bomb shrapnel) +#define RICOCHET_HARD (1<<1) + //turf-only flags #define NOJAUNT_1 (1<<0) #define UNUSED_RESERVATION_TURF_1 (1<<1) diff --git a/code/__DEFINES/food.dm b/code/__DEFINES/food.dm index 62bdd1d8c388..a21fcbb18581 100644 --- a/code/__DEFINES/food.dm +++ b/code/__DEFINES/food.dm @@ -13,6 +13,7 @@ #define PINEAPPLE (1<<12) #define BREAKFAST (1<<13) #define CLOTH (1<<14) +#define GRILLED (1<<15) #define DRINK_NICE 1 #define DRINK_GOOD 2 diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm index ecae9eac9833..3ae473ea2054 100644 --- a/code/__DEFINES/inventory.dm +++ b/code/__DEFINES/inventory.dm @@ -103,7 +103,7 @@ //flags for covering body parts #define GLASSESCOVERSEYES (1<<0) -#define MASKCOVERSEYES (1<<1) // get rid of some of the other retardation in these flags +#define MASKCOVERSEYES (1<<1) // get rid of some of the other stupidness in these flags #define HEADCOVERSEYES (1<<2) // feel free to realloc these numbers for other purposes #define MASKCOVERSMOUTH (1<<3) // on other items, these are just for mask/head #define HEADCOVERSMOUTH (1<<4) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 9876f7925e8a..b7d9def0a2c9 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -160,6 +160,12 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define isitem(A) (istype(A, /obj/item)) +#define isgrenade(A) (istype(A, /obj/item/grenade)) + +#define islandmine(A) (istype(A, /obj/effect/mine)) + +#define isammocasing(A) (istype(A, /obj/item/ammo_casing)) + #define isidcard(I) (istype(I, /obj/item/card/id)) #define isstructure(A) (istype(A, /obj/structure)) diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 16b599859912..c25eaa2a421d 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -91,11 +91,12 @@ #define MASSIVE_OBJ_LAYER 11 #define POINT_LAYER 12 - #define EMISSIVE_BLOCKER_PLANE 12 #define EMISSIVE_BLOCKER_LAYER 12 #define EMISSIVE_BLOCKER_RENDER_TARGET "*EMISSIVE_BLOCKER_PLANE" +#define CHAT_LAYER 12.1 + #define EMISSIVE_PLANE 13 #define EMISSIVE_LAYER 13 #define EMISSIVE_RENDER_TARGET "*EMISSIVE_PLANE" @@ -108,6 +109,8 @@ #define LIGHTING_LAYER 15 #define LIGHTING_RENDER_TARGET "LIGHT_PLANE" +#define RAD_TEXT_LAYER 15.1 + #define ABOVE_LIGHTING_PLANE 16 #define ABOVE_LIGHTING_LAYER 16 #define ABOVE_LIGHTING_RENDER_TARGET "ABOVE_LIGHTING_PLANE" diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 5d940502aeee..79f0f96d467f 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -37,8 +37,10 @@ #define LOG_VIRUS (1 << 16) #define LOG_CLONING (1 << 17) #define LOG_SHUTTLE (1 << 18) -#define LOG_LOOC (1 << 19) //Wasp Edit -#define LOG_SUBTLER (1 << 20) //Wasp Edit +#define LOG_ECON (1 << 19) + +#define LOG_LOOC (1 << 25) //Wasp Edit +#define LOG_SUBTLER (1 << 26) //Wasp Edit //Individual logging panel pages #define INDIVIDUAL_ATTACK_LOG (LOG_ATTACK) @@ -48,7 +50,7 @@ #define INDIVIDUAL_OOC_LOG (LOG_OOC | LOG_ADMIN) #define INDIVIDUAL_LOOC_LOG (LOG_LOOC | LOG_ADMIN) //Wasp Edit #define INDIVIDUAL_OWNERSHIP_LOG (LOG_OWNERSHIP) -#define INDIVIDUAL_SHOW_ALL_LOG (LOG_ATTACK | LOG_SAY | LOG_WHISPER | LOG_EMOTE | LOG_DSAY | LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS | LOG_OOC | LOG_ADMIN | LOG_OWNERSHIP | LOG_GAME | LOG_ADMIN_PRIVATE | LOG_ASAY | LOG_MECHA | LOG_VIRUS | LOG_CLONING | LOG_SHUTTLE | LOG_LOOC) +#define INDIVIDUAL_SHOW_ALL_LOG (LOG_ATTACK | LOG_SAY | LOG_WHISPER | LOG_EMOTE | LOG_DSAY | LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS | LOG_OOC | LOG_ADMIN | LOG_OWNERSHIP | LOG_GAME | LOG_ADMIN_PRIVATE | LOG_ASAY | LOG_MECHA | LOG_VIRUS | LOG_CLONING | LOG_SHUTTLE | LOG_ECON | LOG_LOOC) #define LOGSRC_CLIENT "Client" #define LOGSRC_MOB "Mob" diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 71ea98f9f0d5..14f7ba9a08bd 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -1,17 +1,30 @@ // channel numbers for power -#define EQUIP 1 -#define LIGHT 2 -#define ENVIRON 3 -#define TOTAL 4 //for total power used only -#define STATIC_EQUIP 5 -#define STATIC_LIGHT 6 -#define STATIC_ENVIRON 7 +// These are indexes in a list, and indexes for "dynamic" and static channels should be kept contiguous +#define AREA_USAGE_EQUIP 1 +#define AREA_USAGE_LIGHT 2 +#define AREA_USAGE_ENVIRON 3 +#define AREA_USAGE_STATIC_EQUIP 4 +#define AREA_USAGE_STATIC_LIGHT 5 +#define AREA_USAGE_STATIC_ENVIRON 6 +#define AREA_USAGE_LEN AREA_USAGE_STATIC_ENVIRON // largest idx +/// Index of the first dynamic usage channel +#define AREA_USAGE_DYNAMIC_START AREA_USAGE_EQUIP +/// Index of the last dynamic usage channel +#define AREA_USAGE_DYNAMIC_END AREA_USAGE_ENVIRON +/// Index of the first static usage channel +#define AREA_USAGE_STATIC_START AREA_USAGE_STATIC_EQUIP +/// Index of the last static usage channel +#define AREA_USAGE_STATIC_END AREA_USAGE_STATIC_ENVIRON + //Power use #define NO_POWER_USE 0 #define IDLE_POWER_USE 1 #define ACTIVE_POWER_USE 2 +/// Bitflags for a machine's preferences on when it should start processing. For use with machinery's `processing_flags` var. +#define START_PROCESSING_ON_INIT (1<<0) /// Indicates the machine will automatically start processing right after it's `Initialize()` is ran. +#define START_PROCESSING_MANUALLY (1<<1) /// Machines with this flag will not start processing when it's spawned. Use this if you want to manually control when a machine starts processing. //bitflags for door switches. #define OPEN (1<<0) diff --git a/code/__DEFINES/materials.dm b/code/__DEFINES/materials.dm index 11f16d9ecb86..cfb9fff8b440 100644 --- a/code/__DEFINES/materials.dm +++ b/code/__DEFINES/materials.dm @@ -4,6 +4,9 @@ /// Hard materials, such as iron or metal #define MAT_CATEGORY_RIGID "rigid material" +///Use this flag on TRUE if you want the basic recipes +#define MAT_CATEGORY_BASE_RECIPES "basic recipes" + /// Flag for atoms, this flag ensures it isn't re-colored by materials. Useful for snowflake icons such as default toolboxes. #define MATERIAL_COLOR (1<<0) #define MATERIAL_ADD_PREFIX (1<<1) diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index 04242823456c..82b109c9c70f 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -31,7 +31,7 @@ #define FLOOR(x, y) ( round((x) / (y)) * (y) ) // Similar to clamp but the bottom rolls around to the top and vice versa. min is inclusive, max is exclusive -#define WRAP(val, min, max) ( min == max ? min : (val) - (round(((val) - (min))/((max) - (min))) * ((max) - (min))) ) +#define WRAP(val, min, max) clamp(( min == max ? min : (val) - (round(((val) - (min))/((max) - (min))) * ((max) - (min))) ),min,max) // Real modulus that handles decimals #define MODULUS(x, y) ( (x) - (y) * round((x) / (y)) ) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 7d7f25c9049f..bfb609f2aa90 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -490,3 +490,9 @@ GLOBAL_LIST_INIT(pda_styles, sortList(list(MONO, VT, ORBITRON, SHARE))) // The alpha we give to stuff under tiles, if they want it #define ALPHA_UNDERTILE 128 + +// Anonymous names defines (used in the secrets panel) + +#define ANON_DISABLED "" //so it's falsey +#define ANON_RANDOMNAMES "Random Default" +#define ANON_EMPLOYEENAMES "Employees" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 717f9bae3863..02d6756a804d 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -138,6 +138,7 @@ #define BIOWARE_NERVES "nerves" #define BIOWARE_CIRCULATION "circulation" #define BIOWARE_LIGAMENTS "ligaments" +#define BIOWARE_CORTEX "cortex" //Health hud screws for carbon mobs #define SCREWYHUD_NONE 0 @@ -293,6 +294,7 @@ //MINOR TWEAKS/MISC #define AGE_MIN 17 //youngest a character can be #define AGE_MAX 85 //oldest a character can be +#define AGE_MINOR 20 //legal age of space drinking and smoking #define WIZARD_AGE_MIN 30 //youngest a wizard can be #define APPRENTICE_AGE_MIN 29 //youngest an apprentice can be #define SHOES_SLOWDOWN 0 //How much shoes slow you down by default. Negative values speed you up @@ -350,3 +352,7 @@ #define WABBAJACK (1<<6) #define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; +#define INTERACTING_WITH(X, Y) (Y in X.do_afters) + +/// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine() +#define EXAMINE_MORE_TIME 1 SECONDS diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index 99af63968e4d..653f10cf909d 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -28,6 +28,7 @@ #define IMMUTABLE_SLOW (1<<10) // When players should not be able to change the slowdown of the item (Speed potions, etc) #define IN_STORAGE (1<<11) //is this item in the storage item, such as backpack? used for tooltips #define SURGICAL_TOOL (1<<12) //Tool commonly used for surgery: won't attack targets in an active surgical operation on help intent (in case of mistakes) +#define EYE_STAB (1<<13) /// Item can be used to eyestab // Flags for the clothing_flags var on /obj/item/clothing diff --git a/code/__DEFINES/power.dm b/code/__DEFINES/power.dm index 9e86f93efe8f..4a7f640c640c 100644 --- a/code/__DEFINES/power.dm +++ b/code/__DEFINES/power.dm @@ -4,6 +4,8 @@ #define CABLE_LAYER_3 4 */ +#define MACHINERY_LAYER_1 1 + #define SOLAR_TRACK_OFF 0 #define SOLAR_TRACK_TIMED 1 #define SOLAR_TRACK_AUTO 2 diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 4615af1259bf..764278aaf6ce 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -21,6 +21,7 @@ #define DEADMIN_POSITION_SECURITY (1<<18) #define DEADMIN_POSITION_SILICON (1<<19) #define SOUND_ENDOFROUND (1<<20) +#define ADMIN_IGNORE_CULT_GHOST (1<<21) #define TOGGLES_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|SOUND_ENDOFROUND|MEMBER_PUBLIC|INTENT_STYLE|MIDROUND_ANTAG|SOUND_INSTRUMENTS|SOUND_SHIP_AMBIENCE|SOUND_PRAYERS|SOUND_ANNOUNCEMENTS) diff --git a/code/__DEFINES/reactions.dm b/code/__DEFINES/reactions.dm index fd24313369d7..c4ef4c20d67f 100644 --- a/code/__DEFINES/reactions.dm +++ b/code/__DEFINES/reactions.dm @@ -12,12 +12,13 @@ #define WATER_VAPOR_FREEZE 200 //freon reaction #define FREON_BURN_RATE_DELTA 4 -#define FIRE_FREON_ENERGY_RELEASED -200000 //amount of heat absorbed per mole of burnt freon in the tile +#define FIRE_FREON_ENERGY_RELEASED -300000 //amount of heat absorbed per mole of burnt freon in the tile #define N2O_DECOMPOSITION_MIN_ENERGY 1400 #define N2O_DECOMPOSITION_ENERGY_RELEASED 200000 #define NITRYL_FORMATION_ENERGY 100000 +#define NITROUS_FORMATION_ENERGY 10000 #define TRITIUM_BURN_OXY_FACTOR 100 #define TRITIUM_BURN_TRIT_FACTOR 10 #define TRITIUM_BURN_RADIOACTIVITY_FACTOR 50000 //The neutrons gotta go somewhere. Completely arbitrary number. diff --git a/code/__DEFINES/research.dm b/code/__DEFINES/research.dm index 8e4fc4ca1d81..a0e2fa9d9f93 100644 --- a/code/__DEFINES/research.dm +++ b/code/__DEFINES/research.dm @@ -57,8 +57,6 @@ #define DEPARTMENTAL_FLAG_SCIENCE (1<<3) #define DEPARTMENTAL_FLAG_ENGINEERING (1<<4) #define DEPARTMENTAL_FLAG_SERVICE (1<<5) -#define DEPARTMENTAL_FLAG_ALL (1<<6) //NO THIS DOESN'T ALLOW YOU TO PRINT EVERYTHING, IT'S FOR ALL DEPARTMENTS! -//#define DEPARTMENTAL_FLAG_MINING (1<<7) //I swear I'm actually going to use this eventually leave it here #define DESIGN_ID_IGNORE "IGNORE_THIS_DESIGN" ///For instances where we don't want a design showing up due to it being for debug/sanity purposes diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm index 8da9a8064936..495d47129c33 100644 --- a/code/__DEFINES/robots.dm +++ b/code/__DEFINES/robots.dm @@ -4,7 +4,7 @@ //Bot defines, placed here so they can be read by other things! #define BOT_STEP_DELAY 4 //Delay between movemements -#define BOT_STEP_MAX_RETRIES 5 //Maximum times a bot will retry to step from its position +#define BOT_STEP_MAX_RETRIES 5 //Maximum times a bot will retry to step from its position #define DEFAULT_SCAN_RANGE 7 //default view range for finding targets. @@ -28,6 +28,7 @@ #define BOT_WAIT_FOR_NAV 16 // waiting for nav computation #define BOT_NO_ROUTE 17 // no destination beacon found (or no route) #define BOT_SHOWERSTANCE 18 // cleaning unhygienic humans +#define BOT_TIPPED 19 // someone tipped a medibot over ;_; //Bot types #define SEC_BOT (1<<0) // Secutritrons (Beepsky) and ED-209s diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index f7a95f2f3115..670a30bafb09 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -38,6 +38,8 @@ #define ROLE_LAVALAND "Lavaland" #define ROLE_INTERNAL_AFFAIRS "Internal Affairs Agent" #define ROLE_FAMILIES "Familes Antagonists" +#define ROLE_SYNDICATE_CYBERSUN "Cybersun Space Syndicate" //Ghost role syndi from Forgottenship ruin +#define ROLE_SYNDICATE_CYBERSUN_CAPTAIN "Cybersun Space Syndicate Captain" //Forgottenship captain syndie //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 diff --git a/code/__DEFINES/rust_g.dm b/code/__DEFINES/rust_g.dm index 509ad4615041..aeacdb7c51bd 100644 --- a/code/__DEFINES/rust_g.dm +++ b/code/__DEFINES/rust_g.dm @@ -11,7 +11,7 @@ #define rustg_git_revparse(rev) call(RUST_G, "rg_git_revparse")(rev) #define rustg_git_commit_date(rev) call(RUST_G, "rg_git_commit_date")(rev) -#define rustg_log_write(fname, text) call(RUST_G, "log_write")(fname, text) +#define rustg_log_write(fname, text, format) call(RUST_G, "log_write")(fname, text, format) /proc/rustg_log_close_all() return call(RUST_G, "log_close_all")() // RUST-G defines & procs for HTTP component diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index f4246e19b3cf..515f9f485fb1 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -20,7 +20,7 @@ * * make sure you add an update to the schema_version stable in the db changelog */ -#define DB_MINOR_VERSION 7 +#define DB_MINOR_VERSION 9 //! ## Timing subsystem @@ -133,7 +133,9 @@ #define INIT_ORDER_MINOR_MAPPING -40 #define INIT_ORDER_PATH -50 #define INIT_ORDER_DISCORD -60 +#define INIT_ORDER_EXPLOSIONS -69 #define INIT_ORDER_PERSISTENCE -95 +#define INIT_ORDER_DEMO -99 // o avoid a bunch of changes related to initialization being written, do this last #define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init. // Subsystem fire priority, from lowest to highest priority @@ -165,6 +167,7 @@ #define FIRE_PRIORITY_ATMOS_ADJACENCY 300 #define FIRE_PRIORITY_CHAT 400 #define FIRE_PRIORITY_OVERLAYS 500 +#define FIRE_PRIORITY_EXPLOSIONS 666 #define FIRE_PRIORITY_INPUT 1000 // This must always always be the max highest priority. Player input must never be lost. // SS runlevels @@ -215,3 +218,10 @@ #define SSAIR_HIGHPRESSURE 5 #define SSAIR_HOTSPOTS 6 #define SSAIR_SUPERCONDUCTIVITY 7 +#define SSAIR_REBUILD_PIPENETS 8 + +// Explosion Subsystem subtasks +#define SSEXPLOSIONS_MOVABLES 1 +#define SSEXPLOSIONS_TURFS 2 +#define SSEXPLOSIONS_THROWS 3 + diff --git a/code/__DEFINES/tgs.config.dm b/code/__DEFINES/tgs.config.dm index 32243f7ad407..f06fcc96e801 100644 --- a/code/__DEFINES/tgs.config.dm +++ b/code/__DEFINES/tgs.config.dm @@ -4,8 +4,9 @@ #define TGS_READ_GLOBAL(Name) GLOB.##Name #define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value #define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]") -#define TGS_INFO_LOG(message) log_world("TGS: Info: [##message]") -#define TGS_ERROR_LOG(message) log_world("TGS: Error: [##message]") +#define TGS_INFO_LOG(message) log_world("TGS Info: [##message]") +#define TGS_WARNING_LOG(message) log_world("TGS Warn: [##message]") +#define TGS_ERROR_LOG(message) log_world("TGS Error: [##message]") #define TGS_NOTIFY_ADMINS(event) message_admins(##event) #define TGS_CLIENT_COUNT GLOB.clients.len #define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path) diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index 45a7e46ce3b6..3fc4fca12dbb 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -1,5 +1,7 @@ //tgstation-server DMAPI +#define TGS_DMAPI_VERSION "5.0.0" + //All functions and datums outside this document are subject to change with any version and should not be relied on //CONFIGURATION @@ -17,7 +19,6 @@ //Required interfaces (fill in with your codebase equivalent): //create a global variable named `Name` and set it to `Value` -//These globals must not be modifiable from anywhere outside of the server tools #define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) //Read the value in the global variable `Name` @@ -26,10 +27,10 @@ //Set the value in the global variable `Name` to `Value` #define TGS_WRITE_GLOBAL(Name, Value) -//Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game priveledge escalation +//Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game use of DD -> TGS capabilities #define TGS_PROTECT_DATUM(Path) -//display an announcement `message` from the server to all players +//Display an announcement `message` from the server to all players #define TGS_WORLD_ANNOUNCE(message) //Notify current in-game administrators of a string `event` @@ -38,6 +39,9 @@ //Write an info `message` to a server log #define TGS_INFO_LOG(message) +//Write an warning `message` to a server log +#define TGS_WARNING_LOG(message) + //Write an error `message` to a server log #define TGS_ERROR_LOG(message) @@ -51,7 +55,7 @@ #define TGS_EVENT_PORT_SWAP -2 //before a port change is about to happen, extra parameter is new port #define TGS_EVENT_REBOOT_MODE_CHANGE -1 //before a reboot mode change, extras parameters are the current and new reboot mode enums -//See the descriptions for these codes here: https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs +//See the descriptions for the parameters of these codes here: https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs #define TGS_EVENT_REPO_RESET_ORIGIN 0 #define TGS_EVENT_REPO_CHECKOUT 1 #define TGS_EVENT_REPO_FETCH 2 @@ -109,12 +113,12 @@ //represents a version of tgstation-server /datum/tgs_version - var/suite //The suite version, can be >=3 + var/suite //The suite/major version, can be >=3 //this group of variables can be null to represent a wild card - var/major //The major version var/minor //The minor version var/patch //The patch version + var/deprecated_patch //The legacy version var/raw_parameter //The unparsed parameter var/deprefixed_parameter //The version only bit of raw_parameter @@ -187,6 +191,7 @@ /world/proc/TgsVersion() return +//Gets the name of the TGS instance running the game /world/proc/TgsInstanceName() return @@ -211,7 +216,7 @@ //Gets a list of connected tgs_chat_channel /world/proc/TgsChatChannelInfo() return - + //Sends a message to connected game chats //message: The message to send //channels: optional channels to limit the broadcast to @@ -235,24 +240,24 @@ The MIT License Copyright (c) 2017 Jordan Brown -Permission is hereby granted, free of charge, -to any person obtaining a copy of this software and -associated documentation files (the "Software"), to -deal in the Software without restriction, including -without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom -the Software is furnished to do so, +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 76df451ed9e1..354c09128422 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -1,3 +1,6 @@ +#define SIGNAL_ADDTRAIT(trait_ref) "addtrait [trait_ref]" +#define SIGNAL_REMOVETRAIT(trait_ref) "removetrait [trait_ref]" + // trait accessor defines #define ADD_TRAIT(target, trait, source) \ do { \ @@ -6,12 +9,14 @@ target.status_traits = list(); \ _L = target.status_traits; \ _L[trait] = list(source); \ + SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait)); \ } else { \ _L = target.status_traits; \ if (_L[trait]) { \ _L[trait] |= list(source); \ } else { \ _L[trait] = list(source); \ + SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait)); \ } \ } \ } while (0) @@ -31,7 +36,8 @@ } \ };\ if (!length(_L[trait])) { \ - _L -= trait \ + _L -= trait; \ + SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait)); \ }; \ if (!length(_L)) { \ target.status_traits = null \ @@ -46,11 +52,13 @@ for (var/_T in _L) { \ _L[_T] &= _S;\ if (!length(_L[_T])) { \ - _L -= _T } \ - };\ - if (!length(_L)) { \ - target.status_traits = null\ + _L -= _T; \ + SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T)); \ + }; \ };\ + if (!length(_L)) { \ + target.status_traits = null\ + };\ }\ } while (0) #define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE) @@ -68,6 +76,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai */ //mob traits +#define TRAIT_INCAPACITATED "incapacitated" #define TRAIT_BLIND "blind" #define TRAIT_MUTE "mute" #define TRAIT_EMOTEMUTE "emotemute" @@ -167,12 +176,14 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NAIVE "naive" #define TRAIT_PRIMITIVE "primitive" #define TRAIT_GUNFLIP "gunflip" +#define TRAIT_SPECIAL_TRAUMA_BOOST "special_trauma_boost" ///Increases chance of getting special traumas, makes them harder to cure #define TRAIT_BLOODCRAWL_EAT "bloodcrawl_eat" #define TRAIT_SPACEWALK "spacewalk" #define TRAIT_GAMERGOD "gamer-god" //double arcade prizes #define TRAIT_GIANT "giant" #define TRAIT_DWARF "dwarf" #define TRAIT_SILENT_FOOTSTEPS "silent_footsteps" //makes your footsteps completely silent +#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good.... //non-mob traits #define TRAIT_PARALYSIS "paralysis" //Used for limb-based paralysis, where replacing the limb will fix it diff --git a/code/__DEFINES/wires.dm b/code/__DEFINES/wires.dm index 9c71738d1586..202b1bfc1ad6 100644 --- a/code/__DEFINES/wires.dm +++ b/code/__DEFINES/wires.dm @@ -50,4 +50,5 @@ #define WIRE_ZAP2 "High Voltage Circuit 2" #define WIRE_PRIZEVEND "Emergency Prize Vend" #define WIRE_RESETOWNER "Reset Owner" +#define WIRE_AGELIMIT "Age Limit" diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 4b33d1ec4206..3126e515d882 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -11,6 +11,7 @@ #define LAZYINITLIST(L) if (!L) L = list() #define UNSETEMPTY(L) if (L && !length(L)) L = null +#define LAZYCOPY(L) (L ? L.Copy() : list() ) #define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } } #define LAZYADD(L, I) if(!L) { L = list(); } L += I; #define LAZYOR(L, I) if(!L) { L = list(); } L |= I; diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 7f2cdc186479..e70f0128f0a7 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -4,7 +4,9 @@ #define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) #define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text) #define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text) -#define WRITE_LOG(log, text) rustg_log_write(log, text) +//This is an external call, "true" and "false" are how rust parses out booleans +#define WRITE_LOG(log, text) rustg_log_write(log, text, "true") +#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") //print a warning message to world.log #define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].") @@ -92,6 +94,10 @@ if (CONFIG_GET(flag/log_attack)) WRITE_LOG(GLOB.world_attack_log, "ATTACK: [text]") +/proc/log_econ(text) + if (CONFIG_GET(flag/log_econ)) + WRITE_LOG(GLOB.world_attack_log, "MONEY: [text]") + /proc/log_manifest(ckey, datum/mind/mind,mob/body, latejoin = FALSE) if (CONFIG_GET(flag/log_manifest)) WRITE_LOG(GLOB.world_manifest_log, "[ckey] \\ [body.real_name] \\ [mind.assigned_role] \\ [mind.special_role ? mind.special_role : "NONE"] \\ [latejoin ? "LATEJOIN":"ROUNDSTART"]") @@ -204,8 +210,8 @@ /proc/log_mapping(text) WRITE_LOG(GLOB.world_map_error_log, text) -/* ui logging */ - +/* ui logging */ + /proc/log_tgui(text) WRITE_LOG(GLOB.tgui_log, text) diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm index ba53368ae9ce..623ed18a25c5 100644 --- a/code/__HELPERS/files.dm +++ b/code/__HELPERS/files.dm @@ -3,7 +3,14 @@ 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", "html")) +/client/proc/browse_files(root_type=BROWSE_ROOT_ALL_LOGS, max_iterations=10, list/valid_extensions=list("txt","log","htm", "html")) + // wow why was this ever a parameter + var/root = "data/logs/" + switch(root_type) + if(BROWSE_ROOT_ALL_LOGS) + root = "data/logs/" + if(BROWSE_ROOT_CURRENT_LOGS) + root = "[GLOB.log_directory]/" var/path = root for(var/i=0, i" var/atom/A = thing if (isnull(dir)) @@ -1158,7 +1158,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0 key = "[generate_asset_name(I)].png" register_asset(key, I) for (var/thing2 in targets) - send_asset(thing2, key, FALSE) + send_asset(thing2, key) return "" diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index a593270bc2e7..5bca23058328 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -198,14 +198,15 @@ GLOBAL_LIST_EMPTY(species_list) else return "unknown" -/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null) +///Timed action involving two mobs, the user and the target. +/proc/do_mob(mob/user , mob/target, time = 3 SECONDS, uninterruptible = FALSE, progress = TRUE, datum/callback/extra_checks = null) if(!user || !target) - return 0 + return FALSE var/user_loc = user.loc - var/drifting = 0 + var/drifting = FALSE if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 + drifting = TRUE var/target_loc = target.loc @@ -216,26 +217,26 @@ GLOBAL_LIST_EMPTY(species_list) var/endtime = world.time+time var/starttime = world.time - . = 1 + . = TRUE while (world.time < endtime) stoplag(1) - if (progress) + if(!QDELETED(progbar)) progbar.update(world.time - starttime) if(QDELETED(user) || QDELETED(target)) - . = 0 + . = FALSE break if(uninterruptible) continue if(drifting && !user.inertia_dir) - drifting = 0 + drifting = FALSE user_loc = user.loc if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_held_item() != holding || user.incapacitated() || (extra_checks && !extra_checks.Invoke())) - . = 0 + . = FALSE break - if (progress) - qdel(progbar) + if(!QDELETED(progbar)) + progbar.end_progress() //some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action @@ -252,85 +253,101 @@ GLOBAL_LIST_EMPTY(species_list) checked_health["health"] = health return ..() -/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null) +///Timed action involving one mob user. Target is optional. +/proc/do_after(mob/user, var/delay, needhand = TRUE, atom/target = null, progress = TRUE, datum/callback/extra_checks = null) if(!user) - return 0 + return FALSE var/atom/Tloc = null if(target && !isturf(target)) Tloc = target.loc + if(target) + LAZYADD(user.do_afters, target) + LAZYADD(target.targeted_by, user) + var/atom/Uloc = user.loc - var/drifting = 0 + var/drifting = FALSE if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 + drifting = TRUE var/holding = user.get_active_held_item() - var/holdingnull = 1 //User's hand started out empty, check for an empty hand + var/holdingnull = TRUE //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 + holdingnull = FALSE //Users hand started holding something, check to see if it's still holding that delay *= user.do_after_coefficent() var/datum/progressbar/progbar - if (progress) - progbar = new(user, delay, target) + if(progress) + progbar = new(user, delay, target || user) var/endtime = world.time + delay var/starttime = world.time - . = 1 + . = TRUE while (world.time < endtime) stoplag(1) - if (progress) + if(!QDELETED(progbar)) progbar.update(world.time - starttime) if(drifting && !user.inertia_dir) - drifting = 0 + drifting = FALSE Uloc = user.loc if(QDELETED(user) || user.stat || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke())) - . = 0 + . = FALSE break if(isliving(user)) var/mob/living/L = user if(L.IsStun() || L.IsParalyzed()) - . = 0 + . = FALSE break if(!QDELETED(Tloc) && (QDELETED(target) || Tloc != target.loc)) if((Uloc != Tloc || Tloc != user) && !drifting) - . = 0 + . = FALSE break + if(target && !(target in user.do_afters)) + . = 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) - . = 0 + . = FALSE break if(user.get_active_held_item() != holding) - . = 0 + . = FALSE break - if (progress) - qdel(progbar) + if(!QDELETED(progbar)) + progbar.end_progress() + + if(!QDELETED(target)) + LAZYREMOVE(user.do_afters, target) + LAZYREMOVE(target.targeted_by, user) /mob/proc/do_after_coefficent() // This gets added to the delay on a do_after, default 1 . = 1 return -/proc/do_after_mob(mob/user, list/targets, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks, required_mobility_flags = MOBILITY_STAND) - if(!user || !targets) - return 0 +///Timed action involving at least one mob user and a list of targets. +/proc/do_after_mob(mob/user, list/targets, time = 3 SECONDS, uninterruptible = FALSE, progress = TRUE, datum/callback/extra_checks, required_mobility_flags = MOBILITY_STAND) + if(!user) + return FALSE if(!islist(targets)) targets = list(targets) + if(!length(targets)) + return FALSE var/user_loc = user.loc - var/drifting = 0 + var/drifting = FALSE if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 + drifting = TRUE var/list/originalloc = list() for(var/atom/target in targets) @@ -346,32 +363,32 @@ GLOBAL_LIST_EMPTY(species_list) var/mob/living/L if(isliving(user)) L = user - . = 1 + . = TRUE mainloop: while(world.time < endtime) stoplag(1) - if(progress) + if(!QDELETED(progbar)) progbar.update(world.time - starttime) if(QDELETED(user) || !targets) - . = 0 + . = FALSE break if(uninterruptible) continue if(drifting && !user.inertia_dir) - drifting = 0 + drifting = FALSE user_loc = user.loc if(L && !((L.mobility_flags & required_mobility_flags) == required_mobility_flags)) - . = 0 + . = FALSE break for(var/atom/target in targets) if((!drifting && user_loc != user.loc) || QDELETED(target) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || (extra_checks && !extra_checks.Invoke())) - . = 0 + . = FALSE break mainloop - if(progbar) - qdel(progbar) + if(!QDELETED(progbar)) + progbar.end_progress() /proc/is_species(A, species_datum) . = FALSE @@ -448,6 +465,8 @@ GLOBAL_LIST_EMPTY(species_list) override = TRUE if(HAS_TRAIT(M, TRAIT_SIXTHSENSE) && message_type == DEADCHAT_REGULAR) override = TRUE + if(SSticker.current_state == GAME_STATE_FINISHED) + override = TRUE if(isnewplayer(M) && !override) continue if(M.stat != DEAD && !override) @@ -530,3 +549,63 @@ GLOBAL_LIST_EMPTY(species_list) sleep(1) if(set_original_dir) AM.setDir(originaldir) + +/////////////////////// +///Silicon Mob Procs/// +/////////////////////// + +//Returns a list of unslaved cyborgs +/proc/active_free_borgs() + . = list() + for(var/mob/living/silicon/robot/R in GLOB.alive_mob_list) + if(R.connected_ai || R.shell) + continue + if(R.stat == DEAD) + continue + if(R.emagged || R.scrambledcodes) + continue + . += R + +//Returns a list of AI's +/proc/active_ais(check_mind=FALSE, var/z = null) + . = list() + for(var/mob/living/silicon/ai/A in GLOB.alive_mob_list) + if(A.stat == DEAD) + continue + if(A.control_disabled) + continue + if(check_mind) + if(!A.mind) + continue + if(z && !(z == A.z) && (!is_station_level(z) || !is_station_level(A.z))) //if a Z level was specified, AND the AI is not on the same level, AND either is off the station... + continue + . += A + return . + +//Find an active ai with the least borgs. VERBOSE PROCNAME HUH! +/proc/select_active_ai_with_fewest_borgs(var/z) + var/mob/living/silicon/ai/selected + var/list/active = active_ais(FALSE, z) + for(var/mob/living/silicon/ai/A in active) + if(!selected || (selected.connected_robots.len > A.connected_robots.len)) + selected = A + + return selected + +/proc/select_active_free_borg(mob/user) + var/list/borgs = active_free_borgs() + if(borgs.len) + if(user) + . = input(user,"Unshackled cyborg signals detected:", "Cyborg Selection", borgs[1]) in sortList(borgs) + else + . = pick(borgs) + return . + +/proc/select_active_ai(mob/user, var/z = null) + var/list/ais = active_ais(FALSE, z) + if(ais.len) + if(user) + . = input(user,"AI signals detected:", "AI Selection", ais[1]) in sortList(ais) + else + . = pick(ais) + return . diff --git a/code/__HELPERS/radiation.dm b/code/__HELPERS/radiation.dm index 042f2013638f..6e4315cf3001 100644 --- a/code/__HELPERS/radiation.dm +++ b/code/__HELPERS/radiation.dm @@ -43,3 +43,14 @@ var/turf/_source_T = isturf(source) ? source : get_turf(source) log_game("Radiation pulse with intensity: [intensity] and range modifier: [range_modifier] in [loc_name(_source_T)] ") return TRUE + +/proc/get_rad_contamination(atom/location) + var/rad_strength = 0 + for(var/i in get_rad_contents(location)) // Yes it's intentional that you can't detect radioactive things under rad protection. Gives traitors a way to hide their glowing green rocks. + var/atom/thing = i + if(!thing) + continue + var/datum/component/radioactive/radiation = thing.GetComponent(/datum/component/radioactive) + if(radiation && rad_strength < radiation.strength) + rad_strength = radiation.strength + return rad_strength diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index ae51727d3a38..61eb93869f23 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -244,6 +244,10 @@ CHECK_TICK + set_observer_default_invisibility(0, "The round is over! You are now visible to the living.") + + CHECK_TICK + //These need update to actually reflect the real antagonists //Print a list of antagonists to the server log var/list/total_antagonists = list() diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm index b47bcfad695f..ea4844dcce82 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -36,7 +36,8 @@ GLOBAL_VAR_INIT(midnight_rollovers, 0) GLOBAL_VAR_INIT(rollovercheck_last_timeofday, 0) /proc/update_midnight_rollover() if (world.timeofday < GLOB.rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS! - return GLOB.midnight_rollovers++ + GLOB.midnight_rollovers++ + GLOB.rollovercheck_last_timeofday = world.timeofday return GLOB.midnight_rollovers /proc/weekdayofthemonth() diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 60c05c9897e6..5af0189a64ed 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -232,60 +232,6 @@ Turf and target are separate in case you want to teleport some distance from a t /proc/ionnum() return "[pick("!","@","#","$","%","^","&")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]" -//Returns a list of unslaved cyborgs -/proc/active_free_borgs() - . = list() - for(var/mob/living/silicon/robot/R in GLOB.alive_mob_list) - if(R.connected_ai || R.shell) - continue - if(R.stat == DEAD) - continue - if(R.emagged || R.scrambledcodes) - continue - . += R - -//Returns a list of AI's -/proc/active_ais(check_mind=0) - . = list() - for(var/mob/living/silicon/ai/A in GLOB.alive_mob_list) - if(A.stat == DEAD) - continue - if(A.control_disabled) - continue - if(check_mind) - if(!A.mind) - continue - . += A - return . - -//Find an active ai with the least borgs. VERBOSE PROCNAME HUH! -/proc/select_active_ai_with_fewest_borgs() - var/mob/living/silicon/ai/selected - var/list/active = active_ais() - for(var/mob/living/silicon/ai/A in active) - if(!selected || (selected.connected_robots.len > A.connected_robots.len)) - selected = A - - return selected - -/proc/select_active_free_borg(mob/user) - var/list/borgs = active_free_borgs() - if(borgs.len) - if(user) - . = input(user,"Unshackled cyborg signals detected:", "Cyborg Selection", borgs[1]) in sortList(borgs) - else - . = pick(borgs) - return . - -/proc/select_active_ai(mob/user) - var/list/ais = active_ais() - if(ais.len) - if(user) - . = input(user,"AI signals detected:", "AI Selection", ais[1]) in sortList(ais) - else - . = pick(ais) - return . - //Returns a list of all items of interest with their name /proc/getpois(mobs_only=0,skip_mindless=0) var/list/mobs = sortmobs() @@ -449,7 +395,7 @@ Turf and target are separate in case you want to teleport some distance from a t Gets all contents of contents and returns them all in a list. */ -/atom/proc/GetAllContents(var/T) +/atom/proc/GetAllContents(var/T, ignore_flag_1) var/list/processing_list = list(src) if(T) . = list() @@ -458,14 +404,16 @@ Turf and target are separate in case you want to teleport some distance from a t var/atom/A = processing_list[++i] //Byond does not allow things to be in multiple contents, or double parent-child hierarchies, so only += is needed //This is also why we don't need to check against assembled as we go along - processing_list += A.contents - if(istype(A,T)) - . += A + if (!(A.flags_1 & ignore_flag_1)) + processing_list += A.contents + if(istype(A,T)) + . += A else var/i = 0 while(i < length(processing_list)) var/atom/A = processing_list[++i] - processing_list += A.contents + if (!(A.flags_1 & ignore_flag_1)) + processing_list += A.contents return processing_list /atom/proc/GetAllContentsIgnoring(list/ignore_typecache) @@ -729,22 +677,6 @@ Turf and target are separate in case you want to teleport some distance from a t return null -//For objects that should embed, but make no sense being is_sharp or is_pointed() -//e.g: rods -GLOBAL_LIST_INIT(can_embed_types, typecacheof(list( - /obj/item/stack/rods, - /obj/item/pipe))) - -/proc/can_embed(obj/item/W) - if(W.get_sharpness()) - return 1 - if(is_pointed(W)) - return 1 - - if(is_type_in_typecache(W, GLOB.can_embed_types)) - return 1 - - /* Checks if that loc and dir has an item on the wall */ @@ -1311,6 +1243,10 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) var/obj/structure/window/W = O if(W.ini_dir == dir_to_check || W.ini_dir == FULLTILE_WINDOW_DIR || dir_to_check == FULLTILE_WINDOW_DIR) return FALSE + if(istype(O, /obj/structure/railing)) + var/obj/structure/railing/rail = O + if(rail.ini_dir == dir_to_check || rail.ini_dir == FULLTILE_WINDOW_DIR || dir_to_check == FULLTILE_WINDOW_DIR) + return FALSE return TRUE #define UNTIL(X) while(!(X)) stoplag() diff --git a/code/__HELPERS/unused.dm b/code/__HELPERS/unused.dm deleted file mode 100644 index d4e63eb13aa2..000000000000 --- a/code/__HELPERS/unused.dm +++ /dev/null @@ -1,39 +0,0 @@ - - -/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(src_x, src_y, time, distance, \ - power_x, power_y, dest_x, 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(src_x, src_y, rotation, angle, 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) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 9d3647aed067..e1035b8daa17 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -122,7 +122,6 @@ GLOBAL_LIST_INIT(bitfields, list( "CAN_BE_DIRTY_1" = CAN_BE_DIRTY_1, "CULT_PERMITTED_1" = CULT_PERMITTED_1, "HEAR_1" = HEAR_1, - "CHECK_RICOCHET_1" = CHECK_RICOCHET_1, "CONDUCT_1" = CONDUCT_1, "NO_LAVA_GEN_1" = NO_LAVA_GEN_1, "NODECONSTRUCT_1" = NODECONSTRUCT_1, @@ -135,6 +134,10 @@ GLOBAL_LIST_INIT(bitfields, list( "INITIALIZED_1" = INITIALIZED_1, "ADMIN_SPAWNED_1" = ADMIN_SPAWNED_1 ), + "flags_ricochet" = list( + "RICOCHET_SHINY" = RICOCHET_SHINY, + "RICOCHET_HARD" = RICOCHET_HARD + ), "clothing_flags" = list( "LAVAPROTECT" = LAVAPROTECT, "STOPSPRESSUREDAMAGE" = STOPSPRESSUREDAMAGE, diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index e1eac27f84c2..34d75be405a9 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -244,9 +244,9 @@ GLOBAL_LIST_INIT(TAGGERLOCATIONS, list("Disposals", "Experimentor Lab", "Toxins", "Dormitories", "Virology", "Xenobiology", "Law Office","Detective's Office")) -GLOBAL_LIST_INIT(station_prefixes, world.file2list("strings/station_prefixes.txt") + "") +GLOBAL_LIST_INIT(station_prefixes, world.file2list("strings/station_prefixes.txt")) -GLOBAL_LIST_INIT(station_names, world.file2list("strings/station_names.txt" + "")) +GLOBAL_LIST_INIT(station_names, world.file2list("strings/station_names.txt")) GLOBAL_LIST_INIT(station_suffixes, world.file2list("strings/station_suffixes.txt")) diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index ce043dd91c0f..8d28f1663b28 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -106,6 +106,7 @@ GLOBAL_LIST_INIT(common_loot, list( //common: basic items /obj/item/stack/rods/twentyfive = 1, /obj/item/stack/sheet/metal/twenty = 1, /obj/item/stack/sheet/mineral/plasma = 1, + /obj/item/sign = 1, //assemblies /obj/item/assembly/infra = 1, @@ -192,7 +193,8 @@ GLOBAL_LIST_INIT(uncommon_loot, list(//uncommon: useful items /obj/item/stack/sheet/mineral/wood/fifty = 1, /obj/item/beacon = 1, /obj/item/weaponcrafting/receiver = 1, - /obj/item/paper/fluff/stations/soap =1, //recipes count as crafting. + /obj/item/paper/fluff/stations/soap = 1, //recipes count as crafting. + /obj/item/plaque = 1, ) = 8, list(//medical and chemicals @@ -264,3 +266,21 @@ GLOBAL_LIST_INIT(maintenance_loot, list( GLOB.uncommon_loot = maint_uncommon_weight, GLOB.oddity_loot = maint_oddity_weight, )) + +GLOBAL_LIST_INIT(ratking_trash, list(//Garbage: used by the regal rat mob when spawning garbage. + /obj/item/cigbutt, + /obj/item/trash/cheesie, + /obj/item/trash/candy, + /obj/item/trash/chips, + /obj/item/trash/pistachios, + /obj/item/trash/plate, + /obj/item/trash/popcorn, + /obj/item/trash/raisins, + /obj/item/trash/sosjerky, + /obj/item/trash/syndi_cakes)) + +GLOBAL_LIST_INIT(ratking_coins, list(//Coins: Used by the regal rat mob when spawning coins. + /obj/item/coin/iron, + /obj/item/coin/silver, + /obj/item/coin/plastic, + /obj/item/coin/titanium)) diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index fd80ea5db9b6..f83e953d2e80 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -32,6 +32,8 @@ GLOBAL_LIST_INIT(simple_animals, list(list(),list(),list(),list())) // One for e GLOBAL_LIST_EMPTY(spidermobs) //all sentient spider mobs GLOBAL_LIST_EMPTY(bots_list) GLOBAL_LIST_EMPTY(aiEyes) +///underages who have been reported to security for trying to buy things they shouldn't, so they can't spam +GLOBAL_LIST_EMPTY(narcd_underages) GLOBAL_LIST_EMPTY(language_datum_instances) GLOBAL_LIST_EMPTY(all_languages) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 6dcc62257990..08cac62460e8 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -32,6 +32,8 @@ GLOBAL_LIST_EMPTY(meteor_list) // List of all meteors. GLOBAL_LIST_EMPTY(active_jammers) // List of active radio jammers GLOBAL_LIST_EMPTY(ladders) GLOBAL_LIST_EMPTY(trophy_cases) +///This is a global list of all signs you can change an existing sign or new sign backing to, when using a pen on them. +GLOBAL_LIST_EMPTY(editable_sign_types) GLOBAL_LIST_EMPTY(wire_color_directory) GLOBAL_LIST_EMPTY(wire_name_directory) diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 6c519868c534..16fba338b94d 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -45,6 +45,9 @@ GLOBAL_PROTECT(world_shuttle_log) GLOBAL_VAR(discord_api_log) GLOBAL_PROTECT(discord_api_log) +GLOBAL_VAR(demo_log) +GLOBAL_PROTECT(demo_log) + GLOBAL_LIST_EMPTY(bombers) GLOBAL_PROTECT(bombers) GLOBAL_LIST_EMPTY(admin_log) diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 3efda260b629..1de2cd22fca9 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -16,3 +16,13 @@ GLOBAL_LIST_EMPTY(powernets) GLOBAL_VAR_INIT(bsa_unlock, FALSE) //BSA unlocked by head ID swipes GLOBAL_LIST_EMPTY(player_details) // ckey -> /datum/player_details + +///All currently running polls held as datums +GLOBAL_LIST_EMPTY(polls) +GLOBAL_PROTECT(polls) + +///All poll option datums of running polls +GLOBAL_LIST_EMPTY(poll_options) +GLOBAL_PROTECT(poll_options) + +GLOBAL_VAR_INIT(internal_tick_usage, 0.2 * world.tick_lag) diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 587ecaf5608a..4bdc3c8827a5 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -106,7 +106,8 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_GAMERGOD" = TRAIT_GAMERGOD, "TRAIT_DWARF" = TRAIT_DWARF, "TRAIT_GIANT" = TRAIT_GIANT, - "TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS + "TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS, + "TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT ), /obj/item/bodypart = list( "TRAIT_PARALYSIS" = TRAIT_PARALYSIS diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index 55627186a409..91f6da16a553 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -25,14 +25,7 @@ /client - var/list/atom/selected_target[2] - var/obj/item/active_mousedown_item = null - var/mouseParams = "" - var/mouseLocation = null - var/mouseObject = null var/mouseControlObject = null - var/middragtime = 0 - var/atom/middragatom /client/MouseDown(object, location, control, params) if (mouse_down_icon) diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index a53a304e31d6..492d05c166eb 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -171,4 +171,3 @@ End Waspstation*/ #define ui_wanted_lvl "NORTH,11" - diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index c77880555e50..357d3c453181 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -244,7 +244,7 @@ If you're feeling frisky, examine yourself and click the underlined item to pull /obj/screen/alert/embeddedobject/Click() if(isliving(usr) && usr == owner) - var/mob/living/carbon/human/M = usr + var/mob/living/carbon/M = usr return M.help_shake_act(M) /obj/screen/alert/weightless diff --git a/code/_onclick/hud/families.dm b/code/_onclick/hud/families.dm new file mode 100644 index 000000000000..9ad36a99768e --- /dev/null +++ b/code/_onclick/hud/families.dm @@ -0,0 +1,27 @@ +/obj/screen/wanted + name = "Space Police Alertness" + desc = "Shows the current level of hostility the space police is planning to rain down on you. Better be careful." + icon = 'icons/obj/gang/wanted_160x32.dmi' + icon_state = "wanted_0" + screen_loc = ui_wanted_lvl + ///Wanted level, affects the hud icon. + var/level + ///Boolean, have the cops arrived? If so, the icon stops changing and remains the same. + var/cops_arrived + +/obj/screen/wanted/Initialize() + . = ..() + var/datum/game_mode/gang/F = SSticker.mode + level = F.wanted_level + cops_arrived = F.cops_arrived + update_icon() + +/obj/screen/wanted/MouseEntered(location,control,params) + openToolTip(usr,src,params,title = name,content = desc, theme = "alerttooltipstyle") + +/obj/screen/wanted/MouseExited() + closeToolTip(usr) + +/obj/screen/wanted/update_icon_state() + . = ..() + icon_state = "wanted_[level][cops_arrived ? "_active" : ""]" diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm index 9f41f7cfcdc5..b753c8f5fb00 100644 --- a/code/_onclick/hud/guardian.dm +++ b/code/_onclick/hud/guardian.dm @@ -41,9 +41,6 @@ using.hud = src static_inventory += using -/datum/hud/dextrous/guardian - ui_style = 'icons/mob/guardian.dmi' - /datum/hud/dextrous/guardian/New(mob/living/simple_animal/hostile/guardian/owner) //for a dextrous guardian ..() var/obj/screen/using @@ -72,7 +69,7 @@ static_inventory += using pull_icon = new /obj/screen/pull() - pull_icon.icon = ui_style + pull_icon.icon = 'icons/mob/guardian.dmi' pull_icon.update_icon() pull_icon.screen_loc = ui_living_pull pull_icon.hud = src diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index a11b7d70b05b..2cb7b2cba508 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -57,7 +57,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/obj/screen/healths var/obj/screen/healthdoll var/obj/screen/internals - var/obj/screen/wanted_lvl + var/obj/screen/wanted/wanted_lvl /* Wasp begin var/obj/screen/spacesuit Wasp End - Fuckin' spacesuits. */ @@ -83,11 +83,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list( plane_masters["[instance.plane]"] = instance instance.backdrop(mymob) - wanted_lvl = new /obj/screen() - wanted_lvl.icon = 'icons/obj/gang/wanted_160x32.dmi' - wanted_lvl.icon_state = "wanted_0" - wanted_lvl.screen_loc = ui_wanted_lvl - infodisplay += wanted_lvl owner.overlay_fullscreen("see_through_darkness", /obj/screen/fullscreen/see_through_darkness) /datum/hud/Destroy() diff --git a/code/_onclick/hud/map_popups.dm b/code/_onclick/hud/map_popups.dm index 777ebb0718b2..dc9e255cba93 100644 --- a/code/_onclick/hud/map_popups.dm +++ b/code/_onclick/hud/map_popups.dm @@ -7,10 +7,6 @@ */ var/list/screen_maps = list() -/** - * A screen object, which acts as a container for turfs and other things - * you want to show on the map, which you usually attach to "vis_contents". - */ /obj/screen /** * Map name assigned to this object. @@ -26,6 +22,15 @@ */ var/del_on_map_removal = TRUE +/** + * A screen object, which acts as a container for turfs and other things + * you want to show on the map, which you usually attach to "vis_contents". + */ +/obj/screen/map_view + // Map view has to be on the lowest plane to enable proper lighting + layer = GAME_PLANE + plane = GAME_PLANE + /** * A generic background object. * It is also implicitly used to allocate a rectangle on the map, which will diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index f702f4cb22ed..e3380fa3b7d3 100755 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -1,16 +1,4 @@ -/client - var/list/parallax_layers - var/list/parallax_layers_cached - var/atom/movable/movingmob - var/turf/previous_turf - var/dont_animate_parallax //world.time of when we can state animate()ing parallax again - var/last_parallax_shift //world.time of last update - var/parallax_throttle = 0 //ds between updates - var/parallax_movedir = 0 - var/parallax_layers_max = 4 - var/parallax_animate_timer - /datum/hud/proc/create_parallax(mob/viewmob) var/mob/screenmob = viewmob || mymob var/client/C = screenmob.client diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index 5afabb135040..bcdd5c85004d 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -255,6 +255,7 @@ GLOBAL_LIST_EMPTY(radial_menus) current_user = M.client //Blank menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing",layer = ABOVE_HUD_LAYER) + menu_holder.plane = ABOVE_HUD_PLANE menu_holder.appearance_flags |= KEEP_APART menu_holder.vis_contents += elements + close_button current_user.images += menu_holder @@ -311,4 +312,9 @@ GLOBAL_LIST_EMPTY(radial_menus) var/answer = menu.selected_choice qdel(menu) GLOB.radial_menus -= uniqueid + if(require_near && !in_range(anchor, user)) + return + if(istype(custom_check)) + if(!custom_check.Invoke()) + return return answer diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 8b4d8ebb3fde..91915e6669ad 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -640,9 +640,9 @@ screen_loc = ui_healthdoll /obj/screen/healthdoll/Click() - if (ishuman(usr)) - var/mob/living/carbon/human/H = usr - H.check_self_for_injuries() + if (iscarbon(usr)) + var/mob/living/carbon/C = usr + C.check_self_for_injuries() /obj/screen/healthdoll/living icon_state = "fullhealth0" diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 06b38b1ee637..48b4f532a94d 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -82,6 +82,11 @@ to_chat(user, "You don't want to harm other living beings!") return + if(item_flags & EYE_STAB && user.zone_selected == BODY_ZONE_PRECISE_EYES) + if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) + M = user + return eyestab(M,user) + if(!force) playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), TRUE, -1) else if(hitsound) diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index 285ec2fb94c1..efed103392fc 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -62,6 +62,8 @@ /mob/living/attack_ghost(mob/dead/observer/user) if(user.client && user.health_scan) healthscan(user, src, 1, TRUE) + if(user.client && user.chem_scan) + chemscan(user, src) return ..() // --------------------------------------- diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index fca42a9403e0..b7436509a315 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -391,6 +391,10 @@ config_entry_value = 64 min_val = 0 +/datum/config_entry/number/ratcap + config_entry_value = 64 + min_val = 0 + /datum/config_entry/number/maxfine config_entry_value = 1000 min_val = 0 diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index f8c5dfa225c9..5c0cff1e8812 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -8,6 +8,10 @@ /datum/config_entry/flag/auto_deadmin_players protection = CONFIG_ENTRY_LOCKED +/datum/config_entry/number/auto_deadmin_timegate + config_entry_value = null + protection = CONFIG_ENTRY_LOCKED + /datum/config_entry/flag/auto_deadmin_antagonists protection = CONFIG_ENTRY_LOCKED @@ -39,6 +43,11 @@ /datum/config_entry/flag/hub // if the game appears on the hub or not +/datum/config_entry/number/max_hub_pop //At what pop to take hub off the server + config_entry_value = 0 //0 means disabled + integer = TRUE + min_val = 0 + /datum/config_entry/flag/log_ooc // log OOC channel /datum/config_entry/flag/log_access // log login/logout @@ -68,6 +77,8 @@ /datum/config_entry/flag/log_emote // log emotes +/datum/config_entry/flag/log_econ // log economy actions + /datum/config_entry/flag/log_adminchat // log admin chat messages protection = CONFIG_ENTRY_LOCKED @@ -85,6 +96,9 @@ /datum/config_entry/flag/log_shuttle // log shuttle related actions, ie shuttle computers, shuttle manipulator, emergency console +/// Whether demos are written, if not set demo SS never initializes +/datum/config_entry/flag/demos_enabled + /datum/config_entry/flag/allow_admin_ooccolor // Allows admins with relevant permissions to have their own ooc colour /datum/config_entry/flag/allow_admin_asaycolor //Allows admins with relevant permissions to have a personalized asay color diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 9d89a0d3f0af..2b59d8bfafc1 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -35,6 +35,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/tickdrift = 0 var/sleep_delta = 1 + + ///Only run ticker subsystems for the next n ticks. + var/skip_ticks = 0 var/make_runtime = 0 @@ -335,7 +338,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new new/datum/controller/failsafe() // (re)Start the failsafe. //now do the actual stuff - if (!queue_head || !(iteration % 3)) + if (!skip_ticks) var/checking_runlevel = current_runlevel if(cached_runlevel != checking_runlevel) //resechedule subsystems @@ -381,6 +384,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new iteration++ last_run = world.time + if (skip_ticks) + skip_ticks-- src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta) current_ticklimit = TICK_LIMIT_RUNNING if (processing * sleep_delta <= world.tick_lag) @@ -444,10 +449,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new while (queue_node) if (ran && TICK_USAGE > TICK_LIMIT_RUNNING) break - queue_node_flags = queue_node.flags queue_node_priority = queue_node.queued_priority - + + if (!(queue_node_flags & SS_TICKER) && skip_ticks) + queue_node = queue_node.queue_next + continue //super special case, subsystems where we can't make them pause mid way through //if we can't run them this tick (without going over a tick) //we bump up their priority and attempt to run them next tick @@ -584,14 +591,19 @@ GLOBAL_REAL(Master, /datum/controller/master) = new log_world("MC: SoftReset: Finished.") . = 1 - +/// Warns us that the end of tick byond map_update will be laggier then normal, so that we can just skip running subsystems this tick. +/datum/controller/master/proc/laggy_byond_map_update_incoming() + if (!skip_ticks) + skip_ticks = 1 + /datum/controller/master/stat_entry() if(!statclick) statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) - stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))") - stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])")) + stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)") + stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration]) (TickLimit: [round(Master.current_ticklimit, 0.1)])")) + /datum/controller/master/StartLoadingMap() //disallow more than one map to load at once, multithreading it will just cause race conditions diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 7e9e23bd45f7..085cdbd3e547 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -1,88 +1,91 @@ /** * # Subsystem base class - * + * * Defines a subsystem to be managed by the [Master Controller][/datum/controller/master] * - * Simply define a child of this subsystem, using the [SUBSYSTEM_DEF] macro, and the MC will handle registration. + * Simply define a child of this subsystem, using the [SUBSYSTEM_DEF] macro, and the MC will handle registration. * Changing the name is required **/ - + /datum/controller/subsystem // Metadata; you should define these. - + /// Name of the subsystem - you must change this - name = "fire coderbus" - + name = "fire coderbus" + /// Order of initialization. Higher numbers are initialized first, lower numbers later. Use or create defines such as [INIT_ORDER_DEFAULT] so we can see the order in one file. - var/init_order = INIT_ORDER_DEFAULT - + var/init_order = INIT_ORDER_DEFAULT + /// Time to wait (in deciseconds) between each call to fire(). Must be a positive integer. - var/wait = 20 - + var/wait = 20 + /// Priority Weight: When mutiple subsystems need to run in the same tick, higher priority subsystems will be given a higher share of the tick before MC_TICK_CHECK triggers a sleep, higher priority subsystems also run before lower priority subsystems - var/priority = FIRE_PRIORITY_DEFAULT - + var/priority = FIRE_PRIORITY_DEFAULT + /// [Subsystem Flags][SS_NO_INIT] to control binary behavior. Flags must be set at compile time or before preinit finishes to take full effect. (You can also restart the mc to force them to process again) - var/flags = 0 - + var/flags = 0 + /// This var is set to TRUE after the subsystem has been initialized. var/initialized = FALSE /// Set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later /// use the [SS_NO_FIRE] flag instead for systems that never fire to keep it from even being added to list that is checked every tick var/can_fire = TRUE - + ///Bitmap of what game states can this subsystem fire at. See [RUNLEVELS_DEFAULT] for more details. var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire /* * The following variables are managed by the MC and should not be modified directly. */ - + /// Last world.time the subsystem completed a run (as in wasn't paused by [MC_TICK_CHECK]) var/last_fire = 0 - + /// Scheduled world.time for next fire() var/next_fire = 0 - + /// Running average of the amount of milliseconds it takes the subsystem to complete a run (including all resumes but not the time spent paused) - var/cost = 0 - + var/cost = 0 + /// Running average of the amount of tick usage in percents of a tick it takes the subsystem to complete a run - var/tick_usage = 0 - + var/tick_usage = 0 + /// Running average of the amount of tick usage (in percents of a game tick) the subsystem has spent past its allocated time without pausing var/tick_overrun = 0 - + /// Tracks the current execution state of the subsystem. Used to handle subsystems that sleep in fire so the mc doesn't run them again while they are sleeping var/state = SS_IDLE - + /// Tracks how many fires the subsystem has consecutively paused on in the current run var/paused_ticks = 0 - + /// Tracks how much of a tick the subsystem has consumed in the current run var/paused_tick_usage - + /// Tracks how many fires the subsystem takes to complete a run on average. var/ticks = 1 - + /// Tracks the amount of completed runs for the subsystem var/times_fired = 0 - + /// Time the subsystem entered the queue, (for timing and priority reasons) - var/queued_time = 0 - + var/queued_time = 0 + /// Priority at the time the subsystem entered the queue. Needed to avoid changes in priority (by admins and the like) from breaking things. - var/queued_priority - + var/queued_priority + /// How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out! - var/static/list/failure_strikes - + var/static/list/failure_strikes + /// Next subsystem in the queue of subsystems to run this tick var/datum/controller/subsystem/queue_next /// Previous subsystem in the queue of subsystems to run this tick var/datum/controller/subsystem/queue_prev + //Do not blindly add vars here to the bottom, put it where it goes above + //If your var only has two values, put it in as a flag. + //Do not override ///datum/controller/subsystem/New() diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index c9cb4a62b6c1..a83f4545af54 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -12,12 +12,14 @@ SUBSYSTEM_DEF(air) var/cost_hotspots = 0 var/cost_superconductivity = 0 var/cost_pipenets = 0 + var/cost_rebuilds = 0 var/cost_atmos_machinery = 0 var/list/excited_groups = list() var/list/active_turfs = list() var/list/hotspots = list() var/list/networks = list() + var/list/pipenets_needing_rebuilt = list() var/list/obj/machinery/atmos_machinery = list() var/list/pipe_init_dirs_cache = list() @@ -31,7 +33,7 @@ SUBSYSTEM_DEF(air) var/list/currentrun = list() - var/currentpart = SSAIR_PIPENETS + var/currentpart = SSAIR_REBUILD_PIPENETS var/map_loading = TRUE var/list/queued_for_activation @@ -44,6 +46,7 @@ SUBSYSTEM_DEF(air) msg += "HS:[round(cost_hotspots,1)]|" msg += "SC:[round(cost_superconductivity,1)]|" msg += "PN:[round(cost_pipenets,1)]|" + msg += "RB:[round(cost_rebuilds,1)]|" msg += "AM:[round(cost_atmos_machinery,1)]" msg += "} " msg += "AT:[active_turfs.len]|" @@ -68,6 +71,18 @@ SUBSYSTEM_DEF(air) /datum/controller/subsystem/air/fire(resumed = 0) var/timer = TICK_USAGE_REAL + if(currentpart == SSAIR_REBUILD_PIPENETS) + var/list/pipenet_rebuilds = pipenets_needing_rebuilt + for(var/thing in pipenet_rebuilds) + var/obj/machinery/atmospherics/AT = thing + AT.build_network() + cost_rebuilds = MC_AVERAGE(cost_rebuilds, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + pipenets_needing_rebuilt.Cut() + if(state != SS_RUNNING) + return + resumed = FALSE + currentpart = SSAIR_PIPENETS + if(currentpart == SSAIR_PIPENETS || !resumed) process_pipenets(resumed) cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) @@ -128,7 +143,7 @@ SUBSYSTEM_DEF(air) if(state != SS_RUNNING) return resumed = 0 - currentpart = SSAIR_PIPENETS + currentpart = SSAIR_REBUILD_PIPENETS @@ -147,6 +162,9 @@ SUBSYSTEM_DEF(air) if(MC_TICK_CHECK) return +/datum/controller/subsystem/air/proc/add_to_rebuild_queue(atmos_machine) + if(istype(atmos_machine, /obj/machinery/atmospherics)) + pipenets_needing_rebuilt += atmos_machine /datum/controller/subsystem/air/proc/process_atmos_machinery(resumed = 0) var/seconds = wait * 0.1 diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index 27f6d7c7fe83..bbeb0683f0f4 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -18,7 +18,7 @@ SUBSYSTEM_DEF(chat) return -/datum/controller/subsystem/chat/proc/queue(target, message, handle_whitespace = TRUE, trailing_newline = TRUE) +/datum/controller/subsystem/chat/proc/queue(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = TRUE) if(!target || !message) return @@ -39,7 +39,6 @@ SUBSYSTEM_DEF(chat) if (trailing_newline) message += "
" - //url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript. //Do the double-encoding here to save nanoseconds var/twiceEncoded = url_encode(url_encode(message)) @@ -49,7 +48,7 @@ SUBSYSTEM_DEF(chat) var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible if(!C) - return + continue //Send it to the old style output window. SEND_TEXT(C, original_message) diff --git a/code/datums/explosion.dm b/code/controllers/subsystem/explosions.dm similarity index 50% rename from code/datums/explosion.dm rename to code/controllers/subsystem/explosions.dm index f751de8bc865..856e4c20ca60 100644 --- a/code/datums/explosion.dm +++ b/code/controllers/subsystem/explosions.dm @@ -1,50 +1,177 @@ #define EXPLOSION_THROW_SPEED 4 - GLOBAL_LIST_EMPTY(explosions) -//Against my better judgement, I will return the explosion datum -//If I see any GC errors for it I will find you -//and I will gib you + +SUBSYSTEM_DEF(explosions) + name = "Explosions" + init_order = INIT_ORDER_EXPLOSIONS + priority = FIRE_PRIORITY_EXPLOSIONS + wait = 1 + flags = SS_TICKER|SS_NO_INIT + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + + var/cost_lowturf = 0 + var/cost_medturf = 0 + var/cost_highturf = 0 + var/cost_flameturf = 0 + + var/cost_throwturf = 0 + + var/cost_lowobj = 0 + var/cost_medobj = 0 + var/cost_highobj = 0 + + + var/list/lowturf = list() + var/list/medturf = list() + var/list/highturf = list() + var/list/flameturf = list() + + var/list/throwturf = list() + + var/list/lowobj = list() + var/list/medobj = list() + var/list/highobj = list() + + var/list/explosions = list() + + var/currentpart = SSAIR_PIPENETS + + +/datum/controller/subsystem/explosions/stat_entry(msg) + msg += "C:{" + msg += "LT:[round(cost_lowturf,1)]|" + msg += "MT:[round(cost_medturf,1)]|" + msg += "HT:[round(cost_highturf,1)]|" + msg += "FT:[round(cost_flameturf,1)]||" + + msg += "LO:[round(cost_lowobj,1)]|" + msg += "MO:[round(cost_medobj,1)]|" + msg += "HO:[round(cost_highobj,1)]|" + + msg += "TO:[round(cost_throwturf,1)]" + + msg += "} " + + msg += "AMT:{" + msg += "LT:[lowturf.len]|" + msg += "MT:[medturf.len]|" + msg += "HT:[highturf.len]|" + msg += "FT:[flameturf.len]||" + + msg += "LO:[lowobj.len]|" + msg += "MO:[medobj.len]|" + msg += "HO:[highobj.len]|" + + msg += "TO:[throwturf.len]" + + msg += "} " + ..(msg) + + +#define SSEX_TURF "turf" +#define SSEX_OBJ "obj" + +/datum/controller/subsystem/explosions/proc/is_exploding() + return (lowturf.len || medturf.len || highturf.len || flameturf.len || throwturf.len || lowobj.len || medobj.len || highobj.len) + + +/client/proc/check_bomb_impacts() + set name = "Check Bomb Impact" + set category = "Debug" + + var/newmode = alert("Use reactionary explosions?","Check Bomb Impact", "Yes", "No") + var/turf/epicenter = get_turf(mob) + if(!epicenter) + return + + var/dev = 0 + var/heavy = 0 + var/light = 0 + var/list/choices = list("Small Bomb","Medium Bomb","Big Bomb","Custom Bomb") + var/choice = input("Bomb Size?") in choices + switch(choice) + if(null) + return 0 + if("Small Bomb") + dev = 1 + heavy = 2 + light = 3 + if("Medium Bomb") + dev = 2 + heavy = 3 + light = 4 + if("Big Bomb") + dev = 3 + heavy = 5 + light = 7 + if("Custom Bomb") + dev = input("Devastation range (Tiles):") as num + heavy = input("Heavy impact range (Tiles):") as num + light = input("Light impact range (Tiles):") as num + + var/max_range = max(dev, heavy, light) + var/x0 = epicenter.x + var/y0 = epicenter.y + var/list/wipe_colours = list() + for(var/turf/T in spiral_range_turfs(max_range, epicenter)) + wipe_colours += T + var/dist = cheap_hypotenuse(T.x, T.y, x0, y0) + + if(newmode == "Yes") + var/turf/TT = T + while(TT != epicenter) + TT = get_step_towards(TT,epicenter) + if(TT.density) + dist += TT.explosion_block + + for(var/obj/O in T) + var/the_block = O.explosion_block + dist += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block + + if(dist < dev) + T.color = "red" + T.maptext = "Dev" + else if (dist < heavy) + T.color = "yellow" + T.maptext = "Heavy" + else if (dist < light) + T.color = "blue" + T.maptext = "Light" + else + continue + + addtimer(CALLBACK(GLOBAL_PROC, .proc/wipe_color_and_text, wipe_colours), 100) + +/proc/wipe_color_and_text(list/atom/wiping) + for(var/i in wiping) + var/atom/A = i + A.color = null + A.maptext = "" + +/proc/dyn_explosion(turf/epicenter, power, flash_range, adminlog = TRUE, ignorecap = TRUE, flame_range = 0, silent = FALSE, smoke = TRUE) + if(!power) + return + var/range = 0 + range = round((2 * power)**GLOB.DYN_EX_SCALE) + explosion(epicenter, round(range * 0.1), round(range * 0.5), round(range), flash_range*range, adminlog, ignorecap, flame_range*range, silent, smoke) + +// Using default dyn_ex scale: +// 100 explosion power is a (2, 10, 20) explosion. +// 75 explosion power is a (2, 8, 17) explosion. +// 50 explosion power is a (1, 7, 14) explosion. +// 25 explosion power is a (1, 5, 10) explosion. +// 10 explosion power is a (1, 3, 6) explosion. +// 5 explosion power is a (0, 1, 3) explosion. +// 1 explosion power is a (0, 0, 1) explosion. + /proc/explosion(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = TRUE, ignorecap = FALSE, flame_range = 0, silent = FALSE, smoke = FALSE) - return new /datum/explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke) - -//This datum creates 3 async tasks -//1 GatherSpiralTurfsProc runs spiral_range_turfs(tick_checked = TRUE) to populate the affected_turfs list -//2 CaculateExplosionBlock adds the blockings to the cached_exp_block list -//3 The main thread explodes the prepared turfs - -/datum/explosion - var/explosion_id - var/atom/explosion_source - var/started_at - var/running = TRUE - var/stopped = 0 //This is the number of threads stopped !DOESN'T COUNT THREAD 2! - var/static/id_counter = 0 - -#define EX_PREPROCESS_EXIT_CHECK \ - if(!running) {\ - stopped = 2;\ - qdel(src);\ - return;\ - } - -#define EX_PREPROCESS_CHECK_TICK \ - if(TICK_CHECK) {\ - stoplag();\ - EX_PREPROCESS_EXIT_CHECK\ - } - -/datum/explosion/New(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke) - set waitfor = FALSE - - var/id = ++id_counter - explosion_id = id - explosion_source = epicenter + . = SSexplosions.explode(arglist(args)) +/datum/controller/subsystem/explosions/proc/explode(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke) epicenter = get_turf(epicenter) if(!epicenter) return - GLOB.explosions += src if(isnull(flame_range)) flame_range = light_impact_range if(isnull(flash_range)) @@ -69,21 +196,8 @@ GLOBAL_LIST_EMPTY(explosions) flash_range = min(GLOB.MAX_EX_FLASH_RANGE * cap_multiplier, flash_range) flame_range = min(GLOB.MAX_EX_FLAME_RANGE * cap_multiplier, flame_range) - //DO NOT REMOVE THIS STOPLAG, IT BREAKS THINGS - //not sleeping causes us to ex_act() the thing that triggered the explosion - //doing that might cause it to trigger another explosion - //this is bad - //I would make this not ex_act the thing that triggered the explosion, - //but everything that explodes gives us their loc or a get_turf() - //and somethings expect us to ex_act them so they can qdel() - stoplag() //tldr, let the calling proc call qdel(src) before we explode - - EX_PREPROCESS_EXIT_CHECK - - started_at = REALTIMEOFDAY - var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flame_range) - + var/started_at = REALTIMEOFDAY if(adminlog) message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in [ADMIN_VERBOSEJMP(epicenter)]") log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in [loc_name(epicenter)]") @@ -109,7 +223,8 @@ GLOBAL_LIST_EMPTY(explosions) var/sound/explosion_sound = sound(get_sfx("explosion")) var/sound/far_explosion_sound = sound('sound/effects/explosionfar.ogg') - for(var/mob/M in GLOB.player_list) + for(var/MN in GLOB.player_list) + var/mob/M = MN // Double check for client var/turf/M_turf = get_turf(M) if(M_turf && M_turf.z == z0) @@ -129,12 +244,6 @@ GLOBAL_LIST_EMPTY(explosions) M.playsound_local(epicenter, null, far_volume, 1, frequency, falloff = 5, S = far_explosion_sound) if(baseshakeamount > 0) shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, 2.5)) - EX_PREPROCESS_CHECK_TICK - - //postpone processing for a bit - var/postponeCycles = max(round(devastation_range/8),1) - SSlighting.postpone(postponeCycles) - SSmachines.postpone(postponeCycles) if(heavy_impact_range > 1) var/datum/effect_system/explosion/E @@ -145,16 +254,11 @@ GLOBAL_LIST_EMPTY(explosions) E.set_up(epicenter) E.start() - EX_PREPROCESS_CHECK_TICK - //flash mobs if(flash_range) for(var/mob/living/L in viewers(flash_range, epicenter)) L.flash_act() - EX_PREPROCESS_CHECK_TICK - - var/list/exploded_this_tick = list() //open turfs that need to be blocked off while we sleep var/list/affected_turfs = GatherSpiralTurfs(max_range, epicenter) var/reactionary = CONFIG_GET(flag/reactionary_explosions) @@ -165,12 +269,8 @@ GLOBAL_LIST_EMPTY(explosions) //lists are guaranteed to contain at least 1 turf at this point - var/iteration = 0 - var/affTurfLen = affected_turfs.len - var/expBlockLen = cached_exp_block.len for(var/TI in affected_turfs) var/turf/T = TI - ++iteration var/init_dist = cheap_hypotenuse(T.x, T.y, x0, y0) var/dist = init_dist @@ -192,85 +292,47 @@ GLOBAL_LIST_EMPTY(explosions) else dist = EXPLODE_NONE - //------- EX_ACT AND TURF FIRES ------- - if(T == epicenter) // Ensures explosives detonating from bags trigger other explosives in that bag var/list/items = list() for(var/I in T) var/atom/A = I - if (!(A.flags_1 & PREVENT_CONTENTS_EXPLOSION_1)) //The atom/contents_explosion() proc returns null if the contents ex_acting has been handled by the atom, and TRUE if it hasn't. + if (length(A.contents) && !(A.flags_1 & PREVENT_CONTENTS_EXPLOSION_1)) //The atom/contents_explosion() proc returns null if the contents ex_acting has been handled by the atom, and TRUE if it hasn't. items += A.GetAllContents() for(var/O in items) var/atom/A = O if(!QDELETED(A)) - A.ex_act(dist) + switch(dist) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += A + if(EXPLODE_HEAVY) + SSexplosions.medobj += A + if(EXPLODE_LIGHT) + SSexplosions.lowobj += A + switch(dist) + if(EXPLODE_DEVASTATE) + SSexplosions.highturf += T + if(EXPLODE_HEAVY) + SSexplosions.medturf += T + if(EXPLODE_LIGHT) + SSexplosions.lowturf += T - if(flame_dist && prob(40) && !isspaceturf(T) && !T.density) - new /obj/effect/hotspot(T) //Mostly for ambience! - if(dist > EXPLODE_NONE) - T.explosion_level = max(T.explosion_level, dist) //let the bigger one have it - T.explosion_id = id - T.ex_act(dist) - exploded_this_tick += T + if(flame_dist && prob(40) && !isspaceturf(T) && !T.density) + flameturf += T //--- THROW ITEMS AROUND --- - var/throw_dir = get_dir(epicenter,T) - for(var/obj/item/I in T) - if(!I.anchored) - var/throw_range = rand(throw_dist, max_range) - var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range) - I.throw_at(throw_at, throw_range, EXPLOSION_THROW_SPEED) - - //wait for the lists to repop - var/break_condition - if(reactionary) - //If we've caught up to the density checker thread and there are no more turfs to process - break_condition = iteration == expBlockLen && iteration < affTurfLen + var/throw_range = max_range-throw_dist + var/list/throwingturf = T.explosion_throw_details + if (throwingturf) + if (throwingturf[1] < throw_range) + throwingturf[1] = throw_range + throwingturf[2] = throw_dir + throwingturf[3] = max_range else - //If we've caught up to the turf gathering thread and it's still running - break_condition = iteration == affTurfLen && !stopped - - if(break_condition || TICK_CHECK) - stoplag() - - if(!running) - break - - //update the trackers - affTurfLen = affected_turfs.len - expBlockLen = cached_exp_block.len - - if(break_condition) - if(reactionary) - //until there are more block checked turfs than what we are currently at - //or the explosion has stopped - UNTIL(iteration < affTurfLen || !running) - else - //until there are more gathered turfs than what we are currently at - //or there are no more turfs to gather/the explosion has stopped - UNTIL(iteration < expBlockLen || stopped) - - if(!running) - break - - //update the trackers - affTurfLen = affected_turfs.len - expBlockLen = cached_exp_block.len - - var/circumference = (PI * (init_dist + 4) * 2) //+4 to radius to prevent shit gaps - if(exploded_this_tick.len > circumference) //only do this every revolution - for(var/Unexplode in exploded_this_tick) - var/turf/UnexplodeT = Unexplode - UnexplodeT.explosion_level = 0 - exploded_this_tick.Cut() - - //unfuck the shit - for(var/Unexplode in exploded_this_tick) - var/turf/UnexplodeT = Unexplode - UnexplodeT.explosion_level = 0 - exploded_this_tick.Cut() + T.explosion_throw_details = list(throw_range, throw_dir, max_range) + throwturf += T + var/took = (REALTIMEOFDAY - started_at) / 10 @@ -278,138 +340,175 @@ GLOBAL_LIST_EMPTY(explosions) if(GLOB.Debug2) log_world("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") - if(running) //if we aren't in a hurry - SEND_GLOBAL_SIGNAL(COMSIG_GLOB_EXPLOSION, epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) - - ++stopped - qdel(src) - -#undef EX_PREPROCESS_EXIT_CHECK -#undef EX_PREPROCESS_CHECK_TICK - -//asyncly populate the affected_turfs list -/datum/explosion/proc/GatherSpiralTurfs(range, turf/epicenter) - set waitfor = FALSE - . = list() - spiral_range_turfs(range, epicenter, outlist = ., tick_checked = TRUE) - ++stopped - -/datum/explosion/proc/CaculateExplosionBlock(list/affected_turfs) - set waitfor = FALSE - + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_EXPLOSION, epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) + + +/datum/controller/subsystem/explosions/proc/GatherSpiralTurfs(range, turf/epicenter) + var/list/outlist = list() + var/center = epicenter + var/dist = range + if(!dist) + outlist += center + return outlist + + var/turf/t_center = get_turf(center) + if(!t_center) + return outlist + + var/list/L = outlist + var/turf/T + var/y + var/x + var/c_dist = 1 + L += t_center + + while( c_dist <= dist ) + y = t_center.y + c_dist + x = t_center.x - c_dist + 1 + for(x in x to t_center.x+c_dist) + T = locate(x,y,t_center.z) + if(T) + L += T + + y = t_center.y + c_dist - 1 + x = t_center.x + c_dist + for(y in t_center.y-c_dist to y) + T = locate(x,y,t_center.z) + if(T) + L += T + + y = t_center.y - c_dist + x = t_center.x + c_dist - 1 + for(x in t_center.x-c_dist to x) + T = locate(x,y,t_center.z) + if(T) + L += T + + y = t_center.y - c_dist + 1 + x = t_center.x - c_dist + for(y in y to t_center.y+c_dist) + T = locate(x,y,t_center.z) + if(T) + L += T + c_dist++ + . = L + +/datum/controller/subsystem/explosions/proc/CaculateExplosionBlock(list/affected_turfs) . = list() - var/processed = 0 - while(running) - var/I - for(I in (processed + 1) to affected_turfs.len) // we cache the explosion block rating of every turf in the explosion area - var/turf/T = affected_turfs[I] - var/current_exp_block = T.density ? T.explosion_block : 0 - - for(var/obj/O in T) - var/the_block = O.explosion_block - current_exp_block += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block - - .[T] = current_exp_block - - if(TICK_CHECK) - break - - processed = I - stoplag() - -/datum/explosion/Destroy() - running = FALSE - if(stopped < 2) //wait for main thread and spiral_range thread - return QDEL_HINT_IWILLGC - GLOB.explosions -= src - explosion_source = null - return ..() - -/client/proc/check_bomb_impacts() - set name = "Check Bomb Impact" - set category = "Debug" - - var/newmode = alert("Use reactionary explosions?","Check Bomb Impact", "Yes", "No") - var/turf/epicenter = get_turf(mob) - if(!epicenter) - return - - var/dev = 0 - var/heavy = 0 - var/light = 0 - var/list/choices = list("Small Bomb","Medium Bomb","Big Bomb","Custom Bomb") - var/choice = input("Bomb Size?") in choices - switch(choice) - if(null) - return 0 - if("Small Bomb") - dev = 1 - heavy = 2 - light = 3 - if("Medium Bomb") - dev = 2 - heavy = 3 - light = 4 - if("Big Bomb") - dev = 3 - heavy = 5 - light = 7 - if("Custom Bomb") - dev = input("Devastation range (Tiles):") as num - heavy = input("Heavy impact range (Tiles):") as num - light = input("Light impact range (Tiles):") as num + var/I + for(I in 1 to affected_turfs.len) // we cache the explosion block rating of every turf in the explosion area + var/turf/T = affected_turfs[I] + var/current_exp_block = T.density ? T.explosion_block : 0 - var/max_range = max(dev, heavy, light) - var/x0 = epicenter.x - var/y0 = epicenter.y - var/list/wipe_colours = list() - for(var/turf/T in spiral_range_turfs(max_range, epicenter)) - wipe_colours += T - var/dist = cheap_hypotenuse(T.x, T.y, x0, y0) + for(var/obj/O in T) + var/the_block = O.explosion_block + current_exp_block += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block - if(newmode == "Yes") - var/turf/TT = T - while(TT != epicenter) - TT = get_step_towards(TT,epicenter) - if(TT.density) - dist += TT.explosion_block + .[T] = current_exp_block - for(var/obj/O in T) - var/the_block = O.explosion_block - dist += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block - - if(dist < dev) - T.color = "red" - T.maptext = "Dev" - else if (dist < heavy) - T.color = "yellow" - T.maptext = "Heavy" - else if (dist < light) - T.color = "blue" - T.maptext = "Light" - else - continue - - addtimer(CALLBACK(GLOBAL_PROC, .proc/wipe_color_and_text, wipe_colours), 100) - -/proc/wipe_color_and_text(list/atom/wiping) - for(var/i in wiping) - var/atom/A = i - A.color = null - A.maptext = "" - -/proc/dyn_explosion(turf/epicenter, power, flash_range, adminlog = TRUE, ignorecap = TRUE, flame_range = 0, silent = FALSE, smoke = TRUE) - if(!power) +/datum/controller/subsystem/explosions/fire(resumed = 0) + if (!is_exploding()) return - var/range = 0 - range = round((2 * power)**GLOB.DYN_EX_SCALE) - explosion(epicenter, round(range * 0.25), round(range * 0.5), round(range), flash_range*range, adminlog, ignorecap, flame_range*range, silent, smoke) - -// Using default dyn_ex scale: -// 100 explosion power is a (5, 10, 20) explosion. -// 75 explosion power is a (4, 8, 17) explosion. -// 50 explosion power is a (3, 7, 14) explosion. -// 25 explosion power is a (2, 5, 10) explosion. -// 10 explosion power is a (1, 3, 6) explosion. -// 5 explosion power is a (0, 1, 3) explosion. -// 1 explosion power is a (0, 0, 1) explosion. + var/timer + Master.current_ticklimit = TICK_LIMIT_RUNNING //force using the entire tick if we need it. + + if(currentpart == SSEXPLOSIONS_TURFS) + currentpart = SSEXPLOSIONS_MOVABLES + + timer = TICK_USAGE_REAL + var/list/low_turf = lowturf + lowturf = list() + for(var/thing in low_turf) + if(thing) + var/turf/T = thing + T.explosion_level = max(T.explosion_level, EXPLODE_LIGHT) + T.ex_act(EXPLODE_LIGHT) + cost_lowturf = MC_AVERAGE(cost_lowturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + + timer = TICK_USAGE_REAL + var/list/med_turf = medturf + medturf = list() + for(var/thing in med_turf) + if(thing) + var/turf/T = thing + T.explosion_level = max(T.explosion_level, EXPLODE_HEAVY) + T.ex_act(EXPLODE_HEAVY) + cost_medturf = MC_AVERAGE(cost_medturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + + timer = TICK_USAGE_REAL + var/list/high_turf = highturf + highturf = list() + for(var/thing in high_turf) + if(thing) + var/turf/T = thing + T.explosion_level = max(T.explosion_level, EXPLODE_DEVASTATE) + T.ex_act(EXPLODE_DEVASTATE) + cost_highturf = MC_AVERAGE(cost_highturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + + timer = TICK_USAGE_REAL + var/list/flame_turf = flameturf + flameturf = list() + for(var/thing in flame_turf) + if(thing) + var/turf/T = thing + new /obj/effect/hotspot(T) //Mostly for ambience! + cost_flameturf = MC_AVERAGE(cost_flameturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + + if (low_turf.len || med_turf.len || high_turf.len) + Master.laggy_byond_map_update_incoming() + + if(currentpart == SSEXPLOSIONS_MOVABLES) + currentpart = SSEXPLOSIONS_THROWS + + timer = TICK_USAGE_REAL + var/list/high_obj = highobj + highobj = list() + for(var/thing in high_obj) + if(thing) + var/obj/O = thing + O.ex_act(EXPLODE_DEVASTATE) + cost_highobj = MC_AVERAGE(cost_highobj, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + + timer = TICK_USAGE_REAL + var/list/med_obj = medobj + medobj = list() + for(var/thing in med_obj) + if(thing) + var/obj/O = thing + O.ex_act(EXPLODE_HEAVY) + cost_medobj = MC_AVERAGE(cost_medobj, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + + timer = TICK_USAGE_REAL + var/list/low_obj = lowobj + lowobj = list() + for(var/thing in low_obj) + if(thing) + var/obj/O = thing + O.ex_act(EXPLODE_LIGHT) + cost_lowobj = MC_AVERAGE(cost_lowobj, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + + + if (currentpart == SSEXPLOSIONS_THROWS) + currentpart = SSEXPLOSIONS_TURFS + timer = TICK_USAGE_REAL + var/list/throw_turf = throwturf + throwturf = list() + for (var/thing in throw_turf) + if (!thing) + continue + var/turf/T = thing + var/list/L = T.explosion_throw_details + T.explosion_throw_details = null + if (length(L) != 3) + continue + var/throw_range = L[1] + var/throw_dir = L[2] + var/max_range = L[3] + for(var/atom/movable/A in T) + if(!A.anchored && A.move_resist != INFINITY) + var/atom_throw_range = rand(throw_range, max_range) + var/turf/throw_at = get_ranged_target_turf(A, throw_dir, atom_throw_range) + A.throw_at(throw_at, atom_throw_range, EXPLOSION_THROW_SPEED, quickstart = FALSE) + cost_throwturf = MC_AVERAGE(cost_throwturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + + currentpart = SSEXPLOSIONS_TURFS diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index 0bf1466280dd..f8ca1e7eae4d 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -98,7 +98,7 @@ SUBSYSTEM_DEF(garbage) state = SS_RUNNING break - + /datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_CHECK) diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index 0e9edb162743..547103fa47b0 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -27,7 +27,7 @@ SUBSYSTEM_DEF(input) "T" = "say", "M" = "me", "Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", - "Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", + "Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", "Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"") // Badmins just wanna have fun ♪ diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index dddb560a8234..26bcc8099b85 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -497,13 +497,20 @@ SUBSYSTEM_DEF(job) if(!C?.holder) return TRUE var/datum/job/job = GetJob(rank) + + var/timegate_expired = FALSE + // allow only forcing deadminning in the first X seconds of the round if auto_deadmin_timegate is set in config + var/timegate = CONFIG_GET(number/auto_deadmin_timegate) + if(timegate && (world.time - SSticker.round_start_time > timegate)) + timegate_expired = TRUE + if(!job) return - if((job.auto_deadmin_role_flags & DEADMIN_POSITION_HEAD) && (CONFIG_GET(flag/auto_deadmin_heads) || (C.prefs?.toggles & DEADMIN_POSITION_HEAD))) + if((job.auto_deadmin_role_flags & DEADMIN_POSITION_HEAD) && ((CONFIG_GET(flag/auto_deadmin_heads) && !timegate_expired) || (C.prefs?.toggles & DEADMIN_POSITION_HEAD))) return C.holder.auto_deadmin() - else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SECURITY) && (CONFIG_GET(flag/auto_deadmin_security) || (C.prefs?.toggles & DEADMIN_POSITION_SECURITY))) + else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SECURITY) && ((CONFIG_GET(flag/auto_deadmin_security) && !timegate_expired) || (C.prefs?.toggles & DEADMIN_POSITION_SECURITY))) return C.holder.auto_deadmin() - else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SILICON) && (CONFIG_GET(flag/auto_deadmin_silicons) || (C.prefs?.toggles & DEADMIN_POSITION_SILICON))) //in the event there's ever psuedo-silicon roles added, ie synths. + else if((job.auto_deadmin_role_flags & DEADMIN_POSITION_SILICON) && ((CONFIG_GET(flag/auto_deadmin_silicons) && !timegate_expired) || (C.prefs?.toggles & DEADMIN_POSITION_SILICON))) //in the event there's ever psuedo-silicon roles added, ie synths. return C.holder.auto_deadmin() /datum/controller/subsystem/job/proc/setup_officer_positions() diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm index 8caf2a4623cc..885587ff554b 100644 --- a/code/controllers/subsystem/mobs.dm +++ b/code/controllers/subsystem/mobs.dm @@ -8,6 +8,7 @@ SUBSYSTEM_DEF(mobs) var/static/list/clients_by_zlevel[][] var/static/list/dead_players_by_zlevel[][] = list(list()) // Needs to support zlevel 1 here, MaxZChanged only happens when z2 is created and new_players can login before that. var/static/list/cubemonkeys = list() + var/static/list/cheeserats = list() /datum/controller/subsystem/mobs/stat_entry() ..("P:[GLOB.mob_living_list.len]") diff --git a/code/controllers/subsystem/nightshift.dm b/code/controllers/subsystem/nightshift.dm index 325ba212c6d9..68116483ad49 100644 --- a/code/controllers/subsystem/nightshift.dm +++ b/code/controllers/subsystem/nightshift.dm @@ -35,7 +35,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/server_maint.dm b/code/controllers/subsystem/server_maint.dm index 3be16af9548b..1b94792048af 100644 --- a/code/controllers/subsystem/server_maint.dm +++ b/code/controllers/subsystem/server_maint.dm @@ -90,4 +90,15 @@ SUBSYSTEM_DEF(server_maint) if(tgsversion) SSblackbox.record_feedback("text", "server_tools", 1, tgsversion.raw_parameter) + +/datum/controller/subsystem/server_maint/proc/UpdateHubStatus() + if(!CONFIG_GET(flag/hub) || !CONFIG_GET(number/max_hub_pop)) + return FALSE //no point, hub / auto hub controls are disabled + + var/max_pop = CONFIG_GET(number/max_hub_pop) + + if(GLOB.clients.len > max_pop) + world.update_hub_visibility(FALSE) + else + world.update_hub_visibility(TRUE) #undef PING_BUFFER_TIME diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index 15d2a888689d..9080d0668c01 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -756,7 +756,7 @@ SUBSYSTEM_DEF(shuttle) /datum/controller/subsystem/shuttle/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "shuttle_manipulator", name, 800, 600, master_ui, state) + ui = new(user, src, ui_key, "ShuttleManipulator", name, 800, 600, master_ui, state) ui.open() diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 167b97a0e791..7d415fb678f3 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -27,7 +27,11 @@ SUBSYSTEM_DEF(ticker) var/admin_delay_notice = "" //a message to display to anyone who tries to restart the world after a delay var/ready_for_reboot = FALSE //all roundend preparation done with, all that's left is reboot - var/triai = 0 //Global holder for Triumvirate + ///If not set to ANON_DISABLED then people spawn with a themed anon name (see anonymousnames.dm) + var/anonymousnames = ANON_DISABLED + ///Boolean to see if the game needs to set up a triumvirate ai (see tripAI.dm) + var/triai = FALSE + var/tipped = 0 //Did we broadcast the tip of the day yet? var/selected_tip // What will be the tip of the day? @@ -353,10 +357,7 @@ SUBSYSTEM_DEF(ticker) /datum/controller/subsystem/ticker/proc/station_explosion_detonation(atom/bomb) if(bomb) //BOOM - var/turf/epi = bomb.loc qdel(bomb) - if(epi) - explosion(epi, 0, 256, 512, 0, TRUE, TRUE, 0, TRUE) /datum/controller/subsystem/ticker/proc/create_characters() for(var/i in GLOB.new_player_list) @@ -490,6 +491,7 @@ SUBSYSTEM_DEF(ticker) delay_end = SSticker.delay_end + anonymousnames = SSticker.anonymousnames triai = SSticker.triai tipped = SSticker.tipped selected_tip = SSticker.selected_tip diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm index 4317237e641d..8b2dffc6a182 100644 --- a/code/controllers/subsystem/traumas.dm +++ b/code/controllers/subsystem/traumas.dm @@ -2,7 +2,7 @@ SUBSYSTEM_DEF(traumas) name = "Traumas" flags = SS_NO_FIRE var/list/phobia_types - var/list/phobia_words + var/list/phobia_regexes var/list/phobia_mobs var/list/phobia_objs var/list/phobia_turfs @@ -16,25 +16,25 @@ SUBSYSTEM_DEF(traumas) "skeletons", "snakes", "robots", "doctors", "authority", "the supernatural", "aliens", "strangers", "birds", "falling", "anime")) - phobia_words = list("spiders" = strings(PHOBIA_FILE, "spiders"), - "space" = strings(PHOBIA_FILE, "space"), - "security" = strings(PHOBIA_FILE, "security"), - "clowns" = strings(PHOBIA_FILE, "clowns"), - "greytide" = strings(PHOBIA_FILE, "greytide"), - "lizards" = strings(PHOBIA_FILE, "lizards"), - "skeletons" = strings(PHOBIA_FILE, "skeletons"), - "snakes" = strings(PHOBIA_FILE, "snakes"), - "robots" = strings(PHOBIA_FILE, "robots"), - "doctors" = strings(PHOBIA_FILE, "doctors"), - "authority" = strings(PHOBIA_FILE, "authority"), - "the supernatural" = strings(PHOBIA_FILE, "the supernatural"), - "aliens" = strings(PHOBIA_FILE, "aliens"), - "strangers" = strings(PHOBIA_FILE, "strangers"), - "conspiracies" = strings(PHOBIA_FILE, "conspiracies"), - "birds" = strings(PHOBIA_FILE, "birds"), - "falling" = strings(PHOBIA_FILE, "falling"), - "anime" = strings(PHOBIA_FILE, "anime") - ) + phobia_regexes = list("spiders" = construct_phobia_regex("spiders"), + "space" = construct_phobia_regex("space"), + "security" = construct_phobia_regex("security"), + "clowns" = construct_phobia_regex("clowns"), + "greytide" = construct_phobia_regex("greytide"), + "lizards" = construct_phobia_regex("lizards"), + "skeletons" = construct_phobia_regex("skeletons"), + "snakes" = construct_phobia_regex("snakes"), + "robots" = construct_phobia_regex("robots"), + "doctors" = construct_phobia_regex("doctors"), + "authority" = construct_phobia_regex("authority"), + "the supernatural" = construct_phobia_regex("the supernatural"), + "aliens" = construct_phobia_regex("aliens"), + "strangers" = construct_phobia_regex("strangers"), + "conspiracies" = construct_phobia_regex("conspiracies"), + "birds" = construct_phobia_regex("birds"), + "falling" = construct_phobia_regex("falling"), + "anime" = construct_phobia_regex("anime") + ) phobia_mobs = list("spiders" = typecacheof(list(/mob/living/simple_animal/hostile/poison/giant_spider)), "security" = typecacheof(list(/mob/living/simple_animal/bot/secbot)), @@ -54,30 +54,31 @@ SUBSYSTEM_DEF(traumas) "anime" = typecacheof(list(/mob/living/simple_animal/hostile/guardian)) ) - phobia_objs = list("snakes" = typecacheof(list(/obj/item/rod_of_asclepius)), + phobia_objs = list("snakes" = typecacheof(list(/obj/item/rod_of_asclepius, /obj/item/toy/plush/snakeplushie)), "spiders" = typecacheof(list(/obj/structure/spider)), - "security" = typecacheof(list(/obj/item/clothing/under/rank/security/officer, /obj/item/clothing/under/rank/security/warden, + "security" = typecacheof(list(/obj/item/clothing/under/rank/security/officer, /obj/item/clothing/under/rank/security/warden, /obj/item/clothing/under/rank/security/head_of_security, /obj/item/clothing/under/rank/security/detective, /obj/item/melee/baton, /obj/item/gun/energy/taser, /obj/item/restraints/handcuffs, /obj/machinery/door/airlock/security, /obj/effect/hallucination/simple/securitron)), - "clowns" = typecacheof(list(/obj/item/clothing/under/rank/civilian/clown, /obj/item/clothing/shoes/clown_shoes, + "clowns" = typecacheof(list(/obj/item/clothing/under/rank/civilian/clown, /obj/item/clothing/shoes/clown_shoes, /obj/item/clothing/mask/gas/clown_hat, /obj/item/instrument/bikehorn, /obj/item/pda/clown, /obj/item/grown/bananapeel, /obj/item/reagent_containers/food/snacks/cheesiehonkers, /obj/item/trash/cheesie)), - "greytide" = typecacheof(list(/obj/item/clothing/under/color/grey, /obj/item/melee/baton/cattleprod, + "greytide" = typecacheof(list(/obj/item/clothing/under/color/grey, /obj/item/melee/baton/cattleprod, /obj/item/spear, /obj/item/clothing/mask/gas)), - "lizards" = typecacheof(list(/obj/item/toy/plush/lizardplushie, /obj/item/reagent_containers/food/snacks/kebab/tail, - /obj/item/organ/tail/lizard, /obj/item/reagent_containers/food/drinks/bottle/lizardwine)), + "lizards" = typecacheof(list(/obj/item/toy/plush/lizardplushie, /obj/item/reagent_containers/food/snacks/kebab/tail, /obj/item/organ/tail/lizard, + /obj/item/reagent_containers/food/drinks/bottle/lizardwine, /obj/item/clothing/head/lizard, /obj/item/clothing/shoes/cowboy/lizard)), - "skeletons" = typecacheof(list(/obj/item/organ/tongue/bone, /obj/item/clothing/suit/armor/bone, /obj/item/stack/sheet/bone, + "skeletons" = typecacheof(list(/obj/item/organ/tongue/bone, /obj/item/clothing/suit/armor/bone, /obj/item/stack/sheet/bone, /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton, /obj/effect/decal/remains/human)), - "conspiracies" = typecacheof(list(/obj/item/clothing/under/rank/command/captain, /obj/item/clothing/under/rank/security/head_of_security, + + "conspiracies" = typecacheof(list(/obj/item/clothing/under/rank/command/captain, /obj/item/clothing/under/rank/security/head_of_security, /obj/item/clothing/under/rank/engineering/chief_engineer, /obj/item/clothing/under/rank/medical/chief_medical_officer, /obj/item/clothing/under/rank/command/head_of_personnel, /obj/item/clothing/under/rank/rnd/research_director, ///obj/item/clothing/under/rank/security/head_of_security/grey, /obj/item/clothing/under/rank/security/head_of_security/alt, @@ -91,6 +92,7 @@ SUBSYSTEM_DEF(traumas) /obj/item/clothing/suit/space/hardsuit/ert/engi, /obj/item/clothing/suit/space/hardsuit/ert/med, /obj/item/clothing/suit/space/hardsuit/deathsquad, /obj/item/clothing/head/helmet/space/hardsuit/deathsquad, /obj/machinery/door/airlock/centcom)), + "robots" = typecacheof(list(/obj/machinery/computer/upload, /obj/item/aiModule/, /obj/machinery/recharge_station, /obj/item/aicard, /obj/item/deactivated_swarmer, /obj/effect/mob_spawn/swarmer)), @@ -161,4 +163,17 @@ SUBSYSTEM_DEF(traumas) return ..() +///Creates a regular expression to match against the given phobia +///Capture group 2 = the scary word +///Capture group 3 = an optional suffix on the scary word +/datum/controller/subsystem/traumas/proc/construct_phobia_regex(list/name) + var/list/words = strings(PHOBIA_FILE, name) + if(!length(words)) + CRASH("phobia [name] has no entries") + var/words_match = "" + for(var/word in words) + words_match += "[REGEX_QUOTE(word)]|" + words_match = copytext(words_match, 1, -1) + return regex("(\\b|\\A)([words_match])('?s*)(\\b|\\|)", "ig") + #undef PHOBIA_FILE diff --git a/code/datums/achievements/_achievement_data.dm b/code/datums/achievements/_achievement_data.dm index b6e2cf3f7a97..f353ef03614e 100644 --- a/code/datums/achievements/_achievement_data.dm +++ b/code/datums/achievements/_achievement_data.dm @@ -66,6 +66,8 @@ var/datum/award/A = SSachievements.awards[achievement_type] get_data(achievement_type) //Get the current status first if necessary if(istype(A, /datum/award/achievement)) + if(data[achievement_type]) //You already unlocked it so don't bother running the unlock proc + return data[achievement_type] = TRUE A.on_unlock(user) //Only on default achievement, as scores keep going up. else if(istype(A, /datum/award/score)) @@ -95,7 +97,7 @@ if(!ui) var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/achievements) assets.send(user) - ui = new(user, src, ui_key, "achievements", "Achievements Menu", 800, 1000, master_ui, state) + ui = new(user, src, ui_key, "Achievements", "Achievements Menu", 540, 680, master_ui, state) ui.open() /datum/achievement_data/ui_data(mob/user) diff --git a/code/datums/achievements/misc_achievements.dm b/code/datums/achievements/misc_achievements.dm index c81d2c51ddbc..36c1c75f4af0 100644 --- a/code/datums/achievements/misc_achievements.dm +++ b/code/datums/achievements/misc_achievements.dm @@ -101,3 +101,13 @@ desc = "You were a little too ambitious, but hey, I guess you're still alive?" database_id = MEDAL_SNAIL icon = "snail" + +/datum/award/achievement/misc/lookoutsir + name = "Look Out, Sir!" + desc = "Either awarded for making the ultimate sacrifice for your comrades, or a really dumb attempt at grenade jumping." + database_id = MEDAL_LOOKOUTSIR + +/datum/award/achievement/misc/gottem + name = "HA, GOTTEM" + desc = "Made you look!" + database_id = MEDAL_GOTTEM diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 6b4766aba3a7..6e66c9277aad 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -77,7 +77,9 @@ var/datum/action/innate/imaginary_hide/hide /mob/camera/imaginary_friend/Login() - ..() + . = ..() + if(!. || !client) + return FALSE greet() Show() @@ -149,6 +151,8 @@ friend_talk(message) /mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode) + if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker))) + create_chat_message(speaker, message_language, raw_message, spans, message_mode) to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)) /mob/camera/imaginary_friend/proc/friend_talk(message) diff --git a/code/datums/brain_damage/phobia.dm b/code/datums/brain_damage/phobia.dm index bb8e454d8d60..f35af4f1ca98 100644 --- a/code/datums/brain_damage/phobia.dm +++ b/code/datums/brain_damage/phobia.dm @@ -7,7 +7,7 @@ var/phobia_type var/next_check = 0 var/next_scare = 0 - var/list/trigger_words + var/regex/trigger_regex //instead of cycling every atom, only cycle the relevant types var/list/trigger_mobs var/list/trigger_objs //also checked in mob equipment @@ -24,7 +24,7 @@ gain_text = "You start finding [phobia_type] very unnerving..." lose_text = "You no longer feel afraid of [phobia_type]." scan_desc += " of [phobia_type]" - trigger_words = SStraumas.phobia_words[phobia_type] + trigger_regex = SStraumas.phobia_regexes[phobia_type] trigger_mobs = SStraumas.phobia_mobs[phobia_type] trigger_objs = SStraumas.phobia_objs[phobia_type] trigger_turfs = SStraumas.phobia_turfs[phobia_type] @@ -78,23 +78,16 @@ return if(HAS_TRAIT(owner, TRAIT_FEARLESS)) return - for(var/word in trigger_words) - var/regex/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i") - - if(findtext(hearing_args[HEARING_RAW_MESSAGE], reg)) - addtimer(CALLBACK(src, .proc/freak_out, null, word), 10) //to react AFTER the chat message - hearing_args[HEARING_RAW_MESSAGE] = reg.Replace(hearing_args[HEARING_RAW_MESSAGE], "$1") - break + if(trigger_regex.Find(hearing_args[HEARING_RAW_MESSAGE]) != 0) + addtimer(CALLBACK(src, .proc/freak_out, null, trigger_regex.group[2]), 10) //to react AFTER the chat message + hearing_args[HEARING_RAW_MESSAGE] = trigger_regex.Replace(hearing_args[HEARING_RAW_MESSAGE], "$2$3") /datum/brain_trauma/mild/phobia/handle_speech(datum/source, list/speech_args) if(HAS_TRAIT(owner, TRAIT_FEARLESS)) return - for(var/word in trigger_words) - var/regex/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i") - - if(findtext(speech_args[SPEECH_MESSAGE], reg)) - to_chat(owner, "You can't bring yourself to say the word \"[word]\"!") - speech_args[SPEECH_MESSAGE] = "" + if(trigger_regex.Find(speech_args[SPEECH_MESSAGE]) != 0) + to_chat(owner, "You can't bring yourself to say the word \"[trigger_regex.group[2]]\"!") + speech_args[SPEECH_MESSAGE] = "" /datum/brain_trauma/mild/phobia/proc/freak_out(atom/reason, trigger_word) next_scare = world.time + 120 diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index c033327c91be..d5dd7165db07 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -144,7 +144,9 @@ ..() /mob/living/split_personality/Login() - ..() + . = ..() + if(!. || !client) + return FALSE to_chat(src, "As a split personality, you cannot do anything but observe. However, you will eventually gain control of your body, switching places with the current personality.") to_chat(src, "Do not commit suicide or put the body in a deadly position. Behave like you care about it as much as the owner.") @@ -220,7 +222,9 @@ var/codeword /mob/living/split_personality/traitor/Login() - ..() + . = ..() + if(!. || !client) + return FALSE to_chat(src, "As a brainwashed personality, you cannot do anything yet but observe. However, you may gain control of your body if you hear the special codeword, switching places with the current personality.") to_chat(src, "Your activation codeword is: [codeword]") if(objective) diff --git a/code/datums/browser.dm b/code/datums/browser.dm index 61953c23c8b3..b9d9a6c1237c 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -108,9 +108,9 @@ if (width && height) window_size = "size=[width]x[height];" if (stylesheets.len) - send_asset_list(user, stylesheets, verify=FALSE) + send_asset_list(user, stylesheets) if (scripts.len) - send_asset_list(user, scripts, verify=FALSE) + send_asset_list(user, scripts) user << browse(get_content(), "window=[window_id];[window_size][window_options]") if (use_onclose) setup_onclose() diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm new file mode 100644 index 000000000000..cbcb34ac5ebd --- /dev/null +++ b/code/datums/chatmessage.dm @@ -0,0 +1,236 @@ +#define CHAT_MESSAGE_SPAWN_TIME 0.2 SECONDS +#define CHAT_MESSAGE_LIFESPAN 5 SECONDS +#define CHAT_MESSAGE_EOL_FADE 0.7 SECONDS +#define CHAT_MESSAGE_EXP_DECAY 0.7 // Messages decay at pow(factor, idx in stack) +#define CHAT_MESSAGE_HEIGHT_DECAY 0.9 // Increase message decay based on the height of the message +#define CHAT_MESSAGE_APPROX_LHEIGHT 11 // Approximate height in pixels of an 'average' line, used for height decay +#define CHAT_MESSAGE_WIDTH 96 // pixels +#define CHAT_MESSAGE_MAX_LENGTH 110 // characters +#define WXH_TO_HEIGHT(x) text2num(copytext((x), findtextEx((x), "x") + 1)) // thanks lummox + +/** + * # Chat Message Overlay + * + * Datum for generating a message overlay on the map + */ +/datum/chatmessage + /// The visual element of the chat messsage + var/image/message + /// The location in which the message is appearing + var/atom/message_loc + /// The client who heard this message + var/client/owned_by + /// Contains the scheduled destruction time + var/scheduled_destruction + /// Contains the approximate amount of lines for height decay + var/approx_lines + +/** + * Constructs a chat message overlay + * + * Arguments: + * * text - The text content of the overlay + * * target - The target atom to display the overlay at + * * owner - The mob that owns this overlay, only this mob will be able to view it + * * extra_classes - Extra classes to apply to the span that holds the text + * * lifespan - The lifespan of the message in deciseconds + */ +/datum/chatmessage/New(text, atom/target, mob/owner, list/extra_classes = null, lifespan = CHAT_MESSAGE_LIFESPAN) + . = ..() + if (!istype(target)) + CRASH("Invalid target given for chatmessage") + if(QDELETED(owner) || !istype(owner) || !owner.client) + stack_trace("/datum/chatmessage created with [isnull(owner) ? "null" : "invalid"] mob owner") + qdel(src) + return + INVOKE_ASYNC(src, .proc/generate_image, text, target, owner, extra_classes, lifespan) + +/datum/chatmessage/Destroy() + if (owned_by) + if (owned_by.seen_messages) + LAZYREMOVEASSOC(owned_by.seen_messages, message_loc, src) + owned_by.images.Remove(message) + owned_by = null + message_loc = null + message = null + return ..() + +/** + * Generates a chat message image representation + * + * Arguments: + * * text - The text content of the overlay + * * target - The target atom to display the overlay at + * * owner - The mob that owns this overlay, only this mob will be able to view it + * * extra_classes - Extra classes to apply to the span that holds the text + * * lifespan - The lifespan of the message in deciseconds + */ +/datum/chatmessage/proc/generate_image(text, atom/target, mob/owner, list/extra_classes, lifespan) + // Register client who owns this message + owned_by = owner.client + RegisterSignal(owned_by, COMSIG_PARENT_QDELETING, .proc/qdel, src) + + // Clip message + var/maxlen = owned_by.prefs.max_chat_length + if (length_char(text) > maxlen) + text = copytext_char(text, 1, maxlen + 1) + "..." // BYOND index moment + + // Calculate target color if not already present + if (!target.chat_color || target.chat_color_name != target.name) + target.chat_color = colorize_string(target.name) + target.chat_color_darkened = colorize_string(target.name, 0.85, 0.85) + target.chat_color_name = target.name + + // Get rid of any URL schemes that might cause BYOND to automatically wrap something in an anchor tag + var/static/regex/url_scheme = new(@"[A-Za-z][A-Za-z0-9+-\.]*:\/\/", "g") + text = replacetext(text, url_scheme, "") + + // Reject whitespace + var/static/regex/whitespace = new(@"^\s*$") + if (whitespace.Find(text)) + qdel(src) + return + + // Non mobs speakers can be small + if (!ismob(target)) + extra_classes |= "small" + + // Append radio icon if from a virtual speaker + if (extra_classes.Find("virtual-speaker")) + var/image/r_icon = image('icons/UI_Icons/chat/chat_icons.dmi', icon_state = "radio") + text = "\icon[r_icon] " + text + + // We dim italicized text to make it more distinguishable from regular text + var/tgt_color = extra_classes.Find("italics") ? target.chat_color_darkened : target.chat_color + + // Approximate text height + // Note we have to replace HTML encoded metacharacters otherwise MeasureText will return a zero height + // BYOND Bug #2563917 + // Construct text + var/static/regex/html_metachars = new(@"&[A-Za-z]{1,7};", "g") + var/complete_text = "[text]" + var/mheight = WXH_TO_HEIGHT(owned_by.MeasureText(replacetext(complete_text, html_metachars, "m"), null, CHAT_MESSAGE_WIDTH)) + approx_lines = max(1, mheight / CHAT_MESSAGE_APPROX_LHEIGHT) + + // Translate any existing messages upwards, apply exponential decay factors to timers + message_loc = target + if (owned_by.seen_messages) + var/idx = 1 + var/combined_height = approx_lines + for(var/msg in owned_by.seen_messages[message_loc]) + var/datum/chatmessage/m = msg + animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME) + combined_height += m.approx_lines + var/sched_remaining = m.scheduled_destruction - world.time + if (sched_remaining > CHAT_MESSAGE_SPAWN_TIME) + var/remaining_time = (sched_remaining) * (CHAT_MESSAGE_EXP_DECAY ** idx++) * (CHAT_MESSAGE_HEIGHT_DECAY ** combined_height) + m.scheduled_destruction = world.time + remaining_time + addtimer(CALLBACK(m, .proc/end_of_life), remaining_time, TIMER_UNIQUE|TIMER_OVERRIDE) + + // Build message image + message = image(loc = message_loc, layer = CHAT_LAYER) + message.plane = GAME_PLANE + message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART + message.alpha = 0 + message.pixel_y = owner.bound_height * 0.95 + message.maptext_width = CHAT_MESSAGE_WIDTH + message.maptext_height = mheight + message.maptext_x = (CHAT_MESSAGE_WIDTH - owner.bound_width) * -0.5 + message.maptext = complete_text + + // View the message + LAZYADDASSOC(owned_by.seen_messages, message_loc, src) + owned_by.images |= message + animate(message, alpha = 255, time = CHAT_MESSAGE_SPAWN_TIME) + + // Prepare for destruction + scheduled_destruction = world.time + (lifespan - CHAT_MESSAGE_EOL_FADE) + addtimer(CALLBACK(src, .proc/end_of_life), lifespan - CHAT_MESSAGE_EOL_FADE, TIMER_UNIQUE|TIMER_OVERRIDE) + +/** + * Applies final animations to overlay CHAT_MESSAGE_EOL_FADE deciseconds prior to message deletion + */ +/datum/chatmessage/proc/end_of_life(fadetime = CHAT_MESSAGE_EOL_FADE) + animate(message, alpha = 0, time = fadetime, flags = ANIMATION_PARALLEL) + QDEL_IN(src, fadetime) + +/** + * Creates a message overlay at a defined location for a given speaker + * + * Arguments: + * * speaker - The atom who is saying this message + * * message_language - The language that the message is said in + * * raw_message - The text content of the message + * * spans - Additional classes to be added to the message + * * message_mode - Bitflags relating to the mode of the message + */ +/mob/proc/create_chat_message(atom/movable/speaker, datum/language/message_language, raw_message, list/spans, message_mode) + // Ensure the list we are using, if present, is a copy so we don't modify the list provided to us + spans = spans?.Copy() + + // Check for virtual speakers (aka hearing a message through a radio) + var/atom/movable/originalSpeaker = speaker + if (istype(speaker, /atom/movable/virtualspeaker)) + var/atom/movable/virtualspeaker/v = speaker + speaker = v.source + spans |= "virtual-speaker" + + // Ignore virtual speaker (most often radio messages) from ourself + if (originalSpeaker != src && speaker == src) + return + + // Display visual above source + new /datum/chatmessage(lang_treat(speaker, message_language, raw_message, spans, null, TRUE), speaker, src, spans) + + +// Tweak these defines to change the available color ranges +#define CM_COLOR_SAT_MIN 0.6 +#define CM_COLOR_SAT_MAX 0.7 +#define CM_COLOR_LUM_MIN 0.65 +#define CM_COLOR_LUM_MAX 0.75 + +/** + * Gets a color for a name, will return the same color for a given string consistently within a round.atom + * + * Note that this proc aims to produce pastel-ish colors using the HSL colorspace. These seem to be favorable for displaying on the map. + * + * Arguments: + * * name - The name to generate a color for + * * sat_shift - A value between 0 and 1 that will be multiplied against the saturation + * * lum_shift - A value between 0 and 1 that will be multiplied against the luminescence + */ +/datum/chatmessage/proc/colorize_string(name, sat_shift = 1, lum_shift = 1) + // seed to help randomness + var/static/rseed = rand(1,26) + + // get hsl using the selected 6 characters of the md5 hash + var/hash = copytext(md5(name + GLOB.round_id), rseed, rseed + 6) + var/h = hex2num(copytext(hash, 1, 3)) * (360 / 255) + var/s = (hex2num(copytext(hash, 3, 5)) >> 2) * ((CM_COLOR_SAT_MAX - CM_COLOR_SAT_MIN) / 63) + CM_COLOR_SAT_MIN + var/l = (hex2num(copytext(hash, 5, 7)) >> 2) * ((CM_COLOR_LUM_MAX - CM_COLOR_LUM_MIN) / 63) + CM_COLOR_LUM_MIN + + // adjust for shifts + s *= clamp(sat_shift, 0, 1) + l *= clamp(lum_shift, 0, 1) + + // convert to rgb + var/h_int = round(h/60) // mapping each section of H to 60 degree sections + var/c = (1 - abs(2 * l - 1)) * s + var/x = c * (1 - abs((h / 60) % 2 - 1)) + var/m = l - c * 0.5 + x = (x + m) * 255 + c = (c + m) * 255 + m *= 255 + switch(h_int) + if(0) + return "#[num2hex(c, 2)][num2hex(x, 2)][num2hex(m, 2)]" + if(1) + return "#[num2hex(x, 2)][num2hex(c, 2)][num2hex(m, 2)]" + if(2) + return "#[num2hex(m, 2)][num2hex(c, 2)][num2hex(x, 2)]" + if(3) + return "#[num2hex(m, 2)][num2hex(x, 2)][num2hex(c, 2)]" + if(4) + return "#[num2hex(x, 2)][num2hex(m, 2)][num2hex(c, 2)]" + if(5) + return "#[num2hex(c, 2)][num2hex(m, 2)][num2hex(x, 2)]" diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 984a298bef58..a5fdae4d91ff 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -50,8 +50,9 @@ parent = raw_args[1] var/list/arguments = raw_args.Copy(2) if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE) + stack_trace("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]") qdel(src, TRUE, TRUE) - CRASH("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]") + return _JoinParent(parent) diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm index e65eb50a2dd9..b0f01c1ae59c 100644 --- a/code/datums/components/caltrop.dm +++ b/code/datums/components/caltrop.dm @@ -33,6 +33,8 @@ return //gravity checking only our parent would prevent us from triggering they're using magboots / other gravity assisting items that would cause them to still touch us. if(H.buckled) //if they're buckled to something, that something should be checked instead. return + if(!(H.mobility_flags & MOBILITY_STAND)) //if were not standing we cant step on the caltrop + return var/picked_def_zone = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) var/obj/item/bodypart/O = H.get_bodypart(picked_def_zone) diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm index 77e8e63fbfd1..068769e36dbf 100644 --- a/code/datums/components/crafting/crafting.dm +++ b/code/datums/components/crafting/crafting.dm @@ -326,7 +326,7 @@ cur_subcategory = subcats[1] else cur_subcategory = CAT_NONE - ui = new(user, src, ui_key, "personal_crafting", "Crafting Menu", 700, 800, master_ui, state) + ui = new(user, src, ui_key, "PersonalCrafting", "Crafting Menu", 700, 800, master_ui, state) ui.open() /datum/component/personal_crafting/ui_data(mob/user) diff --git a/code/datums/components/crafting/recipes.dm b/code/datums/components/crafting/recipes.dm index 9ca295821644..8465843492fa 100644 --- a/code/datums/components/crafting/recipes.dm +++ b/code/datums/components/crafting/recipes.dm @@ -375,14 +375,14 @@ result = /obj/item/clothing/head/lizard time = 10 reqs = list(/obj/item/organ/tail/lizard = 1) - category = CAT_MISC + category = CAT_CLOTHING /datum/crafting_recipe/lizardhat_alternate name = "lizard Cloche Hat" result = /obj/item/clothing/head/lizard time = 10 reqs = list(/obj/item/stack/sheet/animalhide/lizard = 1) - category = CAT_MISC + category = CAT_CLOTHING /datum/crafting_recipe/kittyears name = "Kitty Ears" @@ -390,7 +390,7 @@ time = 10 reqs = list(/obj/item/organ/tail/cat = 1, /obj/item/organ/ears/cat = 1) - category = CAT_MISC + category = CAT_CLOTHING /datum/crafting_recipe/skateboard name = "Skateboard" @@ -482,6 +482,12 @@ result = /obj/item/stack/tile/carpet/black/fifty category = CAT_MISC +/datum/crafting_recipe/curtain + name = "Curtains" + reqs = list(/obj/item/stack/sheet/cloth = 4, /obj/item/stack/rods = 1) + result = /obj/structure/curtain/cloth + category = CAT_MISC + /datum/crafting_recipe/showercurtain name = "Shower Curtains" reqs = list(/obj/item/stack/sheet/cloth = 2, /obj/item/stack/sheet/plastic = 2, /obj/item/stack/rods = 1) @@ -812,3 +818,22 @@ /obj/item/stack/sticky_tape = 1) result = /obj/item/clothing/gloves/tackler/offbrand category = CAT_CLOTHING + +/* Wasp edit - Normal BoH +/datum/crafting_recipe/boh + name = "Bag of Holding" + reqs = list( + /obj/item/bag_of_holding_inert = 1, + /obj/item/assembly/signaler/anomaly/bluespace = 1) + result = /obj/item/storage/backpack/holding + category = CAT_CLOTHING +*/ + +/datum/crafting_recipe/ipickaxe + name = "Improvised Pickaxe" + reqs = list( + /obj/item/crowbar = 1, + /obj/item/kitchen/knife = 1, + /obj/item/stack/sticky_tape = 1) + result = /obj/item/pickaxe/improvised + category = CAT_MISC diff --git a/code/datums/components/embedded.dm b/code/datums/components/embedded.dm index 0ac5b4e35e83..d0159cd4a47b 100644 --- a/code/datums/components/embedded.dm +++ b/code/datums/components/embedded.dm @@ -3,12 +3,12 @@ and when it impacts and meets the requirements to stick into something, it instantiates an embedded component. Once the item falls out, the component is destroyed, while the element survives to embed another day. - There are 2 different things that can be embedded presently: humans, and closed turfs (see: walls) + There are 2 different things that can be embedded presently: carbons, and closed turfs (see: walls) - - Human embedding has all the classical embedding behavior, and tracks more events and signals. The main behaviors and hooks to look for are: + - Carbon embedding has all the classical embedding behavior, and tracks more events and signals. The main behaviors and hooks to look for are: -- Every process tick, there is a chance to randomly proc pain, controlled by pain_chance. There may also be a chance for the object to fall out randomly, per fall_chance -- Every time the mob moves, there is a chance to proc jostling pain, controlled by jostle_chance (and only 50% as likely if the mob is walking or crawling) - -- Various signals hooking into human topic() and the embed removal surgery in order to handle removals. + -- Various signals hooking into carbon topic() and the embed removal surgery in order to handle removals. - Turf embedding is much simpler. All we do here is draw an overlay of the item's inhand on the turf, hide the item, and create an HTML link in the turf's inspect that allows you to rip the item out. There's nothing dynamic about this, so far less checks. @@ -24,7 +24,7 @@ Stickables differ from embeds in the following ways: -- Text descriptors use phrasing like "X is stuck to Y" rather than "X is embedded in Y" -- There is no slicing sound on impact - -- All damage checks and bloodloss are skipped for humans + -- All damage checks and bloodloss are skipped for carbons -- Pointy objects create sparks when embedding into a turf */ @@ -32,7 +32,7 @@ /datum/component/embedded dupe_mode = COMPONENT_DUPE_ALLOWED - var/obj/item/bodypart/L + var/obj/item/bodypart/limb var/obj/item/weapon // all of this stuff is explained in _DEFINES/combat.dm @@ -47,6 +47,7 @@ var/jostle_chance var/jostle_pain_mult var/pain_stam_pct + var/embed_chance_turf_mod ///if both our pain multiplier and jostle pain multiplier are 0, we're harmless and can omit most of the damage related stuff var/harmful @@ -54,6 +55,7 @@ /datum/component/embedded/Initialize(obj/item/I, datum/thrownthing/throwingdatum, + obj/item/bodypart/part, embed_chance = EMBED_CHANCE, fall_chance = EMBEDDED_ITEM_FALLOUT, pain_chance = EMBEDDED_PAIN_CHANCE, @@ -64,11 +66,14 @@ ignore_throwspeed_threshold = FALSE, jostle_chance = EMBEDDED_JOSTLE_CHANCE, jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER, - pain_stam_pct = EMBEDDED_PAIN_STAM_PCT) + pain_stam_pct = EMBEDDED_PAIN_STAM_PCT, + embed_chance_turf_mod = EMBED_CHANCE_TURF_MOD) - if((!ishuman(parent) && !isclosedturf(parent)) || !isitem(I)) + if((!iscarbon(parent) && !isclosedturf(parent)) || !isitem(I)) return COMPONENT_INCOMPATIBLE + if(part) + limb = part src.embed_chance = embed_chance src.fall_chance = fall_chance src.pain_chance = pain_chance @@ -80,36 +85,37 @@ src.jostle_chance = jostle_chance src.jostle_pain_mult = jostle_pain_mult src.pain_stam_pct = pain_stam_pct + src.embed_chance_turf_mod = embed_chance_turf_mod src.weapon = I - if(src.pain_mult || src.jostle_pain_mult) + if(!weapon.isEmbedHarmless()) harmful = TRUE - if(ishuman(parent)) - initHuman() + if(iscarbon(parent)) + initCarbon() else if(isclosedturf(parent)) initTurf(throwingdatum) /datum/component/embedded/RegisterWithParent() - if(ishuman(parent)) + if(iscarbon(parent)) RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/jostleCheck) - RegisterSignal(parent, COMSIG_HUMAN_EMBED_RIP, .proc/ripOutHuman) - RegisterSignal(parent, COMSIG_HUMAN_EMBED_REMOVAL, .proc/safeRemoveHuman) + RegisterSignal(parent, COMSIG_CARBON_EMBED_RIP, .proc/ripOutCarbon) + RegisterSignal(parent, COMSIG_CARBON_EMBED_REMOVAL, .proc/safeRemoveCarbon) else if(isclosedturf(parent)) RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examineTurf) + RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/itemMoved) /datum/component/embedded/UnregisterFromParent() - if(ishuman(parent)) - UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_HUMAN_EMBED_RIP, COMSIG_HUMAN_EMBED_REMOVAL)) - else if(isclosedturf(parent)) - UnregisterSignal(parent, COMSIG_PARENT_EXAMINE) + UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_CARBON_EMBED_RIP, COMSIG_CARBON_EMBED_REMOVAL, COMSIG_PARENT_EXAMINE)) /datum/component/embedded/process() - if(ishuman(parent)) - processHuman() + if(iscarbon(parent)) + processCarbon() /datum/component/embedded/Destroy() + if(weapon) + UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING)) if(overlay) var/atom/A = parent A.cut_overlay(overlay, TRUE) @@ -121,29 +127,33 @@ /////////////HUMAN PROCS//////////////// //////////////////////////////////////// -/// Set up an instance of embedding for a human. This is basically an extension of Initialize() so not much to say -/datum/component/embedded/proc/initHuman() +/// Set up an instance of embedding for a carbon. This is basically an extension of Initialize() so not much to say +/datum/component/embedded/proc/initCarbon() START_PROCESSING(SSdcs, src) - var/mob/living/carbon/human/victim = parent - L = pick(victim.bodyparts) - L.embedded_objects |= weapon // on the inside... on the inside... + var/mob/living/carbon/victim = parent + if(!istype(limb)) + limb = pick(victim.bodyparts) + + limb.embedded_objects |= weapon // on the inside... on the inside... weapon.forceMove(victim) + RegisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING), .proc/byeItemCarbon) if(harmful) - victim.visible_message("[weapon] embeds itself in [victim]'s [L.name]!","[weapon] embeds itself in your [L.name]!") + victim.visible_message("[weapon] embeds itself in [victim]'s [limb.name]!",ignored_mobs=victim) + to_chat(victim, "[weapon] embeds itself in your [limb.name]!") victim.throw_alert("embeddedobject", /obj/screen/alert/embeddedobject) playsound(victim,'sound/weapons/bladeslice.ogg', 40) weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody! var/damage = weapon.w_class * impact_pain_mult - L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) + limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded) else - victim.visible_message("[weapon] sticks itself to [victim]'s [L.name]!","[weapon] sticks itself to your [L.name]!") - + victim.visible_message("[weapon] sticks itself to [victim]'s [limb.name]!",ignored_mobs=victim) + to_chat(victim, "[weapon] sticks itself to your [limb.name]!") -/// Called every time a human with a harmful embed moves, rolling a chance for the item to cause pain. The chance is halved if the human is crawling or walking. +/// Called every time a carbon with a harmful embed moves, rolling a chance for the item to cause pain. The chance is halved if the carbon is crawling or walking. /datum/component/embedded/proc/jostleCheck() - var/mob/living/carbon/human/victim = parent + var/mob/living/carbon/victim = parent var/chance = jostle_chance if(victim.m_intent == MOVE_INTENT_WALK || !(victim.mobility_flags & MOBILITY_STAND)) @@ -151,54 +161,73 @@ if(harmful && prob(chance)) var/damage = weapon.w_class * jostle_pain_mult - L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) - to_chat(victim, "[weapon] embedded in your [L.name] jostles and stings!") + limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) + to_chat(victim, "[weapon] embedded in your [limb.name] jostles and stings!") -/// Called when then item randomly falls out of a human. This handles the damage and descriptors, then calls safe_remove() -/datum/component/embedded/proc/fallOutHuman() - var/mob/living/carbon/human/victim = parent +/// Called when then item randomly falls out of a carbon. This handles the damage and descriptors, then calls safe_remove() +/datum/component/embedded/proc/fallOutCarbon() + var/mob/living/carbon/victim = parent if(harmful) var/damage = weapon.w_class * remove_pain_mult - L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) - victim.visible_message("[weapon] falls out of [victim.name]'s [L.name]!","[weapon] falls out of your [L.name]!") + limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) + victim.visible_message("[weapon] falls out of [victim.name]'s [limb.name]!", ignored_mobs=victim) + to_chat(victim, "[weapon] falls out of your [limb.name]!") else - victim.visible_message("[weapon] falls off of [victim.name]'s [L.name]!","[weapon] falls off of your [L.name]!") + victim.visible_message("[weapon] falls off of [victim.name]'s [limb.name]!", ignored_mobs=victim) + to_chat(victim, "[weapon] falls off of your [limb.name]!") - safeRemoveHuman() + safeRemoveCarbon() -/// Called when a human with an object embedded/stuck to them inspects themselves and clicks the appropriate link to begin ripping the item out. This handles the ripping attempt, descriptors, and dealing damage, then calls safe_remove() -/datum/component/embedded/proc/ripOutHuman() - var/mob/living/carbon/human/victim = parent +/// Called when a carbon with an object embedded/stuck to them inspects themselves and clicks the appropriate link to begin ripping the item out. This handles the ripping attempt, descriptors, and dealing damage, then calls safe_remove() +/datum/component/embedded/proc/ripOutCarbon(datum/source, obj/item/I, obj/item/bodypart/limb) + if(I != weapon || src.limb != limb) + return + + var/mob/living/carbon/victim = parent var/time_taken = rip_time * weapon.w_class - victim.visible_message("[victim] attempts to remove [weapon] from [victim.p_their()] [L.name].","You attempt to remove [weapon] from your [L.name]... (It will take [DisplayTimeText(time_taken)].)") + victim.visible_message("[victim] attempts to remove [weapon] from [victim.p_their()] [limb.name].","You attempt to remove [weapon] from your [limb.name]... (It will take [DisplayTimeText(time_taken)].)") if(do_after(victim, time_taken, target = victim)) - if(!weapon || !L || weapon.loc != victim || !(weapon in L.embedded_objects)) + if(!weapon || !limb || weapon.loc != victim || !(weapon in limb.embedded_objects)) qdel(src) + return if(harmful) var/damage = weapon.w_class * remove_pain_mult - L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) //It hurts to rip it out, get surgery you dingus. + limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) //It hurts to rip it out, get surgery you dingus. victim.emote("scream") - victim.visible_message("[victim] successfully rips [weapon] out of [victim.p_their()] [L.name]!", "You successfully remove [weapon] from your [L.name].") + victim.visible_message("[victim] successfully rips [weapon] out of [victim.p_their()] [limb.name]!", "You successfully remove [weapon] from your [limb.name].") else - victim.visible_message("[victim] successfully rips [weapon] off of [victim.p_their()] [L.name]!", "You successfully remove [weapon] from your [L.name].") + victim.visible_message("[victim] successfully rips [weapon] off of [victim.p_their()] [limb.name]!", "You successfully remove [weapon] from your [limb.name].") - safeRemoveHuman(TRUE) + safeRemoveCarbon(TRUE) -/// This proc handles the final step and actual removal of an embedded/stuck item from a human, whether or not it was actually removed safely. +/// This proc handles the final step and actual removal of an embedded/stuck item from a carbon, whether or not it was actually removed safely. /// Pass TRUE for to_hands if we want it to go to the victim's hands when they pull it out -/datum/component/embedded/proc/safeRemoveHuman(to_hands) - var/mob/living/carbon/human/victim = parent - L.embedded_objects -= weapon +/datum/component/embedded/proc/safeRemoveCarbon(to_hands) + var/mob/living/carbon/victim = parent + limb.embedded_objects -= weapon - if(!victim) - weapon.forceMove(get_turf(weapon)) + UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING)) // have to unhook these here so they don't also register as having disappeared + + if(!weapon) + if(!victim.has_embedded_objects()) + victim.clear_alert("embeddedobject") + SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded") + qdel(src) + return + + if(weapon.unembedded()) // if it deleted itself + weapon = null + if(!victim.has_embedded_objects()) + victim.clear_alert("embeddedobject") + SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded") qdel(src) + return if(to_hands) victim.put_in_hands(weapon) @@ -211,25 +240,46 @@ qdel(src) -/// Items embedded/stuck to humans both check whether they randomly fall out (if applicable), as well as if the target mob and limb still exists. -/// Items harmfully embedded in humans have an additional check for random pain (if applicable) -/datum/component/embedded/proc/processHuman() - var/mob/living/carbon/human/victim = parent +/// Something deleted or moved our weapon while it was embedded, how rude! +/datum/component/embedded/proc/byeItemCarbon() + var/mob/living/carbon/victim = parent + limb.embedded_objects -= weapon + UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING)) + + if(victim) + to_chat(victim, "\The [weapon] that was embedded in your [limb.name] disappears!") + if(!victim.has_embedded_objects()) + victim.clear_alert("embeddedobject") + SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded") + weapon = null + qdel(src) + - if(!victim || !L) // in case the victim and/or their limbs exploded (say, due to a sticky bomb) +/// Items embedded/stuck to carbons both check whether they randomly fall out (if applicable), as well as if the target mob and limb still exists. +/// Items harmfully embedded in carbons have an additional check for random pain (if applicable) +/datum/component/embedded/proc/processCarbon() + var/mob/living/carbon/victim = parent + + if(!victim || !limb) // in case the victim and/or their limbs exploded (say, due to a sticky bomb) weapon.forceMove(get_turf(weapon)) qdel(src) if(victim.stat == DEAD) return - if(harmful && prob(pain_chance)) - var/damage = weapon.w_class * pain_mult - L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) - to_chat(victim, "[weapon] embedded in your [L.name] hurts!") + var/damage = weapon.w_class * pain_mult + var/chance = pain_chance + if(pain_stam_pct && victim.stam_paralyzed) //if it's a less-lethal embed, give them a break if they're already stamcritted + chance *= 0.3 + damage *= 0.7 + + if(harmful && prob(chance)) + limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) + to_chat(victim, "[weapon] embedded in your [limb.name] hurts!") if(prob(fall_chance)) - fallOutHuman() + fallOutCarbon() + //////////////////////////////////////// @@ -299,6 +349,7 @@ if(do_after(us, 30, target = parent)) us.put_in_hands(weapon) + weapon.unembedded() qdel(src) @@ -306,4 +357,5 @@ /datum/component/embedded/proc/itemMoved() weapon.invisibility = initial(weapon.invisibility) weapon.visible_message("[weapon] falls loose from [parent].") + weapon.unembedded() qdel(src) diff --git a/code/datums/components/fantasy/suffixes.dm b/code/datums/components/fantasy/suffixes.dm index 0dd37edb6f47..4b2cea028a9a 100644 --- a/code/datums/components/fantasy/suffixes.dm +++ b/code/datums/components/fantasy/suffixes.dm @@ -143,7 +143,7 @@ var/obj/projectile/picked_projectiletype = pickweight(weighted_projectile_types) var/obj/item/master = comp.parent - comp.appliedComponents += master.AddComponent(/datum/component/shrapnel, picked_projectiletype) + comp.appliedComponents += master.AddComponent(/datum/component/mirv, picked_projectiletype) return "[newName] of [initial(picked_projectiletype.name)] shrapnel" /datum/fantasy_affix/strength diff --git a/code/datums/components/gps.dm b/code/datums/components/gps.dm index d3f1a91b1178..200ec2b956b6 100644 --- a/code/datums/components/gps.dm +++ b/code/datums/components/gps.dm @@ -89,7 +89,7 @@ GLOBAL_LIST_EMPTY(GPS_list) // Variable window height, depending on how many GPS units there are // to show, clamped to relatively safe range. var/gps_window_height = clamp(325 + GLOB.GPS_list.len * 14, 325, 700) - ui = new(user, src, ui_key, "gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height + ui = new(user, src, ui_key, "Gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height ui.open() ui.set_autoupdate(state = updating) diff --git a/code/datums/components/hot_ice.dm b/code/datums/components/hot_ice.dm index 0ccb935f7617..9c0c4730df5d 100644 --- a/code/datums/components/hot_ice.dm +++ b/code/datums/components/hot_ice.dm @@ -12,7 +12,7 @@ RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/attackby_react) RegisterSignal(parent, COMSIG_ATOM_FIRE_ACT, .proc/flame_react) -/datum/component/thermite/UnregisterFromParent() +/datum/component/hot_ice/UnregisterFromParent() UnregisterSignal(parent, COMSIG_PARENT_ATTACKBY) UnregisterSignal(parent, COMSIG_ATOM_FIRE_ACT) @@ -28,7 +28,7 @@ qdel(parent) /datum/component/hot_ice/proc/flame_react(datum/source, exposed_temperature, exposed_volume) - if(exposed_temperature > 300) + if(exposed_temperature > T0C + 100) hot_ice_melt() /datum/component/hot_ice/proc/attackby_react(datum/source, obj/item/thing, mob/user, params) diff --git a/code/datums/components/shrapnel.dm b/code/datums/components/mirv.dm similarity index 69% rename from code/datums/components/shrapnel.dm rename to code/datums/components/mirv.dm index f549fbbb3429..bf9413a5c041 100644 --- a/code/datums/components/shrapnel.dm +++ b/code/datums/components/mirv.dm @@ -1,33 +1,36 @@ -/datum/component/shrapnel +/datum/component/mirv var/projectile_type var/radius // shoots a projectile for every turf on this radius from the hit target var/override_projectile_range -/datum/component/shrapnel/Initialize(projectile_type, radius=1, override_projectile_range) - if(!isgun(parent) && !ismachinery(parent) && !isstructure(parent)) +/datum/component/mirv/Initialize(projectile_type, radius=1, override_projectile_range) + if(!isgun(parent) && !ismachinery(parent) && !isstructure(parent) && !isgrenade(parent)) return COMPONENT_INCOMPATIBLE src.projectile_type = projectile_type src.radius = radius src.override_projectile_range = override_projectile_range -/datum/component/shrapnel/RegisterWithParent() + if(isgrenade(parent)) + parent.AddComponent(/datum/component/pellet_cloud, projectile_type=projectile_type) + +/datum/component/mirv/RegisterWithParent() if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit) -/datum/component/shrapnel/UnregisterFromParent() +/datum/component/mirv/UnregisterFromParent() UnregisterSignal(parent, list(COMSIG_PROJECTILE_ON_HIT)) -/datum/component/shrapnel/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle) +/datum/component/mirv/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle) do_shrapnel(firer, target) -/datum/component/shrapnel/proc/do_shrapnel(mob/firer, atom/target) +/datum/component/mirv/proc/do_shrapnel(mob/firer, atom/target) if(radius < 1) return var/turf/target_turf = get_turf(target) for(var/turf/shootat_turf in RANGE_TURFS(radius, target) - RANGE_TURFS(radius-1, target)) - var/obj/projectile/P = new projectile_type(target_turf) + var/obj/projectile/P = new projectile_type(target_turf) //Shooting Code: P.range = radius+1 if(override_projectile_range) diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm index 474d5049c5d9..435fabdf7f83 100644 --- a/code/datums/components/orbiter.dm +++ b/code/datums/components/orbiter.dm @@ -59,7 +59,9 @@ orbiter.orbiting = src RegisterSignal(orbiter, COMSIG_MOVABLE_MOVED, .proc/orbiter_move_react) SEND_SIGNAL(parent, COMSIG_ATOM_ORBIT_BEGIN, orbiter) + var/matrix/initial_transform = matrix(orbiter.transform) + orbiters[orbiter] = initial_transform // Head first! if(pre_rotation) @@ -76,8 +78,6 @@ orbiter.SpinAnimation(rotation_speed, -1, clockwise, rotation_segments, parallel = FALSE) - //we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit - orbiter.transform = initial_transform orbiter.forceMove(get_turf(parent)) to_chat(orbiter, "Now orbiting [parent].") @@ -87,6 +87,8 @@ UnregisterSignal(orbiter, COMSIG_MOVABLE_MOVED) SEND_SIGNAL(parent, COMSIG_ATOM_ORBIT_STOP, orbiter) orbiter.SpinAnimation(0, 0) + if(istype(orbiters[orbiter],/matrix)) //This is ugly. + orbiter.transform = orbiters[orbiter] orbiters -= orbiter orbiter.stop_orbit(src) orbiter.orbiting = null diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm new file mode 100644 index 000000000000..b0e9a3fccccd --- /dev/null +++ b/code/datums/components/pellet_cloud.dm @@ -0,0 +1,236 @@ +/* + * This component is used when you want to create a bunch of shrapnel or projectiles (say, shrapnel from a fragmentation grenade, or buckshot from a shotgun) from a central point, + * without necessarily printing a separate message for every single impact. This component should be instantiated right when you need it (like the moment of firing), then activated + * by signal. + * + * Pellet cloud currently works on two classes of sources: directed (ammo casings), and circular (grenades, landmines). + * -Directed: This means you're shooting multiple pellets, like buckshot. If an ammo casing is defined as having multiple pellets, it will automatically create a pellet cloud + * and call COMSIG_PELLET_CLOUD_INIT (see [/obj/item/ammo_casing/proc/fire_casing]). Thus, the only projectiles fired will be the ones fired here. + * The magnitude var controls how many pellets are created. + * -Circular: This results in a big spray of shrapnel flying all around the detonation point when the grenade fires COMSIG_GRENADE_PRIME or landmine triggers COMSIG_MINE_TRIGGERED. + * The magnitude var controls how big the detonation radius is (the bigger the magnitude, the more shrapnel is created). Grenades can be covered with bodies to reduce shrapnel output. + * + * Once all of the fired projectiles either hit a target or disappear due to ranging out/whatever else, we resolve the list of all the things we hit and print aggregate messages so we get + * one "You're hit by 6 buckshot pellets" vs 6x "You're hit by the buckshot blah blah" messages. + * + * Note that this is how all guns handle shooting ammo casings with multiple pellets, in case such a thing comes up. +*/ + +/datum/component/pellet_cloud + var/projectile_type /// What's the projectile path of the shrapnel we're shooting? + + var/num_pellets /// How many shrapnel projectiles are we responsible for tracking? May be reduced for grenades if someone dives on top of it. Defined by ammo casing for casings, derived from magnitude otherwise + var/radius = 4 /// For grenades/landmines, how big is the radius of turfs we're targeting? Note this does not effect the projectiles range, only how many we generate + + var/list/pellets = list() /// The list of pellets we're responsible for tracking, once these are all accounted for, we finalize. + var/list/targets_hit = list() /// An associated list with the atom hit as the key and how many pellets they've eaten for the value, for printing aggregate messages + var/list/bodies /// For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs()] + var/list/purple_hearts /// For grenades, tracking people who die covering a grenade for achievement purposes, see [/datum/component/pellet_cloud/proc/handle_martyrs()] + + var/terminated /// how many pellets ranged out without hitting anything + var/hits /// how many pellets impacted something + + var/mob/living/shooter /// for if we're an ammo casing being fired + +/datum/component/pellet_cloud/Initialize(projectile_type=/obj/item/shrapnel, magnitude=5) + if(!isammocasing(parent) && !isgrenade(parent) && !islandmine(parent)) + return COMPONENT_INCOMPATIBLE + + if(magnitude < 1) + stack_trace("Invalid magnitude [magnitude] < 1 on pellet_cloud, parent: [parent]") + magnitude = 1 + + src.projectile_type = projectile_type + + if(isammocasing(parent)) + num_pellets = magnitude + else if(isgrenade(parent) || islandmine(parent)) + radius = magnitude + +/datum/component/pellet_cloud/Destroy(force, silent) + purple_hearts = null + pellets = null + targets_hit = null + bodies = null + return ..() + +/datum/component/pellet_cloud/RegisterWithParent() + if(isammocasing(parent)) + RegisterSignal(parent, COMSIG_PELLET_CLOUD_INIT, .proc/create_casing_pellets) + else if(isgrenade(parent)) + RegisterSignal(parent, COMSIG_GRENADE_ARMED, .proc/grenade_armed) + RegisterSignal(parent, COMSIG_GRENADE_PRIME, .proc/create_blast_pellets) + else if(islandmine(parent)) + RegisterSignal(parent, COMSIG_MINE_TRIGGERED, .proc/create_blast_pellets) + +/datum/component/pellet_cloud/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_PELLET_CLOUD_INIT, COMSIG_GRENADE_PRIME, COMSIG_GRENADE_ARMED, COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_UNCROSSED, COMSIG_MINE_TRIGGERED, COMSIG_ITEM_DROPPED)) + +/** + * create_casing_pellets() is for directed pellet clouds for ammo casings that have multiple pellets (buckshot and scatter lasers for instance) + * + * Honestly this is mostly just a rehash of [/obj/item/ammo_casing/proc/fire_casing()] for pellet counts > 1, except this lets us tamper with the pellets and hook onto them for tracking purposes. + * The arguments really don't matter, this proc is triggered by COMSIG_PELLET_CLOUD_INIT which is only for this really, it's just a big mess of the state vars we need for doing the stuff over here. + */ +/datum/component/pellet_cloud/proc/create_casing_pellets(obj/item/ammo_casing/A, atom/target, mob/living/user, fired_from, randomspread, spread, zone_override, params, distro) + shooter = user + var/targloc = get_turf(target) + if(!zone_override) + zone_override = shooter.zone_selected + + for(var/i in 1 to num_pellets) + A.ready_proj(target, user, SUPPRESSED_VERY, zone_override, fired_from) + if(distro) + if(randomspread) + spread = round((rand() - 0.5) * distro) + else //Smart spread + spread = round((i / num_pellets - 0.5) * distro) + + RegisterSignal(A.BB, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/pellet_hit) + RegisterSignal(A.BB, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PARENT_QDELETING), .proc/pellet_range) + pellets += A.BB + if(!A.throw_proj(target, targloc, shooter, params, spread)) + return + if(i != num_pellets) + A.newshot() + +/** + * create_blast_pellets() is for when we have a central point we want to shred the surroundings of with a ring of shrapnel, namely frag grenades and landmines. + * + * Note that grenades have extra handling for someone throwing themselves/being thrown on top of it, while landmines do not (obviously, it's a landmine!). See [/datum/component/pellet_cloud/proc/handle_martyrs()] + */ +/datum/component/pellet_cloud/proc/create_blast_pellets() + var/atom/A = parent + var/total_pellets_absorbed = 0 + + if(isgrenade(parent)) // handle_martyrs can reduce the radius and thus the number of pellets we produce if someone dives on top of a frag grenade + total_pellets_absorbed = handle_martyrs() // note that we can modify radius in this proc + + if(radius < 1) + return + + var/list/all_the_turfs_were_gonna_lacerate = RANGE_TURFS(radius, A) - RANGE_TURFS(radius-1, A) + num_pellets = all_the_turfs_were_gonna_lacerate.len + total_pellets_absorbed + + for(var/T in all_the_turfs_were_gonna_lacerate) + var/turf/shootat_turf = T + pew(shootat_turf) + +/** + * handle_martyrs() is used for grenades that shoot shrapnel to check if anyone threw themselves/were thrown on top of the grenade, thus absorbing a good chunk of the shrapnel + * + * Between the time the grenade is armed and the actual detonation, we set var/list/bodies to the list of mobs currently on the new tile, as if the grenade landed on top of them, tracking if any of them move off the tile and removing them from the "under" list + * Once the grenade detonates, handle_martyrs() is called and gets all the new mobs on the tile, and add the ones not in var/list/bodies to var/list/martyrs + * We then iterate through the martyrs and reduce the shrapnel magnitude for each mob on top of it, shredding each of them with some of the shrapnel they helped absorb. This can snuff out all of the shrapnel if there's enough bodies + * + * Note we track anyone who's alive and client'd when they get shredded in var/list/purple_hearts, for achievement checking later + */ +/datum/component/pellet_cloud/proc/handle_martyrs() + var/list/martyrs = list() + for(var/mob/living/body in get_turf(parent)) + if(!(body in bodies)) + martyrs += body // promoted from a corpse to a hero + + var/magnitude_absorbed + var/total_pellets_absorbed + + for(var/M in martyrs) + var/mob/living/martyr = M + if(radius > 4) + martyr.visible_message("[martyr] heroically covers \the [parent] with [martyr.p_their()] body, absorbing a load of the shrapnel!", "You heroically cover \the [parent] with your body, absorbing a load of the shrapnel!") + magnitude_absorbed += round(radius * 0.5) + else if(radius >= 2) + martyr.visible_message("[martyr] heroically covers \the [parent] with [martyr.p_their()] body, absorbing some of the shrapnel!", "You heroically cover \the [parent] with your body, absorbing some of the shrapnel!") + magnitude_absorbed += 2 + else + martyr.visible_message("[martyr] heroically covers \the [parent] with [martyr.p_their()] body, snuffing out the shrapnel!", "You heroically cover \the [parent] with your body, snuffing out the shrapnel!") + magnitude_absorbed = radius + + var/pellets_absorbed = (radius ** 2) - ((radius - magnitude_absorbed - 1) ** 2) + radius -= magnitude_absorbed + total_pellets_absorbed += round(pellets_absorbed/2) + + if(martyr.stat != DEAD && martyr.client) + LAZYADD(purple_hearts, martyr) + + for(var/i in 1 to round(pellets_absorbed/2)) + pew(martyr) + + if(radius < 1) + break + + return total_pellets_absorbed + +///One of our pellets hit something, record what it was and check if we're done (terminated == num_pellets) +/datum/component/pellet_cloud/proc/pellet_hit(obj/projectile/P, atom/movable/firer, atom/target, Angle) + pellets -= P + terminated++ + hits++ + targets_hit[target]++ + UnregisterSignal(P, list(COMSIG_PARENT_QDELETING, COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PROJECTILE_SELF_ON_HIT)) + if(terminated == num_pellets) + finalize() + +///One of our pellets disappeared due to hitting their max range (or just somehow got qdel'd), remove it from our list and check if we're done (terminated == num_pellets) +/datum/component/pellet_cloud/proc/pellet_range(obj/projectile/P) + pellets -= P + terminated++ + UnregisterSignal(P, list(COMSIG_PARENT_QDELETING, COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PROJECTILE_SELF_ON_HIT)) + if(terminated == num_pellets) + finalize() + +/// Minor convenience function for creating each shrapnel piece with circle explosions, mostly stolen from the MIRV component +/datum/component/pellet_cloud/proc/pew(atom/target, spread=0) + var/obj/projectile/P = new projectile_type(get_turf(parent)) + + //Shooting Code: + P.spread = spread + P.original = target + P.fired_from = parent + P.firer = parent // don't hit ourself that would be really annoying + P.permutated += parent // don't hit the target we hit already with the flak + P.suppressed = SUPPRESSED_VERY // set the projectiles to make no message so we can do our own aggregate message + P.preparePixelProjectile(target, parent) + RegisterSignal(P, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/pellet_hit) + RegisterSignal(P, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PARENT_QDELETING), .proc/pellet_range) + pellets += P + P.fire() + +///All of our pellets are accounted for, time to go target by target and tell them how many things they got hit by. +/datum/component/pellet_cloud/proc/finalize() + var/obj/projectile/P = projectile_type + var/proj_name = initial(P.name) + + for(var/atom/target in targets_hit) + var/num_hits = targets_hit[target] + if(num_hits > 1) + target.visible_message("[target] is hit by [num_hits] [proj_name]s!", null, null, COMBAT_MESSAGE_RANGE, target) + to_chat(target, "You're hit by [num_hits] [proj_name]s!") + else + target.visible_message("[target] is hit by a [proj_name]!", null, null, COMBAT_MESSAGE_RANGE, target) + to_chat(target, "You're hit by [num_hits] [proj_name]s!") + + for(var/M in purple_hearts) + var/mob/living/martyr = M + if(martyr.stat == DEAD && martyr.client) + martyr.client.give_award(/datum/award/achievement/misc/lookoutsir, martyr) + + qdel(parent) + qdel(src) + +/// Look alive, we're armed! Now we start watching to see if anyone's covering us +/datum/component/pellet_cloud/proc/grenade_armed() + LAZYINITLIST(bodies) + RegisterSignal(parent, list(COMSIG_ITEM_DROPPED, COMSIG_MOVABLE_MOVED), .proc/grenade_moved) + RegisterSignal(parent, COMSIG_MOVABLE_UNCROSSED, .proc/grenade_uncrossed) + +/// Our grenade has moved, reset var/list/bodies so we're "on top" of any mobs currently on the tile +/datum/component/pellet_cloud/proc/grenade_moved() + LAZYCLEARLIST(bodies) + for(var/mob/living/L in get_turf(parent)) + bodies += L + +/// Someone who was originally "under" the grenade has moved off the tile and is now eligible for being a martyr and "covering" it +/datum/component/pellet_cloud/proc/grenade_uncrossed(datum/source, atom/movable/AM) + bodies -= AM + diff --git a/code/datums/components/plumbing/_plumbing.dm b/code/datums/components/plumbing/_plumbing.dm index 3d494a6b3e7e..1f61f0925968 100644 --- a/code/datums/components/plumbing/_plumbing.dm +++ b/code/datums/components/plumbing/_plumbing.dm @@ -105,9 +105,9 @@ for(var/D in GLOB.cardinals) var/color var/direction - if(D & demand_connects) + if(D & initial(demand_connects)) color = "red" //red because red is mean and it takes - else if(D & supply_connects) + else if(D & initial(supply_connects)) color = "blue" //blue is nice and gives else continue diff --git a/code/datums/components/pricetag.dm b/code/datums/components/pricetag.dm index 3220dc425a5c..ff915d1285a7 100644 --- a/code/datums/components/pricetag.dm +++ b/code/datums/components/pricetag.dm @@ -8,10 +8,9 @@ owner = _owner if(_profit_ratio) profit_ratio = _profit_ratio - RegisterSignal(parent, COMSIG_ITEM_SOLD, .proc/split_profit) - RegisterSignal(parent, COMSIG_STRUCTURE_UNWRAPPED, .proc/Unwrapped) - RegisterSignal(parent, COMSIG_ITEM_UNWRAPPED, .proc/Unwrapped) - RegisterSignal(parent, COMSIG_ITEM_SPLIT_PROFIT, .proc/return_ratio) + RegisterSignal(parent, list(COMSIG_ITEM_SOLD), .proc/split_profit) + RegisterSignal(parent, list(COMSIG_STRUCTURE_UNWRAPPED, COMSIG_ITEM_UNWRAPPED), .proc/Unwrapped) + RegisterSignal(parent, list(COMSIG_ITEM_SPLIT_PROFIT, COMSIG_ITEM_SPLIT_PROFIT_DRY), .proc/return_ratio) /datum/component/pricetag/proc/Unwrapped() qdel(src) //Once it leaves it's wrapped container, the object in question should lose it's pricetag component. diff --git a/code/datums/components/radioactive.dm b/code/datums/components/radioactive.dm index 0b9b62eb7c6a..7e62afc5ae4b 100644 --- a/code/datums/components/radioactive.dm +++ b/code/datums/components/radioactive.dm @@ -7,7 +7,6 @@ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS var/source - var/hl3_release_date //the half-life measured in ticks var/strength var/can_contaminate @@ -17,7 +16,6 @@ source = _source hl3_release_date = _half_life can_contaminate = _can_contaminate - if(istype(parent, /atom)) RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/rad_examine) if(istype(parent, /obj/item)) @@ -25,21 +23,25 @@ RegisterSignal(parent, COMSIG_ITEM_ATTACK_OBJ, .proc/rad_attack) else return COMPONENT_INCOMPATIBLE - if(strength > RAD_MINIMUM_CONTAMINATION) SSradiation.warn(src) - + //Let's make er glow + //This relies on parent not being a turf or something. IF YOU CHANGE THAT, CHANGE THIS + var/atom/movable/master = parent + master.add_filter("rad_glow", 2, list("type" = "outline", "color" = "#39ff1430", "size" = 2)) + addtimer(CALLBACK(src, .proc/glow_loop, master), rand(1,19))//Things should look uneven START_PROCESSING(SSradiation, src) /datum/component/radioactive/Destroy() STOP_PROCESSING(SSradiation, src) + var/atom/movable/master = parent + master.remove_filter("rad_glow") return ..() /datum/component/radioactive/process() if(!prob(50)) return radiation_pulse(parent, strength, RAD_DISTANCE_COEFFICIENT*2, FALSE, can_contaminate) - if(!hl3_release_date) return strength -= strength / hl3_release_date @@ -47,6 +49,12 @@ qdel(src) return PROCESS_KILL +/datum/component/radioactive/proc/glow_loop(atom/movable/master) + var/filter = master.get_filter("rad_glow") + if(filter) + animate(filter, alpha = 110, time = 15, loop = -1) + animate(alpha = 40, time = 25) + /datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, _strength, _source, _half_life, _can_contaminate) if(!i_am_original) return @@ -70,9 +78,10 @@ out += "[length(out) ? " and it " : "[master] "]seems to be glowing a bit." if(RAD_AMOUNT_HIGH to INFINITY) //At this level the object can contaminate other objects out += "[length(out) ? " and it " : "[master] "]hurts to look at." - else - out += "." - to_chat(user, out.Join()) + if(!LAZYLEN(out)) + return + out += "." + to_chat(user, "[out.Join()]") /datum/component/radioactive/proc/rad_attack(datum/source, atom/movable/target, mob/living/user) radiation_pulse(parent, strength/20) diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm index ace6116b4847..130f26a0504e 100644 --- a/code/datums/components/slippery.dm +++ b/code/datums/components/slippery.dm @@ -23,3 +23,14 @@ /datum/component/slippery/proc/Slip_on_wearer(datum/source, atom/movable/AM, mob/living/crossed) if(!(crossed.mobility_flags & MOBILITY_STAND) && !crossed.buckle_lying) Slip(source, AM) + +/datum/component/slippery/clowning //used for making the clown PDA only slip if the clown is wearing his shoes and the elusive banana-skin belt + +/datum/component/slippery/clowning/Slip_on_wearer(datum/source, atom/movable/AM, mob/living/crossed) + var/obj/item/I = crossed.get_item_by_slot(ITEM_SLOT_FEET) + if(!(crossed.mobility_flags & MOBILITY_STAND) && !crossed.buckle_lying) + if(istype(I, /obj/item/clothing/shoes/clown_shoes)) + Slip(source, AM) + else + to_chat(crossed,"[parent] failed to slip anyone. Perhaps I shouldn't have abandoned my legacy...") + diff --git a/code/datums/components/storage/concrete/wallet.dm b/code/datums/components/storage/concrete/wallet.dm index 0b8638d16759..c019bb0a596b 100644 --- a/code/datums/components/storage/concrete/wallet.dm +++ b/code/datums/components/storage/concrete/wallet.dm @@ -1,5 +1,5 @@ /datum/component/storage/concrete/wallet/on_alt_click(datum/source, mob/user) - if(!isliving(user) || !user.CanReach(parent)) + if(!isliving(user) || !user.CanReach(parent) || user.incapacitated()) return if(locked) to_chat(user, "[parent] seems to be locked!") diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 840893d906b3..367fbc42d8d2 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -217,7 +217,7 @@ var/list/rejections = list() while(do_after(M, 10, TRUE, parent, FALSE, CALLBACK(src, .proc/handle_mass_pickup, things, I.loc, rejections, progress))) stoplag(1) - qdel(progress) + progress.end_progress() to_chat(M, "You put everything you could [insert_preposition] [parent].") /datum/component/storage/proc/handle_mass_item_insertion(list/things, datum/component/storage/src_object, mob/user, datum/progressbar/progress) @@ -275,7 +275,7 @@ var/datum/progressbar/progress = new(M, length(things), T) while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress))) stoplag(1) - qdel(progress) + progress.end_progress() /datum/component/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress, trigger_on_found = TRUE) var/atom/real_location = real_location() @@ -780,7 +780,7 @@ return hide_from(target) /datum/component/storage/proc/on_alt_click(datum/source, mob/user) - if(!isliving(user) || !user.CanReach(parent)) + if(!isliving(user) || !user.CanReach(parent) || user.incapacitated()) return if(locked) to_chat(user, "[parent] seems to be locked!") @@ -793,17 +793,15 @@ playsound(A, "rustle", 50, TRUE, -5) return - if(!user.incapacitated()) - var/obj/item/I = locate() in real_location() - if(!I) - return - A.add_fingerprint(user) - remove_from_storage(I, get_turf(user)) - if(!user.put_in_hands(I)) - to_chat(user, "You fumble for [I] and it falls on the floor.") - return - user.visible_message("[user] draws [I] from [parent]!", "You draw [I] from [parent].") + var/obj/item/I = locate() in real_location() + if(!I) + return + A.add_fingerprint(user) + remove_from_storage(I, get_turf(user)) + if(!user.put_in_hands(I)) + to_chat(user, "You fumble for [I] and it falls on the floor.") return + user.visible_message("[user] draws [I] from [parent]!", "You draw [I] from [parent].") /datum/component/storage/proc/action_trigger(datum/signal_source, datum/action/source) gather_mode_switch(source.owner) diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm index 9980a2fc96fd..7d2036657650 100644 --- a/code/datums/components/tackle.dm +++ b/code/datums/components/tackle.dm @@ -70,6 +70,9 @@ if(!user.in_throw_mode || user.get_active_held_item() || user.pulling || user.buckling) return + if(!A || !(isturf(A) || isturf(A.loc))) + return + if(HAS_TRAIT(user, TRAIT_HULK)) to_chat(user, "You're too angry to remember how to tackle!") return @@ -100,10 +103,11 @@ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/checkObstacle) playsound(user, 'sound/weapons/thudswoosh.ogg', 40, TRUE, -1) + var/leap_word = isfelinid(user) ? "pounce" : "leap" ///If cat, "pounce" instead of "leap". if(can_see(user, A, 7)) - user.visible_message("[user] leaps at [A]!", "You leap at [A]!") + user.visible_message("[user] [leap_word]s at [A]!", "You [leap_word] at [A]!") else - user.visible_message("[user] leaps!", "You leap!") + user.visible_message("[user] [leap_word]s!", "You [leap_word]!") if(get_dist(user, A) < min_distance) A = get_ranged_target_turf(user, get_dir(user, A), min_distance) //TODO: this only works in cardinals/diagonals, make it work with in-betweens too! @@ -146,6 +150,7 @@ var/mob/living/carbon/target = hit var/mob/living/carbon/human/T = target var/mob/living/carbon/human/S = user + var/tackle_word = isfelinid(user) ? "pounce" : "tackle" ///If cat, "pounce" instead of "tackle". var/roll = rollTackle(target) tackling = FALSE @@ -153,8 +158,8 @@ switch(roll) if(-INFINITY to -5) - user.visible_message("[user] botches [user.p_their()] tackle and slams [user.p_their()] head into [target], knocking [user.p_them()]self silly!", "You botch your tackle and slam your head into [target], knocking yourself silly!", target) - to_chat(target, "[user] botches [user.p_their()] tackle and slams [user.p_their()] head into you, knocking [user.p_them()]self silly!") + user.visible_message("[user] botches [user.p_their()] [tackle_word] and slams [user.p_their()] head into [target], knocking [user.p_them()]self silly!", "You botch your [tackle_word] and slam your head into [target], knocking yourself silly!", target) + to_chat(target, "[user] botches [user.p_their()] [tackle_word] and slams [user.p_their()] head into you, knocking [user.p_them()]self silly!") user.Paralyze(30) var/obj/item/bodypart/head/hed = user.get_bodypart(BODY_ZONE_HEAD) @@ -163,8 +168,8 @@ user.gain_trauma(/datum/brain_trauma/mild/concussion) if(-4 to -2) // glancing blow at best - user.visible_message("[user] lands a weak tackle on [target], briefly knocking [target.p_them()] off-balance!", "You land a weak tackle on [target], briefly knocking [target.p_them()] off-balance!", target) - to_chat(target, "[user] lands a weak tackle on you, briefly knocking you off-balance!") + user.visible_message("[user] lands a weak [tackle_word] on [target], briefly knocking [target.p_them()] off-balance!", "You land a weak [tackle_word] on [target], briefly knocking [target.p_them()] off-balance!", target) + to_chat(target, "[user] lands a weak [tackle_word] on you, briefly knocking you off-balance!") user.Knockdown(30) if(ishuman(target) && !T.has_movespeed_modifier(/datum/movespeed_modifier/shove)) @@ -172,8 +177,8 @@ addtimer(CALLBACK(T, /mob/living/carbon/human/proc/clear_shove_slowdown), SHOVE_SLOWDOWN_LENGTH) if(-1 to 0) // decent hit, both parties are about equally inconvenienced - user.visible_message("[user] lands a passable tackle on [target], sending them both tumbling!", "You land a passable tackle on [target], sending you both tumbling!", target) - to_chat(target, "[user] lands a passable tackle on you, sending you both tumbling!") + user.visible_message("[user] lands a passable [tackle_word] on [target], sending them both tumbling!", "You land a passable [tackle_word] on [target], sending you both tumbling!", target) + to_chat(target, "[user] lands a passable [tackle_word] on you, sending you both tumbling!") target.adjustStaminaLoss(stamina_cost) target.Paralyze(5) @@ -181,8 +186,8 @@ target.Knockdown(25) if(1 to 2) // solid hit, tackler has a slight advantage - user.visible_message("[user] lands a solid tackle on [target], knocking them both down hard!", "You land a solid tackle on [target], knocking you both down hard!", target) - to_chat(target, "[user] lands a solid tackle on you, knocking you both down hard!") + user.visible_message("[user] lands a solid [tackle_word] on [target], knocking them both down hard!", "You land a solid [tackle_word] on [target], knocking you both down hard!", target) + to_chat(target, "[user] lands a solid [tackle_word] on you, knocking you both down hard!") target.adjustStaminaLoss(30) target.Paralyze(5) @@ -190,8 +195,8 @@ target.Knockdown(20) if(3 to 4) // really good hit, the target is definitely worse off here. Without positive modifiers, this is as good a tackle as you can land - user.visible_message("[user] lands an expert tackle on [target], knocking [target.p_them()] down hard while landing on [user.p_their()] feet with a passive grip!", "You land an expert tackle on [target], knocking [target.p_them()] down hard while landing on your feet with a passive grip!", target) - to_chat(target, "[user] lands an expert tackle on you, knocking you down hard and maintaining a passive grab!") + user.visible_message("[user] lands an expert [tackle_word] on [target], knocking [target.p_them()] down hard while landing on [user.p_their()] feet with a passive grip!", "You land an expert [tackle_word] on [target], knocking [target.p_them()] down hard while landing on your feet with a passive grip!", target) + to_chat(target, "[user] lands an expert [tackle_word] on you, knocking you down hard and maintaining a passive grab!") user.SetKnockdown(0) user.forceMove(get_turf(target)) @@ -203,8 +208,8 @@ S.setGrabState(GRAB_PASSIVE) if(5 to INFINITY) // absolutely BODIED - user.visible_message("[user] lands a monster tackle on [target], knocking [target.p_them()] senseless and applying an aggressive pin!", "You land a monster tackle on [target], knocking [target.p_them()] senseless and applying an aggressive pin!", target) - to_chat(target, "[user] lands a monster tackle on you, knocking you senseless and aggressively pinning you!") + user.visible_message("[user] lands a monster [tackle_word] on [target], knocking [target.p_them()] senseless and applying an aggressive pin!", "You land a monster [tackle_word] on [target], knocking [target.p_them()] senseless and applying an aggressive pin!", target) + to_chat(target, "[user] lands a monster [tackle_word] on you, knocking you senseless and aggressively pinning you!") user.SetKnockdown(0) user.forceMove(get_turf(target)) @@ -421,10 +426,10 @@ for(var/i = 0, i < speed, i++) var/obj/item/shard/shard = new /obj/item/shard(get_turf(user)) shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult=3, pain_chance=5) - shard.AddElement(/datum/element/embed, shard.embedding) + shard.updateEmbedding() user.hitby(shard, skipcatch = TRUE, hitpush = FALSE) shard.embedding = list() - shard.AddElement(/datum/element/embed, shard.embedding) + shard.updateEmbedding() W.obj_destruction() user.adjustStaminaLoss(10 * speed) user.Paralyze(30) diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm index a6d28a97b73d..499f2db7fbe7 100644 --- a/code/datums/components/uplink.dm +++ b/code/datums/components/uplink.dm @@ -125,9 +125,10 @@ active = TRUE ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "uplink", name, 620, 580, master_ui, state) - ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input. - ui.set_style("syndicate") + ui = new(user, src, ui_key, "Uplink", name, 620, 580, master_ui, state) + // This UI is only ever opened by one person, + // and never is updated outside of user input. + ui.set_autoupdate(FALSE) ui.open() /datum/component/uplink/ui_data(mob/user) @@ -136,8 +137,7 @@ var/list/data = list() data["telecrystals"] = telecrystals data["lockable"] = lockable - data["compact_mode"] = compact_mode - + data["compactMode"] = compact_mode return data /datum/component/uplink/ui_static_data(mob/user) @@ -179,19 +179,16 @@ /datum/component/uplink/ui_act(action, params) if(!active) return - switch(action) if("buy") - var/item = params["item"] - + var/item_name = params["name"] var/list/buyable_items = list() for(var/category in uplink_items) buyable_items += uplink_items[category] - - if(item in buyable_items) - var/datum/uplink_item/I = buyable_items[item] + if(item_name in buyable_items) + var/datum/uplink_item/I = buyable_items[item_name] MakePurchase(usr, I) - . = TRUE + return TRUE if("lock") active = FALSE locked = TRUE @@ -200,9 +197,10 @@ SStgui.close_uis(src) if("select") selected_cat = params["category"] + return TRUE if("compact_toggle") compact_mode = !compact_mode - return TRUE + return TRUE /datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U) if(!istype(U)) diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 56c2ca351929..0f5121425a77 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -148,16 +148,17 @@ "Medical" = GLOB.medical_positions, "Science" = GLOB.science_positions, "Supply" = GLOB.supply_positions, - "Civilian" = GLOB.civilian_positions, + "Service" = GLOB.service_positions, "Silicon" = GLOB.nonhuman_positions ) for(var/datum/data/record/t in GLOB.data_core.general) var/name = t.fields["name"] var/rank = t.fields["rank"] + var/truerank = t.fields["truerank"] var/has_department = FALSE for(var/department in departments) var/list/jobs = departments[department] - if(rank in jobs || (t.fields["truerank"] && (t.fields["truerank"] in jobs))) //Wasp edit - alt titles ((((((fun))) with (parenthesis) and (AND/OR/in)))) + if((rank in jobs) || (truerank in jobs)) //Wasp edit - alt titles if(!manifest_out[department]) manifest_out[department] = list() manifest_out[department] += list(list( @@ -209,7 +210,6 @@ var/static/list/show_directions = list(SOUTH, WEST) if(H.mind && (H.mind.assigned_role != H.mind.special_role)) var/assignment - var/trueassignment //wasp edit - alt titles if(H.mind.assigned_role) assignment = H.mind.assigned_role else if(H.job) @@ -218,6 +218,7 @@ assignment = "Unassigned" //Wasp begin - Alt job titles + var/trueassignment = assignment if(C && C.prefs && C.prefs.alt_titles_preferences[assignment]) trueassignment = assignment assignment = C.prefs.alt_titles_preferences[assignment] diff --git a/code/datums/diseases/wizarditis.dm b/code/datums/diseases/wizarditis.dm index a226da8472f6..b7a74615cb7f 100644 --- a/code/datums/diseases/wizarditis.dm +++ b/code/datums/diseases/wizarditis.dm @@ -9,7 +9,7 @@ viable_mobtypes = list(/mob/living/carbon/human) disease_flags = CAN_CARRY|CAN_RESIST|CURABLE permeability_mod = 0.75 - desc = "Some speculate that this virus is the cause of the Space Wizard Federation's existence. Subjects affected show the signs of mental retardation, yelling obscure sentences or total gibberish. On late stages subjects sometime express the feelings of inner power, and, cite, 'the ability to control the forces of cosmos themselves!' A gulp of strong, manly spirits usually reverts them to normal, humanlike, condition." + desc = "Some speculate that this virus is the cause of the Space Wizard Federation's existence. Subjects affected show the signs of brain damage, yelling obscure sentences or total gibberish. On late stages subjects sometime express the feelings of inner power, and, cite, 'the ability to control the forces of cosmos themselves!' A gulp of strong, manly spirits usually reverts them to normal, humanlike, condition." severity = DISEASE_SEVERITY_HARMFUL required_organs = list(/obj/item/bodypart/head) diff --git a/code/datums/dna.dm b/code/datums/dna.dm index 3b2f04c134d7..51cffae318d1 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -12,6 +12,7 @@ var/list/previous = list() //For temporary name/ui/ue/blood_type modifications var/mob/living/holder var/mutation_index[DNA_MUTATION_BLOCKS] //List of which mutations this carbon has and its assigned block + var/default_mutation_genes[DNA_MUTATION_BLOCKS] //List of the default genes from this mutation to allow DNA Scanner highlighting var/stability = 100 var/scrambled = FALSE //Did we take something like mutagen? In that case we cant get our genes scanned to instantly cheese all the powers. @@ -53,10 +54,12 @@ destination.flavor_text = destination.dna.features["flavor_text"] //Update the flavor_text to use new dna text if(transfer_SE) destination.dna.mutation_index = mutation_index + destination.dna.default_mutation_genes = default_mutation_genes /datum/dna/proc/copy_dna(datum/dna/new_dna) new_dna.unique_enzymes = unique_enzymes new_dna.mutation_index = mutation_index + new_dna.default_mutation_genes = default_mutation_genes new_dna.uni_identity = uni_identity new_dna.blood_type = blood_type new_dna.features = features.Copy() @@ -130,15 +133,18 @@ if(!LAZYLEN(mutations_temp)) return mutation_index.Cut() + default_mutation_genes.Cut() shuffle_inplace(mutations_temp) if(ismonkey(holder)) mutations |= new RACEMUT(MUT_NORMAL) mutation_index[RACEMUT] = GET_SEQUENCE(RACEMUT) else mutation_index[RACEMUT] = create_sequence(RACEMUT, FALSE) + default_mutation_genes[RACEMUT] = mutation_index[RACEMUT] for(var/i in 2 to DNA_MUTATION_BLOCKS) var/datum/mutation/human/M = mutations_temp[i] mutation_index[M.type] = create_sequence(M.type, FALSE, M.difficulty) + default_mutation_genes[M.type] = mutation_index[M.type] shuffle_inplace(mutation_index) //Used to generate original gene sequences for every mutation @@ -332,7 +338,7 @@ return dna -/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, newreal_name, newblood_type, datum/species/mrace, newfeatures, list/mutations, force_transfer_mutations) +/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, list/default_mutation_genes, newreal_name, newblood_type, datum/species/mrace, newfeatures, list/mutations, force_transfer_mutations) //Do not use force_transfer_mutations for stuff like cloners without some precautions, otherwise some conditional mutations could break (timers, drill hat etc) if(newfeatures) dna.features = newfeatures @@ -356,6 +362,10 @@ if(LAZYLEN(mutation_index)) dna.mutation_index = mutation_index.Copy() + if(LAZYLEN(default_mutation_genes)) + dna.default_mutation_genes = default_mutation_genes.Copy() + else + dna.default_mutation_genes = mutation_index.Copy() domutcheck() if(mrace || newfeatures || ui) @@ -448,8 +458,11 @@ . = TRUE if(on) mutation_index[HM.type] = GET_SEQUENCE(HM.type) + default_mutation_genes[HM.type] = mutation_index[HM.type] else if(GET_SEQUENCE(HM.type) == mutation_index[HM.type]) mutation_index[HM.type] = create_sequence(HM.type, FALSE, HM.difficulty) + default_mutation_genes[HM.type] = mutation_index[HM.type] + /datum/dna/proc/activate_mutation(mutation) //note that this returns a boolean and not a new mob if(!mutation) diff --git a/code/datums/elements/embed.dm b/code/datums/elements/embed.dm index ab2c0d537063..fd74cc0e7c10 100644 --- a/code/datums/elements/embed.dm +++ b/code/datums/elements/embed.dm @@ -1,15 +1,12 @@ /* - The presence of this element allows an item to embed itself in a human or turf when it is thrown into a target (whether by hand, gun, or explosive wave) - with either at least 4 throwspeed (EMBED_THROWSPEED_THRESHOLD) or ignore_throwspeed_threshold set to TRUE. + The presence of this element allows an item (or a projectile carrying an item) to embed itself in a human or turf when it is thrown into a target (whether by hand, gun, or explosive wave) with either + at least 4 throwspeed (EMBED_THROWSPEED_THRESHOLD) or ignore_throwspeed_threshold set to TRUE. Items meant to be used as shrapnel for projectiles should have ignore_throwspeed_threshold set to true. - This element is granted primarily to any /obj/item that has something in its /embedding var, which should be formatted as a list. If you wish to be able to - grant/rescind the ability for an item to embed (say, when activating and deactivating an edagger), you can do so in two ways: - - 1. Drop the throw_speed var below EMBED_THROWSPEED_THRESHOLD (object will still be able to otherwise embed if thrown at high speed by something else like a blast) - 2. Add/Remove the embed element as needed (won't be able to embed at all) + Whether we're dealing with a direct /obj/item (throwing a knife at someone) or an /obj/projectile with a shrapnel_type, how we handle things plays out the same, with one extra step separating them. + Items simply make their COMSIG_MOVABLE_IMPACT or COMSIG_MOVABLE_IMPACT_ZONE check (against a closed turf or a carbon, respectively), while projectiles check on COMSIG_PROJECTILE_SELF_ON_HIT. + Upon a projectile hitting a valid target, it spawns whatever type of payload it has defined, then has that try to embed itself in the target on its own. Otherwise non-embeddable or stickable items can be made embeddable/stickable through wizard events/sticky tape/admin memes. - */ #define STANDARD_WALL_HARDNESS 40 @@ -17,6 +14,7 @@ /datum/element/embed element_flags = ELEMENT_BESPOKE id_arg_index = 2 + var/initialized = FALSE /// whether we can skip assigning all the vars (since these are bespoke elements, we don't have to reset the vars every time we attach to something, we already know what we are!) // all of this stuff is explained in _DEFINES/combat.dm var/embed_chance @@ -30,102 +28,196 @@ var/jostle_chance var/jostle_pain_mult var/pain_stam_pct + var/payload_type + var/embed_chance_turf_mod -/datum/element/embed/Attach(datum/target, list/embedArgs) +/datum/element/embed/Attach(datum/target, embed_chance, fall_chance, pain_chance, pain_mult, remove_pain_mult, impact_pain_mult, rip_time, ignore_throwspeed_threshold, jostle_chance, jostle_pain_mult, pain_stam_pct, embed_chance_turf_mod, projectile_payload=/obj/item/shard) . = ..() - parseArgs(arglist(embedArgs)) - if(!isitem(target)) + if(!isitem(target) && !isprojectile(target)) return ELEMENT_INCOMPATIBLE - RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, .proc/checkEmbedMob) - RegisterSignal(target, COMSIG_MOVABLE_IMPACT, .proc/checkEmbedOther) - RegisterSignal(target, COMSIG_ELEMENT_ATTACH, .proc/severancePackage) - RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/examined) + if(isitem(target)) + RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, .proc/checkEmbedMob) + RegisterSignal(target, COMSIG_MOVABLE_IMPACT, .proc/checkEmbedOther) + RegisterSignal(target, COMSIG_ELEMENT_ATTACH, .proc/severancePackage) + RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/examined) + RegisterSignal(target, COMSIG_EMBED_TRY_FORCE, .proc/tryForceEmbed) + RegisterSignal(target, COMSIG_ITEM_DISABLE_EMBED, .proc/detachFromWeapon) + if(!initialized) + src.embed_chance = embed_chance + src.fall_chance = fall_chance + src.pain_chance = pain_chance + src.pain_mult = pain_mult + src.remove_pain_mult = remove_pain_mult + src.rip_time = rip_time + src.impact_pain_mult = impact_pain_mult + src.ignore_throwspeed_threshold = ignore_throwspeed_threshold + src.jostle_chance = jostle_chance + src.jostle_pain_mult = jostle_pain_mult + src.pain_stam_pct = pain_stam_pct + src.embed_chance_turf_mod = embed_chance_turf_mod + initialized = TRUE + else + payload_type = projectile_payload + RegisterSignal(target, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/checkEmbedProjectile) -/datum/element/embed/Detach(obj/item/target) +/datum/element/embed/Detach(obj/target) . = ..() - UnregisterSignal(target, list(COMSIG_MOVABLE_IMPACT_ZONE, COMSIG_ELEMENT_ATTACH, COMSIG_MOVABLE_IMPACT, COMSIG_PARENT_EXAMINE)) + if(isitem(target)) + UnregisterSignal(target, list(COMSIG_MOVABLE_IMPACT_ZONE, COMSIG_ELEMENT_ATTACH, COMSIG_MOVABLE_IMPACT, COMSIG_PARENT_EXAMINE, COMSIG_EMBED_TRY_FORCE, COMSIG_ITEM_DISABLE_EMBED)) + else + UnregisterSignal(target, list(COMSIG_PROJECTILE_SELF_ON_HIT)) /// Checking to see if we're gonna embed into a human -/datum/element/embed/proc/checkEmbedMob(obj/item/weapon, mob/living/carbon/human/victim, hit_zone, datum/thrownthing/throwingdatum) - if(!istype(victim)) +/datum/element/embed/proc/checkEmbedMob(obj/item/weapon, mob/living/carbon/victim, hit_zone, datum/thrownthing/throwingdatum, forced=FALSE) + if(!istype(victim) || HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE)) + return + + var/actual_chance = embed_chance + + if(!weapon.isEmbedHarmless()) // all the armor in the world won't save you from a kick me sign + var/armor = max(victim.run_armor_check(hit_zone, "bullet", silent=TRUE), victim.run_armor_check(hit_zone, "bomb", silent=TRUE)) // we'll be nice and take the better of bullet and bomb armor + + if(armor) // we only care about armor penetration if there's actually armor to penetrate + var/pen_mod = -armor + weapon.armour_penetration // even a little bit of armor can make a big difference for shrapnel with large negative armor pen + actual_chance += pen_mod // doing the armor pen as a separate calc just in case this ever gets expanded on + if(actual_chance <= 0) + victim.visible_message("[weapon] bounces off [victim]'s armor!", "[weapon] bounces off your armor!", vision_distance = COMBAT_MESSAGE_RANGE) + return + + var/roll_embed = prob(actual_chance) + var/pass = forced || ((((throwingdatum ? throwingdatum.speed : weapon.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || ignore_throwspeed_threshold) && roll_embed) + if(!pass) return - if((((throwingdatum ? throwingdatum.speed : weapon.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || ignore_throwspeed_threshold) && prob(embed_chance) && !HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE)) - victim.AddComponent(/datum/component/embedded,\ - weapon,\ - throwingdatum,\ - embed_chance = embed_chance,\ - fall_chance = fall_chance,\ - pain_chance = pain_chance,\ - pain_mult = pain_mult,\ - remove_pain_mult = remove_pain_mult,\ - rip_time = rip_time,\ - ignore_throwspeed_threshold = ignore_throwspeed_threshold,\ - jostle_chance = jostle_chance,\ - jostle_pain_mult = jostle_pain_mult,\ - pain_stam_pct = pain_stam_pct) - - -/// We need the hit_zone if we're embedding into a human, so this proc only handled if we're embedding into a turf -/datum/element/embed/proc/checkEmbedOther(obj/item/weapon, turf/closed/hit, datum/thrownthing/throwingdatum) + var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || pick(victim.bodyparts) + victim.AddComponent(/datum/component/embedded,\ + weapon,\ + throwingdatum,\ + part = limb,\ + embed_chance = embed_chance,\ + fall_chance = fall_chance,\ + pain_chance = pain_chance,\ + pain_mult = pain_mult,\ + remove_pain_mult = remove_pain_mult,\ + rip_time = rip_time,\ + ignore_throwspeed_threshold = ignore_throwspeed_threshold,\ + jostle_chance = jostle_chance,\ + jostle_pain_mult = jostle_pain_mult,\ + pain_stam_pct = pain_stam_pct,\ + embed_chance_turf_mod = embed_chance_turf_mod) + + return TRUE + +/// We need the hit_zone if we're embedding into a human, so this proc only handles if we're embedding into a turf +/datum/element/embed/proc/checkEmbedOther(obj/item/weapon, turf/closed/hit, datum/thrownthing/throwingdatum, forced=FALSE) if(!istype(hit)) return - var/chance = embed_chance + var/chance = embed_chance + embed_chance_turf_mod if(iswallturf(hit)) var/turf/closed/wall/W = hit chance += 2 * (W.hardness - STANDARD_WALL_HARDNESS) - if((((throwingdatum ? throwingdatum.speed : weapon.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || ignore_throwspeed_threshold) && prob(chance)) - hit.AddComponent(/datum/component/embedded,\ - weapon,\ - throwingdatum,\ - embed_chance = embed_chance,\ - fall_chance = fall_chance,\ - pain_chance = pain_chance,\ - pain_mult = pain_mult,\ - remove_pain_mult = remove_pain_mult,\ - rip_time = rip_time,\ - ignore_throwspeed_threshold = ignore_throwspeed_threshold,\ - jostle_chance = jostle_chance,\ - jostle_pain_mult = jostle_pain_mult,\ - pain_stam_pct = pain_stam_pct) - -/datum/element/embed/proc/parseArgs(embed_chance = EMBED_CHANCE, - fall_chance = EMBEDDED_ITEM_FALLOUT, - pain_chance = EMBEDDED_PAIN_CHANCE, - pain_mult = EMBEDDED_PAIN_MULTIPLIER, - remove_pain_mult = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER, - rip_time = EMBEDDED_UNSAFE_REMOVAL_TIME, - impact_pain_mult = EMBEDDED_IMPACT_PAIN_MULTIPLIER, - ignore_throwspeed_threshold = FALSE, - jostle_chance = EMBEDDED_JOSTLE_CHANCE, - jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER, - pain_stam_pct = EMBEDDED_PAIN_STAM_PCT) - - src.embed_chance = embed_chance - src.fall_chance = fall_chance - src.pain_chance = pain_chance - src.pain_mult = pain_mult - src.remove_pain_mult = remove_pain_mult - src.impact_pain_mult = impact_pain_mult - src.rip_time = rip_time - src.ignore_throwspeed_threshold = ignore_throwspeed_threshold - src.jostle_chance = jostle_chance - src.jostle_pain_mult = jostle_pain_mult - src.pain_stam_pct = pain_stam_pct + if(!forced && chance <= 0 || embed_chance_turf_mod <= -100) + return + + var/pass = ((((throwingdatum ? throwingdatum.speed : weapon.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || ignore_throwspeed_threshold) && prob(chance)) + if(!pass) + return + + hit.AddComponent(/datum/component/embedded,\ + weapon,\ + throwingdatum,\ + embed_chance = embed_chance,\ + fall_chance = fall_chance,\ + pain_chance = pain_chance,\ + pain_mult = pain_mult,\ + remove_pain_mult = remove_pain_mult,\ + rip_time = rip_time,\ + ignore_throwspeed_threshold = ignore_throwspeed_threshold,\ + jostle_chance = jostle_chance,\ + jostle_pain_mult = jostle_pain_mult,\ + pain_stam_pct = pain_stam_pct,\ + embed_chance_turf_mod = embed_chance_turf_mod) + + return TRUE ///A different embed element has been attached, so we'll detach and let them handle things /datum/element/embed/proc/severancePackage(obj/item/weapon, datum/element/E) if(istype(E, /datum/element/embed)) Detach(weapon) +///If we don't want to be embeddable anymore (deactivating an e-dagger for instance) +/datum/element/embed/proc/detachFromWeapon(obj/weapon) + Detach(weapon) + +///Someone inspected our embeddable item /datum/element/embed/proc/examined(obj/item/I, mob/user, list/examine_list) - if(!pain_mult && !jostle_pain_mult) + if(I.isEmbedHarmless()) examine_list += "[I] feels sticky, and could probably get stuck to someone if thrown properly!" else examine_list += "[I] has a fine point, and could probably embed in someone if thrown properly!" + +/** + * checkEmbedProjectile() is what we get when a projectile with a defined shrapnel_type impacts a target. + * + * If we hit a valid target (carbon or closed turf), we create the shrapnel_type object and immediately call tryEmbed() on it, targeting what we impacted. That will lead + * it to call tryForceEmbed() on its own embed element (it's out of our hands here, our projectile is done), where it will run through all the checks it needs to. + */ +/datum/element/embed/proc/checkEmbedProjectile(obj/projectile/P, atom/movable/firer, atom/hit) + if(!iscarbon(hit) && !isclosedturf(hit)) + Detach(P) + return // we don't care + + var/obj/item/payload = new payload_type(get_turf(hit)) + var/did_embed + if(iscarbon(hit)) + var/mob/living/carbon/C = hit + var/obj/item/bodypart/limb = C.get_bodypart(C.check_limb_hit(P.def_zone)) + did_embed = payload.tryEmbed(limb) + else + did_embed = payload.tryEmbed(hit) + + if(!did_embed) + payload.failedEmbed() + Detach(P) + +/** + * tryForceEmbed() is called here when we fire COMSIG_EMBED_TRY_FORCE from [/obj/item/proc/tryEmbed]. Mostly, this means we're a piece of shrapnel from a projectile that just impacted something, and we're trying to embed in it. + * + * The reason for this extra mucking about is avoiding having to do an extra hitby(), and annoying the target by impacting them once with the projectile, then again with the shrapnel (which likely represents said bullet), and possibly + * AGAIN if we actually embed. This way, we save on at least one message. Runs the standard embed checks on the mob/turf. + * + * Arguments: + * * I- what we're trying to embed, obviously + * * target- what we're trying to shish-kabob, either a bodypart, a carbon, or a closed turf + * * hit_zone- if our target is a carbon, try to hit them in this zone, if we don't have one, pick a random one. If our target is a bodypart, we already know where we're hitting. + * * forced- if we want this to succeed 100% + */ +/datum/element/embed/proc/tryForceEmbed(obj/item/I, atom/target, hit_zone, forced=FALSE) + var/obj/item/bodypart/limb + var/mob/living/carbon/C + var/turf/closed/T + + if(!forced && !prob(embed_chance)) + return + + if(iscarbon(target)) + C = target + if(!hit_zone) + limb = pick(C.bodyparts) + hit_zone = limb.body_zone + else if(isbodypart(target)) + limb = target + C = limb.owner + else if(isclosedturf(target)) + T = target + + if(C) + return checkEmbedMob(I, C, hit_zone, forced=TRUE) + else if(T) + return checkEmbedOther(I, T, forced=TRUE) diff --git a/code/datums/elements/waddling.dm b/code/datums/elements/waddling.dm index 894d33455cb1..81a36333420f 100644 --- a/code/datums/elements/waddling.dm +++ b/code/datums/elements/waddling.dm @@ -20,5 +20,6 @@ /datum/element/waddling/proc/Waddle(atom/movable/target) animate(target, pixel_z = 4, time = 0) - animate(pixel_z = 0, transform = turn(matrix(), pick(-12, 0, 12)), time=2) - animate(pixel_z = 0, transform = matrix(), time = 0) + var/prev_trans = matrix(target.transform) + animate(pixel_z = 0, transform = turn(target.transform, pick(-12, 0, 12)), time=2) + animate(pixel_z = 0, transform = prev_trans, time = 0) diff --git a/code/datums/embedding_behavior.dm b/code/datums/embedding_behavior.dm deleted file mode 100644 index 449fb3ba1cc7..000000000000 --- a/code/datums/embedding_behavior.dm +++ /dev/null @@ -1,73 +0,0 @@ -#define EMBEDID "embed-[embed_chance]-[fall_chance]-[pain_chance]-[pain_mult]-[fall_pain_mult]-[impact_pain_mult]-[rip_pain_mult]-[rip_time]-[ignore_throwspeed_threshold]-[jostle_chance]-[jostle_pain_mult]-[pain_stam_pct]" - -/proc/getEmbeddingBehavior(embed_chance = EMBED_CHANCE, - fall_chance = EMBEDDED_ITEM_FALLOUT, - pain_chance = EMBEDDED_PAIN_CHANCE, - pain_mult = EMBEDDED_PAIN_MULTIPLIER, - fall_pain_mult = EMBEDDED_FALL_PAIN_MULTIPLIER, - impact_pain_mult = EMBEDDED_IMPACT_PAIN_MULTIPLIER, - rip_pain_mult = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER, - rip_time = EMBEDDED_UNSAFE_REMOVAL_TIME, - ignore_throwspeed_threshold = FALSE, - jostle_chance = EMBEDDED_JOSTLE_CHANCE, - jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER, - pain_stam_pct = EMBEDDED_PAIN_STAM_PCT) - . = locate(EMBEDID) - if (!.) - . = new /datum/embedding_behavior(embed_chance, fall_chance, pain_chance, pain_mult, fall_pain_mult, impact_pain_mult, rip_pain_mult, rip_time, ignore_throwspeed_threshold, jostle_chance, jostle_pain_mult, pain_stam_pct) - -/datum/embedding_behavior - var/embed_chance - var/fall_chance - var/pain_chance - var/pain_mult //The coefficient of multiplication for the damage this item does while embedded (this*w_class) - var/fall_pain_mult //The coefficient of multiplication for the damage this item does when falling out of a limb (this*w_class) - var/impact_pain_mult //The coefficient of multiplication for the damage this item does when first embedded (this*w_class) - var/rip_pain_mult //The coefficient of multiplication for the damage removing this without surgery causes (this*w_class) - var/rip_time //A time in ticks, multiplied by the w_class. - var/ignore_throwspeed_threshold //if we don't give a damn about EMBED_THROWSPEED_THRESHOLD - var/jostle_chance //Chance to cause pain every time the victim moves (1/2 chance if they're walking or crawling) - var/jostle_pain_mult //The coefficient of multiplication for the damage when jostle damage is applied (this*w_class) - var/pain_stam_pct //Percentage of all pain damage dealt as stamina instead of brute (none by default) - -/datum/embedding_behavior/New(embed_chance = EMBED_CHANCE, - fall_chance = EMBEDDED_ITEM_FALLOUT, - pain_chance = EMBEDDED_PAIN_CHANCE, - pain_mult = EMBEDDED_PAIN_MULTIPLIER, - fall_pain_mult = EMBEDDED_FALL_PAIN_MULTIPLIER, - impact_pain_mult = EMBEDDED_IMPACT_PAIN_MULTIPLIER, - rip_pain_mult = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER, - rip_time = EMBEDDED_UNSAFE_REMOVAL_TIME, - ignore_throwspeed_threshold = FALSE, - jostle_chance = EMBEDDED_JOSTLE_CHANCE, - jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER, - pain_stam_pct = EMBEDDED_PAIN_STAM_PCT) - src.embed_chance = embed_chance - src.fall_chance = fall_chance - src.pain_chance = pain_chance - src.pain_mult = pain_mult - src.fall_pain_mult = fall_pain_mult - src.impact_pain_mult = impact_pain_mult - src.rip_pain_mult = rip_pain_mult - src.rip_time = rip_time - src.ignore_throwspeed_threshold = ignore_throwspeed_threshold - src.jostle_chance = jostle_chance - src.jostle_pain_mult = jostle_pain_mult - src.pain_stam_pct = pain_stam_pct - tag = EMBEDID - -/datum/embedding_behavior/proc/setRating(embed_chance, fall_chance, pain_chance, pain_mult, fall_pain_mult, impact_pain_mult, rip_pain_mult, rip_time, ignore_throwspeed_threshold) - return getEmbeddingBehavior((isnull(embed_chance) ? src.embed_chance : embed_chance),\ - (isnull(fall_chance) ? src.fall_chance : fall_chance),\ - (isnull(pain_chance) ? src.pain_chance : pain_chance),\ - (isnull(pain_mult) ? src.pain_mult : pain_mult),\ - (isnull(fall_pain_mult) ? src.fall_pain_mult : fall_pain_mult),\ - (isnull(impact_pain_mult) ? src.impact_pain_mult : impact_pain_mult),\ - (isnull(rip_pain_mult) ? src.rip_pain_mult : rip_pain_mult),\ - (isnull(rip_time) ? src.rip_time : rip_time),\ - (isnull(ignore_throwspeed_threshold) ? src.ignore_throwspeed_threshold : ignore_throwspeed_threshold),\ - (isnull(jostle_chance) ? src.jostle_chance : jostle_chance),\ - (isnull(jostle_pain_mult) ? src.jostle_pain_mult : jostle_pain_mult),\ - (isnull(pain_stam_pct) ? src.pain_stam_pct : pain_stam_pct)) - -#undef EMBEDID diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm index a2bdec433b76..8f0ed6aed6d3 100644 --- a/code/datums/holocall.dm +++ b/code/datums/holocall.dm @@ -88,6 +88,7 @@ dialed_holopads.Cut() if(calling_holopad) + calling_holopad.calling = FALSE calling_holopad.outgoing_call = null calling_holopad.SetLightsAndPower() calling_holopad = null @@ -154,6 +155,7 @@ if(!Check()) return + calling_holopad.calling = FALSE hologram = H.activate_holo(user) hologram.HC = src @@ -189,7 +191,6 @@ . = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME) if(!.) calling_holopad.say("No answer received.") - calling_holopad.temp = "" if(!.) testing("Holocall Check fail") diff --git a/code/datums/hud.dm b/code/datums/hud.dm index e2b8cfeba482..a3c4884d49b0 100644 --- a/code/datums/hud.dm +++ b/code/datums/hud.dm @@ -13,6 +13,7 @@ GLOBAL_LIST_INIT(huds, list( DATA_HUD_ABDUCTOR = new/datum/atom_hud/abductor(), DATA_HUD_SENTIENT_DISEASE = new/datum/atom_hud/sentient_disease(), DATA_HUD_AI_DETECT = new/datum/atom_hud/ai_detector(), + DATA_HUD_FAN = new/datum/atom_hud/data/human/fan_hud(), ANTAG_HUD_CULT = new/datum/atom_hud/antag(), ANTAG_HUD_REV = new/datum/atom_hud/antag(), ANTAG_HUD_OPS = new/datum/atom_hud/antag(), diff --git a/code/datums/keybinding/human.dm b/code/datums/keybinding/human.dm index 43f5653472c1..60ebff6afb6e 100644 --- a/code/datums/keybinding/human.dm +++ b/code/datums/keybinding/human.dm @@ -8,7 +8,7 @@ /datum/keybinding/human/quick_equip hotkey_keys = list("E") name = "quick_equip" - full_name = "Quick Equip" + full_name = "Quick equip" description = "Quickly puts an item in the best slot available" /datum/keybinding/human/quick_equip/down(client/user) @@ -16,24 +16,44 @@ H.quick_equip() return TRUE -/datum/keybinding/human/quick_equipbelt +/datum/keybinding/human/quick_equip_belt hotkey_keys = list("ShiftE") - name = "quick_equipbelt" + name = "quick_equip_belt" full_name = "Quick equip belt" description = "Put held thing in belt or take out most recent thing from belt" + ///which slot are we trying to quickdraw from/quicksheathe into? + var/slot_type = ITEM_SLOT_BELT + ///what we should call slot_type in messages (including failure messages) + var/slot_item_name = "belt" -/datum/keybinding/human/quick_equipbelt/down(client/user) +/datum/keybinding/human/quick_equip_belt/down(client/user) var/mob/living/carbon/human/H = user.mob - H.smart_equipbelt() + H.smart_equip_targeted(slot_type, slot_item_name) return TRUE -/datum/keybinding/human/bag_equip +/datum/keybinding/human/quick_equip_belt/quick_equip_bag hotkey_keys = list("ShiftB") - name = "bag_equip" - full_name = "Bag equip" + name = "quick_equip_bag" + full_name = "Quick equip bag" description = "Put held thing in backpack or take out most recent thing from backpack" + slot_type = ITEM_SLOT_BACK + slot_item_name = "backpack" -/datum/keybinding/human/bag_equip/down(client/user) +/datum/keybinding/human/quick_equip_belt/quick_equip_suit_storage + hotkey_keys = list("ShiftQ") + name = "quick_equip_suit_storage" + full_name = "Quick equip suit storage slot" + description = "Put held thing in suit storage slot item or take out most recent thing from suit storage slot item" + slot_type = ITEM_SLOT_SUITSTORE + slot_item_name = "suit storage slot item" + +/datum/keybinding/human/equipment_swap + hotkey_keys = list("V") + name = "equipment_swap" + full_name = "Equipment Swap" + description = "Equip the currently held item by swapping it out with the already equipped item after a small delay" + +/datum/keybinding/human/equipment_swap/down(client/user) var/mob/living/carbon/human/H = user.mob - H.smart_equipbag() + H.equipment_swap() return TRUE diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm index 90e1627a2494..c6c7db14c9ea 100644 --- a/code/datums/martial/sleeping_carp.dm +++ b/code/datums/martial/sleeping_carp.dm @@ -28,7 +28,7 @@ ///this var is so that the strong punch is always aiming for the body part the user is targeting and not trying to apply to the chest before deviating var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - var/atk_verb = pick("kick", "chop", "hit", "slam") + var/atk_verb = pick("precisely kick", "brutally chop", "cleanly hit", "viciously slam") ///this is the critical hit damage added to the attack if it rolls, it starts at 0 because it'll be changed when rolled var/crit_damage = 0 D.visible_message("[A] [atk_verb]s [D]!", \ @@ -37,7 +37,7 @@ if(prob(10)) crit_damage += 20 playsound(get_turf(D), 'sound/weapons/bite.ogg', 50, TRUE, -1) - D.visible_message("[D] sputters blood as the blow strikes them with inhuman force!", "You are struck with incredible precision by [A]!") + D.visible_message("[D] is sent reeling as the blow strikes them with inhuman force!", "You are struck with incredible precision by [A]!") log_combat(A, D, "critcal strong punched (Sleeping Carp)")//log it here because a critical can swing for 40 force and it's important for the sake of how hard they hit else playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, TRUE, -1) @@ -63,12 +63,13 @@ playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, TRUE, -1) if((D.mobility_flags & MOBILITY_STAND)) D.apply_damage(10, A.dna.species.attack_type, BODY_ZONE_HEAD) + D.apply_damage(40, STAMINA, BODY_ZONE_HEAD) D.Knockdown(40) D.visible_message("[A] kicks [D] in the head, sending them face first into the floor!", \ "You are kicked in the head by [A], sending you crashing to the floor!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A) - if(!(D.mobility_flags & MOBILITY_STAND)) + else D.apply_damage(5, A.dna.species.attack_type, BODY_ZONE_HEAD) - D.adjustStaminaLoss(40) + D.apply_damage(40, STAMINA, BODY_ZONE_HEAD) D.drop_all_held_items() D.visible_message("[A] kicks [D] in the head!", \ "You are kicked in the head by [A]!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A) diff --git a/code/datums/materials/_material.dm b/code/datums/materials/_material.dm index 74fcea782d38..20390376ad8d 100644 --- a/code/datums/materials/_material.dm +++ b/code/datums/materials/_material.dm @@ -68,6 +68,12 @@ Simple datum which is instanced once per type and is used for every object of sa if(istype(source, /turf)) //turfs on_applied_turf(source, amount, material_flags) + source.mat_update_desc(src) + +///This proc is called when a material updates an object's description +/atom/proc/mat_update_desc(/datum/material/mat) + return + ///This proc is called when the material is added to an object specifically. /datum/material/proc/on_applied_obj(obj/o, amount, material_flags) if(material_flags & MATERIAL_AFFECT_STATISTICS) diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm index f62076698b22..8b74d8e5fac3 100644 --- a/code/datums/materials/basemats.dm +++ b/code/datums/materials/basemats.dm @@ -4,7 +4,7 @@ id = "iron" desc = "Common iron ore often found in sedimentary and igneous layers of the crust." color = "#878687" - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/metal value_per_unit = 0.0025 @@ -15,12 +15,12 @@ desc = "Glass forged by melting sand." color = "#88cdf1" alpha = 150 - categories = list(MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) integrity_modifier = 0.1 sheet_type = /obj/item/stack/sheet/glass value_per_unit = 0.0025 beauty_modifier = 0.05 - armor_modifiers = list("melee" = 0.2, "bullet" = 0.2, "laser" = 0, "energy" = 1, "bomb" = 0, "bio" = 0.2, "rad" = 0.2, "fire" = 1, "acid" = 0.2) // yeah ok retard + armor_modifiers = list("melee" = 0.2, "bullet" = 0.2, "laser" = 0, "energy" = 1, "bomb" = 0, "bio" = 0.2, "rad" = 0.2, "fire" = 1, "acid" = 0.2) /* Color matrices are like regular colors but unlike with normal colors, you can go over 255 on a channel. @@ -33,7 +33,7 @@ Unless you know what you're doing, only use the first three numbers. They're in id = "silver" desc = "Silver" color = list(255/255, 284/255, 302/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/silver value_per_unit = 0.025 beauty_modifier = 0.075 @@ -45,7 +45,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "Gold" color = list(340/255, 240/255, 50/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //gold is shiny, but not as bright as bananium strength_modifier = 1.2 - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/gold value_per_unit = 0.0625 beauty_modifier = 0.15 @@ -57,7 +57,7 @@ Unless you know what you're doing, only use the first three numbers. They're in id = "diamond" desc = "Highly pressurized carbon" color = list(48/255, 272/255, 301/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/diamond alpha = 132 value_per_unit = 0.25 @@ -70,7 +70,7 @@ Unless you know what you're doing, only use the first three numbers. They're in id = "uranium" desc = "Uranium" color = rgb(48, 237, 26) - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/uranium value_per_unit = 0.05 beauty_modifier = 0.3 //It shines so beautiful @@ -90,7 +90,7 @@ Unless you know what you're doing, only use the first three numbers. They're in id = "plasma" desc = "Isn't plasma a state of matter? Oh whatever." color = list(298/255, 46/255, 352/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/plasma value_per_unit = 0.1 beauty_modifier = 0.15 @@ -125,7 +125,7 @@ Unless you know what you're doing, only use the first three numbers. They're in id = "bananium" desc = "Material with hilarious properties" color = list(460/255, 464/255, 0, 0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //obnoxiously bright yellow - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/bananium value_per_unit = 0.5 beauty_modifier = 0.5 @@ -150,7 +150,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "Titanium" color = "#b3c0c7" strength_modifier = 1.3 - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/titanium value_per_unit = 0.0625 beauty_modifier = 0.05 @@ -162,7 +162,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "Runite" color = "#3F9995" strength_modifier = 1.3 - categories = list(MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/runite value_per_unit = 0.3 beauty_modifier = 0.5 @@ -176,7 +176,7 @@ Unless you know what you're doing, only use the first three numbers. They're in color = "#caccd9" strength_modifier = 0.85 sheet_type = /obj/item/stack/sheet/plastic - categories = list(MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) value_per_unit = 0.0125 beauty_modifier = -0.01 armor_modifiers = list("melee" = 1.5, "bullet" = 1.1, "laser" = 0.3, "energy" = 0.5, "bomb" = 1, "bio" = 1, "rad" = 1, "fire" = 1.1, "acid" = 1) @@ -197,8 +197,8 @@ Unless you know what you're doing, only use the first three numbers. They're in color = "#bb8e53" strength_modifier = 0.5 sheet_type = /obj/item/stack/sheet/mineral/wood - categories = list(MAT_CATEGORY_RIGID = TRUE) - value_per_unit = 0.06 + categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) + value_per_unit = 0.01 beauty_modifier = 0.1 armor_modifiers = list("melee" = 1.1, "bullet" = 1.1, "laser" = 0.4, "energy" = 0.4, "bomb" = 1, "bio" = 0.2, "rad" = 0, "fire" = 0, "acid" = 0.3) @@ -221,7 +221,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "A powerful material made out of magic, I mean science!" color = "#6d7e8e" strength_modifier = 1.5 - categories = list(MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/adamantine value_per_unit = 0.25 beauty_modifier = 0.4 @@ -233,7 +233,7 @@ Unless you know what you're doing, only use the first three numbers. They're in id = "mythril" desc = "How this even exists is byond me" color = "#f2d5d7" - categories = list(MAT_CATEGORY_RIGID = TRUE) + categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) sheet_type = /obj/item/stack/sheet/mineral/mythril value_per_unit = 0.75 strength_modifier = 1.2 @@ -259,13 +259,153 @@ Unless you know what you're doing, only use the first three numbers. They're in alpha = 150 categories = list(MAT_CATEGORY_RIGID = TRUE) sheet_type = /obj/item/stack/sheet/hot_ice - value_per_unit = 0.5 - beauty_modifier = 0.5 + value_per_unit = 0.2 + beauty_modifier = 0.2 /datum/material/hot_ice/on_applied(atom/source, amount, material_flags) . = ..() - source.AddComponent(/datum/component/hot_ice, "plasma", amount*50, amount*20+300) + source.AddComponent(/datum/component/hot_ice, "plasma", amount*150, amount*20+300) /datum/material/hot_ice/on_removed(atom/source, amount, material_flags) - qdel(source.GetComponent(/datum/component/hot_ice, "plasma", amount*50, amount*20+300)) + qdel(source.GetComponent(/datum/component/hot_ice, "plasma", amount*150, amount*20+300)) + return ..() + +//I don't like sand. It's coarse, and rough, and irritating, and it gets everywhere. +/datum/material/sand + name = "sand" + id = "sand" + desc = "You know, it's amazing just how structurally sound sand can be." + color = "#EDC9AF" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/sheet/sandblock + value_per_unit = 0.001 + strength_modifier = 0.5 + integrity_modifier = 0.1 + armor_modifiers = list("melee" = 0.25, "bullet" = 0.25, "laser" = 1.25, "energy" = 0.25, "bomb" = 0.25, "bio" = 0.25, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5) + beauty_modifier = 0.25 + turf_sound_override = FOOTSTEP_SAND + texture_layer_icon_state = "sand" + +//And now for our lavaland dwelling friends, sand, but in stone form! Truly revolutionary. +/datum/material/sandstone + name = "sandstone" + id = "sandstone" + desc = "Bialtaakid 'ant taerif ma hdha." + color = "#B77D31" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/sheet/mineral/sandstone + value_per_unit = 0.0025 + armor_modifiers = list("melee" = 0.5, "bullet" = 0.5, "laser" = 1.25, "energy" = 0.5, "bomb" = 0.5, "bio" = 0.25, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5) + beauty_modifier = 0.3 + turf_sound_override = FOOTSTEP_WOOD + texture_layer_icon_state = "brick" + +/datum/material/snow + name = "snow" + id = "snow" + desc = "There's no business like snow business." + color = "#FFFFFF" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/sheet/mineral/snow + value_per_unit = 0.0025 + armor_modifiers = list("melee" = 0.25, "bullet" = 0.25, "laser" = 0.25, "energy" = 0.25, "bomb" = 0.25, "bio" = 0.25, "rad" = 1.5, "fire" = 0.25, "acid" = 1.5) + beauty_modifier = 0.3 + turf_sound_override = FOOTSTEP_SAND + texture_layer_icon_state = "sand" + +/datum/material/runedmetal + name = "runed metal" + id = "runed metal" + desc = "Mir'ntrath barhah Nar'sie." + color = "#3C3434" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/sheet/runed_metal + value_per_unit = 0.75 + armor_modifiers = list("melee" = 1.2, "bullet" = 1.2, "laser" = 1, "energy" = 1, "bomb" = 1.2, "bio" = 1.2, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5) + beauty_modifier = -0.15 + texture_layer_icon_state = "runed" + +/datum/material/bronze + name = "bronze" + id = "bronze" + desc = "Clock Cult? Never heard of it." + color = "#92661A" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/tile/bronze + value_per_unit = 0.025 + armor_modifiers = list("melee" = 1, "bullet" = 1, "laser" = 1, "energy" = 1, "bomb" = 1, "bio" = 1, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5) + beauty_modifier = 0.2 + +/datum/material/paper + name = "paper" + id = "paper" + desc = "Ten thousand folds of pure starchy power." + color = "#E5DCD5" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/sheet/paperframes + value_per_unit = 0.0025 + armor_modifiers = list("melee" = 0.1, "bullet" = 0.1, "laser" = 0.1, "energy" = 0.1, "bomb" = 0.1, "bio" = 0.1, "rad" = 1.5, "fire" = 0, "acid" = 1.5) + beauty_modifier = 0.3 + turf_sound_override = FOOTSTEP_SAND + texture_layer_icon_state = "paper" + +/datum/material/paper/on_applied_obj(obj/source, amount, material_flags) + . = ..() + if(material_flags & MATERIAL_AFFECT_STATISTICS) + var/obj/paper = source + paper.resistance_flags |= FLAMMABLE + paper.obj_flags |= UNIQUE_RENAME + +/datum/material/paper/on_removed_obj(obj/source, material_flags) + if(material_flags & MATERIAL_AFFECT_STATISTICS) + var/obj/paper = source + paper.resistance_flags &= ~FLAMMABLE return ..() + +/datum/material/cardboard + name = "cardboard" + id = "cardboard" + desc = "They say cardboard is used by hobos to make incredible things." + color = "#5F625C" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/sheet/cardboard + value_per_unit = 0.003 + armor_modifiers = list("melee" = 0.25, "bullet" = 0.25, "laser" = 0.25, "energy" = 0.25, "bomb" = 0.25, "bio" = 0.25, "rad" = 1.5, "fire" = 0, "acid" = 1.5) + beauty_modifier = -0.1 + +/datum/material/cardboard/on_applied_obj(obj/source, amount, material_flags) + . = ..() + if(material_flags & MATERIAL_AFFECT_STATISTICS) + var/obj/cardboard = source + cardboard.resistance_flags |= FLAMMABLE + cardboard.obj_flags |= UNIQUE_RENAME + +/datum/material/cardboard/on_removed_obj(obj/source, material_flags) + if(material_flags & MATERIAL_AFFECT_STATISTICS) + var/obj/cardboard = source + cardboard.resistance_flags &= ~FLAMMABLE + return ..() + +/datum/material/bone + name = "bone" + id = "bone" + desc = "Man, building with this will make you the coolest caveman on the block." + color = "#e3dac9" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/sheet/bone + value_per_unit = 0.05 + armor_modifiers = list("melee" = 1.2, "bullet" = 0.75, "laser" = 0.75, "energy" = 1.2, "bomb" = 1, "bio" = 1, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5) + beauty_modifier = -0.2 + +/datum/material/bamboo + name = "bamboo" + id = "bamboo" + desc = "If it's good enough for pandas, it's good enough for you." + color = "#339933" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/sheet/mineral/bamboo + value_per_unit = 0.0025 + armor_modifiers = list("melee" = 0.5, "bullet" = 0.5, "laser" = 0.5, "energy" = 0.5, "bomb" = 0.5, "bio" = 0.51, "rad" = 1.5, "fire" = 0.5, "acid" = 1.5) + beauty_modifier = 0.2 + turf_sound_override = FOOTSTEP_WOOD + texture_layer_icon_state = "bamboo" diff --git a/code/datums/materials/pizza.dm b/code/datums/materials/pizza.dm new file mode 100644 index 000000000000..6ab79e3a2065 --- /dev/null +++ b/code/datums/materials/pizza.dm @@ -0,0 +1,31 @@ +/datum/material/pizza + name = "pizza" + id = "pizza" + desc = "~Jamme, jamme, n'coppa, jamme ja! Jamme, jamme, n'coppa jamme ja, funi-culi funi-cala funi-culi funi-cala!! Jamme jamme ja funiculi funicula!~" + color = "#FF9F23" + categories = list(MAT_CATEGORY_RIGID = TRUE) + sheet_type = /obj/item/stack/sheet/pizza + value_per_unit = 0.05 + beauty_modifier = 0.1 + strength_modifier = 0.7 + armor_modifiers = list("melee" = 0.3, "bullet" = 0.3, "laser" = 1.2, "energy" = 1.2, "bomb" = 0.3, "bio" = 0, "rad" = 0.7, "fire" = 1, "acid" = 1) + item_sound_override = 'sound/effects/meatslap.ogg' + turf_sound_override = FOOTSTEP_MEAT + texture_layer_icon_state = "pizza" + +/datum/material/pizza/on_removed(atom/source, material_flags) + . = ..() + qdel(source.GetComponent(/datum/component/edible)) + +/datum/material/pizza/on_applied_obj(obj/O, amount, material_flags) + . = ..() + make_edible(O, amount, material_flags) + +/datum/material/pizza/on_applied_turf(turf/T, amount, material_flags) + . = ..() + make_edible(T, amount, material_flags) + +/datum/material/pizza/proc/make_edible(atom/source, amount, material_flags) + var/nutriment_count = 3 * (amount / MINERAL_MATERIAL_AMOUNT) + var/oil_count = 2 * (amount / MINERAL_MATERIAL_AMOUNT) + source.AddComponent(/datum/component/edible, list(/datum/reagent/consumable/nutriment = nutriment_count, /datum/reagent/consumable/cooking_oil = oil_count), null, GRAIN | MEAT | DAIRY | VEGETABLES, null, 30, list("crust", "tomato", "cheese", "meat")) diff --git a/code/datums/mutations/_mutations.dm b/code/datums/mutations/_mutations.dm index 5222de9e0426..548a982d0b6b 100644 --- a/code/datums/mutations/_mutations.dm +++ b/code/datums/mutations/_mutations.dm @@ -176,23 +176,22 @@ power.panel = "Genetic" owner.AddSpell(power) return TRUE - // Runs through all the coefficients and uses this to determine which chromosomes the // mutation can take. Stores these as text strings in a list. /datum/mutation/human/proc/update_valid_chromosome_list() valid_chrom_list.Cut() - + if(can_chromosome == CHROMOSOME_NEVER) valid_chrom_list += "none" return - - valid_chrom_list += "reinforcement" - + + valid_chrom_list += "Reinforcement" + if(stabilizer_coeff != -1) - valid_chrom_list += "stabilizer" + valid_chrom_list += "Stabilizer" if(synchronizer_coeff != -1) - valid_chrom_list += "synchronizer" + valid_chrom_list += "Synchronizer" if(power_coeff != -1) - valid_chrom_list += "power" + valid_chrom_list += "Power" if(energy_coeff != -1) - valid_chrom_list += "energetic" + valid_chrom_list += "Energetic" diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm index fad4dc587ff9..e5b3e0e52905 100644 --- a/code/datums/mutations/actions.dm +++ b/code/datums/mutations/actions.dm @@ -371,14 +371,9 @@ var/obj/item/bodypart/L = spikey.checkembedded() - L.embedded_objects -= spikey //this is where it would deal damage, if it transfers chems it removes itself so no damage spikey.forceMove(get_turf(L)) transfered.visible_message("[spikey] falls out of [transfered]!") - if(!transfered.has_embedded_objects()) - transfered.clear_alert("embeddedobject") - SEND_SIGNAL(transfered, COMSIG_CLEAR_MOOD_EVENT, "embedded") - spikey.unembedded() //spider webs /datum/mutation/human/webbing diff --git a/code/datums/mutations/hulk.dm b/code/datums/mutations/hulk.dm index 2d6682f37e56..fe0e5d668a24 100644 --- a/code/datums/mutations/hulk.dm +++ b/code/datums/mutations/hulk.dm @@ -16,10 +16,11 @@ /datum/mutation/human/hulk/on_acquiring(mob/living/carbon/human/owner) if(..()) return - ADD_TRAIT(owner, TRAIT_STUNIMMUNE, TRAIT_HULK) - ADD_TRAIT(owner, TRAIT_PUSHIMMUNE, TRAIT_HULK) - ADD_TRAIT(owner, TRAIT_CHUNKYFINGERS, TRAIT_HULK) - ADD_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, TRAIT_HULK) + ADD_TRAIT(owner, TRAIT_STUNIMMUNE, GENETIC_MUTATION) + ADD_TRAIT(owner, TRAIT_PUSHIMMUNE, GENETIC_MUTATION) + ADD_TRAIT(owner, TRAIT_CHUNKYFINGERS, GENETIC_MUTATION) + ADD_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, GENETIC_MUTATION) + ADD_TRAIT(owner, TRAIT_HULK, GENETIC_MUTATION) owner.update_body_parts() SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "hulk", /datum/mood_event/hulk) RegisterSignal(owner, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, .proc/on_attack_hand) @@ -47,10 +48,11 @@ /datum/mutation/human/hulk/on_losing(mob/living/carbon/human/owner) if(..()) return - REMOVE_TRAIT(owner, TRAIT_STUNIMMUNE, TRAIT_HULK) - REMOVE_TRAIT(owner, TRAIT_PUSHIMMUNE, TRAIT_HULK) - REMOVE_TRAIT(owner, TRAIT_CHUNKYFINGERS, TRAIT_HULK) - REMOVE_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, TRAIT_HULK) + REMOVE_TRAIT(owner, TRAIT_STUNIMMUNE, GENETIC_MUTATION) + REMOVE_TRAIT(owner, TRAIT_PUSHIMMUNE, GENETIC_MUTATION) + REMOVE_TRAIT(owner, TRAIT_CHUNKYFINGERS, GENETIC_MUTATION) + REMOVE_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, GENETIC_MUTATION) + REMOVE_TRAIT(owner, TRAIT_HULK, GENETIC_MUTATION) owner.update_body_parts() SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "hulk") UnregisterSignal(owner, COMSIG_HUMAN_EARLY_UNARMED_ATTACK) diff --git a/code/datums/mutations/speech.dm b/code/datums/mutations/speech.dm index 84584ccac1b8..cc4aff1602a2 100644 --- a/code/datums/mutations/speech.dm +++ b/code/datums/mutations/speech.dm @@ -14,7 +14,7 @@ /datum/mutation/human/wacky name = "Wacky" - desc = "Unknown." + desc = "You are not a clown. You are the entire circus." quality = MINOR_NEGATIVE text_gain_indication = "You feel an off sensation in your voicebox." text_lose_indication = "The off sensation passes." @@ -49,76 +49,6 @@ return REMOVE_TRAIT(owner, TRAIT_MUTE, GENETIC_MUTATION) - -/datum/mutation/human/smile - name = "Smile" - desc = "Causes the user to be in constant mania." - quality = MINOR_NEGATIVE - text_gain_indication = "You feel so happy. Nothing can be wrong with anything. :)" - text_lose_indication = "Everything is terrible again. :(" - -/datum/mutation/human/smile/on_acquiring(mob/living/carbon/human/owner) - if(..()) - return - RegisterSignal(owner, COMSIG_MOB_SAY, .proc/handle_speech) - -/datum/mutation/human/smile/on_losing(mob/living/carbon/human/owner) - if(..()) - return - UnregisterSignal(owner, COMSIG_MOB_SAY) - -/datum/mutation/human/smile/proc/handle_speech(datum/source, list/speech_args) - var/message = speech_args[SPEECH_MESSAGE] - if(message) - message = " [message] " - //Time for a friendly game of SS13 - message = replacetext(message," stupid "," smart ") - message = replacetext(message," retard "," genius ") - message = replacetext(message," unrobust "," robust ") - message = replacetext(message," dumb "," smart ") - message = replacetext(message," awful "," great ") - message = replacetext(message," gay ",pick(" nice "," ok "," alright ")) - message = replacetext(message," horrible "," fun ") - message = replacetext(message," terrible "," terribly fun ") - message = replacetext(message," terrifying "," wonderful ") - message = replacetext(message," gross "," cool ") - message = replacetext(message," disgusting "," amazing ") - message = replacetext(message," loser "," winner ") - message = replacetext(message," useless "," useful ") - message = replacetext(message," oh god "," cheese and crackers ") - message = replacetext(message," jesus "," gee wiz ") - message = replacetext(message," weak "," strong ") - message = replacetext(message," kill "," hug ") - message = replacetext(message," murder "," tease ") - message = replacetext(message," ugly "," beautiful ") - message = replacetext(message," douchbag "," nice guy ") - message = replacetext(message," douchebag "," nice guy ") - message = replacetext(message," whore "," lady ") - message = replacetext(message," nerd "," smart guy ") - message = replacetext(message," moron "," fun person ") - message = replacetext(message," IT'S LOOSE "," EVERYTHING IS FINE ") - message = replacetext(message," sex "," hug fight ") - message = replacetext(message," idiot "," genius ") - message = replacetext(message," fat "," thin ") - message = replacetext(message," beer "," water with ice ") - message = replacetext(message," drink "," water ") - message = replacetext(message," feminist "," empowered woman ") - message = replacetext(message," i hate you "," you're mean ") - message = replacetext(message," nigger "," african american ") - message = replacetext(message," jew "," jewish ") - message = replacetext(message," shit "," shiz ") - message = replacetext(message," crap "," poo ") - message = replacetext(message," slut "," tease ") - message = replacetext(message," ass "," butt ") - message = replacetext(message," damn "," dang ") - message = replacetext(message," fuck "," ") - message = replacetext(message," penis "," privates ") - message = replacetext(message," cunt "," privates ") - message = replacetext(message," dick "," jerk ") - message = replacetext(message," vagina "," privates ") - speech_args[SPEECH_MESSAGE] = trim(message) - - /datum/mutation/human/unintelligible name = "Unintelligible" desc = "Partially inhibits the vocal center of the brain, severely distorting speech." @@ -251,7 +181,6 @@ message = replacetext(message," thanks "," thank you, thank you very much ") message = replacetext(message," what are you "," whatcha ") message = replacetext(message," yes ",pick(" sure", "yea ")) - message = replacetext(message," faggot "," square ") message = replacetext(message," muh valids "," my kicks ") speech_args[SPEECH_MESSAGE] = trim(message) diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm index 2d3ad4551f2c..f9452c989707 100644 --- a/code/datums/progressbar.dm +++ b/code/datums/progressbar.dm @@ -2,81 +2,133 @@ #define PROGRESSBAR_ANIMATION_TIME 5 /datum/progressbar - var/goal = 1 - var/last_progress = 0 + ///The progress bar visual element. var/image/bar - var/shown = 0 + ///The target where this progress bar is applied and where it is shown. + var/atom/bar_loc + ///The mob whose client sees the progress bar. var/mob/user - var/client/client - var/listindex + ///The client seeing the progress bar. + var/client/user_client + ///Effectively the number of steps the progress bar will need to do before reaching completion. + var/goal = 1 + ///Control check to see if the progress was interrupted before reaching its goal. + var/last_progress = 0 + ///Variable to ensure smooth visual stacking on multiple progress bars. + var/listindex = 0 + /datum/progressbar/New(mob/User, goal_number, atom/target) . = ..() if (!istype(target)) EXCEPTION("Invalid target given") - if (goal_number) - goal = goal_number - bar = image('icons/effects/progessbar.dmi', target, "prog_bar_0", HUD_LAYER) + if(QDELETED(User) || !istype(User)) + stack_trace("/datum/progressbar created with [isnull(User) ? "null" : "invalid"] user") + qdel(src) + return + if(!isnum(goal_number)) + stack_trace("/datum/progressbar created with [isnull(User) ? "null" : "invalid"] goal_number") + qdel(src) + return + goal = goal_number + bar_loc = target + bar = image('icons/effects/progessbar.dmi', bar_loc, "prog_bar_0", HUD_LAYER) bar.plane = ABOVE_HUD_PLANE bar.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA user = User - if(user) - client = user.client - LAZYINITLIST(user.progressbars) - LAZYINITLIST(user.progressbars[bar.loc]) - var/list/bars = user.progressbars[bar.loc] - bars.Add(src) + LAZYADDASSOC(user.progressbars, bar_loc, src) + var/list/bars = user.progressbars[bar_loc] listindex = bars.len + + if(user.client) + user_client = user.client + add_prog_bar_image_to_client() + + RegisterSignal(user, COMSIG_PARENT_QDELETING, .proc/on_user_delete) + RegisterSignal(user, COMSIG_MOB_LOGOUT, .proc/clean_user_client) + RegisterSignal(user, COMSIG_MOB_LOGIN, .proc/on_user_login) + + +/datum/progressbar/Destroy() + if(user) + for(var/pb in user.progressbars[bar_loc]) + var/datum/progressbar/progress_bar = pb + if(progress_bar == src || progress_bar.listindex <= listindex) + continue + progress_bar.listindex-- + + progress_bar.bar.pixel_y = 32 + (PROGRESSBAR_HEIGHT * (progress_bar.listindex - 1)) + var/dist_to_travel = 32 + (PROGRESSBAR_HEIGHT * (progress_bar.listindex - 1)) - PROGRESSBAR_HEIGHT + animate(progress_bar.bar, pixel_y = dist_to_travel, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING) + + LAZYREMOVEASSOC(user.progressbars, bar_loc, src) + user = null + + if(user_client) + clean_user_client() + + bar_loc = null + + if(bar) + QDEL_NULL(bar) + + return ..() + + +///Called right before the user's Destroy() +/datum/progressbar/proc/on_user_delete(datum/source) + user.progressbars = null //We can simply nuke the list and stop worrying about updating other prog bars if the user itself is gone. + user = null + qdel(src) + + +///Removes the progress bar image from the user_client and nulls the variable, if it exists. +/datum/progressbar/proc/clean_user_client(datum/source) + if(!user_client) //Disconnected, already gone. + return + user_client.images -= bar + user_client = null + + +///Called by user's Login(), it transfers the progress bar image to the new client. +/datum/progressbar/proc/on_user_login(datum/source) + if(user_client) + if(user_client == user.client) //If this was not client handling I'd condemn this sanity check. But clients are fickle things. + return + clean_user_client() + if(!user.client) //Clients can vanish at any time, the bastards. + return + user_client = user.client + add_prog_bar_image_to_client() + + +///Adds a smoothly-appearing progress bar image to the player's screen. +/datum/progressbar/proc/add_prog_bar_image_to_client() bar.pixel_y = 0 bar.alpha = 0 + user_client.images += bar animate(bar, pixel_y = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)), alpha = 255, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING) -/datum/progressbar/proc/update(progress) - if (!user || !user.client) - shown = FALSE - return - if (user.client != client) - if (client) - client.images -= bar - if (user.client) - user.client.images += bar +///Updates the progress bar image visually. +/datum/progressbar/proc/update(progress) progress = clamp(progress, 0, goal) + if(progress == last_progress) + return last_progress = progress bar.icon_state = "prog_bar_[round(((progress / goal) * 100), 5)]" - if (!shown) - user.client.images += bar - shown = TRUE -/datum/progressbar/proc/shiftDown() - --listindex - bar.pixel_y = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)) - var/dist_to_travel = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)) - PROGRESSBAR_HEIGHT - animate(bar, pixel_y = dist_to_travel, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING) -/datum/progressbar/Destroy() +///Called on progress end, be it successful or a failure. Wraps up things to delete the datum and bar. +/datum/progressbar/proc/end_progress() if(last_progress != goal) bar.icon_state = "[bar.icon_state]_fail" - for(var/I in user.progressbars[bar.loc]) - var/datum/progressbar/P = I - if(P != src && P.listindex > listindex) - P.shiftDown() - - var/list/bars = user.progressbars[bar.loc] - bars.Remove(src) - if(!bars.len) - LAZYREMOVE(user.progressbars, bar.loc) animate(bar, alpha = 0, time = PROGRESSBAR_ANIMATION_TIME) - addtimer(CALLBACK(src, .proc/remove_from_client), PROGRESSBAR_ANIMATION_TIME, TIMER_CLIENT_TIME) - QDEL_IN(bar, PROGRESSBAR_ANIMATION_TIME * 2) //for garbage collection safety - . = ..() -/datum/progressbar/proc/remove_from_client() - if(client) - client.images -= bar - client = null + QDEL_IN(src, PROGRESSBAR_ANIMATION_TIME) + #undef PROGRESSBAR_ANIMATION_TIME #undef PROGRESSBAR_HEIGHT diff --git a/code/datums/radiation_wave.dm b/code/datums/radiation_wave.dm index 5a3c62fb5919..6daff5f821a8 100644 --- a/code/datums/radiation_wave.dm +++ b/code/datums/radiation_wave.dm @@ -118,7 +118,6 @@ // modify the ignored_things list in __HELPERS/radiation.dm instead var/static/list/blacklisted = typecacheof(list( /turf, - /mob, /obj/structure/cable, /obj/machinery/atmospherics, /obj/item/ammo_casing, diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm index 8612c3d3872c..a1cd69108710 100644 --- a/code/datums/ruins/space.dm +++ b/code/datums/ruins/space.dm @@ -295,7 +295,6 @@ suffix = "forgottenship.dmm" name = "Syndicate Forgotten Ship" description = "Seemingly abandoned ship went of course right into NT controlled space. It seems that malfunction caused most systems to turn off, except for sleepers." - /datum/map_template/ruin/space/hellfactory id = "hellfactory" suffix = "hellfactory.dmm" diff --git a/code/datums/shuttles.dm b/code/datums/shuttles.dm index 6479b918b0fb..862b8df4531e 100644 --- a/code/datums/shuttles.dm +++ b/code/datums/shuttles.dm @@ -381,6 +381,13 @@ description = "On the smaller size with a modern design, this shuttle is for the crew who like the cosier things, while still being able to stretch their legs." credit_cost = 1000 +/datum/map_template/shuttle/emergency/cruise + suffix = "cruise" + name = "The NTSS Independence" + description = "Ordinarily reserved for special functions and events, the Cruise Shuttle Independence can bring a summery cheer to your next station evacuation for a 'modest' fee!" + admin_notes = "This motherfucker is BIG. You might need to force dock it." + credit_cost = 50000 + /datum/map_template/shuttle/ferry/base suffix = "base" name = "transport ferry" diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm index 9107e45e051f..a6f49e9c323c 100644 --- a/code/datums/spawners_menu.dm +++ b/code/datums/spawners_menu.dm @@ -9,7 +9,7 @@ /datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "spawners_menu", "Spawners Menu", 700, 600, master_ui, state) + ui = new(user, src, ui_key, "SpawnersMenu", "Spawners Menu", 700, 600, master_ui, state) ui.open() /datum/spawners_menu/ui_data(mob/user) diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 5c53475e2e8d..d4cdaeeac843 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -1,3 +1,5 @@ +#define TRAIT_STATUS_EFFECT(effect_id) "[effect_id]-trait" + //Largely negative status effects go here, even if they have small benificial effects //STUN EFFECTS /datum/status_effect/incapacitating @@ -20,11 +22,22 @@ owner.update_mobility() if(needs_update_stat || issilicon(owner)) //silicons need stat updates in addition to normal canmove updates owner.update_stat() + return ..() //STUN /datum/status_effect/incapacitating/stun id = "stun" +/datum/status_effect/incapacitating/stun/on_apply() + . = ..() + if(!.) + return + ADD_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id)) + +/datum/status_effect/incapacitating/stun/on_remove() + REMOVE_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id)) + return ..() + //KNOCKDOWN /datum/status_effect/incapacitating/knockdown id = "knockdown" @@ -36,11 +49,31 @@ /datum/status_effect/incapacitating/paralyzed id = "paralyzed" +/datum/status_effect/incapacitating/paralyzed/on_apply() + . = ..() + if(!.) + return + ADD_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id)) + +/datum/status_effect/incapacitating/paralyzed/on_remove() + REMOVE_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id)) + return ..() + //UNCONSCIOUS /datum/status_effect/incapacitating/unconscious id = "unconscious" needs_update_stat = TRUE +/datum/status_effect/incapacitating/unconscious/on_apply() + . = ..() + if(!.) + return + ADD_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id)) + +/datum/status_effect/incapacitating/unconscious/on_remove() + REMOVE_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id)) + return ..() + /datum/status_effect/incapacitating/unconscious/tick() if(owner.getStaminaLoss()) owner.adjustStaminaLoss(-0.3) //reduce stamina loss by 0.3 per tick, 6 per 2 seconds @@ -52,18 +85,14 @@ needs_update_stat = TRUE var/mob/living/carbon/carbon_owner var/mob/living/carbon/human/human_owner - var/heal = FALSE // WaspStation Edit -/datum/status_effect/incapacitating/sleeping/on_creation(mob/living/new_owner, updating_canmove, healing) // WaspStation Edit Added healing +/datum/status_effect/incapacitating/sleeping/on_creation(mob/living/new_owner, updating_canmove) . = ..() if(.) if(iscarbon(owner)) //to avoid repeated istypes carbon_owner = owner if(ishuman(owner)) human_owner = owner - heal = healing // WaspStation Edit - -/* WaspStation Edit Moved to WaspStation folder /datum/status_effect/incapacitating/sleeping/Destroy() carbon_owner = null @@ -96,8 +125,6 @@ if(prob(10) && owner.health > owner.crit_threshold) owner.emote("snore") - Wasp Station Edit End */ - /obj/screen/alert/status_effect/asleep name = "Asleep" desc = "You've fallen asleep. Wait a bit and you should wake up. Unless you don't, considering how helpless you are." diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm index 1af6bde32724..aacbbf3f8120 100644 --- a/code/datums/traits/good.dm +++ b/code/datums/traits/good.dm @@ -76,6 +76,8 @@ datum/quirk/fan_clown "hands" = ITEM_SLOT_HANDS, ) H.equip_in_one_of_slots(B, slots , qdel_on_fail = TRUE) + var/datum/atom_hud/fan = GLOB.huds[DATA_HUD_FAN] + fan.add_hud_to(H) datum/quirk/fan_mime name = "Mime Fan" @@ -94,6 +96,8 @@ datum/quirk/fan_mime "hands" = ITEM_SLOT_HANDS, ) H.equip_in_one_of_slots(B, slots , qdel_on_fail = TRUE) + var/datum/atom_hud/fan = GLOB.huds[DATA_HUD_FAN] + fan.add_hud_to(H) /datum/quirk/freerunning name = "Freerunning" diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm index 92a620da874d..455af8f9ff58 100644 --- a/code/datums/traits/negative.dm +++ b/code/datums/traits/negative.dm @@ -111,7 +111,7 @@ if("Cook") heirloom_type = pick(/obj/item/reagent_containers/food/condiment/saltshaker, /obj/item/kitchen/rollingpin, /obj/item/clothing/head/chefhat) if("Botanist") - heirloom_type = pick(/obj/item/cultivator, /obj/item/reagent_containers/glass/bucket, /obj/item/storage/bag/plants, /obj/item/toy/plush/beeplushie) + heirloom_type = pick(/obj/item/cultivator, /obj/item/reagent_containers/glass/bucket, /obj/item/toy/plush/beeplushie) if("Bartender") heirloom_type = pick(/obj/item/reagent_containers/glass/rag, /obj/item/clothing/head/that, /obj/item/reagent_containers/food/drinks/shaker) if("Curator") @@ -125,6 +125,8 @@ heirloom_type = /obj/item/reagent_containers/food/drinks/flask/gold if("Head of Security") heirloom_type = /obj/item/book/manual/wiki/security_space_law + if("Head of Personnel") + heirloom_type = /obj/item/reagent_containers/food/drinks/trophy/silver_cup if("Warden") heirloom_type = /obj/item/book/manual/wiki/security_space_law if("Security Officer") @@ -144,21 +146,21 @@ heirloom_type = /obj/item/toy/plush/slimeplushie if("Roboticist") heirloom_type = pick(subtypesof(/obj/item/toy/prize)) //look at this nerd + if("Geneticist") + heirloom_type = /obj/item/clothing/under/shorts/purple //Medical if("Chief Medical Officer") - heirloom_type = pick(/obj/item/clothing/neck/stethoscope, /obj/item/bodybag) + heirloom_type = /obj/item/storage/firstaid/ancient/heirloom if("Medical Doctor") - heirloom_type = pick(/obj/item/clothing/neck/stethoscope, /obj/item/bodybag) + heirloom_type = /obj/item/storage/firstaid/ancient/heirloom if("Paramedic") - heirloom_type = pick(/obj/item/clothing/neck/stethoscope, /obj/item/bodybag) + heirloom_type = /obj/item/storage/firstaid/ancient/heirloom if("Psychologist") heirloom_type = /obj/item/storage/pill_bottle if("Chemist") heirloom_type = /obj/item/book/manual/wiki/chemistry if("Virologist") heirloom_type = /obj/item/reagent_containers/syringe - if("Geneticist") - heirloom_type = /obj/item/clothing/under/shorts/purple //Engineering if("Chief Engineer") heirloom_type = pick(/obj/item/clothing/head/hardhat/white, /obj/item/screwdriver, /obj/item/wrench, /obj/item/weldingtool, /obj/item/crowbar, /obj/item/wirecutters) @@ -183,7 +185,8 @@ var/list/slots = list( "in your left pocket" = ITEM_SLOT_LPOCKET, "in your right pocket" = ITEM_SLOT_RPOCKET, - "in your backpack" = ITEM_SLOT_BACKPACK + "in your backpack" = ITEM_SLOT_BACKPACK, + "in your hands" = ITEM_SLOT_HANDS ) where = H.equip_in_one_of_slots(heirloom, slots, FALSE) || "at your feet" diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm index 55263a326a12..46d1a7261f68 100644 --- a/code/datums/wires/_wires.dm +++ b/code/datums/wires/_wires.dm @@ -219,7 +219,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if (!ui) - ui = new(user, src, ui_key, "wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state) + ui = new(user, src, ui_key, "Wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state) ui.open() /datum/wires/ui_data(mob/user) diff --git a/code/datums/wires/conveyor.dm b/code/datums/wires/conveyor.dm new file mode 100644 index 000000000000..0e82fb13cfbd --- /dev/null +++ b/code/datums/wires/conveyor.dm @@ -0,0 +1,17 @@ +/datum/wires/conveyor + holder_type = /obj/machinery/conveyor_switch + proper_name = "conveyor" + /// var holder that logs who put the assembly inside and gets transfered to the switch on pulse + var/mob/fingerman + +/datum/wires/conveyor/New(atom/holder) + add_duds(1) + ..() + +/datum/wires/conveyor/on_pulse(wire) + var/obj/machinery/conveyor_switch/C = holder + C.interact(fingerman) + +/datum/wires/conveyor/interactable(mob/user) + fingerman = user + return TRUE diff --git a/code/datums/wires/mulebot.dm b/code/datums/wires/mulebot.dm index 1d34ae1dd02b..9c71c9568f21 100644 --- a/code/datums/wires/mulebot.dm +++ b/code/datums/wires/mulebot.dm @@ -18,11 +18,14 @@ /datum/wires/mulebot/on_pulse(wire) var/mob/living/simple_animal/bot/mulebot/M = holder + if(!M.has_power(TRUE)) + return //logically mulebots can't flash and beep if they don't have power. switch(wire) if(WIRE_POWER1, WIRE_POWER2) holder.visible_message("[icon2html(M, viewers(holder))] The charge light flickers.") if(WIRE_AVOIDANCE) holder.visible_message("[icon2html(M, viewers(holder))] The external warning lights flash briefly.") + flick("[M.base_icon]1", M) if(WIRE_LOADCHECK) holder.visible_message("[icon2html(M, viewers(holder))] The load platform clunks.") if(WIRE_MOTOR1, WIRE_MOTOR2) diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index 6a2fa7481b57..c24e40150b3d 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -33,9 +33,9 @@ if(!R.emagged) var/new_ai if(user) - new_ai = select_active_ai(user) + new_ai = select_active_ai(user, R.z) else - new_ai = select_active_ai(R) + new_ai = select_active_ai(R, R.z) R.notify_ai(DISCONNECT) if(new_ai && (new_ai != R.connected_ai)) R.connected_ai = new_ai diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm index 3e1a58a1e5be..46217d334283 100644 --- a/code/datums/wires/vending.dm +++ b/code/datums/wires/vending.dm @@ -24,6 +24,7 @@ status += "The red light is [V.shoot_inventory ? "off" : "blinking"]." status += "The green light is [V.extended_inventory ? "on" : "off"]." status += "A [V.scan_id ? "purple" : "yellow"] light is on." + status += "A white light is [V.age_restrictions ? "on" : "off"]." status += "The speaker light is [V.shut_up ? "off" : "on"]." return status @@ -40,6 +41,8 @@ V.scan_id = !V.scan_id if(WIRE_SPEAKER) V.shut_up = !V.shut_up + if(WIRE_AGELIMIT) + V.age_restrictions = !V.age_restrictions /datum/wires/vending/on_cut(wire, mend) var/obj/machinery/vending/V = holder diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm index 649f052ae7b4..5c08e8349df2 100644 --- a/code/datums/world_topic.dm +++ b/code/datums/world_topic.dm @@ -27,11 +27,16 @@ /datum/world_topic/proc/TryRun(list/input) key_valid = config && (CONFIG_GET(string/comms_key) == input["key"]) - if(require_comms_key && !key_valid) - return "Bad Key" input -= "key" - . = Run(input) - if(islist(.)) + if(require_comms_key && !key_valid) + . = "Bad Key" + if (input["format"] == "json") + . = list("error" = .) + else + . = Run(input) + if (input["format"] == "json") + . = json_encode(.) + else if(islist(.)) . = list2params(.) /datum/world_topic/proc/Run(list/input) @@ -149,6 +154,8 @@ .["players"] = GLOB.clients.len .["revision"] = GLOB.revdata.commit .["revision_date"] = GLOB.revdata.date + .["hub"] = GLOB.hub_visibility + var/list/adm = get_admin_counts() var/list/presentmins = adm["present"] @@ -183,7 +190,7 @@ .["hard_popcap"] = CONFIG_GET(number/hard_popcap) || 0 .["extreme_popcap"] = CONFIG_GET(number/extreme_popcap) || 0 .["popcap"] = max(CONFIG_GET(number/soft_popcap), CONFIG_GET(number/hard_popcap), CONFIG_GET(number/extreme_popcap)) //generalized field for this concept for use across ss13 codebases - + .["bunkered"] = CONFIG_GET(flag/panic_bunker) || FALSE if(SSshuttle && SSshuttle.emergency) .["shuttle_mode"] = SSshuttle.emergency.mode // Shuttle status, see /__DEFINES/stat.dm diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm index a148308d00e6..c67662928ac1 100644 --- a/code/game/area/Space_Station_13_areas.dm +++ b/code/game/area/Space_Station_13_areas.dm @@ -791,6 +791,9 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/medical/psychology name = "Psychology Office" icon_state = "psychology" + mood_bonus = 3 + mood_message = "I feel at ease here.\n" + ambientsounds = list('sound/ambience/aurora_caelus_short.ogg') //Security diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 4c2b70c7668a..486bf49ec647 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -43,12 +43,6 @@ var/power_equip = TRUE var/power_light = TRUE var/power_environ = TRUE - var/used_equip = 0 - var/used_light = 0 - var/used_environ = 0 - var/static_equip - var/static_light = 0 - var/static_environ var/has_gravity = 0 ///Are you forbidden from teleporting to the area? (centcom, mobs, wizard, hand teleporter) @@ -78,6 +72,9 @@ /// Wasp Addition - Color on minimaps, if it's null (which is default) it makes one at random. var/minimap_color + + var/list/power_usage + /** * A list of teleport locations @@ -128,6 +125,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) // rather than waiting for atoms to initialize. if (unique) GLOB.areas_by_type[type] = src + power_usage = new /list(AREA_USAGE_LEN) // Some atoms would like to use power in Initialize() return ..() /** @@ -474,11 +472,11 @@ GLOBAL_LIST_EMPTY(teleportlocs) if(always_unpowered) return 0 switch(chan) - if(EQUIP) + if(AREA_USAGE_EQUIP) return power_equip - if(LIGHT) + if(AREA_USAGE_LIGHT) return power_light - if(ENVIRON) + if(AREA_USAGE_ENVIRON) return power_environ return 0 @@ -492,51 +490,27 @@ GLOBAL_LIST_EMPTY(teleportlocs) /** * Called when the area power status changes * - * Updates the area icon and calls power change on all machinees in the area + * Updates the area icon, calls power change on all machinees in the area, and sends the `COMSIG_AREA_POWER_CHANGE` signal. */ /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.) + SEND_SIGNAL(src, COMSIG_AREA_POWER_CHANGE) update_icon() -/** - * Return the usage of power per channel - */ -/area/proc/usage(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 /** * Add a static amount of power load to an area * * Possible channels - * *STATIC_EQUIP - * *STATIC_LIGHT - * *STATIC_ENVIRON + * *AREA_USAGE_STATIC_EQUIP + * *AREA_USAGE_STATIC_LIGHT + * *AREA_USAGE_STATIC_ENVIRON */ /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 + if(AREA_USAGE_STATIC_START to AREA_USAGE_STATIC_END) + power_usage[powerchannel] += value /** * Clear all power usage in area @@ -544,22 +518,17 @@ GLOBAL_LIST_EMPTY(teleportlocs) * Clears all power used for equipment, light and environment channels */ /area/proc/clear_usage() - used_equip = 0 - used_light = 0 - used_environ = 0 + for(var/i in AREA_USAGE_DYNAMIC_START to AREA_USAGE_DYNAMIC_END) + power_usage[i] = 0 /** * Add a power value amount to the stored used_x variables */ /area/proc/use_power(amount, chan) - switch(chan) - if(EQUIP) - used_equip += amount - if(LIGHT) - used_light += amount - if(ENVIRON) - used_environ += amount + if(AREA_USAGE_DYNAMIC_START to AREA_USAGE_DYNAMIC_END) + power_usage[chan] += amount + /** * Call back when an atom enters an area diff --git a/code/game/area/areas/holodeck.dm b/code/game/area/areas/holodeck.dm index e511afe361db..0f324d3edba2 100644 --- a/code/game/area/areas/holodeck.dm +++ b/code/game/area/areas/holodeck.dm @@ -24,26 +24,19 @@ ASSERT(!istype(A, /area/holodeck)) return A.powered(chan) -/area/holodeck/usage(var/chan) - if(!linked) - return 0 - var/area/A = get_area(linked) - ASSERT(!istype(A, /area/holodeck)) - return A.usage(chan) - /area/holodeck/addStaticPower(value, powerchannel) if(!linked) return var/area/A = get_area(linked) ASSERT(!istype(A, /area/holodeck)) - return A.addStaticPower(value,powerchannel) + return ..() /area/holodeck/use_power(amount, chan) if(!linked) return 0 var/area/A = get_area(linked) ASSERT(!istype(A, /area/holodeck)) - return A.use_power(amount,chan) + return ..() /* diff --git a/code/game/atoms.dm b/code/game/atoms.dm index df1cfd733de0..e08e6a15f387 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -16,6 +16,13 @@ ///Intearaction flags var/interaction_flags_atom = NONE + var/flags_ricochet = NONE + + ///When a projectile tries to ricochet off this atom, the projectile ricochet chance is multiplied by this + var/ricochet_chance_mod = 1 + ///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom + var/ricochet_damage_mod = 0.33 + ///Reagents holder var/datum/reagents/reagents = null @@ -60,6 +67,8 @@ var/custom_price ///Economy cost of item in premium vendor var/custom_premium_price + ///Whether spessmen with an ID with an age below AGE_MINOR (20 by default) can buy this item + var/age_restricted = FALSE //List of datums orbiting this atom var/datum/component/orbiter/orbiters @@ -80,6 +89,19 @@ var/list/alternate_appearances + ///Mobs that are currently do_after'ing this atom, to be cleared from on Destroy() + var/list/targeted_by + + /// Last appearance of the atom for demo saving purposes + var/image/demo_last_appearance + + /// Last name used to calculate a color for the chatmessage overlays + var/chat_color_name + /// Last color calculated for the the chatmessage overlays + var/chat_color + /// A luminescence-shifted value of the last color calculated for chatmessage overlays + var/chat_color_darkened + /** * Called when an atom is created in byond (built in engine proc) * @@ -146,6 +168,9 @@ stack_trace("Warning: [src]([type]) initialized multiple times!") flags_1 |= INITIALIZED_1 + if(loc) + SEND_SIGNAL(loc, COMSIG_ATOM_CREATED, src) /// Sends a signal that the new atom `src`, has been created at `loc` + //atom color stuff if(color) add_atom_colour(color, FIXED_COLOUR_PRIORITY) @@ -213,12 +238,29 @@ LAZYCLEARLIST(overlays) LAZYCLEARLIST(priority_overlays) + for(var/i in targeted_by) + var/mob/M = i + LAZYREMOVE(M.do_afters, src) + + targeted_by = null QDEL_NULL(light) return ..() /atom/proc/handle_ricochet(obj/projectile/P) - return + 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)) + var/a_incidence_s = abs(incidence_s) + if(a_incidence_s > 90 && a_incidence_s < 270) + return FALSE + if((P.flag in list("bullet", "bomb")) && P.ricochet_incidence_leeway) + if((a_incidence_s < 90 && a_incidence_s < 90 - P.ricochet_incidence_leeway) || (a_incidence_s > 270 && a_incidence_s -270 > P.ricochet_incidence_leeway)) + return + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + P.setAngle(new_angle_s) + return TRUE ///Can the mover object pass this atom, while heading for the target turf /atom/proc/CanPass(atom/movable/mover, turf/target) @@ -399,6 +441,26 @@ /atom/proc/is_drainable() return reagents && (reagents.flags & DRAINABLE) +/** Handles exposing this atom to a list of reagents. + * + * Sends COMSIG_ATOM_EXPOSE_REAGENTS + * Calls expose_atom() for every reagent in the reagent list. + * + * Arguments: + * - [reagents][/list]: The list of reagents the atom is being exposed to. + * - [source][/datum/reagents]: The reagent holder the reagents are being sourced from. + * - method: How the atom is being exposed to the reagents. + * - volume_modifier: Volume multiplier. + * - show_message: Whether to display anything to mobs when they are exposed. + */ +/atom/proc/expose_reagents(list/reagents, datum/reagents/source, method=TOUCH, volume_modifier=1, show_message=TRUE) + if((. = SEND_SIGNAL(src, COMSIG_ATOM_EXPOSE_REAGENTS, reagents, source, method, volume_modifier, show_message)) & COMPONENT_NO_EXPOSE_REAGENTS) + return + + for(var/reagent in reagents) + var/datum/reagent/R = reagent + . |= R.expose_atom(src, reagents[R]) + /// Are you allowed to drop this atom /atom/proc/AllowDrop() return FALSE @@ -650,8 +712,8 @@ else return FALSE -///Called when gravity returns after floating I think -/atom/proc/handle_fall() +///Used for making a sound when a mob involuntarily falls into the ground. +/atom/proc/handle_fall(mob/faller) return ///Respond to the singularity eating this atom @@ -752,7 +814,7 @@ var/datum/component/storage/STR = GetComponent(/datum/component/storage) while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(STR, /datum/component/storage.proc/handle_mass_item_insertion, things, src_object, user, progress))) stoplag(1) - qdel(progress) + progress.end_progress() to_chat(user, "You dump as much of [src_object.parent]'s contents [STR.insert_preposition]to [src] as you can.") STR.orient2hud(user) src_object.orient2hud(user) @@ -1137,6 +1199,8 @@ log_comment(log_text) if(LOG_TELECOMMS) log_telecomms(log_text) + if(LOG_ECON) + log_econ(log_text) if(LOG_OOC) log_ooc(log_text) if(LOG_ADMIN) @@ -1226,6 +1290,11 @@ if(filter_data && filter_data[name]) return filters[filter_data.Find(name)] +/atom/movable/proc/remove_filter(name) + if(filter_data && filter_data[name]) + filter_data -= name + update_filters() + /atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels) @@ -1300,3 +1369,17 @@ max_grav = max(G.setting,max_grav) return max_grav return SSmapping.level_trait(T.z, ZTRAIT_GRAVITY) + +/** + * Called when a mob examines (shift click or verb) this atom twice (or more) within EXAMINE_MORE_TIME (default 1.5 seconds) + * + * This is where you can put extra information on something that may be superfluous or not important in critical gameplay + * moments, while allowing people to manually double-examine to take a closer look + * + * Produces a signal [COMSIG_PARENT_EXAMINE_MORE] + */ +/atom/proc/examine_more(mob/user) + . = list() + SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE_MORE, user, .) + if(!LAZYLEN(.)) // lol ..length + return list("You examine [src] closer, but find nothing of interest...") diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 5774e1b1e27c..04e8ca7802ea 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -43,6 +43,9 @@ var/zfalling = FALSE + ///Last location of the atom for demo recording purposes + var/atom/demo_last_loc + /// Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE] var/blocks_emissive = FALSE ///Internal holder for emissive blocker object, do not use directly use blocks_emissive @@ -602,7 +605,7 @@ return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, force, gentle) ///If this returns FALSE then callback will not be called. -/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE) +/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE, quickstart = TRUE) . = FALSE if (!target || speed <= 0) return @@ -687,7 +690,8 @@ SSthrowing.processing[src] = TT if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun)) SSthrowing.currentrun[src] = TT - TT.tick() + if (quickstart) + TT.tick() /atom/movable/proc/handle_buckled_mob_movement(newloc,direct) for(var/m in buckled_mobs) diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 4dbafaa202cd..f06e636e3609 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -49,6 +49,9 @@ /datum/atom_hud/data/human/security/advanced hud_icons = list(ID_HUD, IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD, WANTED_HUD, NANITE_HUD) +/datum/atom_hud/data/human/fan_hud + hud_icons = list(FAN_HUD) + /datum/atom_hud/data/diagnostic /datum/atom_hud/data/diagnostic/basic @@ -210,6 +213,24 @@ holder.icon_state = "hudhealthy" +/*********************************************** + FAN HUDs! For identifying other fans on-sight. +************************************************/ + +//HOOKS + +/mob/living/carbon/human/proc/fan_hud_set_fandom() + var/image/holder = hud_list[FAN_HUD] + var/icon/I = icon(icon, icon_state, dir) + holder.pixel_y = I.Height() - world.icon_size + holder.icon_state = "hudfan_no" + var/obj/item/clothing/under/U = get_item_by_slot(ITEM_SLOT_ICLOTHING) + if(U) + if(istype(U.attached_accessory, /obj/item/clothing/accessory/fan_mime_pin)) + holder.icon_state = "fan_mime_pin" + else if(istype(U.attached_accessory, /obj/item/clothing/accessory/fan_clown_pin)) + holder.icon_state = "fan_clown_pin" + /*********************************************** Security HUDs! Basic mode shows only the job. ************************************************/ diff --git a/code/game/gamemodes/clown_ops/clown_ops.dm b/code/game/gamemodes/clown_ops/clown_ops.dm index 255f13520cf1..5017b1d38c06 100644 --- a/code/game/gamemodes/clown_ops/clown_ops.dm +++ b/code/game/gamemodes/clown_ops/clown_ops.dm @@ -39,6 +39,8 @@ id = /obj/item/card/id/syndicate backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\ /obj/item/kitchen/knife/combat/survival, + /obj/item/dnainjector/clumsymut, //in case you want to be clumsy for the memes + /obj/item/storage/box/syndie_kit/clownpins, //for any guns that you get your grubby little clown op mitts on /obj/item/reagent_containers/spray/waterflower/lube) implants = list(/obj/item/implant/sad_trombone) @@ -47,15 +49,8 @@ /datum/outfit/syndicate/clownop/no_crystals tc = 0 -/datum/outfit/syndicate/clownop/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - if(visualsOnly) - return - H.dna.add_mutation(CLOWNMUT) - /datum/outfit/syndicate/clownop/leader name = "Clown Operative Leader - Basic" id = /obj/item/card/id/syndicate/nuke_leader gloves = /obj/item/clothing/gloves/krav_maga/combatglovesplus - r_hand = /obj/item/nuclear_challenge/clownops command_radio = TRUE diff --git a/code/game/gamemodes/clown_ops/clown_weapons.dm b/code/game/gamemodes/clown_ops/clown_weapons.dm index 95e632f63f54..146a3c3d338f 100644 --- a/code/game/gamemodes/clown_ops/clown_weapons.dm +++ b/code/game/gamemodes/clown_ops/clown_weapons.dm @@ -138,7 +138,7 @@ var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery) slipper.signal_enabled = active -/obj/item/shield/energy/bananium/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE) +/obj/item/shield/energy/bananium/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE) if(active) if(iscarbon(thrower)) var/mob/living/carbon/C = thrower diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index 33a1fc8dd86a..7cb23b74ba0d 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -81,7 +81,7 @@ antag_datum = /datum/antagonist/rev/head antag_flag = ROLE_REV_HEAD antag_flag_override = ROLE_REV - restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director") + restricted_roles = list("AI", "Cyborg", "Prisoner", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director") enemy_roles = list("AI", "Cyborg", "Security Officer","Detective","Head of Security", "Captain", "Warden") required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 606e8f052a6e..aa2720cfa04f 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -466,7 +466,7 @@ valid_positions += GLOB.medical_positions valid_positions += GLOB.science_positions valid_positions += GLOB.supply_positions - valid_positions += GLOB.civilian_positions + valid_positions += GLOB.service_positions valid_positions += GLOB.security_positions if(CONFIG_GET(flag/reopen_roundstart_suicide_roles_command_positions)) valid_positions += GLOB.command_positions //add any remaining command positions @@ -707,7 +707,7 @@ var/list/human_garbage = list() round_credits += "

The Hardy Civilians:

" len_before_addition = round_credits.len - for(var/datum/mind/current in SSticker.mode.get_all_by_department(GLOB.civilian_positions)) + for(var/datum/mind/current in SSticker.mode.get_all_by_department(GLOB.service_positions)) if(current.assigned_role == "Assistant") human_garbage += current else diff --git a/code/game/gamemodes/gang/gang.dm b/code/game/gamemodes/gang/gang.dm index 97bb8d25ad35..381535838f9d 100644 --- a/code/game/gamemodes/gang/gang.dm +++ b/code/game/gamemodes/gang/gang.dm @@ -30,6 +30,7 @@ GLOBAL_VAR_INIT(deaths_during_shift, 0) protected_jobs = list() var/check_counter = 0 var/endtime = null + var/start_time = null var/fuckingdone = FALSE var/time_to_end = 60 MINUTES var/gangs_to_generate = 3 @@ -41,10 +42,9 @@ GLOBAL_VAR_INIT(deaths_during_shift, 0) var/gangs_still_alive = 0 var/sent_announcement = FALSE var/list/gang_locations = list() - var/lock_stars = FALSE var/cops_arrived = FALSE var/gang_balance_cap = 5 - var/current_stars = "wanted_0" + var/wanted_level = 0 /datum/game_mode/gang/warriors name = "Warriors" @@ -69,6 +69,8 @@ GLOBAL_VAR_INIT(deaths_during_shift, 0) log_game("[key_name(gangbanger)] has been selected as a starting gangster!") antag_candidates.Remove(gangbanger) for(var/j = 0, j < gangs_to_generate, j++) + if(!antag_candidates.len) + break var/datum/mind/one_eight_seven_on_an_undercover_cop = antag_pick(antag_candidates) pigs += one_eight_seven_on_an_undercover_cop undercover_cops += one_eight_seven_on_an_undercover_cop @@ -76,6 +78,7 @@ GLOBAL_VAR_INIT(deaths_during_shift, 0) log_game("[key_name(one_eight_seven_on_an_undercover_cop)] has been selected as a starting undercover cop!") antag_candidates.Remove(one_eight_seven_on_an_undercover_cop) endtime = world.time + time_to_end + start_time = world.time return TRUE /datum/game_mode/gang/post_setup() @@ -112,7 +115,6 @@ GLOBAL_VAR_INIT(deaths_during_shift, 0) for(var/datum/mind/undercover_cop in undercover_cops) var/datum/antagonist/ert/families/undercover_cop/one_eight_seven_on_an_undercover_cop = new() undercover_cop.add_antag_datum(one_eight_seven_on_an_undercover_cop) - undercover_cop.current.playsound_local(undercover_cop.current, 'sound/effects/families_police.ogg', 100, FALSE, pressure_affected = FALSE) for(var/datum/mind/gangbanger in gangbangers) var/gang_to_use = pick_n_take(gangs_to_use) @@ -188,44 +190,7 @@ GLOBAL_VAR_INIT(deaths_during_shift, 0) return FALSE /datum/game_mode/gang/process() - if(sent_announcement) - for(var/mob/M in GLOB.player_list) - if(M.hud_used && !istype(M, /mob/dead/new_player)) - var/datum/hud/H = M.hud_used - var/icon_state_to_use = "wanted_1" - if(lock_stars) - H.wanted_lvl.icon_state = current_stars - continue - if(GLOB.joined_player_list.len > LOWPOP_FAMILIES_COUNT) - switch(GLOB.deaths_during_shift) - if(0 to TWO_STARS_HIGHPOP-1) - icon_state_to_use = "wanted_1" - if(TWO_STARS_HIGHPOP to THREE_STARS_HIGHPOP-1) - icon_state_to_use = "wanted_2" - if(THREE_STARS_HIGHPOP to FOUR_STARS_HIGHPOP-1) - icon_state_to_use = "wanted_3" - if(FOUR_STARS_HIGHPOP to FIVE_STARS_HIGHPOP-1) - icon_state_to_use = "wanted_4" - if(FIVE_STARS_HIGHPOP to INFINITY) - icon_state_to_use = "wanted_5" - else - switch(GLOB.deaths_during_shift) - if(0 to TWO_STARS_LOW-1) - icon_state_to_use = "wanted_1" - if(TWO_STARS_LOW to THREE_STARS_LOW-1) - icon_state_to_use = "wanted_2" - if(THREE_STARS_LOW to FOUR_STARS_LOW-1) - icon_state_to_use = "wanted_3" - if(FOUR_STARS_LOW to FIVE_STARS_LOW-1) - icon_state_to_use = "wanted_4" - if(FIVE_STARS_LOW to INFINITY) - icon_state_to_use = "wanted_5" - if(cops_arrived) - icon_state_to_use += "_active" - lock_stars = TRUE - current_stars = icon_state_to_use - H.wanted_lvl.icon_state = icon_state_to_use - + check_wanted_level() check_counter++ if(check_counter >= 5) if (world.time > endtime && !fuckingdone) @@ -238,61 +203,142 @@ GLOBAL_VAR_INIT(deaths_during_shift, 0) check_gang_clothes() check_rollin_with_crews() +///Checks if our wanted level has changed. Only actually does something post the initial announcement and until the cops are here. After that its locked. +/datum/game_mode/gang/proc/check_wanted_level() + if(!sent_announcement || cops_arrived) + return + var/new_wanted_level + if(GLOB.joined_player_list.len > LOWPOP_FAMILIES_COUNT) + switch(GLOB.deaths_during_shift) + if(0 to TWO_STARS_HIGHPOP-1) + new_wanted_level = 1 + if(TWO_STARS_HIGHPOP to THREE_STARS_HIGHPOP-1) + new_wanted_level = 2 + if(THREE_STARS_HIGHPOP to FOUR_STARS_HIGHPOP-1) + new_wanted_level = 3 + if(FOUR_STARS_HIGHPOP to FIVE_STARS_HIGHPOP-1) + new_wanted_level = 4 + if(FIVE_STARS_HIGHPOP to INFINITY) + new_wanted_level = 5 + else + switch(GLOB.deaths_during_shift) + if(0 to TWO_STARS_LOW-1) + new_wanted_level = 1 + if(TWO_STARS_LOW to THREE_STARS_LOW-1) + new_wanted_level = 2 + if(THREE_STARS_LOW to FOUR_STARS_LOW-1) + new_wanted_level = 3 + if(FOUR_STARS_LOW to FIVE_STARS_LOW-1) + new_wanted_level = 4 + if(FIVE_STARS_LOW to INFINITY) + new_wanted_level = 5 + update_wanted_level(new_wanted_level) + +///Updates the icon states for everyone and sends outs announcements regarding the police. +/datum/game_mode/gang/proc/update_wanted_level(newlevel) + if(newlevel > wanted_level) + on_gain_wanted_level(newlevel) + else if (newlevel < wanted_level) + on_lower_wanted_level(newlevel) + wanted_level = newlevel + for(var/i in GLOB.player_list) + var/mob/M = i + if(!M.hud_used?.wanted_lvl) + continue + var/datum/hud/H = M.hud_used + H.wanted_lvl.level = newlevel + H.wanted_lvl.cops_arrived = cops_arrived + H.wanted_lvl.update_icon() + +/datum/game_mode/gang/proc/on_gain_wanted_level(newlevel) + var/announcement_message + switch(newlevel) + if(2) + announcement_message = "Small amount of police vehicles have been spotted en route towards [station_name()]. They will arrive at the 50 minute mark." + endtime = start_time + 50 MINUTES + if(3) + announcement_message = "A large detachment police vehicles have been spotted en route towards [station_name()]. They will arrive at the 40 minute mark." + endtime = start_time + 40 MINUTES + if(4) + announcement_message = "A detachment of top-trained agents has been spotted on their way to [station_name()]. They will arrive at the 35 minute mark." + endtime = start_time + 35 MINUTES + if(5) + announcement_message = "The fleet enroute to [station_name()] now consists of national guard personnel. They will arrive at the 30 minute mark." + endtime = start_time + 30 MINUTES + priority_announce(announcement_message, "Station Spaceship Detection Systems") + +/datum/game_mode/gang/proc/on_lower_wanted_level(newlevel) + var/announcement_message + switch(newlevel) + if(1) + announcement_message = "There are now only a few police vehicle headed towards [station_name()]. They will arrive at the 60 minute mark." + endtime = start_time + 60 MINUTES + if(2) + announcement_message = "There seem to be fewer police vehicles headed towards [station_name()]. They will arrive at the 50 minute mark." + endtime = start_time + 50 MINUTES + if(3) + announcement_message = "There are no longer top-trained agents in the fleet headed towards [station_name()]. They will arrive at the 40 minute mark." + endtime = start_time + 40 MINUTES + if(4) + announcement_message = "The convoy enroute to [station_name()] seems to no longer consist of national guard personnel. They will arrive at the 35 minute mark." + endtime = start_time + 35 MINUTES + priority_announce(announcement_message, "Station Spaceship Detection Systems") + /datum/game_mode/gang/proc/send_in_the_fuzz() var/team_size var/cops_to_send var/announcement_message = "PUNK ASS BALLA BITCH" var/announcer = "Spinward Stellar Coalition" if(GLOB.joined_player_list.len > LOWPOP_FAMILIES_COUNT) - switch(GLOB.deaths_during_shift) - if(0 to TWO_STARS_HIGHPOP-1) + switch(wanted_level) + if(1) team_size = 8 cops_to_send = /datum/antagonist/ert/families/beatcop announcement_message = "Hello, crewmembers of [station_name()]! We've received a few calls about some potential violent gang activity on board your station, so we're sending some beat cops to check things out. Nothing extreme, just a courtesy call. However, while they check things out for about 10 minutes, we're going to have to ask that you keep your escape shuttle parked.\n\nHave a pleasant day!" announcer = "Spinward Stellar Coalition Police Department" - if(TWO_STARS_HIGHPOP to THREE_STARS_HIGHPOP-1) + if(2) team_size = 9 cops_to_send = /datum/antagonist/ert/families/beatcop/armored announcement_message = "Crewmembers of [station_name()]. We have received confirmed reports of violent gang activity from your station. We are dispatching some armed officers to help keep the peace and investigate matters. Do not get in their way, and comply with any and all requests from them. We have blockaded the local warp gate, and your shuttle cannot depart for another 10 minutes.\n\nHave a secure day." announcer = "Spinward Stellar Coalition Police Department" - if(THREE_STARS_HIGHPOP to FOUR_STARS_HIGHPOP-1) + if(3) team_size = 10 cops_to_send = /datum/antagonist/ert/families/beatcop/swat announcement_message = "Crewmembers of [station_name()]. We have received confirmed reports of extreme gang activity from your station resulting in heavy civilian casualties. The Spinward Stellar Coalition does not tolerate abuse towards our citizens, and we will be responding in force to keep the peace and reduce civilian casualties. We have your station surrounded, and all gangsters must drop their weapons and surrender peacefully.\n\nHave a secure day." announcer = "Spinward Stellar Coalition Police Department" - if(FOUR_STARS_HIGHPOP to FIVE_STARS_HIGHPOP-1) + if(4) team_size = 11 cops_to_send = /datum/antagonist/ert/families/beatcop/fbi announcement_message = "We are dispatching our top agents to [station_name()] at the request of the Spinward Stellar Coalition government due to an extreme terrorist level threat against this Nanotrasen owned station. All gangsters must surrender IMMEDIATELY. Failure to comply can and will result in death. We have blockaded your warp gates and will not allow any escape until the situation is resolved within our standard response time of 10 minutes.\n\nSurrender now or face the consequences of your actions." announcer = "Federal Bureau of Investigation" - if(FIVE_STARS_HIGHPOP to INFINITY) + if(5) team_size = 12 cops_to_send = /datum/antagonist/ert/families/beatcop/military announcement_message = "Due to an insane level of civilian casualties aboard [station_name()], we have dispatched the National Guard to curb any and all gang activity on board the station. We have heavy cruisers watching the shuttle. Attempt to leave before we allow you to, and we will obliterate your station and your escape shuttle.\n\nYou brought this on yourselves by murdering so many civilians." announcer = "Spinward Stellar Coalition National Guard" else - switch(GLOB.deaths_during_shift) - if(0 to TWO_STARS_LOW-1) + switch(wanted_level) + if(1) team_size = 5 cops_to_send = /datum/antagonist/ert/families/beatcop announcement_message = "Hello, crewmembers of [station_name()]! We've received a few calls about some potential violent gang activity on board your station, so we're sending some beat cops to check things out. Nothing extreme, just a courtesy call. However, while they check things out for about 10 minutes, we're going to have to ask that you keep your escape shuttle parked.\n\nHave a pleasant day!" announcer = "Spinward Stellar Coalition Police Department" - if(TWO_STARS_LOW to THREE_STARS_LOW-1) + if(2) team_size = 6 cops_to_send = /datum/antagonist/ert/families/beatcop/armored announcement_message = "Crewmembers of [station_name()]. We have received confirmed reports of violent gang activity from your station. We are dispatching some armed officers to help keep the peace and investigate matters. Do not get in their way, and comply with any and all requests from them. We have blockaded the local warp gate, and your shuttle cannot depart for another 10 minutes.\n\nHave a secure day." announcer = "Spinward Stellar Coalition Police Department" - if(THREE_STARS_LOW to FOUR_STARS_LOW-1) + if(3) team_size = 7 cops_to_send = /datum/antagonist/ert/families/beatcop/swat announcement_message = "Crewmembers of [station_name()]. We have received confirmed reports of extreme gang activity from your station resulting in heavy civilian casualties. The Spinward Stellar Coalition does not tolerate abuse towards our citizens, and we will be responding in force to keep the peace and reduce civilian casualties. We have your station surrounded, and all gangsters must drop their weapons and surrender peacefully.\n\nHave a secure day." announcer = "Spinward Stellar Coalition Police Department" - if(FOUR_STARS_LOW to FIVE_STARS_LOW-1) + if(4) team_size = 8 cops_to_send = /datum/antagonist/ert/families/beatcop/fbi announcement_message = "We are dispatching our top agents to [station_name()] at the request of the Spinward Stellar Coalition government due to an extreme terrorist level threat against this Nanotrasen owned station. All gangsters must surrender IMMEDIATELY. Failure to comply can and will result in death. We have blockaded your warp gates and will not allow any escape until the situation is resolved within our standard response time of 10 minutes.\n\nSurrender now or face the consequences of your actions." announcer = "Federal Bureau of Investigation" - if(FIVE_STARS_LOW to INFINITY) + if(5) team_size = 10 cops_to_send = /datum/antagonist/ert/families/beatcop/military announcement_message = "Due to an insane level of civilian casualties aboard [station_name()], we have dispatched the National Guard to curb any and all gang activity on board the station. We have heavy cruisers watching the shuttle. Attempt to leave before we allow you to, and we will obliterate your station and your escape shuttle.\n\nYou brought this on yourselves by murdering so many civilians." @@ -333,6 +379,7 @@ GLOBAL_VAR_INIT(deaths_during_shift, 0) log_game("[key_name(cop)] has been selected as an [ert_antag.name]") numagents-- cops_arrived = TRUE + update_wanted_level() //Will make sure our icon updates properly addtimer(CALLBACK(src, .proc/end_hostile_sit), 10 MINUTES) return TRUE diff --git a/code/game/gamemodes/gang/gang_things.dm b/code/game/gamemodes/gang/gang_things.dm index 735196759e23..d28bb2eaedfe 100644 --- a/code/game/gamemodes/gang/gang_things.dm +++ b/code/game/gamemodes/gang/gang_things.dm @@ -27,7 +27,11 @@ var/datum/game_mode/gang/F = SSticker.mode var/datum/antagonist/gang/swappin_sides = new gang_to_use() user.mind.add_antag_datum(swappin_sides) + var/policy = get_policy(ROLE_FAMILIES) + if(policy) + to_chat(user, policy) swappin_sides.my_gang = team_to_use + user.playsound_local(user, 'sound/ambience/antag/thatshowfamiliesworks.ogg', 100, FALSE, pressure_affected = FALSE) team_to_use.add_member(user.mind) for(var/threads in team_to_use.free_clothes) new threads(get_turf(user)) diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index ff65fcef1b74..cb024c308eff 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -82,7 +82,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event ////////////////////// /obj/effect/meteor - name = "the concept of meteor" + name = "\proper the concept of meteor" desc = "You should probably run instead of gawking at this." icon = 'icons/obj/meteor.dmi' icon_state = "small" @@ -144,11 +144,23 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event if(A != src) if(isliving(A)) A.visible_message("[src] slams into [A].", "[src] slams into you!.") - A.ex_act(hitpwr) + switch(hitpwr) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += A + if(EXPLODE_HEAVY) + SSexplosions.medobj += A + if(EXPLODE_LIGHT) + SSexplosions.lowobj += A //then, ram the turf if it still exists if(T) - T.ex_act(hitpwr) + switch(hitpwr) + if(EXPLODE_DEVASTATE) + SSexplosions.highturf += T + if(EXPLODE_HEAVY) + SSexplosions.medturf += T + if(EXPLODE_LIGHT) + SSexplosions.lowturf += T diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 0e08ce802b9a..29e282c1c391 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -21,9 +21,9 @@ Class Variables: 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 + AREA_USAGE_EQUIP:0 -- Equipment Channel + AREA_USAGE_LIGHT:2 -- Lighting Channel + AREA_USAGE_ENVIRON:3 -- Environment Channel component_parts (list) A list of component parts of machine used by frame based machines. @@ -53,11 +53,11 @@ Class Procs: 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' + powered(chan = -1) 'modules/power/power.dm' Checks to see if area that contains the object has power available for power - channel given in 'chan'. + channel given in 'chan'. -1 defaults to power_channel - use_power(amount, chan=EQUIP) 'modules/power/power.dm' + use_power(amount, chan=-1) 'modules/power/power.dm' Deducts 'amount' from the power channel 'chan' of the area that contains the object. power_change() 'modules/power/power.dm' @@ -92,6 +92,8 @@ Class Procs: pressure_resistance = 15 max_integrity = 200 layer = BELOW_OBJ_LAYER //keeps shit coming out of the machine from ending up underneath it. + flags_ricochet = RICOCHET_HARD + ricochet_chance_mod = 0.3 anchored = TRUE interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT @@ -103,8 +105,8 @@ Class Procs: //2 = run auto, use active var/idle_power_usage = 0 var/active_power_usage = 0 - var/power_channel = EQUIP - //EQUIP,ENVIRON or LIGHT + var/power_channel = AREA_USAGE_EQUIP + //AREA_USAGE_EQUIP,AREA_USAGE_ENVIRON or AREA_USAGE_LIGHT var/wire_compatible = FALSE var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. @@ -113,7 +115,10 @@ Class Procs: var/critical_machine = FALSE //If this machine is critical to station operation and should have the area be excempted from power failures. var/list/occupant_typecache //if set, turned into typecache in Initialize, other wise, defaults to mob/living typecache var/atom/movable/occupant = null - var/speed_process = FALSE // Process as fast as possible? + /// Viable flags to go here are START_PROCESSING_ON_INIT, or START_PROCESSING_MANUALLY. See code\__DEFINES\machines.dm for more information on these flags. + var/processing_flags = START_PROCESSING_ON_INIT + /// What subsystem this machine will use, which is generally SSmachines or SSfastprocess. By default all machinery use SSmachines. This fires a machine's process() roughly every 2 seconds. + var/subsystem_type = /datum/controller/subsystem/machines var/obj/item/circuitboard/circuit // Circuit to be created and inserted when the machinery is created var/interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_SET_MACHINE @@ -137,16 +142,24 @@ Class Procs: circuit = new circuit circuit.apply_default_parts(src) - if(!speed_process) - START_PROCESSING(SSmachines, src) - else - START_PROCESSING(SSfastprocess, src) + if(processing_flags & START_PROCESSING_ON_INIT) + begin_processing() - if (occupant_typecache) + if(occupant_typecache) occupant_typecache = typecacheof(occupant_typecache) return INITIALIZE_HINT_LATELOAD +/// Helper proc for telling a machine to start processing with the subsystem type that is located in its `subsystem_type` var. +/obj/machinery/proc/begin_processing() + var/datum/controller/subsystem/processing/subsystem = locate(subsystem_type) in Master.subsystems + START_PROCESSING(subsystem, src) + +/// Helper proc for telling a machine to stop processing with the subsystem type that is located in its `subsystem_type` var. +/obj/machinery/proc/end_processing() + var/datum/controller/subsystem/processing/subsystem = locate(subsystem_type) in Master.subsystems + STOP_PROCESSING(subsystem, src) + /obj/machinery/LateInitialize() . = ..() power_change() @@ -154,10 +167,7 @@ Class Procs: /obj/machinery/Destroy() GLOB.machines.Remove(src) - if(!speed_process) - STOP_PROCESSING(SSmachines, src) - else - STOP_PROCESSING(SSfastprocess, src) + end_processing() dropContents() if(length(component_parts)) for(var/atom/A in component_parts) @@ -238,24 +248,36 @@ Class Procs: return !(machine_stat & (NOPOWER|BROKEN|MAINT)) /obj/machinery/can_interact(mob/user) - var/silicon = issiliconoradminghost(user) - if((machine_stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE)) + if((machine_stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE)) // Check if the machine is broken, and if we can still interact with it if so return FALSE - if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN)) + + var/silicon = issilicon(user) + if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN)) // Check if we can interact with an open panel machine, if the panel is open if(!silicon || !(interaction_flags_machine & INTERACT_MACHINE_OPEN_SILICON)) return FALSE - if(silicon) + if(silicon || IsAdminGhost(user)) // If we are an AI or adminghsot, make sure the machine allows silicons to interact if(!(interaction_flags_machine & INTERACT_MACHINE_ALLOW_SILICON)) return FALSE - else - if(interaction_flags_machine & INTERACT_MACHINE_REQUIRES_SILICON) + + else if(isliving(user)) // If we are a living human + var/mob/living/L = user + + if(interaction_flags_machine & INTERACT_MACHINE_REQUIRES_SILICON) // First make sure the machine doesn't require silicon interaction return FALSE - if(!Adjacent(user)) - var/mob/living/carbon/H = user + + if(!Adjacent(user)) // Next make sure we are next to the machine unless we have telekinesis + var/mob/living/carbon/H = L if(!(istype(H) && H.has_dna() && H.dna.check_mutation(TK))) return FALSE - return TRUE + + if(L.incapacitated()) // Finally make sure we aren't incapacitated + return FALSE + + else // If we aren't a silicon, living, or admin ghost, bad! + return FALSE + + return TRUE // If we pass all these checks, woohoo! We can interact /obj/machinery/proc/check_nap_violations() if(!SSeconomy.full_ancap) diff --git a/code/game/machinery/airlock_control.dm b/code/game/machinery/airlock_control.dm index a213cb2a8169..200488b9cbf5 100644 --- a/code/game/machinery/airlock_control.dm +++ b/code/game/machinery/airlock_control.dm @@ -90,7 +90,7 @@ name = "airlock sensor" resistance_flags = FIRE_PROOF - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON var/id_tag var/master_tag diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm index ac09ce38e95e..9df4e22a48fb 100644 --- a/code/game/machinery/announcement_system.dm +++ b/code/game/machinery/announcement_system.dm @@ -99,25 +99,24 @@ GLOBAL_LIST_EMPTY(announcement_systems) //config stuff -/obj/machinery/announcement_system/ui_interact(mob/user) +/obj/machinery/announcement_system/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) . = ..() - if(!user.canUseTopic(src, !issilicon(user))) - return - if(machine_stat & BROKEN) - visible_message("[src] buzzes.", "You hear a faint buzz.") - playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, TRUE) - return - - - var/contents = "Arrival Announcement: ([(arrivalToggle ? "On" : "Off")])
\n[arrival]

\n" - contents += "Departmental Head Announcement: ([(newheadToggle ? "On" : "Off")])
\n[newhead]

\n" - - var/datum/browser/popup = new(user, "announcement_config", "Automated Announcement Configuration", 370, 220) - popup.set_content(contents) - popup.open() - -/obj/machinery/announcement_system/Topic(href, href_list) - if(..()) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "AutomatedAnnouncement", "Automated Announcement System", 500, 225, master_ui, state) + ui.open() + +/obj/machinery/announcement_system/ui_data() + var/list/data = list() + data["arrival"] = arrival + data["arrivalToggle"] = arrivalToggle + data["newhead"] = newhead + data["newheadToggle"] = newheadToggle + return data + +/obj/machinery/announcement_system/ui_act(action, param) + . = ..() + if(.) return if(!usr.canUseTopic(src, !issilicon(usr))) return @@ -125,30 +124,28 @@ GLOBAL_LIST_EMPTY(announcement_systems) visible_message("[src] buzzes.", "You hear a faint buzz.") playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, TRUE) return - - if(href_list["ArrivalTopic"]) - var/NewMessage = stripped_input(usr, "Enter in the arrivals announcement configuration.", "Arrivals Announcement Config", arrival) - if(!usr.canUseTopic(src, !issilicon(usr))) - return - if(NewMessage) - arrival = NewMessage - log_game("The arrivals announcement was updated: [NewMessage] by:[key_name(usr)]") - else if(href_list["NewheadTopic"]) - var/NewMessage = stripped_input(usr, "Enter in the departmental head announcement configuration.", "Head Departmental Announcement Config", newhead) - if(!usr.canUseTopic(src, !issilicon(usr))) - return - if(NewMessage) - newhead = NewMessage - log_game("The head announcement was updated: [NewMessage] by:[key_name(usr)]") - else if(href_list["NewheadT-Topic"]) - newheadToggle = !newheadToggle - update_icon() - else if(href_list["ArrivalT-Topic"]) - arrivalToggle = !arrivalToggle - update_icon() - + switch(action) + if("ArrivalText") + var/NewMessage = trim(html_encode(param["newText"]), MAX_MESSAGE_LEN) + if(!usr.canUseTopic(src, !issilicon(usr))) + return + if(NewMessage) + arrival = NewMessage + log_game("The arrivals announcement was updated: [NewMessage] by:[key_name(usr)]") + if("NewheadText") + var/NewMessage = trim(html_encode(param["newText"]), MAX_MESSAGE_LEN) + if(!usr.canUseTopic(src, !issilicon(usr))) + return + if(NewMessage) + newhead = NewMessage + log_game("The head announcement was updated: [NewMessage] by:[key_name(usr)]") + if("NewheadToggle") + newheadToggle = !newheadToggle + update_icon() + if("ArrivalToggle") + arrivalToggle = !arrivalToggle + update_icon() add_fingerprint(usr) - interact(usr) /obj/machinery/announcement_system/attack_robot(mob/living/silicon/user) . = attack_ai(user) diff --git a/code/game/machinery/autodoc.dm b/code/game/machinery/autodoc.dm index b12feb096bf1..c3caf76b233f 100644 --- a/code/game/machinery/autodoc.dm +++ b/code/game/machinery/autodoc.dm @@ -149,12 +149,12 @@ else overlays += "[icon_state]_door_off" if(occupant) - if(powered(EQUIP)) + if(powered(AREA_USAGE_EQUIP)) overlays += "[icon_state]_stack" overlays += "[icon_state]_yellow" else overlays += "[icon_state]_red" - else if(powered(EQUIP)) + else if(powered(AREA_USAGE_EQUIP)) overlays += "[icon_state]_red" if(panel_open) overlays += "[icon_state]_panel" diff --git a/code/game/machinery/bank_machine.dm b/code/game/machinery/bank_machine.dm index c75ed084a098..1f3c57abcf00 100644 --- a/code/game/machinery/bank_machine.dm +++ b/code/game/machinery/bank_machine.dm @@ -65,7 +65,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "bank_machine", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "BankMachine", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/bank_machine/ui_data(mob/user) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index d99e70f754eb..97a8e811293d 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/stationobjs.dmi' icon_state = "doorctrl" var/skin = "doorctrl" - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON var/obj/item/assembly/device var/obj/item/electronics/airlock/board var/device_type = null diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index c847d71476fa..2a4fd3452fd4 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -6,7 +6,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 5 active_power_usage = 60 - power_channel = EQUIP + power_channel = AREA_USAGE_EQUIP circuit = /obj/item/circuitboard/machine/cell_charger pass_flags = PASSTABLE var/obj/item/stock_parts/cell/charging = null diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index fdb3751872c0..c3fcdd8d0e6e 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -23,12 +23,12 @@ find_table() /obj/machinery/computer/operating/Destroy() - for(var/direction in GLOB.cardinals) - table = locate(/obj/structure/table/optable, get_step(src, direction)) + for(var/direction in GLOB.alldirs) + table = locate(/obj/structure/table/optable) in get_step(src, direction) if(table && table.computer == src) table.computer = null else - sbed = locate(/obj/machinery/stasis, get_step(src, direction)) + sbed = locate(/obj/machinery/stasis) in get_step(src, direction) if(sbed && sbed.op_computer == src) sbed.op_computer = null . = ..() @@ -52,13 +52,13 @@ advanced_surgeries |= D.surgery /obj/machinery/computer/operating/proc/find_table() - for(var/direction in GLOB.cardinals) - table = locate(/obj/structure/table/optable, get_step(src, direction)) + for(var/direction in GLOB.alldirs) + table = locate(/obj/structure/table/optable) in get_step(src, direction) if(table) table.computer = src break else - sbed = locate(/obj/machinery/stasis, get_step(src, direction)) + sbed = locate(/obj/machinery/stasis) in get_step(src, direction) if(sbed) sbed.op_computer = src break @@ -66,7 +66,7 @@ /obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "operating_computer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "OperatingComputer", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/operating/ui_data(mob/user) @@ -82,21 +82,20 @@ data["patient"] = null if(table) data["table"] = table - if(!table.check_patient()) + if(!table.check_eligible_patient()) return data data["patient"] = list() patient = table.patient else if(sbed) data["table"] = sbed - if(!sbed.check_patient()) + if(!ishuman(sbed.occupant) && !ismonkey(sbed.occupant)) return data data["patient"] = list() patient = sbed.occupant else data["patient"] = null return data - switch(patient.stat) if(CONSCIOUS) data["patient"]["stat"] = "Conscious" @@ -118,8 +117,8 @@ data["patient"]["fireLoss"] = patient.getFireLoss() data["patient"]["toxLoss"] = patient.getToxLoss() data["patient"]["oxyLoss"] = patient.getOxyLoss() + data["procedures"] = list() if(patient.surgeries.len) - data["procedures"] = list() for(var/datum/surgery/procedure in patient.surgeries) var/datum/surgery_step/surgery_step = procedure.get_surgery_step() var/chems_needed = surgery_step.get_chem_list() diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index 6ffef5fb5db1..a918a6bc88f8 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -26,7 +26,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "ai_restorer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AiRestorer", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/aifixer/ui_data(mob/user) diff --git a/code/game/machinery/computer/apc_control.dm b/code/game/machinery/computer/apc_control.dm index 10994a9f4c2d..b3b2c02a9ea9 100644 --- a/code/game/machinery/computer/apc_control.dm +++ b/code/game/machinery/computer/apc_control.dm @@ -3,19 +3,21 @@ desc = "Used to remotely control the flow of power to different parts of the station." icon_screen = "solar" icon_keyboard = "power_key" - req_access = list(ACCESS_ENGINE) + req_access = list(ACCESS_CE) circuit = /obj/item/circuitboard/computer/apc_control light_color = LIGHT_COLOR_YELLOW var/mob/living/operator //Who's operating the computer right now var/obj/machinery/power/apc/active_apc //The APC we're using right now - var/list/result_filters //For sorting the results - var/checking_logs = 0 + var/should_log = TRUE + var/restoring = FALSE var/list/logs - var/auth_id = "\[NULL\]" + var/auth_id = "\[NULL\]:" + ui_x = 550 + ui_y = 500 -/obj/machinery/computer/apc_control/Initialize() +/obj/machinery/computer/apc_control/Initialize(mapload, obj/item/circuitboard/C) . = ..() - result_filters = list("Name" = null, "Charge Above" = null, "Charge Below" = null, "Responsive" = null) + logs = list() /obj/machinery/computer/apc_control/process() if(operator && (!operator.Adjacent(src) || machine_stat)) @@ -34,170 +36,164 @@ if(!IsAdminGhost(user)) to_chat(user,"[src] does not support AI control.") //You already have APC access, cheater! return - ..(user) + ..() /obj/machinery/computer/apc_control/proc/check_apc(obj/machinery/power/apc/APC) return APC.z == z && !APC.malfhack && !APC.aidisabled && !(APC.obj_flags & EMAGGED) && !APC.machine_stat && !istype(APC.area, /area/ai_monitored) && !APC.area.outdoors -/obj/machinery/computer/apc_control/ui_interact(mob/living/user) - . = ..() - var/dat - if(authenticated) - if(!checking_logs) - dat += "Logged in as [auth_id].

" - dat += "Filters
" - dat += "Name: [result_filters["Name"] ? result_filters["Name"] : "None set"]
" - dat += "Charge: \>[result_filters["Charge Above"] ? result_filters["Charge Above"] : "NaN"]% and \<[result_filters["Charge Below"] ? result_filters["Charge Below"] : "NaN"]%
" - dat += "Accessible: [result_filters["Responsive"] ? "Non-Responsive Only" : "All"]

" - for(var/A in GLOB.apcs_list) - if(check_apc(A)) - var/obj/machinery/power/apc/APC = A - if(result_filters["Name"] && !findtext(APC.name, result_filters["Name"]) && !findtext(APC.area.name, result_filters["Name"])) - continue - if(result_filters["Charge Above"] && (!APC.cell || (APC.cell && (APC.cell.charge / APC.cell.maxcharge) < result_filters["Charge Above"] / 100))) - continue - if(result_filters["Charge Below"] && APC.cell && (APC.cell.charge / APC.cell.maxcharge) > result_filters["Charge Below"] / 100) - continue - if(result_filters["Responsive"] && !APC.aidisabled) - continue - dat += "[A]
\ - Charge: [APC.cell ? "[DisplayEnergy(APC.cell.charge)] / [DisplayEnergy(APC.cell.maxcharge)] ([round((APC.cell.charge / APC.cell.maxcharge) * 100)]%)" : "No Powercell Installed"]
\ - Area: [APC.area]
\ - [APC.aidisabled || APC.panel_open ? "APC does not respond to interface query." : "APC responds to interface query."]

" - dat += "Check Logs
" - dat += "Log Out
" - if(obj_flags & EMAGGED) - dat += "WARNING: Logging functionality partially disabled from outside source.
" - dat += "Restore logging functionality?
" - else - if(logs.len) - for(var/entry in logs) - dat += "[entry]
" - else - dat += "No activity has been recorded at this time.
" - if(obj_flags & EMAGGED) - dat += "@#%! CLEAR LOGS" - dat += "Return" - operator = user - else - dat = "Please swipe a valid ID to log in..." - var/datum/browser/popup = new(user, "apc_control", name, 600, 400) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() +/obj/machinery/computer/apc_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state. + operator = user + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "ApcControl", "APC Controller", ui_x, ui_y, master_ui, state) + ui.open() + + +/obj/machinery/computer/apc_control/ui_data(mob/user) + var/list/data = list() + data["auth_id"] = auth_id + data["authenticated"] = authenticated + data["emagged"] = obj_flags & EMAGGED + data["logging"] = should_log + data["restoring"] = restoring + data["logs"] = list() + data["apcs"] = list() -/obj/machinery/computer/apc_control/Topic(href, href_list) + for(var/entry in logs) + data["logs"] += list(list("entry" = entry)) + + for(var/apc in GLOB.apcs_list) + if(check_apc(apc)) + var/obj/machinery/power/apc/A = apc + var/has_cell = (A.cell) ? TRUE : FALSE + data["apcs"] += list(list( + "name" = A.area.name, + "operating" = A.operating, + "charge" = (has_cell) ? A.cell.percent() : "NOCELL", + "load" = DisplayPower(A.lastused_total), + "charging" = A.charging, + "chargeMode" = A.chargemode, + "eqp" = A.equipment, + "lgt" = A.lighting, + "env" = A.environ, + "responds" = A.aidisabled || A.panel_open, + "ref" = REF(A) + ) + ) + return data + +/obj/machinery/computer/apc_control/ui_act(action, params) if(..()) return - if(!usr || !usr.canUseTopic(src, !issilicon(usr)) || machine_stat || QDELETED(src)) - return - if(href_list["authenticate"]) - var/obj/item/card/id/ID = usr.get_idcard(TRUE) - if(ID && istype(ID)) - if(check_access(ID)) + switch(action) + if("log-in") + if(obj_flags & EMAGGED) authenticated = TRUE - auth_id = "[ID.registered_name] ([ID.assignment])" - log_activity("logged in") - playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE) - if(href_list["log_out"]) - log_activity("logged out") - playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE) - authenticated = FALSE - auth_id = "\[NULL\]" - if(href_list["restore_logging"]) - to_chat(usr, "[icon2html(src, usr)] Logging functionality restored from backup data.") - obj_flags &= ~EMAGGED - LAZYADD(logs, "-=- Logging restored to full functionality at this point -=-") - if(href_list["access_apc"]) - playsound(src, "terminal_type", 50, FALSE) - var/obj/machinery/power/apc/APC = locate(href_list["access_apc"]) in GLOB.apcs_list - if(!APC || APC.aidisabled || APC.panel_open || QDELETED(APC)) - to_chat(usr, "[icon2html(src, usr)] APC does not return interface request. Remote access may be disabled.") - return - if(active_apc) - to_chat(usr, "[icon2html(src, usr)] Disconnected from [active_apc].") - active_apc.say("Remote access canceled. Interface locked.") - playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, FALSE) - playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, FALSE) - active_apc.locked = TRUE - active_apc.update_icon() - active_apc.remote_control = null - active_apc = null - to_chat(usr, "[icon2html(src, usr)] Connected to APC in [get_area_name(APC.area, TRUE)]. Interface request sent.") - log_activity("remotely accessed APC in [get_area_name(APC.area, TRUE)]") - APC.remote_control = src - APC.ui_interact(usr) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) - message_admins("[ADMIN_LOOKUPFLW(usr)] remotely accessed [APC] from [src] at [AREACOORD(src)].") - log_game("[key_name(usr)] remotely accessed [APC] from [src] at [AREACOORD(src)].") - if(APC.locked) - APC.say("Remote access detected. Interface unlocked.") - playsound(APC, 'sound/machines/boltsup.ogg', 25, FALSE) - playsound(APC, 'sound/machines/terminal_alert.ogg', 50, FALSE) - APC.locked = FALSE - APC.update_icon() - active_apc = APC - if(href_list["name_filter"]) - playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE) - var/new_filter = stripped_input(usr, "What name are you looking for?", name) - if(!src || !usr || !usr.canUseTopic(src, !issilicon(usr)) || machine_stat || QDELETED(src)) - return - log_activity("changed name filter to \"[new_filter]\"") - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) - result_filters["Name"] = new_filter - if(href_list["above_filter"]) - playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE) - var/new_filter = input(usr, "Enter a percentage from 1-100 to sort by (greater than).", name) as null|num - if(!src || !usr || !usr.canUseTopic(src, !issilicon(usr)) || machine_stat || QDELETED(src)) - return - log_activity("changed greater than charge filter to \"[new_filter]\"") - if(new_filter) - new_filter = clamp(new_filter, 0, 100) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) - result_filters["Charge Above"] = new_filter - if(href_list["below_filter"]) - playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE) - var/new_filter = input(usr, "Enter a percentage from 1-100 to sort by (lesser than).", name) as null|num - if(!src || !usr || !usr.canUseTopic(src, !issilicon(usr)) || machine_stat || QDELETED(src)) - return - log_activity("changed lesser than charge filter to \"[new_filter]\"") - if(new_filter) - new_filter = clamp(new_filter, 0, 100) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) - result_filters["Charge Below"] = new_filter - if(href_list["access_filter"]) - if(isnull(result_filters["Responsive"])) - result_filters["Responsive"] = 1 - log_activity("sorted by non-responsive APCs only") - else - result_filters["Responsive"] = !result_filters["Responsive"] - log_activity("sorted by all APCs") - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) - if(href_list["check_logs"]) - checking_logs = TRUE - log_activity("checked logs") - if(href_list["check_apcs"]) - checking_logs = FALSE - log_activity("checked APCs") - if(href_list["clear_logs"]) - logs = list() - ui_interact(usr) //Refresh the UI after a filter changes + auth_id = "Unknown (Unknown):" + log_activity("[auth_id] logged in to the terminal") + return + var/obj/item/card/id/ID = operator.get_idcard(TRUE) + if(ID && istype(ID)) + if(check_access(ID)) + authenticated = TRUE + auth_id = "[ID.registered_name] ([ID.assignment]):" + log_activity("[auth_id] logged in to the terminal") + playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE) + else + auth_id = "[ID.registered_name] ([ID.assignment]):" + log_activity("[auth_id] attempted to log into the terminal") + return + auth_id = "Unknown (Unknown):" + log_activity("[auth_id] attempted to log into the terminal") + if("log-out") + log_activity("[auth_id] logged out of the terminal") + playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE) + authenticated = FALSE + auth_id = "\[NULL\]" + if("toggle-logs") + should_log = !should_log + log_game("[key_name(operator)] set the logs of [src] in [AREACOORD(src)] [should_log ? "On" : "Off"]") + if("restore-console") + restoring = TRUE + addtimer(CALLBACK(src, .proc/restore_comp), rand(3,5) * 9) + if("access-apc") + var/ref = params["ref"] + playsound(src, "terminal_type", 50, FALSE) + var/obj/machinery/power/apc/APC = locate(ref) in GLOB.apcs_list + if(!APC) + return + if(active_apc) + to_chat(operator, "[icon2html(src, auth_id)] Disconnected from [active_apc].") + active_apc.say("Remote access canceled. Interface locked.") + playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, FALSE) + playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, FALSE) + active_apc.locked = TRUE + active_apc.update_icon() + active_apc.remote_control = null + active_apc = null + APC.remote_control = src + APC.ui_interact(operator) + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) + log_game("[key_name(operator)] remotely accessed [APC] from [src] at [AREACOORD(src)].") + log_activity("[auth_id] remotely accessed APC in [get_area_name(APC.area, TRUE)]") + if(APC.locked) + APC.say("Remote access detected. Interface unlocked.") + playsound(APC, 'sound/machines/boltsup.ogg', 25, FALSE) + playsound(APC, 'sound/machines/terminal_alert.ogg', 50, FALSE) + APC.locked = FALSE + APC.update_icon() + active_apc = APC + if("check-logs") + log_activity("Checked Logs") + if("check-apcs") + log_activity("Checked APCs") + if("toggle-minor") + var/ref = params["ref"] + var/type = params["type"] + var/value = params["value"] + var/obj/machinery/power/apc/target = locate(ref) in GLOB.apcs_list + if(!target) + return + target.vars[type] = target.setsubsystem(text2num(value)) + target.update_icon() + target.update() + var/setTo = "" + switch(target.vars[type]) + if(0) + setTo = "Off" + if(1) + setTo = "Auto Off" + if(2) + setTo = "On" + if(3) + setTo = "Auto On" + log_activity("Set APC [target.area.name] [type] to [setTo]") + log_game("[key_name(operator)] Set APC [target.area.name] [type] to [setTo]]") + if("breaker") + var/ref = params["ref"] + var/obj/machinery/power/apc/target = locate(ref) in GLOB.apcs_list + target.toggle_breaker() + var/setTo = target.operating ? "On" : "Off" + log_activity("Turned APC [target.area.name]'s breaker [setTo]") /obj/machinery/computer/apc_control/emag_act(mob/user) - if(!authenticated) - to_chat(user, "You bypass [src]'s access requirements using your emag.") - authenticated = TRUE - log_activity("logged in") - else if(!(obj_flags & EMAGGED)) - if(user) - user.visible_message("You emag [src], disabling precise logging and allowing you to clear logs.") - log_game("[key_name(user)] emagged [src] at [AREACOORD(src)], disabling operator tracking.") - obj_flags |= EMAGGED + if(obj_flags & EMAGGED) + return + obj_flags |= EMAGGED + log_game("[key_name(user)] emagged [src] at [AREACOORD(src)]") playsound(src, "sparks", 50, TRUE) /obj/machinery/computer/apc_control/proc/log_activity(log_text) - var/op_string = operator && !(obj_flags & EMAGGED) ? operator : "\[NULL OPERATOR\]" - LAZYADD(logs, "([station_time_timestamp()]) [op_string] [log_text]") + if(!should_log) + return + LAZYADD(logs, "([station_time_timestamp()]): [auth_id] [log_text]") + +/obj/machinery/computer/apc_control/proc/restore_comp() + obj_flags &= ~EMAGGED + should_log = TRUE + log_game("[key_name(operator)] restored the logs of [src] in [AREACOORD(src)]") + log_activity("-=- Logging restored to full functionality at this point -=-") + restoring = FALSE /mob/proc/using_power_flow_console() for(var/obj/machinery/computer/apc_control/A in range(1, src)) diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 5b54bdbe1860..f59e7f54e16b 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -352,6 +352,14 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list( blocked = FALSE return +/obj/machinery/computer/arcade/battle/examine_more(mob/user) + var/list/msg = list("You notice some writing scribbled on the side of [src]...") + msg += "\tsmart -> defend, defend, light attack" + msg += "\tshotgun -> defend, defend, power attack" + msg += "\tshort temper -> counter, counter, counter" + msg += "\tpoisonous -> light attack, light attack, light attack" + msg += "\tchonker -> power attack, power attack, power attack" + return msg /obj/machinery/computer/arcade/battle/emag_act(mob/user) if(obj_flags & EMAGGED) @@ -377,15 +385,6 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list( // *** THE ORION TRAIL ** // -/obj/item/gamer_pamphlet - name = "pamphlet - \'Violent Video Games and You\'" - desc = "A pamphlet encouraging the reader to maintain a balanced lifestyle and take care of their mental health, while still enjoying video games in a healthy way. You probably don't need this..." - icon = 'icons/obj/bureaucracy.dmi' - icon_state = "pamphlet" - item_state = "paper" - w_class = WEIGHT_CLASS_TINY - - #define ORION_TRAIL_WINTURN 9 //Orion Trail Events @@ -510,7 +509,7 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list( if(gamers[gamer] == -1) say("WARNING: Continued antisocial behavior detected: Dispensing self-help literature.") - new /obj/item/gamer_pamphlet(get_turf(src)) + new /obj/item/paper/pamphlet/violent_video_games(drop_location()) gamers[gamer]-- return diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index 5c61a0dca9da..cd88eaa10fa1 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -25,7 +25,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_alert", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AtmosAlertConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/atmos_alert/ui_data(mob/user) diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index 7c4222a07ea6..06ea408add3d 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -128,7 +128,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers) datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_control", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/atmos_control/ui_data(mob/user) @@ -280,7 +280,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers) datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_control", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/atmos_control/tank/ui_data(mob/user) diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index c81bc8f89777..3b07ae3591bd 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -15,7 +15,7 @@ // Stuff needed to render the map var/map_name var/const/default_map_size = 15 - var/obj/screen/cam_screen + var/obj/screen/map_view/cam_screen var/obj/screen/plane_master/lighting/cam_plane_master var/obj/screen/background/cam_background @@ -79,7 +79,7 @@ user.client.register_map_obj(cam_plane_master) user.client.register_map_obj(cam_background) // Open UI - ui = new(user, src, ui_key, "camera_console", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "CameraConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/security/ui_data() diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 6210631d2b16..d780c2f3542a 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -259,6 +259,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) var/target_name = inserted_modify_id ? html_encode(inserted_modify_id.name) : "--------" var/target_owner = (inserted_modify_id && inserted_modify_id.registered_name) ? html_encode(inserted_modify_id.registered_name) : "--------" var/target_rank = (inserted_modify_id && inserted_modify_id.assignment) ? html_encode(inserted_modify_id.assignment) : "Unassigned" + var/target_age = (inserted_modify_id && inserted_modify_id.registered_age) ? html_encode(inserted_modify_id.registered_age) : "--------" if(!authenticated) header += {"
Please insert the cards into the slots
@@ -305,7 +306,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) registered name: - + registered age: + Assignment: "} @@ -484,6 +486,14 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if (authenticated) var/t2 = inserted_modify_id if ((authenticated && inserted_modify_id == t2 && (in_range(src, usr) || issilicon(usr)) && isturf(loc))) + var/newAge = text2num(href_list["setage"])|null + if(newAge && isnum(newAge)) + inserted_modify_id.registered_age = newAge + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) + else if(!isnull(newAge)) + to_chat(usr, "Invalid age entered- age not updated.") + updateUsrDialog() + var/newName = reject_bad_name(href_list["reg"]) if(newName) inserted_modify_id.registered_name = newName diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 87db32b9a91f..e926a920c010 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -83,7 +83,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new) datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if (!ui) - ui = new(user, src, ui_key, "crew", "crew monitor", 800, 600 , master_ui, state) + ui = new(user, src, ui_key, "CrewConsole", "crew monitor", 800, 600 , master_ui, state) ui.open() /datum/crewmonitor/proc/show(mob/M, source) diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm index 0535ea8aa346..fe7a31cc2a0b 100644 --- a/code/game/machinery/computer/dna_console.dm +++ b/code/game/machinery/computer/dna_console.dm @@ -1,25 +1,41 @@ +/// Base timeout for creating mutation activators and other injectors #define INJECTOR_TIMEOUT 100 +/// Maximum number of genetic makeup storage slots in DNA Console #define NUMBER_OF_BUFFERS 3 +/// Timeout for DNA Scramble in DNA Consoles #define SCRAMBLE_TIMEOUT 600 -#define JOKER_TIMEOUT 12000 //20 minutes +/// Timeout for using the Joker feature to solve a gene in DNA Console +#define JOKER_TIMEOUT 12000 +/// How much time DNA Scanner upgrade tiers remove from JOKER_TIMEOUT #define JOKER_UPGRADE 3000 +/// Maximum value for radiaton strength when pulsing enzymes #define RADIATION_STRENGTH_MAX 15 -#define RADIATION_STRENGTH_MULTIPLIER 1 //larger has more range +/// Larger multipliers will affect the range of values when pulsing enzymes +#define RADIATION_STRENGTH_MULTIPLIER 1 +/// Maximum value for the radiation pulse duration when pulsing enzymes #define RADIATION_DURATION_MAX 30 -#define RADIATION_ACCURACY_MULTIPLIER 3 //larger is less accurate +/// Large values reduce pulse accuracy and may pulse other enzymes than selected +#define RADIATION_ACCURACY_MULTIPLIER 3 +/// Special status indicating a scanner occupant is transforming eg. from monkey to human +#define STATUS_TRANSFORMING 4 -#define RADIATION_IRRADIATION_MULTIPLIER 1 //multiplier for how much radiation a test subject receives +/// Multiplier for how much radiation received from DNA Console functionality +#define RADIATION_IRRADIATION_MULTIPLIER 1 -#define SCANNER_ACTION_SE 1 -#define SCANNER_ACTION_UI 2 -#define SCANNER_ACTION_UE 3 -#define SCANNER_ACTION_MIXED 4 +/// Flag for the mutation ref search system. Search will include scanner occupant +#define SEARCH_OCCUPANT 1 +/// Flag for the mutation ref search system. Search will include console storage +#define SEARCH_STORED 2 +/// Flag for the mutation ref search system. Search will include diskette storage +#define SEARCH_DISKETTE 4 +/// Flag for the mutation ref search system. Search will include advanced injector mutations +#define SEARCH_ADV_INJ 8 /obj/machinery/computer/scan_consolenew - name = "\improper DNA scanner access console" + name = "DNA Console" desc = "Scan DNA." icon_screen = "dna" icon_keyboard = "med_key" @@ -31,38 +47,114 @@ active_power_usage = 400 light_color = LIGHT_COLOR_BLUE + /// Link to the techweb's stored research. Used to retrieve stored mutations var/datum/techweb/stored_research + /// Maximum number of mutations that DNA Consoles are able to store var/max_storage = 6 - var/combine + /// Duration for enzyme radiation pulses var/radduration = 2 + /// Strength for enzyme radiation pulses var/radstrength = 1 + /// Maximum number of chromosomes that DNA Consoles are able to store. var/max_chromosomes = 6 - ///Amount of mutations we can store - var/list/buffer[NUMBER_OF_BUFFERS] - ///mutations we have stored + /// Maximum number of enzymes we can store + var/list/genetic_makeup_buffer[NUMBER_OF_BUFFERS] + /// List of all mutations stored on the DNA Console var/list/stored_mutations = list() - ///chromosomes we have stored + /// List of all chromosomes stored in the DNA Console var/list/stored_chromosomes = list() - ///combinations of injectors for the 'injector selection'. format is list("Elsa" = list(Cryokinesis, Geladikinesis), "The Hulk" = list(Hulk, Gigantism), etc) Glowy and the gang being an initialized datum - var/list/injector_selection = list() - ///max amount of selections you can make + /// Assoc list of all advanced injectors. Keys are injector names. Values are lists of mutations. + var/list/list/injector_selection = list() + /// Maximum number of advanced injectors that DNA Consoles store var/max_injector_selections = 2 - ///hard-cap on the advanced dna injector + /// Maximum number of mutation that an advanced injector can store var/max_injector_mutations = 10 - ///the max instability of the advanced injector. + /// Maximum total instability of all combined mutations allowed on an advanced injector var/max_injector_instability = 50 - var/injectorready = 0 //world timer cooldown var + /// World time when injectors are ready to be printed + var/injectorready = 0 + /// World time when JOKER algorithm can be used in DNA Consoles var/jokerready = 0 + /// World time when Scramble can be used in DNA Consoles var/scrambleready = 0 - var/current_screen = "mainmenu" - var/current_mutation //what block are we inspecting? only used when screen = "info" - var/current_storage //what storage block are we looking at? - var/obj/machinery/dna_scannernew/connected = null + + /// Currently stored genetic data diskette var/obj/item/disk/data/diskette = null + + /// Current delayed action, used for delayed enzyme transfer on scanner door close var/list/delayed_action = null + /// Index of the enzyme being modified during delayed enzyme pulse operations + var/rad_pulse_index = 0 + /// World time when the enzyme pulse should complete + var/rad_pulse_timer = 0 + + /// Used for setting tgui data - Whether the connected DNA Scanner is usable + var/can_use_scanner = FALSE + /// Used for setting tgui data - Whether the current DNA Scanner occupant is viable for genetic modification + var/is_viable_occupant = FALSE + /// Used for setting tgui data - Whether Scramble DNA is ready + var/is_scramble_ready = FALSE + /// Used for setting tgui data - Whether JOKER algorithm is ready + var/is_joker_ready = FALSE + /// Used for setting tgui data - Whether injectors are ready to be printed + var/is_injector_ready = FALSE + /// Used for setting tgui data - Wheher an enzyme pulse operation is ongoing + var/is_pulsing_rads = FALSE + /// Used for setting tgui data - Time until scramble is ready + var/time_to_scramble = 0 + /// Used for setting tgui data - Time until joker is ready + var/time_to_joker = 0 + /// Used for setting tgui data - Time until injectors are ready + var/time_to_injector = 0 + /// Used for setting tgui data - Time until the enzyme pulse is complete + var/time_to_pulse = 0 + + /// Currently connected DNA Scanner + var/obj/machinery/dna_scannernew/connected_scanner = null + /// Current DNA Scanner occupant + var/mob/living/carbon/scanner_occupant = null + + /// Used for setting tgui data - List of occupant mutations + var/list/tgui_occupant_mutations = list() + /// Used for setting tgui data - List of DNA Console stored mutations + var/list/tgui_console_mutations = list() + /// Used for setting tgui data - List of diskette stored mutations + var/list/tgui_diskette_mutations = list() + /// Used for setting tgui data - List of DNA Console chromosomes + var/list/tgui_console_chromosomes = list() + /// Used for setting tgui data - List of occupant mutations + var/list/tgui_genetic_makeup = list() + /// Used for setting tgui data - List of occupant mutations + var/list/tgui_advinjector_mutations = list() + + + /// State of tgui view, i.e. which tab is currently active, or which genome we're currently looking at. + var/list/list/tgui_view_state = list() + +/obj/machinery/computer/scan_consolenew/process() + . = ..() + + // This is for pulsing the UI element with radiation as part of genetic makeup + // If rad_pulse_index > 0 then it means we're attempting a rad pulse + if((rad_pulse_index > 0) && (rad_pulse_timer <= world.time)) + rad_pulse() + return + /obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params) + // Store chromosomes in the console if there's room + if (istype(I, /obj/item/chromosome)) + if(LAZYLEN(stored_chromosomes) < max_chromosomes) + I.forceMove(src) + stored_chromosomes += I + to_chat(user, "You insert [I].") + else + to_chat(user, "You cannot store any more chromosomes!") + return + + // Insert data disk if console disk slot is empty + // Swap data disk if there is one already a disk in the console if (istype(I, /obj/item/disk/data)) //INSERT SOME DISKETTES if (!user.transferItemToLoc(I,src)) return @@ -71,16 +163,10 @@ diskette = null diskette = I to_chat(user, "You insert [I].") - updateUsrDialog() - return - if (istype(I, /obj/item/chromosome)) - if(LAZYLEN(stored_chromosomes) < max_chromosomes) - I.forceMove(src) - stored_chromosomes += I - to_chat(user, "You insert [I].") - else - to_chat(user, "You cannot store any more chromosomes!") return + + // Recycle non-activator used injectors + // Turn activator used injectors (aka research injectors) to chromosomes if(istype(I, /obj/item/dnainjector/activator)) var/obj/item/dnainjector/activator/A = I if(A.used) @@ -101,851 +187,1759 @@ qdel(I) return - else - return ..() + return ..() /obj/machinery/computer/scan_consolenew/Initialize() . = ..() - for(var/direction in GLOB.cardinals) - connected = locate(/obj/machinery/dna_scannernew, get_step(src, direction)) - if(!isnull(connected)) - break + + // Connect with a nearby DNA Scanner on init + connect_to_scanner() + + // Set appropriate ready timers and limits for machines functions injectorready = world.time + INJECTOR_TIMEOUT scrambleready = world.time + SCRAMBLE_TIMEOUT jokerready = world.time + JOKER_TIMEOUT + // Set the default tgui state + set_default_state() + + // Link machine with research techweb. Used for discovering and accessing + // already discovered mutations stored_research = SSresearch.science_tech /obj/machinery/computer/scan_consolenew/examine(mob/user) . = ..() - if(jokerready < world.time) - . += "JOKER algorithm available." - else - . += "JOKER algorithm available in about [round(0.00166666667 * (jokerready - world.time))] minutes." -/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, last_change) +/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) . = ..() - if(!user) - return - var/datum/browser/popup = new(user, "scannernew", "DNA Modifier Console", 800, 630) // Set up the popup browser window - if(user.client) - var/datum/asset/simple/assets = get_asset_datum(/datum/asset/simple/genetics) - assets.send(user.client) - popup.add_stylesheet("scannernew", 'html/browser/scannernew.css') - - var/mob/living/carbon/viable_occupant - var/list/occupant_status = list("
Subject Status:
") - var/scanner_status - var/list/temp_html = list() - if(connected && connected.is_operational()) - if(connected.occupant) //set occupant_status message - viable_occupant = connected.occupant - if(viable_occupant.has_dna() && !HAS_TRAIT(viable_occupant, TRAIT_RADIMMUNE) && !HAS_TRAIT(viable_occupant, TRAIT_BADDNA) || (connected.scan_level == 3)) //occupant is viable for dna modification - occupant_status += "[viable_occupant.name] => " - switch(viable_occupant.stat) - if(CONSCIOUS) - occupant_status += "Conscious" - if(UNCONSCIOUS) - occupant_status += "Unconscious" - else - occupant_status += "DEAD" - occupant_status += "
" - occupant_status += "
Health:
[viable_occupant.health] %
" - occupant_status += "
Radiation Level:
[viable_occupant.radiation/(RAD_MOB_SAFE/100)] %
" - occupant_status += "
Unique Enzymes :
[viable_occupant.dna.unique_enzymes]
" - occupant_status += "
Last Operation:
[last_change ? last_change : "----"]
" - else - viable_occupant = null - occupant_status += "Invalid DNA structure" - else - occupant_status += "No subject detected" - if(connected.state_open) - scanner_status = "Open" - else - scanner_status = "Closed" - if(connected.locked) - scanner_status += "(Locked)" - else - scanner_status += "(Unlocked)" + // Most of ui_interact is spent setting variables for passing to the tgui + // interface. + // We can also do some general state processing here too as it's a good + // indication that a player is using the console. + var/scanner_op = scanner_operational() + var/can_modify_occ = can_modify_occupant() + // Check for connected AND operational scanner. + if(scanner_op) + can_use_scanner = TRUE else - occupant_status += "----" - scanner_status += "Error: No scanner detected" - - var/list/status = list("
") - status += "
Scanner:
[scanner_status]
" - status += occupant_status + can_use_scanner = FALSE + connected_scanner = null + is_viable_occupant = FALSE - - status += "

Radiation Emitter Status

" - var/stddev = radstrength*RADIATION_STRENGTH_MULTIPLIER - status += "
Output Level:
[radstrength]
" - status += "
  \> Mutation:
(-[stddev] to +[stddev] = 68 %) (-[2*stddev] to +[2*stddev] = 95 %)
" - if(connected) - stddev = RADIATION_ACCURACY_MULTIPLIER/(radduration + (connected.precision_coeff ** 2)) + // Check for a viable occupant in the scanner. + if(can_modify_occ) + is_viable_occupant = TRUE else - stddev = RADIATION_ACCURACY_MULTIPLIER/radduration - var/chance_to_hit - switch(stddev) //hardcoded values from a z-table for a normal distribution - if(0 to 0.25) - chance_to_hit = ">95 %" - if(0.25 to 0.5) - chance_to_hit = "68-95 %" - if(0.5 to 0.75) - chance_to_hit = "55-68 %" - else - chance_to_hit = "<38 %" - status += "
Pulse Duration:
[radduration]
" - status += "
  \> Accuracy:
[chance_to_hit]
" - status += "
" // Close statusDisplay div - var/list/buttons = list("Scan") - if(connected) - buttons += "[connected.state_open ? "Close" : "Open"] Scanner" - if (connected.state_open) - buttons += "[connected.locked ? "Unlock" : "Lock"] Scanner" + is_viable_occupant = FALSE + + + // Populates various buffers for passing to tgui + build_mutation_list(can_modify_occ) + build_genetic_makeup_list() + + // Populate variables for passing to tgui interface + is_scramble_ready = (scrambleready < world.time) + time_to_scramble = round((scrambleready - world.time)/10) + + is_joker_ready = (jokerready < world.time) + time_to_joker = round((jokerready - world.time)/10) + + is_injector_ready = (injectorready < world.time) + time_to_injector = round((injectorready - world.time)/10) + + is_pulsing_rads = ((rad_pulse_index > 0) && (rad_pulse_timer > world.time)) + time_to_pulse = round((rad_pulse_timer - world.time)/10) + + // Attempt to update tgui ui, open and update if needed. + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + + if(!ui) + ui = new(user, src, ui_key, "DnaConsole", name, 539, 710, master_ui, state) + ui.open() + +/obj/machinery/computer/scan_consolenew/ui_data(mob/user) + var/list/data = list() + + data["view"] = tgui_view_state + data["storage"] = list() + + // This block of code generates the huge data structure passed to the tgui + // interface for displaying all the various bits of console/scanner data + // Should all be very self-explanatory + data["isScannerConnected"] = can_use_scanner + if(can_use_scanner) + data["scannerOpen"] = connected_scanner.state_open + data["scannerLocked"] = connected_scanner.locked + data["radStrength"] = radstrength + data["radDuration"] = radduration + data["stdDevStr"] = radstrength * RADIATION_STRENGTH_MULTIPLIER + switch(RADIATION_ACCURACY_MULTIPLIER / (radduration + (connected_scanner.precision_coeff ** 2))) //hardcoded values from a z-table for a normal distribution + if(0 to 0.25) + data["stdDevAcc"] = ">95 %" + if(0.25 to 0.5) + data["stdDevAcc"] = "68-95 %" + if(0.5 to 0.75) + data["stdDevAcc"] = "55-68 %" + else + data["stdDevAcc"] = "<38 %" + + data["isViableSubject"] = is_viable_occupant + if(is_viable_occupant) + data["subjectName"] = scanner_occupant.name + if(scanner_occupant.transformation_timer) + data["subjectStatus"] = STATUS_TRANSFORMING else - buttons += "[connected.locked ? "Unlock" : "Lock"] Scanner" + data["subjectStatus"] = scanner_occupant.stat + data["subjectHealth"] = scanner_occupant.health + data["subjectRads"] = scanner_occupant.radiation/(RAD_MOB_SAFE/100) + data["subjectEnzymes"] = scanner_occupant.dna.unique_enzymes + data["isMonkey"] = ismonkey(scanner_occupant) + data["subjectUNI"] = scanner_occupant.dna.uni_identity + data["storage"]["occupant"] = tgui_occupant_mutations + //data["subjectMutations"] = tgui_occupant_mutations else - buttons += "Open Scanner Lock Scanner" - if(viable_occupant && (scrambleready < world.time)) - buttons += "Scramble DNA" + data["subjectName"] = null + data["subjectStatus"] = null + data["subjectHealth"] = null + data["subjectRads"] = null + data["subjectEnzymes"] = null + //data["subjectMutations"] = null + data["storage"]["occupant"] = null + + data["hasDelayedAction"] = (delayed_action != null) + data["isScrambleReady"] = is_scramble_ready + data["isJokerReady"] = is_joker_ready + data["isInjectorReady"] = is_injector_ready + data["scrambleSeconds"] = time_to_scramble + data["jokerSeconds"] = time_to_joker + data["injectorSeconds"] = time_to_injector + data["isPulsingRads"] = is_pulsing_rads + data["radPulseSeconds"] = time_to_pulse + + if(diskette != null) + data["hasDisk"] = TRUE + data["diskCapacity"] = diskette.max_mutations - LAZYLEN(diskette.mutations) + data["diskReadOnly"] = diskette.read_only + //data["diskMutations"] = tgui_diskette_mutations + data["storage"]["disk"] = tgui_diskette_mutations + data["diskHasMakeup"] = (LAZYLEN(diskette.genetic_makeup_buffer) > 0) + data["diskMakeupBuffer"] = diskette.genetic_makeup_buffer.Copy() else - buttons += "Scramble DNA" - if(diskette) - buttons += "Disk" - else - buttons += "Disk" - if(current_screen == "mutations") - buttons += "
Mutations" - else - buttons += "
Mutations" - if((current_screen == "mainmenu") || !current_screen) - buttons += "Genetic Sequencer" - else - buttons += "Genetic Sequencer" - if(current_screen == "ui") - buttons += "Unique Identifiers" - else - buttons += "Unique Identifiers" - if(current_screen == "advinjector") - buttons += "Adv. Injectors" - else - buttons += "Adv. Injectors" + data["hasDisk"] = FALSE + data["diskCapacity"] = 0 + data["diskReadOnly"] = TRUE + //data["diskMutations"] = null + data["storage"]["disk"] = null + data["diskHasMakeup"] = FALSE + data["diskMakeupBuffer"] = null + + data["mutationCapacity"] = max_storage - LAZYLEN(stored_mutations) + //data["mutationStorage"] = tgui_console_mutations + data["storage"]["console"] = tgui_console_mutations + data["chromoCapacity"] = max_chromosomes - LAZYLEN(stored_chromosomes) + data["chromoStorage"] = tgui_console_chromosomes + data["makeupCapacity"] = NUMBER_OF_BUFFERS + data["makeupStorage"] = tgui_genetic_makeup + + //data["advInjectors"] = tgui_advinjector_mutations + data["storage"]["injector"] = tgui_advinjector_mutations + data["maxAdvInjectors"] = max_injector_selections + + return data + +/obj/machinery/computer/scan_consolenew/ui_act(action, var/list/params) + if(..()) + return TRUE - switch(current_screen) - if("working") - temp_html += status - temp_html += "

System Busy

" - temp_html += "Working ... Please wait ([DisplayTimeText(radduration*10)])" - if("ui") - temp_html += status - temp_html += buttons - temp_html += "

Unique Identifiers

" - temp_html += "-- Output Level ++" - temp_html += "
-- Pulse Duration ++" - temp_html += "

Irradiate Subject

" - temp_html += "
Unique Identifier:
" - var/max_line_len = 7*DNA_BLOCK_SIZE - if(viable_occupant) - temp_html += "
1
" - var/char = "" - var/ui_text = viable_occupant.dna.uni_identity - var/len_byte = length(ui_text) - var/char_it = 0 - for(var/byte_it = 1, byte_it <= len_byte, byte_it += length(char)) - char_it++ - char = ui_text[byte_it] - temp_html += "[char]" - if((char_it % max_line_len) == 0) - temp_html += "
" - if((char_it % DNA_BLOCK_SIZE) == 0 && byte_it < len_byte) - temp_html += "
[(char_it / DNA_BLOCK_SIZE) + 1]
" - else - temp_html += "---------" - temp_html += "

Buffer Menu

" - - if(istype(buffer)) - for(var/i=1, i<=buffer.len, i++) - temp_html += "
Slot [i]: " - var/list/buffer_slot = buffer[i] - if( !buffer_slot || !buffer_slot.len || !buffer_slot["name"] || !((buffer_slot["UI"] && buffer_slot["UE"]) || buffer_slot["SE"]) ) - temp_html += "
\tNo Data" - if(viable_occupant) - temp_html += "
Save to Buffer" - else - temp_html += "
Save to Buffer" - temp_html += "Clear Buffer" - if(diskette) - temp_html += "Load from Disk" - else - temp_html += "Load from Disk" - temp_html += "Save to Disk" - else - var/ui = buffer_slot["UI"] - var/ue = buffer_slot["UE"] - var/name = buffer_slot["name"] - var/label = buffer_slot["label"] - var/blood_type = buffer_slot["blood_type"] - temp_html += "
\tLabel: [label ? label : name]" - temp_html += "
\tSubject: [name]" - if(ue && name && blood_type) - temp_html += "
\tBlood Type: [blood_type]" - temp_html += "
\tUE: [ue] " - if(viable_occupant) - temp_html += "Occupant" - else - temp_html += "Occupant" - temp_html += "Occupant:Delayed" - if(injectorready < world.time) - temp_html += "Injector" - else - temp_html += "Injector" - else - temp_html += "
\tBlood Type: No Data" - temp_html += "
\tUE: No Data" - if(ui) - temp_html += "
\tUI: [ui] " - if(viable_occupant) - temp_html += "Occupant" - else - temp_html += "Occupant" - temp_html += "Occupant:Delayed" - if(injectorready < world.time) - temp_html += "Injector" - else - temp_html += "Injector" - else - temp_html += "
\tUI: No Data" - if(ue && name && blood_type && ui) - temp_html += "
\tUI+UE: [ui]/[ue] " - if(viable_occupant) - temp_html += "Occupant" - else - temp_html += "Occupant" - temp_html += "Occupant:Delayed" - if(injectorready < world.time) - temp_html += "UI+UE Injector" - else - temp_html += "UI+UE Injector" - if(viable_occupant) - temp_html += "
Save to Buffer" - else - temp_html += "
Save to Buffer" - temp_html += "Clear Buffer" - if(diskette) - temp_html += "Load from Disk" - else - temp_html += "Load from Disk" - if(diskette && !diskette.read_only) - temp_html += "Save to Disk" - else - temp_html += "Save to Disk" - if("disk") - temp_html += status - temp_html += buttons - if(diskette) - temp_html += "

[diskette.name]


" - temp_html += "Eject Disk
" - if(LAZYLEN(diskette.mutations)) - temp_html += "" - for(var/datum/mutation/human/A in diskette.mutations) - temp_html += "" - temp_html += "" - if(LAZYLEN(stored_mutations) < max_storage) - temp_html += "" - else - temp_html += "" - temp_html += "" - temp_html += "
[A.name]DeleteImportImport
" - else - temp_html += "
Load diskette to start ----------" - if("info") - if(LAZYLEN(stored_mutations)) - if(LAZYLEN(stored_mutations) >= current_storage) - var/datum/mutation/human/HM = stored_mutations[current_storage] - if(HM) - temp_html += display_sequence(HM.type, current_storage) - else - current_screen = "mainmenu" - if("mutations") - temp_html += status - temp_html += buttons - temp_html += "

Mutation Storage:

" - temp_html += "" - for(var/datum/mutation/human/HM in stored_mutations) - var/i = stored_mutations.Find(HM) - temp_html += "" - if(diskette) - temp_html += "" - else - temp_html += "" - temp_html += "" - if(combine == HM.type) - temp_html += "" - else - temp_html += "" - temp_html += "
[HM.name]ExportExportDeleteCombine
Combine

" - temp_html += "

Chromosome Storage:

" - temp_html += "" - for(var/i in 1 to stored_chromosomes.len) - var/obj/item/chromosome/CM = stored_chromosomes[i] - temp_html += "
" - temp_html += "
[CM.name]
" - if("advinjector") - temp_html += status - temp_html += buttons - temp_html += "
Advanced Injectors:

" - temp_html += "" - for(var/A in injector_selection) - temp_html += "
[A]" - var/list/true_selection = injector_selection[A] - temp_html += "
" - for(var/B in true_selection) - var/datum/mutation/human/HM = B - var/mutcolor - switch(HM.quality) - if(POSITIVE) - mutcolor = "good" - if(MINOR_NEGATIVE) - mutcolor = "average" - if(NEGATIVE) - mutcolor = "bad" - temp_html += "
[HM.name] " - temp_html += "Remove
" - if(injectorready < world.time) - temp_html += "
Print Advanced Injector" - else - temp_html += "
Printer ready in [DisplayTimeText(injectorready - world.time, 1)]" - temp_html += "Remove Injector
" - temp_html += "
" + . = TRUE - else - temp_html += status - temp_html += buttons - temp_html += "
Genetic Sequence:

" - if(viable_occupant) - if(viable_occupant) - for(var/A in get_mutation_list()) - temp_html += display_inactive_sequence(A) - temp_html += "
" - else - temp_html += "----" - if(viable_occupant && (current_mutation in get_mutation_list(viable_occupant))) - temp_html += display_sequence(current_mutation) - temp_html += "

" - else - temp_html += "----------" + add_fingerprint(usr) + usr.set_machine(src) - popup.set_content(temp_html.Join()) - popup.open() + switch(action) + // Connect this DNA Console to a nearby DNA Scanner + // Usually only activate as an option if there is no connected scanner + if("connect_scanner") + connect_to_scanner() + return -/obj/machinery/computer/scan_consolenew/proc/display_inactive_sequence(mutation) - var/temp_html = "" - var/class = "unselected" - var/mob/living/carbon/viable_occupant = get_viable_occupant() - if(!viable_occupant) - return + // Toggle the door open/closed status on attached DNA Scanner + if("toggle_door") + // GUARD CHECK - Scanner still connected and operational? + if(!scanner_operational()) + return - var/location = viable_occupant.dna.mutation_index.Find(mutation) //We do this because we dont want people using sysexp or similair tools to just read the mutations. - - if(!location) //Do this only when needed, dont make a list with mutations for every iteration if you dont need to - var/list/mutations = get_mutation_list(TRUE) - if(mutation in mutations) - location = mutations.Find(mutation) - if(mutation == current_mutation) - class = "selected" - if(location > DNA_MUTATION_BLOCKS) - temp_html += "Extra Mutation" - else if(mutation in stored_research.discovered_mutations) - temp_html += "Discovered Mutation" - else - temp_html += "Undiscovered" - return temp_html + connected_scanner.toggle_open(usr) + return -/obj/machinery/computer/scan_consolenew/proc/display_sequence(mutation, storage_slot) //Storage slot is for when viewing from the stored mutations - var/temp_html = "" - if(!mutation) - temp_html += "ERR-" - return - var/mut_name = "Unknown gene" - var/mut_desc = "No information available." - var/alias - var/discovered = FALSE - var/active = FALSE - var/scrambled = FALSE - var/instability - var/mob/living/carbon/viable_occupant = get_viable_occupant() - var/datum/mutation/human/HM = get_valid_mutation(mutation) - - if(viable_occupant) - var/datum/mutation/human/M = viable_occupant.dna.get_mutation(mutation) - if(M) - scrambled = M.scrambled - active = TRUE - var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(mutation) - alias = A.alias - if(active && !scrambled) - discover(mutation) - if(stored_research && (mutation in stored_research.discovered_mutations)) - mut_name = A.name - mut_desc = A.desc - discovered = TRUE - instability = A.instability - var/extra - if(viable_occupant && !(storage_slot || viable_occupant.dna.mutation_in_sequence(mutation))) - extra = TRUE - if(discovered && !scrambled) - var/mutcolor - switch(A.quality) - if(POSITIVE) - mutcolor = "good" - if(MINOR_NEGATIVE) - mutcolor = "average" - if(NEGATIVE) - mutcolor = "bad" - if(HM) - instability *= GET_MUTATION_STABILIZER(HM) - temp_html += "
[mut_name] ([alias])
" - temp_html += "
Instability : [round(instability)]
" - else - temp_html += "
[alias]
" - temp_html += "
[mut_desc]
" - if(active && !storage_slot) - if(HM?.can_chromosome && (HM in viable_occupant.dna.mutations)) - var/i = viable_occupant.dna.mutations.Find(HM) - var/chromosome_name = "----" - if(HM.chromosome_name) - chromosome_name = HM.chromosome_name - temp_html += "
Chromosome status: [chromosome_name]
" - temp_html += "
Compatible chromosomes: [jointext(HM.valid_chrom_list, ", ")]
" - - temp_html += "
Sequence:

" - if(!scrambled) - for(var/block in 1 to A.blocks) - var/whole_sequence = get_valid_gene_string(mutation) - var/sequence = copytext_char(whole_sequence, 1+(block-1)*(DNA_SEQUENCE_LENGTH*2),(DNA_SEQUENCE_LENGTH*2*block+1)) - temp_html += "
" - for(var/i in 1 to DNA_SEQUENCE_LENGTH) - var/num = 1+(i-1)*2 - var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1)) - if(sequence[num] == "X") - temp_html += "" - else - temp_html += "" - temp_html += "" - for(var/i in 1 to DNA_SEQUENCE_LENGTH) - temp_html += "" - temp_html += "" - for(var/i in 1 to DNA_SEQUENCE_LENGTH) - var/num = i*2 - var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1)) - - if(sequence[num] == "X") - temp_html += "" - else - temp_html += "" - temp_html += "
|
" - temp_html += "




" - else - temp_html = "
Sequence unreadable due to unpredictable mutation.
" - if((active || storage_slot) && (injectorready < world.time) && !scrambled) - temp_html += "Print Activator" - temp_html += "Print Mutator" - else - temp_html += "Print Activator" - temp_html += "Print Mutator" - temp_html += "
" - if(storage_slot) - temp_html += "Delete" - if((LAZYLEN(stored_mutations) < max_storage) && diskette && !diskette.read_only) - temp_html += "Export" - else - temp_html += "Export" - temp_html += "Back" - else if(active && !scrambled) - temp_html += "Store" - temp_html += "Adv. Injector" - if(extra || scrambled) - temp_html += "Nullify" - else - temp_html += "Nullify" - temp_html += "
" - return temp_html + // Toggle the door bolts on the attached DNA Scanner + if("toggle_lock") + // GUARD CHECK - Scanner still connected and operational? + if(!scanner_operational()) + return -/obj/machinery/computer/scan_consolenew/Topic(href, href_list) - if(..()) - return - if(current_screen == "working") - return + connected_scanner.locked = !connected_scanner.locked + return - add_fingerprint(usr) - usr.set_machine(src) + // Scramble scanner occupant's DNA + if("scramble_dna") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + // GUARD CHECK - Is scramble DNA actually ready? + if(!can_modify_occupant() || !(scrambleready < world.time)) + return + + scanner_occupant.dna.remove_all_mutations(list(MUT_NORMAL, MUT_EXTRA)) + scanner_occupant.dna.generate_dna_blocks() + scrambleready = world.time + SCRAMBLE_TIMEOUT + to_chat(usr,"DNA scrambled.") + scanner_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER*50/(connected_scanner.damage_coeff ** 2) + return + + // Check whether a specific mutation is eligible for discovery within the + // scanner occupant + // This is additionally done when a mutation's tab is selected in the tgui + // interface. This is because some mutations, such as Monkified on monkeys, + // are infact completed by default but not yet discovered. Likewise, all + // mutations can have their sequence completed while Monkified is still an + // active mutation and thus won't immediately be discovered but could be + // discovered when Monkified is removed + // ---------------------------------------------------------------------- // + // params["alias"] - Alias of a mutation. The alias is the "hidden" name of + // the mutation, for example "Mutation 5" or "Mutation 33" + if("check_discovery") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + if(!can_modify_occupant()) + return + + // GUARD CHECK - Have we somehow cheekily swapped occupants? This is + // unexpected. + if(!(scanner_occupant == connected_scanner.occupant)) + return + + check_discovery(params["alias"]) + return - var/mob/living/carbon/viable_occupant = get_viable_occupant() - - //Basic Tasks/////////////////////////////////////////// - var/num = round(text2num(href_list["num"])) - var/last_change - switch(href_list["task"]) - if("togglelock") - if(connected) - connected.locked = !connected.locked - if("toggleopen") - if(connected) - connected.toggle_open(usr) - if("setduration") - if(!num) - num = round(input(usr, "Choose pulse duration:", "Input an Integer", null) as num|null) - if(num) - radduration = WRAP(num, 1, RADIATION_DURATION_MAX+1) - if("setstrength") - if(!num) - num = round(input(usr, "Choose pulse strength:", "Input an Integer", null) as num|null) - if(num) - radstrength = WRAP(num, 1, RADIATION_STRENGTH_MAX+1) - if("screen") - current_screen = href_list["text"] - if("scramble") - if(viable_occupant && (scrambleready < world.time)) - viable_occupant.dna.remove_all_mutations(list(MUT_NORMAL, MUT_EXTRA)) - viable_occupant.dna.generate_dna_blocks() - scrambleready = world.time + SCRAMBLE_TIMEOUT - to_chat(usr,"DNA scrambled.") - viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER*50/(connected.damage_coeff ** 2) - - if("setbufferlabel") - var/text = sanitize(input(usr, "Input a new label:", "Input a Text", null) as text|null) - if(num && text) - num = clamp(num, 1, NUMBER_OF_BUFFERS) - var/list/buffer_slot = buffer[num] - if(istype(buffer_slot)) - buffer_slot["label"] = text - if("setbuffer") - if(num && viable_occupant) - num = clamp(num, 1, NUMBER_OF_BUFFERS) - buffer[num] = list( - "label"="Buffer[num]:[viable_occupant.real_name]", - "UI"=viable_occupant.dna.uni_identity, - "UE"=viable_occupant.dna.unique_enzymes, - "name"=viable_occupant.real_name, - "blood_type"=viable_occupant.dna.blood_type - ) - if("clearbuffer") - if(num) - num = clamp(num, 1, NUMBER_OF_BUFFERS) - var/list/buffer_slot = buffer[num] - if(istype(buffer_slot)) - buffer_slot.Cut() - if("transferbuffer") - if(num && viable_occupant) - switch(href_list["text"]) //Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove - if("ui") - apply_buffer(SCANNER_ACTION_UI,num) - if("ue") - apply_buffer(SCANNER_ACTION_UE,num) - if("mixed") - apply_buffer(SCANNER_ACTION_MIXED,num) - if("injector") - if(num && injectorready < world.time) - num = clamp(num, 1, NUMBER_OF_BUFFERS) - var/list/buffer_slot = buffer[num] - if(istype(buffer_slot)) - var/obj/item/dnainjector/timed/I - switch(href_list["text"]) - if("ui") - if(buffer_slot["UI"]) - I = new /obj/item/dnainjector/timed(loc) - I.fields = list("UI"=buffer_slot["UI"]) - if(connected) - I.damage_coeff = connected.damage_coeff - if("ue") - if(buffer_slot["name"] && buffer_slot["UE"] && buffer_slot["blood_type"]) - I = new /obj/item/dnainjector/timed(loc) - I.fields = list("name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"]) - if(connected) - I.damage_coeff = connected.damage_coeff - if("mixed") - if(buffer_slot["UI"] && buffer_slot["name"] && buffer_slot["UE"] && buffer_slot["blood_type"]) - I = new /obj/item/dnainjector/timed(loc) - I.fields = list("UI"=buffer_slot["UI"],"name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"]) - if(connected) - I.damage_coeff = connected.damage_coeff - if(I) - injectorready = world.time + INJECTOR_TIMEOUT - if("loaddisk") - if(num && diskette && diskette.fields) - num = clamp(num, 1, NUMBER_OF_BUFFERS) - buffer[num] = diskette.fields.Copy() - if("savedisk") - if(num && diskette && !diskette.read_only) - num = clamp(num, 1, NUMBER_OF_BUFFERS) - var/list/buffer_slot = buffer[num] - if(istype(buffer_slot)) - diskette.name = "data disk \[[buffer_slot["label"]]\]" - diskette.fields = buffer_slot.Copy() - if("ejectdisk") - if(diskette) - diskette.forceMove(drop_location()) - diskette = null - if("setdelayed") - if(num) - delayed_action = list("action"=text2num(href_list["delayaction"]),"buffer"=num) - if("pulseui") - if(num && viable_occupant && connected) - radduration = WRAP(radduration, 1, RADIATION_DURATION_MAX+1) - radstrength = WRAP(radstrength, 1, RADIATION_STRENGTH_MAX+1) - - var/locked_state = connected.locked - connected.locked = TRUE - - current_screen = "working" - ui_interact(usr) - - sleep(radduration*10) - current_screen = "ui" - - if(viable_occupant && connected && connected.occupant==viable_occupant) - viable_occupant.radiation += (RADIATION_IRRADIATION_MULTIPLIER*radduration*radstrength)/(connected.damage_coeff ** 2) //Read comment in "transferbuffer" section above for explanation - switch(href_list["task"]) //Same thing as there but values are even lower, on best part they are about 0.0*, effectively no damage - if("pulseui") - var/len = length_char(viable_occupant.dna.uni_identity) - num = WRAP(num, 1, len+1) - num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2 - //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low - var/block = round((num-1)/DNA_BLOCK_SIZE)+1 - var/subblock = num - block*DNA_BLOCK_SIZE - last_change = "UI #[block]-[subblock]; " - - var/hex = copytext_char(viable_occupant.dna.uni_identity, num, num+1) - last_change += "[hex]" - hex = scramble(hex, radstrength, radduration) - last_change += "->[hex]" - - viable_occupant.dna.uni_identity = copytext_char(viable_occupant.dna.uni_identity, 1, num) + hex + copytext_char(viable_occupant.dna.uni_identity, num + 1) - viable_occupant.updateappearance(mutations_overlay_update=1) + // Check all mutations of the occupant and check if any are discovered. + // This is called when the Genetic Sequencer is selected. It'll do things + // like immediately discover Monkified without needing to click through + // the mutation tabs and handle cases where mutations are solved but not + // discovered due to the Monkified mutation being active then removed. + if("all_check_discovery") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + if(!can_modify_occupant()) + return + + // GUARD CHECK - Have we somehow cheekily swapped occupants? This is + // unexpected. + if(!(scanner_occupant == connected_scanner.occupant)) + return + + // Go over all standard mutations and check if they've been discovered. + for(var/mutation_type in scanner_occupant.dna.mutation_index) + var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(mutation_type) + check_discovery(HM.alias) + + return + + // Set a gene in a mutation's genetic sequence. Will also check for mutations + // discovery as part of the process. + // ---------------------------------------------------------------------- // + // params["alias"] - Alias of a mutation. The alias is the "hidden" name of + // the mutation, for example "Mutation 5" or "Mutation 33" + // params["gene"] - The letter of the new gene + // params["pos"] - The BYOND index of the letter in the gene sequence to be + // changed. Expects a text string from TGUI and will convert to a number + if("pulse_gene") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + if(!can_modify_occupant()) + return + + // GUARD CHECK - Have we somehow cheekily swapped occupants? This is + // unexpected. + if(!(scanner_occupant == connected_scanner.occupant)) + return + + // GUARD CHECK - Is the occupant currently undergoing some form of + // transformation? If so, we don't want to be pulsing genes. + if(scanner_occupant.transformation_timer) + to_chat(usr,"Gene pulse failed: The scanner occupant undergoing a transformation.") + return + + // Resolve mutation's BYOND path from the alias + var/alias = params["alias"] + var/path = GET_MUTATION_TYPE_FROM_ALIAS(alias) + + // Make sure the occupant still has this mutation + if(!(path in scanner_occupant.dna.mutation_index)) + return + + // Resolve BYOND path to genome sequence of scanner occupant + var/sequence = GET_GENE_STRING(path, scanner_occupant.dna) + + var/newgene = params["gene"] + var/genepos = text2num(params["pos"]) + + // If the new gene is J, this means we're dealing with a JOKER + // GUARD CHECK - Is JOKER actually ready? + if((newgene == "J") && (jokerready < world.time)) + var/truegenes = GET_SEQUENCE(path) + newgene = truegenes[genepos] + jokerready = world.time + JOKER_TIMEOUT - (JOKER_UPGRADE * (connected_scanner.precision_coeff-1)) + + // If the gene is an X, we want to update the default genes with the new + // X to allow highlighting logic to work on the tgui interface. + if(newgene == "X") + var/defaultseq = scanner_occupant.dna.default_mutation_genes[path] + defaultseq = copytext_char(defaultseq, 1, genepos) + newgene + copytext_char(defaultseq, genepos + 1) + scanner_occupant.dna.default_mutation_genes[path] = defaultseq + + // Copy genome to scanner occupant and do some basic mutation checks as + // we've increased the occupant rads + sequence = copytext_char(sequence, 1, genepos) + newgene + copytext_char(sequence, genepos + 1) + scanner_occupant.dna.mutation_index[path] = sequence + scanner_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected_scanner.damage_coeff + scanner_occupant.domutcheck() + + // GUARD CHECK - Modifying genetics can lead to edge cases where the + // scanner occupant is qdel'd and replaced with a different entity. + // Examples of this include adding/removing the Monkified mutation which + // qdels the previous entity and creates a brand new one in its place. + // We should redo all of our occupant modification checks again, although + // it is less than ideal. + if(!can_modify_occupant()) + return + + // Check if we cracked a mutation + check_discovery(alias) + + return + + // Apply a chromosome to a specific mutation. + // ---------------------------------------------------------------------- // + // params["mutref"] - ATOM Ref of specific mutation to apply the chromo to + // params["chromo"] - Name of the chromosome to apply to the mutation + if("apply_chromo") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + if(!can_modify_occupant()) + return + + // GUARD CHECK - Have we somehow cheekily swapped occupants? This is + // unexpected. + if(!(scanner_occupant == connected_scanner.occupant)) + return + + var/bref = params["mutref"] + + // GUARD CHECK - Only search occupant for this specific ref, since your + // can only apply chromosomes to mutations occupants. + var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_OCCUPANT) + + // GUARD CHECK - This should not be possible. Unexpected result + if(!HM) + return + + // Look through our stored chromos and compare names to find a + // stored chromo we can apply. + for(var/obj/item/chromosome/CM in stored_chromosomes) + if(CM.can_apply(HM) && (CM.name == params["chromo"])) + stored_chromosomes -= CM + CM.apply(HM) + + return + + // Print any type of standard injector, limited right now to activators that + // activate a dormant mutation and mutators that forcibly create a new + // MUT_EXTRA mutation + // ---------------------------------------------------------------------- // + // params["mutref"] - ATOM Ref of specific mutation to create an injector of + // params["is_activator"] - Is this an "Activator" style injector, also + // referred to as a "Research" type. Expects a string with 0 or 1, which + // then gets converted to a number. + // params["source"] - The source the request came from. + // Expected results: + // "occupant" - From genetic sequencer + // "console" - From DNA Console storage + // "disk" - From inserted diskette + if("print_injector") + // Because printing mutators and activators share a bunch of code, + // it makes sense to keep them both together and set unique vars + // later in the code + + // As a side note, because mutations can contain unique metadata, + // this system uses BYOND Atom Refs to safely and accurately + // identify mutations from big ol' lists + + // GUARD CHECK - Is the injector actually ready? + if(world.time < injectorready) + return + + var/search_flags = 0 + + switch(params["source"]) + if("occupant") + // GUARD CHECK - Make sure we can modify the occupant before we + // attempt to search them for any given mutation refs. This could + // lead to no search flags being passed to get_mut_by_ref and this + // is intended functionality to prevent any cheese or abuse + if(can_modify_occupant()) + search_flags |= SEARCH_OCCUPANT + if("console") + search_flags |= SEARCH_STORED + if("disk") + search_flags |= SEARCH_DISKETTE + + var/bref = params["mutref"] + var/datum/mutation/human/HM = get_mut_by_ref(bref, search_flags) + + // GUARD CHECK - This should not be possible. Unexpected result + if(!HM) + return + + // Create a new DNA Injector and add the appropriate mutations to it + var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc) + I.add_mutations += new HM.type(copymut = HM) + + var/is_activator = text2num(params["is_activator"]) + + // Activators are also called "research" injectors and are used to create + // chromosomes by recycling at the DNA Console + if(is_activator) + I.name = "[HM.name] activator" + I.research = TRUE + // If there's an operational connected scanner, we can use its upgrades + // to improve our injector's radiation generation + if(scanner_operational()) + I.damage_coeff = connected_scanner.damage_coeff*4 + injectorready = world.time + INJECTOR_TIMEOUT * (1 - 0.1 * connected_scanner.precision_coeff) else - current_screen = "mainmenu" - - if(connected) - connected.locked = locked_state - if("inspect") - if(viable_occupant) - var/list/mutations = get_mutation_list(TRUE) - if(current_mutation == mutations[num]) - current_mutation = null + injectorready = world.time + INJECTOR_TIMEOUT + else + I.name = "[HM.name] mutator" + I.doitanyway = TRUE + // If there's an operational connected scanner, we can use its upgrades + // to improve our injector's radiation generation + if(scanner_operational()) + I.damage_coeff = connected_scanner.damage_coeff + injectorready = world.time + INJECTOR_TIMEOUT * 5 * (1 - 0.1 * connected_scanner.precision_coeff) else - current_mutation = mutations[num] - - if("inspectstorage") - current_storage = num - current_screen = "info" - if("savemut") - if(viable_occupant) - var/succes - if(LAZYLEN(stored_mutations) < max_storage) - var/mutation = text2path(href_list["path"]) - if(ispath(mutation, /datum/mutation/human)) //sanity checks - var/datum/mutation/human/HM = viable_occupant.dna.get_mutation(mutation) - if(HM) - var/datum/mutation/human/A = new HM.type() - A.copy_mutation(HM) - succes = TRUE - stored_mutations += A - to_chat(usr,"Mutation succesfully stored.") - if(!succes) //we can exactly return here - to_chat(usr,"Mutation storage is full.") - if("deletemut") - var/datum/mutation/human/HM = stored_mutations[num] + injectorready = world.time + INJECTOR_TIMEOUT * 5 + + return + + // Save a mutation to the console's storage buffer. + // ---------------------------------------------------------------------- // + // params["mutref"] - ATOM Ref of specific mutation to store + // params["source"] - The source the request came from. + // Expected results: + // "occupant" - From genetic sequencer + // "disk" - From inserted diskette + if("save_console") + var/search_flags = 0 + + switch(params["source"]) + if("occupant") + // GUARD CHECK - Make sure we can modify the occupant before we + // attempt to search them for any given mutation refs. This could + // lead to no search flags being passed to get_mut_by_ref and this + // is intended functionality to prevent any cheese or abuse + if(can_modify_occupant()) + search_flags |= SEARCH_OCCUPANT + if("disk") + search_flags |= SEARCH_DISKETTE + + // GUARD CHECK - Is mutation storage full? + if(LAZYLEN(stored_mutations) >= max_storage) + to_chat(usr,"Mutation storage is full.") + return + + var/bref = params["mutref"] + var/datum/mutation/human/HM = get_mut_by_ref(bref, search_flags) + + // GUARD CHECK - This should not be possible. Unexpected result + if(!HM) + return + + var/datum/mutation/human/A = new HM.type() + A.copy_mutation(HM) + stored_mutations += A + to_chat(usr,"Mutation successfully stored.") + return + + // Save a mutation to the diskette's storage buffer. + // ---------------------------------------------------------------------- // + // params["mutref"] - ATOM Ref of specific mutation to store + // params["source"] - The source the request came from + // Expected results: + // "occupant" - From genetic sequencer + // "console" - From DNA Console storage + if("save_disk") + // GUARD CHECK - This code shouldn't even be callable without a diskette + // inserted. Unexpected result + if(!diskette) + return + + // GUARD CHECK - Make sure the disk is not full + if(LAZYLEN(diskette.mutations) >= diskette.max_mutations) + to_chat(usr,"Disk storage is full.") + return + + // GUARD CHECK - Make sure the disk isn't set to read only, as we're + // attempting to write to it + if(diskette.read_only) + to_chat(usr,"Disk is set to read only mode.") + return + + var/search_flags = 0 + + switch(params["source"]) + if("occupant") + // GUARD CHECK - Make sure we can modify the occupant before we + // attempt to search them for any given mutation refs. This could + // lead to no search flags being passed to get_mut_by_ref and this + // is intended functionality to prevent any cheese or abuse + if(can_modify_occupant()) + search_flags |= SEARCH_OCCUPANT + if("console") + search_flags |= SEARCH_STORED + + var/bref = params["mutref"] + var/datum/mutation/human/HM = get_mut_by_ref(bref, search_flags) + + // GUARD CHECK - This should not be possible. Unexpected result + if(!HM) + return + + var/datum/mutation/human/A = new HM.type() + A.copy_mutation(HM) + diskette.mutations += A + to_chat(usr,"Mutation successfully stored to disk.") + return + + // Completely removes a MUT_EXTRA mutation or mutation with corrupt gene + // sequence from the scanner occupant + // ---------------------------------------------------------------------- // + // params["mutref"] - ATOM Ref of specific mutation to nullify + if("nullify") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + if(!can_modify_occupant()) + return + + var/bref = params["mutref"] + var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_OCCUPANT) + + // GUARD CHECK - This should not be possible. Unexpected result + if(!HM) + return + + // GUARD CHECK - Nullify should only be used on scrambled or "extra" + // mutations. + if(!HM.scrambled && !(HM.class == MUT_EXTRA)) + return + + scanner_occupant.dna.remove_mutation(HM.type) + return + + // Deletes saved mutation from console buffer. + // ---------------------------------------------------------------------- // + // params["mutref"] - ATOM Ref of specific mutation to delete + if("delete_console_mut") + var/bref = params["mutref"] + var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_STORED) + if(HM) stored_mutations.Remove(HM) qdel(HM) - current_screen = "mutations" - if("activator") - if(injectorready < world.time) - var/mutation = text2path(href_list["path"]) - if(ispath(mutation, /datum/mutation/human)) - var/datum/mutation/human/HM = get_valid_mutation(mutation) - if(HM) - var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc) - I.add_mutations += new HM.type (copymut = HM) - I.name = "[HM.name] activator" - I.research = TRUE - if(connected) - I.damage_coeff = connected.damage_coeff*4 - injectorready = world.time + INJECTOR_TIMEOUT * (1 - 0.1 * connected.precision_coeff) //precision_coeff being the matter bin rating - else - injectorready = world.time + INJECTOR_TIMEOUT - if("mutator") - if(injectorready < world.time) - var/mutation = text2path(href_list["path"]) - if(ispath(mutation, /datum/mutation/human)) - var/datum/mutation/human/HM = get_valid_mutation(mutation) - if(HM) - var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc) - I.add_mutations += new HM.type (copymut = HM) - I.doitanyway = TRUE - I.name = "[HM.name] injector" - if(connected) - I.damage_coeff = connected.damage_coeff - injectorready = world.time + INJECTOR_TIMEOUT * 5 * (1 - 0.1 * connected.precision_coeff) - else - injectorready = world.time + INJECTOR_TIMEOUT * 5 - - if("advinjector") - var/selection = href_list["injector"] - if(injectorready < world.time) - if(injector_selection.Find(selection)) - var/list/true_selection = injector_selection[selection] - if(LAZYLEN(injector_selection)) - var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc) - for(var/A in true_selection) - var/datum/mutation/human/HM = A - I.add_mutations += new HM.type (copymut = HM) - I.doitanyway = TRUE - I.name = "Advanced [selection] injector" - if(connected) - I.damage_coeff = connected.damage_coeff - injectorready = world.time + INJECTOR_TIMEOUT * 8 * (1 - 0.1 * connected.precision_coeff) - else - injectorready = world.time + INJECTOR_TIMEOUT * 8 - if("nullify") - if(viable_occupant) - var/datum/mutation/human/A = viable_occupant.dna.get_mutation(current_mutation) - if(A && (!viable_occupant.dna.mutation_in_sequence(current_mutation) || A.scrambled)) - viable_occupant.dna.remove_mutation(current_mutation) - current_screen = "mainmenu" - current_mutation = null - if("pulsegene") - if(current_screen != "info") - var/path = GET_MUTATION_TYPE_FROM_ALIAS(href_list["alias"]) - if(viable_occupant && num && (path in viable_occupant.dna.mutation_index)) - var/list/genes = list("A","T","G","C","X") - if(jokerready < world.time) - genes += "JOKER" - var/sequence = GET_GENE_STRING(path, viable_occupant.dna) - var/original = sequence[num] - var/new_gene = input("From [original] to-", "New block", original) as null|anything in genes - if(!new_gene) - new_gene = original - if(viable_occupant == get_viable_occupant()) //No cheesing - if((new_gene == "JOKER") && (jokerready < world.time)) - var/true_genes = GET_SEQUENCE(current_mutation) - new_gene = true_genes[num] - jokerready = world.time + JOKER_TIMEOUT - (JOKER_UPGRADE * (connected.precision_coeff-1)) - sequence = copytext_char(sequence, 1, num) + new_gene + copytext_char(sequence, num + 1) - viable_occupant.dna.mutation_index[path] = sequence - viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected.damage_coeff - viable_occupant.domutcheck() - if("exportdiskmut") - if(diskette && !diskette.read_only) - var/path = text2path(href_list["path"]) - if(ispath(path, /datum/mutation/human)) - var/datum/mutation/human/A = get_valid_mutation(path) - if(A && diskette && (LAZYLEN(diskette.mutations) < diskette.max_mutations)) - var/datum/mutation/human/HM = new A.type() - diskette.mutations += HM - HM.copy_mutation(A) - to_chat(usr, "Successfully wrote [A.name] to [diskette.name].") - if("deletediskmut") - if(diskette && !diskette.read_only) - if(num && (LAZYLEN(diskette.mutations) >= num)) - var/datum/mutation/human/A = diskette.mutations[num] - diskette.mutations.Remove(A) - qdel(A) - if("importdiskmut") - if(diskette && (LAZYLEN(diskette.mutations) >= num)) - if(LAZYLEN(stored_mutations) < max_storage) - var/datum/mutation/human/A = diskette.mutations[num] - var/datum/mutation/human/HM = new A.type() - HM.copy_mutation(A) - stored_mutations += HM - to_chat(usr,"Successfully wrote [A.name] to storage.") - if("combine") - if(num && (LAZYLEN(stored_mutations) >= num)) - if(LAZYLEN(stored_mutations) < max_storage) - var/datum/mutation/human/A = stored_mutations[num] - var/path = A.type - if(combine) - var/result_path = get_mixed_mutation(combine, path) - if(result_path) - stored_mutations += new result_path() - to_chat(usr, "Success! New mutation has been added to storage") - discover(result_path) - combine = null - else - to_chat(usr, "Failed. No mutation could be created.") - combine = null - else - combine = path - to_chat(usr,"Selected [A.name] for combining") - else - to_chat(usr, "Not enough space to store potential mutation.") - if("ejectchromosome") - if(LAZYLEN(stored_chromosomes) >= num) - var/obj/item/chromosome/CM = stored_chromosomes[num] - CM.forceMove(drop_location()) - adjust_item_drop_location(CM) - stored_chromosomes -= CM - if("applychromosome") - if(viable_occupant && (LAZYLEN(viable_occupant.dna.mutations) >= num)) - var/datum/mutation/human/HM = viable_occupant.dna.mutations[num] - var/list/chromosomes = list() - for(var/obj/item/chromosome/CM in stored_chromosomes) - if(CM.can_apply(HM)) - chromosomes += CM - if(chromosomes.len) - var/obj/item/chromosome/CM = input("Select a chromosome to apply", "Apply Chromosome") as null|anything in sortNames(chromosomes) - if(CM) - to_chat(usr, "You apply [CM] to [HM.name].") - stored_chromosomes -= CM - CM.apply(HM) - if("expand_advinjector") - var/mutation = text2path(href_list["path"]) - var/datum/mutation/human/HM = get_valid_mutation(mutation) - if(HM && LAZYLEN(injector_selection)) - var/which_injector = input(usr, "Select Adv. Injector", "Advanced Injectors") as null|anything in injector_selection - if(injector_selection.Find(which_injector)) - var/list/true_selection = injector_selection[which_injector] - var/total_instability - for(var/B in true_selection) - var/datum/mutation/human/mootacion = B - total_instability += mootacion.instability - total_instability += HM.instability - if((total_instability > max_injector_instability) || (true_selection.len + 1) > max_injector_mutations) - to_chat(usr, "Adding more mutations would make the advanced injector too unstable!") - else - true_selection += HM //reminder that this works. because I keep forgetting this works - if("remove_from_advinjector") - var/mutation = text2path(href_list["path"]) - var/selection = href_list["injector"] - if(injector_selection.Find(selection)) - var/list/true_selection = injector_selection[selection] - for(var/B in true_selection) - var/datum/mutation/human/HM = B - if(HM.type == mutation) - true_selection -= HM - break - - if("remove_advinjector") - var/selection = href_list["injector"] - for(selection in injector_selection) - if(selection == selection) - injector_selection.Remove(selection) - - if("add_advinjector") - if(LAZYLEN(injector_selection) < max_injector_selections) - var/new_selection = stripped_input(usr, "Enter Adv. Injector name", "Advanced Injectors") - if(new_selection && !(new_selection in injector_selection)) - injector_selection[new_selection] = list() - - - - ui_interact(usr,last_change) - -/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd) //hexadecimal genetics. dont confuse with scramble button + return + + // Deletes saved mutation from disk buffer. + // ---------------------------------------------------------------------- // + // params["mutref"] - ATOM Ref of specific mutation to delete + if("delete_disk_mut") + // GUARD CHECK - This code shouldn't even be callable without a diskette + // inserted. Unexpected result + if(!diskette) + return + + // GUARD CHECK - Make sure the disk isn't set to read only, as we're + // attempting to write to it (via deletion) + if(diskette.read_only) + to_chat(usr,"Disk is set to read only mode.") + return + + var/bref = params["mutref"] + var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_DISKETTE) + + if(HM) + diskette.mutations.Remove(HM) + qdel(HM) + + return + + // Ejects a stored chromosome from the DNA Console + // ---------------------------------------------------------------------- // + // params["chromo"] - Text string of the chromosome name + if("eject_chromo") + var/chromname = params["chromo"] + + for(var/obj/item/chromosome/CM in stored_chromosomes) + if(chromname == CM.name) + CM.forceMove(drop_location()) + adjust_item_drop_location(CM) + stored_chromosomes -= CM + return + + return + + // Combines two mutations from the console to try and create a new mutation + // ---------------------------------------------------------------------- // + // params["firstref"] - ATOM Ref of first mutation for combination + // params["secondref"] - ATOM Ref of second mutation for combination + // mutation + if("combine_console") + // GUaRD CHECK - Make sure mutation storage isn't full. If it is, we won't + // be able to store the new combo mutation + if(LAZYLEN(stored_mutations) >= max_storage) + to_chat(usr,"Mutation storage is full.") + return + + // GUARD CHECK - We're running a research-type operation. If, for some + // reason, somehow the DNA Console has been disconnected from the research + // network - Or was never in it to begin with - don't proceed + if(!stored_research) + return + + var/first_bref = params["firstref"] + var/second_bref = params["secondref"] + + // GUARD CHECK - Find the source and destination mutations on the console + // and make sure they actually exist. + var/datum/mutation/human/source_mut = get_mut_by_ref(first_bref, SEARCH_STORED | SEARCH_DISKETTE) + if(!source_mut) + return + + var/datum/mutation/human/dest_mut = get_mut_by_ref(second_bref, SEARCH_STORED | SEARCH_DISKETTE) + if(!dest_mut) + return + + // Attempt to mix the two mutations to get a new type + var/result_path = get_mixed_mutation(source_mut.type, dest_mut.type) + + if(!result_path) + return + + // If we got a new type, add it to our storage + stored_mutations += new result_path() + to_chat(usr, "Success! New mutation has been added to console storage.") + + // If it's already discovered, end here. Otherwise, add it to the list of + // discovered mutations. + // We've already checked for stored_research earlier + if(result_path in stored_research.discovered_mutations) + return + + var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(result_path) + stored_research.discovered_mutations += result_path + say("Successfully mutated [HM.name].") + return + + // Combines two mutations from the disk to try and create a new mutation + // ---------------------------------------------------------------------- // + // params["firstref"] - ATOM Ref of first mutation for combination + // params["secondref"] - ATOM Ref of second mutation for combination + // mutation + if("combine_disk") + // GUARD CHECK - This code shouldn't even be callable without a diskette + // inserted. Unexpected result + if(!diskette) + return + + // GUARD CHECK - Make sure the disk is not full. + if(LAZYLEN(diskette.mutations) >= diskette.max_mutations) + to_chat(usr,"Disk storage is full.") + return + + // GUARD CHECK - Make sure the disk isn't set to read only, as we're + // attempting to write to it + if(diskette.read_only) + to_chat(usr,"Disk is set to read only mode.") + return + + // GUARD CHECK - We're running a research-type operation. If, for some + // reason, somehow the DNA Console has been disconnected from the research + // network - Or was never in it to begin with - don't proceed + if(!stored_research) + return + + var/first_bref = params["firstref"] + var/second_bref = params["secondref"] + + // GUARD CHECK - Find the source and destination mutations on the console + // and make sure they actually exist. + var/datum/mutation/human/source_mut = get_mut_by_ref(first_bref, SEARCH_STORED | SEARCH_DISKETTE) + if(!source_mut) + return + + var/datum/mutation/human/dest_mut = get_mut_by_ref(second_bref, SEARCH_STORED | SEARCH_DISKETTE) + if(!dest_mut) + return + + // Attempt to mix the two mutations to get a new type + var/result_path = get_mixed_mutation(source_mut.type, dest_mut.type) + + if(!result_path) + return + + // If we got a new type, add it to our storage + diskette.mutations += new result_path() + to_chat(usr, "Success! New mutation has been added to the disk.") + + // If it's already discovered, end here. Otherwise, add it to the list of + // discovered mutations + // We've already checked for stored_research earlier + if(result_path in stored_research.discovered_mutations) + return + + var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(result_path) + stored_research.discovered_mutations += result_path + say("Successfully mutated [HM.name].") + return + + // Sets the Genetic Makeup pulse strength. + // ---------------------------------------------------------------------- // + // params["val"] - New strength value as text string, converted to number + // later on in code + if("set_pulse_strength") + var/value = round(text2num(params["val"])) + radstrength = WRAP(value, 1, RADIATION_STRENGTH_MAX+1) + return + + // Sets the Genetic Makeup pulse duration + // ---------------------------------------------------------------------- // + // params["val"] - New strength value as text string, converted to number + // later on in code + if("set_pulse_duration") + var/value = round(text2num(params["val"])) + radduration = WRAP(value, 1, RADIATION_DURATION_MAX+1) + return + + // Saves Genetic Makeup information to disk + // ---------------------------------------------------------------------- // + // params["index"] - The BYOND index of the console genetic makeup buffer to + // copy to disk + if("save_makeup_disk") + // GUARD CHECK - This code shouldn't even be callable without a diskette + // inserted. Unexpected result + if(!diskette) + return + + // GUARD CHECK - Make sure the disk isn't set to read only, as we're + // attempting to write to it + if(diskette.read_only) + to_chat(usr,"Disk is set to read only mode.") + return + + // Convert the index to a number and clamp within the array range + var/buffer_index = text2num(params["index"]) + buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS) + + var/list/buffer_slot = genetic_makeup_buffer[buffer_index] + + // GUARD CHECK - This should not be possible to activate on a buffer slot + // that doesn't have any genetic data. Unexpected result + if(!istype(buffer_slot)) + return + + diskette.genetic_makeup_buffer = buffer_slot.Copy() + return + + // Loads Genetic Makeup from disk to a console buffer + // ---------------------------------------------------------------------- // + // params["index"] - The BYOND index of the console genetic makeup buffer to + // copy to. Expected as text string, converted to number later + if("load_makeup_disk") + // GUARD CHECK - This code shouldn't even be callable without a diskette + // inserted. Unexpected result + if(!diskette) + return + + // GUARD CHECK - This should not be possible to activate on a diskette + // that doesn't have any genetic data. Unexpected result + if(LAZYLEN(diskette.genetic_makeup_buffer) == 0) + return + + // Convert the index to a number and clamp within the array range, then + // copy the data from the disk to that buffer + var/buffer_index = text2num(params["index"]) + buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS) + genetic_makeup_buffer[buffer_index] = diskette.genetic_makeup_buffer.Copy() + return + + // Deletes genetic makeup buffer from the inserted diskette + if("del_makeup_disk") + // GUARD CHECK - This code shouldn't even be callable without a diskette + // inserted. Unexpected result + if(!diskette) + return + + // GUARD CHECK - Make sure the disk isn't set to read only, as we're + // attempting to write (via deletion) to it + if(diskette.read_only) + to_chat(usr,"Disk is set to read only mode.") + return + + diskette.genetic_makeup_buffer.Cut() + return + + // Saves the scanner occupant's genetic makeup to a given console buffer + // ---------------------------------------------------------------------- // + // params["index"] - The BYOND index of the console genetic makeup buffer to + // save the new genetic data to. Expected as text string, converted to + // number later + if("save_makeup_console") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + if(!can_modify_occupant()) + return + + // Convert the index to a number and clamp within the array range, then + // copy the data from the disk to that buffer + var/buffer_index = text2num(params["index"]) + buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS) + + // Set the new information + genetic_makeup_buffer[buffer_index] = list( + "label"="Slot [buffer_index]:[scanner_occupant.real_name]", + "UI"=scanner_occupant.dna.uni_identity, + "UE"=scanner_occupant.dna.unique_enzymes, + "name"=scanner_occupant.real_name, + "blood_type"=scanner_occupant.dna.blood_type) + + return + + // Deleted genetic makeup data from a console buffer slot + // ---------------------------------------------------------------------- // + // params["index"] - The BYOND index of the console genetic makeup buffer to + // delete the genetic data from. Expected as text string, converted to + // number later + if("del_makeup_console") + // Convert the index to a number and clamp within the array range, then + // copy the data from the disk to that buffer + var/buffer_index = text2num(params["index"]) + buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS) + var/list/buffer_slot = genetic_makeup_buffer[buffer_index] + + // GUARD CHECK - This shouldn't be possible to execute this on a null + // buffer. Unexpected resut + if(!istype(buffer_slot)) + return + + genetic_makeup_buffer[buffer_index] = null + return + + // Eject stored diskette from console + if("eject_disk") + // GUARD CHECK - This code shouldn't even be callable without a diskette + // inserted. Unexpected result + if(!diskette) + return + + diskette.forceMove(drop_location()) + diskette = null + return + + // Create a Genetic Makeup injector. These injectors are timed and thus are + // only temporary + // ---------------------------------------------------------------------- // + // params["index"] - The BYOND index of the console genetic makeup buffer to + // create the makeup injector from. Expected as text string, converted to + // number later + // params["type"] - Type of injector to create + // Expected results: + // "ue" - Unique Enzyme, changes name and blood type + // "ui" - Unique Identity, changes looks + // "mixed" - Combination of both ue and ui + if("makeup_injector") + // Convert the index to a number and clamp within the array range, then + // copy the data from the disk to that buffer + var/buffer_index = text2num(params["index"]) + buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS) + var/list/buffer_slot = genetic_makeup_buffer[buffer_index] + + // GUARD CHECK - This shouldn't be possible to execute this on a null + // buffer. Unexpected resut + if(!istype(buffer_slot)) + return + + var/type = params["type"] + var/obj/item/dnainjector/timed/I + + switch(type) + if("ui") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["UI"]) + to_chat(usr,"Genetic data corrupted, unable to create injector.") + return + + I = new /obj/item/dnainjector/timed(loc) + I.fields = list("UI"=buffer_slot["UI"]) + + // If there is a connected scanner, we can use its upgrades to reduce + // the radiation generated by this injector + if(scanner_operational()) + I.damage_coeff = connected_scanner.damage_coeff + if("ue") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) + to_chat(usr,"Genetic data corrupted, unable to create injector.") + return + + I = new /obj/item/dnainjector/timed(loc) + I.fields = list("name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"]) + + // If there is a connected scanner, we can use its upgrades to reduce + // the radiation generated by this injector + if(scanner_operational()) + I.damage_coeff = connected_scanner.damage_coeff + if("mixed") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) + to_chat(usr,"Genetic data corrupted, unable to create injector.") + return + + I = new /obj/item/dnainjector/timed(loc) + I.fields = list("UI"=buffer_slot["UI"],"name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"]) + + // If there is a connected scanner, we can use its upgrades to reduce + // the radiation generated by this injector + if(scanner_operational()) + I.damage_coeff = connected_scanner.damage_coeff + + // If we successfully created an injector, don't forget to set the new + // ready timer. + if(I) + injectorready = world.time + INJECTOR_TIMEOUT + + return + + // Applies a genetic makeup buffer to the scanner occupant + // ---------------------------------------------------------------------- // + // params["index"] - The BYOND index of the console genetic makeup buffer to + // apply to the scanner occupant. Expected as text string, converted to + // number later + // params["type"] - Type of genetic makeup copy to implement + // Expected results: + // "ue" - Unique Enzyme, changes name and blood type + // "ui" - Unique Identity, changes looks + // "mixed" - Combination of both ue and ui + if("makeup_apply") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + if(!can_modify_occupant()) + return + + // Convert the index to a number and clamp within the array range, then + // copy the data from the disk to that buffer + var/buffer_index = text2num(params["index"]) + buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS) + var/list/buffer_slot = genetic_makeup_buffer[buffer_index] + + // GUARD CHECK - This shouldn't be possible to execute this on a null + // buffer. Unexpected resut + if(!istype(buffer_slot)) + return + + var/type = params["type"] + + apply_genetic_makeup(type, buffer_slot) + return + + // Applies a genetic makeup buffer to the next scanner occupant. This sets + // some code that will run when the connected DNA Scanner door is next + // closed + // This allows people to self-modify their genetic makeup, as tgui + // interfaces can not be accessed while inside the DNA Scanner and genetic + // makeup injectors are only temporary + // ---------------------------------------------------------------------- // + // params["index"] - The BYOND index of the console genetic makeup buffer to + // apply to the scanner occupant. Expected as text string, converted to + // number later + // params["type"] - Type of genetic makeup copy to implement + // Expected results: + // "ue" - Unique Enzyme, changes name and blood type + // "ui" - Unique Identity, changes looks + // "mixed" - Combination of both ue and ui + if("makeup_delay") + // Convert the index to a number and clamp within the array range, then + // copy the data from the disk to that buffer + var/buffer_index = text2num(params["index"]) + buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS) + var/list/buffer_slot = genetic_makeup_buffer[buffer_index] + + // GUARD CHECK - This shouldn't be possible to execute this on a null + // buffer. Unexpected resut + if(!istype(buffer_slot)) + return + + var/type = params["type"] + + // Set the delayed action. The next time the scanner door is closed, + // unless this is cancelled in the UI, the action will happen + delayed_action = list("type" = type, "buffer_slot" = buffer_slot) + return + + // Attempts to modify the indexed element of the Unique Identity string + // This is a time delayed action that is handled in process() + // ---------------------------------------------------------------------- // + // params["index"] - The BYOND index of the Unique Identity string to + // attempt to modify + if("makeup_pulse") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + if(!can_modify_occupant()) + return + + // Set the appropriate timer and index to pulse. This is then managed + // later on in process() + var/len = length_char(scanner_occupant.dna.uni_identity) + rad_pulse_timer = world.time + (radduration*10) + rad_pulse_index = WRAP(text2num(params["index"]), 1, len+1) + begin_processing() + return + + // Cancels the delayed action - In this context it is not the radiation + // pulse from "makeup_pulse", which can not be cancelled. It is instead + // the delayed genetic transfer from "makeup_delay" + if("cancel_delay") + delayed_action = null + return + + // Creates a new advanced injector storage buffer in the console + // ---------------------------------------------------------------------- // + // params["name"] - The name to apply to the new injector + if("new_adv_inj") + // GUARD CHECK - Make sure we can make a new injector. This code should + // not be called if we're already maxed out and this is an Unexpected + // result + if(!(LAZYLEN(injector_selection) < max_injector_selections)) + return + + // GUARD CHECK - Sanitise and trim the proposed name. This prevents HTML + // injection and equivalent as tgui input is not stripped + var/inj_name = params["name"] + inj_name = trim(sanitize(inj_name)) + + // GUARD CHECK - If the name is null or blank, or the name is already in + // the list of advanced injectors, we want to reject it as we can't have + // duplicate named advanced injectors + if(!inj_name || (inj_name in injector_selection)) + return + + injector_selection[inj_name] = list() + return + + // Deleted an advanced injector storage buffer from the console + // ---------------------------------------------------------------------- // + // params["name"] - The name of the injector to delete + if("del_adv_inj") + var/inj_name = params["name"] + + // GUARD CHECK - If the name is null or blank, reject. + // GUARD CHECK - If the name isn't in the list of advanced injectors, we + // want to reject this as it shouldn't be possible ever do this. + // Unexpected result + if(!inj_name || !(inj_name in injector_selection)) + return + + injector_selection.Remove(inj_name) + return + + // Creates an injector from an advanced injector buffer + // ---------------------------------------------------------------------- // + // params["name"] - The name of the injector to print + if("print_adv_inj") + // As a side note, because mutations can contain unique metadata, + // this system uses BYOND Atom Refs to safely and accurately + // identify mutations from big ol' lists. + + // GUARD CHECK - Is the injector actually ready? + if(world.time < injectorready) + return + + var/inj_name = params["name"] + + // GUARD CHECK - If the name is null or blank, reject. + // GUARD CHECK - If the name isn't in the list of advanced injectors, we + // want to reject this as it shouldn't be possible ever do this. + // Unexpected result + if(!inj_name || !(inj_name in injector_selection)) + return + + var/list/injector = injector_selection[inj_name] + var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc) + + // Run through each mutation in our Advanced Injector and add them to a + // new injector + for(var/A in injector) + var/datum/mutation/human/HM = A + I.add_mutations += new HM.type(copymut=HM) + + // Force apply any mutations, this is functionality similar to mutators + I.doitanyway = TRUE + I.name = "Advanced [inj_name] injector" + + // If there's an operational connected scanner, we can use its upgrades + // to improve our injector's radiation generation + if(scanner_operational()) + I.damage_coeff = connected_scanner.damage_coeff + injectorready = world.time + INJECTOR_TIMEOUT * 8 * (1 - 0.1 * connected_scanner.precision_coeff) + else + injectorready = world.time + INJECTOR_TIMEOUT * 8 + + return + + // Adds a mutation to an advanced injector + // ---------------------------------------------------------------------- // + // params["mutref"] - ATOM Ref of specific mutation to add to the injector + // params["advinj"] - Name of the advanced injector to add the mutation to + if("add_advinj_mut") + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + // This is needed because this operation can only be completed from the + // genetic sequencer. + if(!can_modify_occupant()) + return + + var/adv_inj = params["advinj"] + + // GUARD CHECK - Make sure our advanced injector actually exists. This + // should not be possible. Unexpected result + if(!(adv_inj in injector_selection)) + return + + // GUARD CHECK - Make sure we limit the number of mutations appropriately + if(LAZYLEN(injector_selection[adv_inj]) >= max_injector_mutations) + to_chat(usr,"Advanced injector mutation storage is full.") + return + + var/mut_source = params["source"] + var/search_flag = 0 + + switch(mut_source) + if("disk") + search_flag = SEARCH_DISKETTE + if("occupant") + search_flag = SEARCH_OCCUPANT + if("console") + search_flag = SEARCH_STORED + + if(!search_flag) + return + + var/bref = params["mutref"] + // We've already made sure we can modify the occupant, so this is safe to + // call + var/datum/mutation/human/HM = get_mut_by_ref(bref, search_flag) + + // GUARD CHECK - This should not be possible. Unexpected result + if(!HM) + return + + // We want to make sure we stick within the instability limit. + // We start with the instability of the mutation we're intending to add. + var/instability_total = HM.instability + + // We then add the instabilities of all other mutations in the injector, + // remembering to apply the Stabilizer chromosome modifiers + for(var/datum/mutation/human/I in injector_selection[adv_inj]) + instability_total += I.instability * GET_MUTATION_STABILIZER(I) + + // If this would take us over the max instability, we inform the user. + if(instability_total > max_injector_instability) + to_chat(usr,"Extra mutation would make the advanced injector too instable.") + return + + // If we've got here, all our checks are passed and we can successfully + // add the mutation to the advanced injector. + var/datum/mutation/human/A = new HM.type() + A.copy_mutation(HM) + injector_selection[adv_inj] += A + to_chat(usr,"Mutation successfully added to advanced injector.") + return + + // Deletes a mutation from an advanced injector + // ---------------------------------------------------------------------- // + // params["mutref"] - ATOM Ref of specific mutation to del from the injector + if("delete_injector_mut") + var/bref = params["mutref"] + + var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_ADV_INJ) + + // GUARD CHECK - This should not be possible. Unexpected result + if(!HM) + return + + // Check Advanced Injectors to find and remove the mutation + for(var/I in injector_selection) + if(injector_selection["[I]"].Remove(HM)) + qdel(HM) + return + + return + + // Sets a new tgui view state + // ---------------------------------------------------------------------- // + // params["id"] - Key for the state to set + // params[...] - Every other element is used to set state variables + if("set_view") + for (var/key in params) + if(key == "src") + continue + tgui_view_state[key] = params[key] + return TRUE + return FALSE + +/** + * Applies the enzyme buffer to the current scanner occupant + * + * Applies the type of a specific genetic makeup buffer to the current scanner + * occupant + * + * Arguments: + * * type - "ui"/"ue"/"mixed" - Which part of the enzyme buffer to apply + * * buffer_slot - Index of the enzyme buffer to apply + */ +/obj/machinery/computer/scan_consolenew/proc/apply_genetic_makeup(type, buffer_slot) + // Note - This proc is only called from code that has already performed the + // necessary occupant guard checks. If you call this code yourself, please + // apply can_modify_occupant() or equivalent checks first. + + // Pre-calc the rad increase since we'll be using it in all the possible + // operations + var/rad_increase = rand(100/(connected_scanner.damage_coeff ** 2),250/(connected_scanner.damage_coeff ** 2)) + + switch(type) + if("ui") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["UI"]) + to_chat(usr,"Genetic data corrupted, unable to apply genetic data.") + return FALSE + scanner_occupant.dna.uni_identity = buffer_slot["UI"] + scanner_occupant.updateappearance(mutations_overlay_update=1) + scanner_occupant.radiation += rad_increase + scanner_occupant.domutcheck() + return TRUE + if("ue") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) + to_chat(usr,"Genetic data corrupted, unable to apply genetic data.") + return FALSE + scanner_occupant.real_name = buffer_slot["name"] + scanner_occupant.name = buffer_slot["name"] + scanner_occupant.dna.unique_enzymes = buffer_slot["UE"] + scanner_occupant.dna.blood_type = buffer_slot["blood_type"] + scanner_occupant.radiation += rad_increase + scanner_occupant.domutcheck() + return TRUE + if("mixed") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) + to_chat(usr,"Genetic data corrupted, unable to apply genetic data.") + return FALSE + scanner_occupant.dna.uni_identity = buffer_slot["UI"] + scanner_occupant.updateappearance(mutations_overlay_update=1) + scanner_occupant.real_name = buffer_slot["name"] + scanner_occupant.name = buffer_slot["name"] + scanner_occupant.dna.unique_enzymes = buffer_slot["UE"] + scanner_occupant.dna.blood_type = buffer_slot["blood_type"] + scanner_occupant.radiation += rad_increase + scanner_occupant.domutcheck() + return TRUE + + return FALSE +/** + * Checks if there is a connected DNA Scanner that is operational + */ +/obj/machinery/computer/scan_consolenew/proc/scanner_operational() + if(!connected_scanner) + return FALSE + + return (connected_scanner && connected_scanner.is_operational()) + +/** + * Checks if there is a valid DNA Scanner occupant for genetic modification + * + * Checks if there is a valid subject in the DNA Scanner that can be genetically + * modified. Will set the scanner occupant var as part of this check. + * Requires that the scanner can be operated and will return early if it can't + */ +/obj/machinery/computer/scan_consolenew/proc/can_modify_occupant() + // GUARD CHECK - We always want to perform the scanner operational check as + // part of checking if we can modify the occupant. + // We can never modify the occupant of a broken scanner. + if(!scanner_operational()) + return FALSE + + if(!connected_scanner.occupant) + return FALSE + + scanner_occupant = connected_scanner.occupant + + // Check validity of occupent for DNA Modification + // DNA Modification: + // requires DNA + // this DNA can not be bad + // is done via radiation bursts, so radiation immune carbons are not viable + // And the DNA Scanner itself must have a valid scan level + if(scanner_occupant.has_dna() && !HAS_TRAIT(scanner_occupant, TRAIT_RADIMMUNE) && !HAS_TRAIT(scanner_occupant, TRAIT_BADDNA) || (connected_scanner.scan_level == 3)) + return TRUE + + return FALSE + +/** + * Checks for adjacent DNA scanners and connects when it finds a viable one + * + * Seearches cardinal directions in order. Stops when it finds a viable DNA Scanner. + * Will connect to a broken scanner if no functional scanner is available. + * Links itself to the DNA Scanner to receive door open and close events. + */ +/obj/machinery/computer/scan_consolenew/proc/connect_to_scanner() + var/obj/machinery/dna_scannernew/test_scanner = null + var/obj/machinery/dna_scannernew/broken_scanner = null + + // Look in each cardinal direction and try and find a DNA Scanner + // If you find a DNA Scanner, check to see if it broken or working + // If it's working, set the current scanner and return early + // If it's not working, remember it anyway as a broken scanner + for(var/direction in GLOB.cardinals) + test_scanner = locate(/obj/machinery/dna_scannernew, get_step(src, direction)) + if(!isnull(test_scanner)) + if(test_scanner.is_operational()) + connected_scanner = test_scanner + connected_scanner.linked_console = src + return + else + broken_scanner = test_scanner + + // Ultimately, if we have a broken scanner, we'll attempt to connect to it as + // a fallback case, but the code above will prefer a working scanner + if(!isnull(broken_scanner)) + connected_scanner = broken_scanner + connected_scanner.linked_console = src + +/** + * Called by connected DNA Scanners when their doors close. + * + * Sets the new scanner occupant and completes delayed enzyme transfer if one + * is queued. + */ +/obj/machinery/computer/scan_consolenew/proc/on_scanner_close() + // Set the appropriate occupant now the scanner is closed + if(connected_scanner.occupant) + scanner_occupant = connected_scanner.occupant + else + scanner_occupant = null + + // If we have a delayed action - In this case the only delayed action is + // applying a genetic makeup buffer the next time the DNA Scanner is closed - + // we want to perform it. + // GUARD CHECK - Make sure we can modify the occupant, apply_genetic_makeup() + // assumes we've already done this. + if(delayed_action && can_modify_occupant()) + var/type = delayed_action["type"] + var/buffer_slot = delayed_action["buffer_slot"] + if(apply_genetic_makeup(type, buffer_slot)) + to_chat(connected_scanner.occupant, "[src] activates!") + delayed_action = null + +/** + * Called by connected DNA Scanners when their doors open. + * + * Clears enzyme pulse operations, stops processing and nulls the current + * scanner occupant var. + */ +/obj/machinery/computer/scan_consolenew/proc/on_scanner_open() + // If we had a radiation pulse action ongoing, we want to stop this. + // Imagine it being like a microwave stopping when you open the door. + rad_pulse_index = 0 + rad_pulse_timer = 0 + end_processing() + scanner_occupant = null + +/** + * Builds the genetic makeup list which will be sent to tgui interface. + */ +/obj/machinery/computer/scan_consolenew/proc/build_genetic_makeup_list() + // No code will ever null this list, we can safely Cut it. + tgui_genetic_makeup.Cut() + + for(var/i=1, i <= NUMBER_OF_BUFFERS, i++) + if(genetic_makeup_buffer[i]) + tgui_genetic_makeup["[i]"] = genetic_makeup_buffer[i].Copy() + else + tgui_genetic_makeup["[i]"] = null + +/** + * Builds the genetic makeup list which will be sent to tgui interface. + * + * Will iterate over the connected scanner occupant, DNA Console, inserted + * diskette and chromosomes and any advanced injectors, building the main data + * structures which get passed to the tgui interface. + */ +/obj/machinery/computer/scan_consolenew/proc/build_mutation_list(can_modify_occ) + // No code will ever null these lists. We can safely Cut them. + tgui_occupant_mutations.Cut() + tgui_diskette_mutations.Cut() + tgui_console_mutations.Cut() + tgui_console_chromosomes.Cut() + tgui_advinjector_mutations.Cut() + + // ------------------------------------------------------------------------ // + // GUARD CHECK - Can we genetically modify the occupant? This check will have + // previously included checks to make sure the DNA Scanner is still + // operational + if(can_modify_occ) + // ---------------------------------------------------------------------- // + // Start cataloguing all mutations that the occupant has by default + for(var/mutation_type in scanner_occupant.dna.mutation_index) + var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(mutation_type) + + var/list/mutation_data = list() + var/text_sequence = scanner_occupant.dna.mutation_index[mutation_type] + var/default_sequence = scanner_occupant.dna.default_mutation_genes[mutation_type] + var/discovered = (stored_research && (mutation_type in stored_research.discovered_mutations)) + + mutation_data["Alias"] = HM.alias + mutation_data["Sequence"] = text_sequence + mutation_data["DefaultSeq"] = default_sequence + mutation_data["Discovered"] = discovered + mutation_data["Source"] = "occupant" + + // We only want to pass this information along to the tgui interface if + // the mutation has been discovered. Prevents people being able to cheese + // or "hack" their way to figuring out what undiscovered mutations are + if(discovered) + mutation_data["Name"] = HM.name + mutation_data["Description"] = HM.desc + mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM) + mutation_data["Quality"] = HM.quality + + // Assume the mutation is normal unless assigned otherwise. + var/mut_class = MUT_NORMAL + + // Check if the mutation is currently activated. If it is, we can add even + // MORE information to send to tgui. + var/datum/mutation/human/A = scanner_occupant.dna.get_mutation(mutation_type) + if(A) + mutation_data["Active"] = TRUE + mutation_data["Scrambled"] = A.scrambled + mutation_data["Class"] = A.class + mut_class = A.class + mutation_data["CanChromo"] = A.can_chromosome + mutation_data["ByondRef"] = REF(A) + mutation_data["Type"] = A.type + if(A.can_chromosome) + mutation_data["ValidChromos"] = jointext(A.valid_chrom_list, ", ") + mutation_data["AppliedChromo"] = A.chromosome_name + mutation_data["ValidStoredChromos"] = build_chrom_list(A) + else + mutation_data["Active"] = FALSE + mutation_data["Scrambled"] = FALSE + mutation_data["Class"] = MUT_NORMAL + + // Technically NONE of these mutations should be MUT_EXTRA but this will + // catch any weird edge cases + // Assign icons by priority - MUT_EXTRA will ALSO be discovered, so it + // has a higher priority for icon/image assignment + if (mut_class == MUT_EXTRA) + mutation_data["Image"] = "dna_extra.gif" + else if(discovered) + mutation_data["Image"] = "dna_discovered.gif" + else + mutation_data["Image"] = "dna_undiscovered.gif" + + tgui_occupant_mutations += list(mutation_data) + + // ---------------------------------------------------------------------- // + // Now get additional/"extra" mutations that they shouldn't have by default + for(var/datum/mutation/human/HM in scanner_occupant.dna.mutations) + // If it's in the mutation index array, we've already catalogued this + // mutation and can safely skip over it. It really shouldn't be, but this + // will catch any weird edge cases + if(HM.type in scanner_occupant.dna.mutation_index) + continue + + var/list/mutation_data = list() + var/text_sequence = GET_SEQUENCE(HM.type) + + // These will all be active mutations. They're added by injector and their + // sequencing code can't be changed. They can only be nullified, which + // completely removes them. + var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(HM.type) + + mutation_data["Alias"] = A.alias + mutation_data["Sequence"] = text_sequence + mutation_data["Discovered"] = TRUE + mutation_data["Quality"] = HM.quality + mutation_data["Source"] = "occupant" + + mutation_data["Name"] = HM.name + mutation_data["Description"] = HM.desc + mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM) + + mutation_data["Active"] = TRUE + mutation_data["Scrambled"] = HM.scrambled + mutation_data["Class"] = HM.class + mutation_data["CanChromo"] = HM.can_chromosome + mutation_data["ByondRef"] = REF(HM) + mutation_data["Type"] = HM.type + + if(HM.can_chromosome) + mutation_data["ValidChromos"] = jointext(HM.valid_chrom_list, ", ") + mutation_data["AppliedChromo"] = HM.chromosome_name + mutation_data["ValidStoredChromos"] = build_chrom_list(HM) + + // Nothing in this list should be undiscovered. Technically nothing + // should be anything but EXTRA. But we're just handling some edge cases. + if (HM.class == MUT_EXTRA) + mutation_data["Image"] = "dna_extra.gif" + else + mutation_data["Image"] = "dna_discovered.gif" + + tgui_occupant_mutations += list(mutation_data) + + // ------------------------------------------------------------------------ // + // Build the list of mutations stored within the DNA Console + for(var/datum/mutation/human/HM in stored_mutations) + var/list/mutation_data = list() + + var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(HM.type) + + mutation_data["Alias"] = A.alias + mutation_data["Name"] = HM.name + mutation_data["Source"] = "console" + mutation_data["Active"] = TRUE + mutation_data["Description"] = HM.desc + mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM) + mutation_data["ByondRef"] = REF(HM) + mutation_data["Type"] = HM.type + + mutation_data["CanChromo"] = HM.can_chromosome + if(HM.can_chromosome) + mutation_data["ValidChromos"] = jointext(HM.valid_chrom_list, ", ") + mutation_data["AppliedChromo"] = HM.chromosome_name + mutation_data["ValidStoredChromos"] = build_chrom_list(HM) + + tgui_console_mutations += list(mutation_data) + + // ------------------------------------------------------------------------ // + // Build the list of chromosomes stored within the DNA Console + var/chrom_index = 1 + for(var/obj/item/chromosome/CM in stored_chromosomes) + var/list/chromo_data = list() + + chromo_data["Name"] = CM.name + chromo_data["Description"] = CM.desc + chromo_data["Index"] = chrom_index + + tgui_console_chromosomes += list(chromo_data) + ++chrom_index + + // ------------------------------------------------------------------------ // + // Build the list of mutations stored on any inserted diskettes + if(diskette) + for(var/datum/mutation/human/HM in diskette.mutations) + var/list/mutation_data = list() + + var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(HM.type) + + mutation_data["Alias"] = A.alias + mutation_data["Name"] = HM.name + mutation_data["Active"] = TRUE + //mutation_data["Sequence"] = GET_SEQUENCE(HM.type) + mutation_data["Source"] = "disk" + mutation_data["Description"] = HM.desc + mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM) + mutation_data["ByondRef"] = REF(HM) + mutation_data["Type"] = HM.type + + mutation_data["CanChromo"] = HM.can_chromosome + if(HM.can_chromosome) + mutation_data["ValidChromos"] = jointext(HM.valid_chrom_list, ", ") + mutation_data["AppliedChromo"] = HM.chromosome_name + mutation_data["ValidStoredChromos"] = build_chrom_list(HM) + + tgui_diskette_mutations += list(mutation_data) + + // ------------------------------------------------------------------------ // + // Build the list of mutations stored within any Advanced Injectors + if(LAZYLEN(injector_selection)) + for(var/I in injector_selection) + var/list/mutations = list() + for(var/datum/mutation/human/HM in injector_selection[I]) + var/list/mutation_data = list() + + var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(HM.type) + + mutation_data["Alias"] = A.alias + mutation_data["Name"] = HM.name + mutation_data["Active"] = TRUE + //mutation_data["Sequence"] = GET_SEQUENCE(HM.type) + mutation_data["Source"] = "injector" + mutation_data["Description"] = HM.desc + mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM) + mutation_data["ByondRef"] = REF(HM) + mutation_data["Type"] = HM.type + + if(HM.can_chromosome) + mutation_data["AppliedChromo"] = HM.chromosome_name + + mutations += list(mutation_data) + tgui_advinjector_mutations += list(list( + "name" = "[I]", + "mutations" = mutations, + )) + +/** + * Takes any given chromosome and calculates chromosome compatibility + * + * Will iterate over the stored chromosomes in the DNA Console and will check + * whether it can be applied to the supplied mutation. Then returns a list of + * names of chromosomes that were compatible. + * + * Arguments: + * * mutation - The mutation to check chromosome compatibility with + */ +/obj/machinery/computer/scan_consolenew/proc/build_chrom_list(mutation) + var/list/chromosomes = list() + + for(var/obj/item/chromosome/CM in stored_chromosomes) + if(CM.can_apply(mutation)) + chromosomes += CM.name + + return chromosomes + +/** + * Checks whether a mutation alias has been discovered + * + * Checks whether a given mutation's genetic sequence has been completed and + * discovers it if appropriate + * + * Arguments: + * * alias - Alias of the mutation to check (ie "Mutation 51" or "Mutation 12") + */ +/obj/machinery/computer/scan_consolenew/proc/check_discovery(alias) + // Note - All code paths that call this have already done checks on the + // current occupant to prevent cheese and other abuses. If you call this + // proc please also do the following checks first: + // if(!can_modify_occupant()) + // return + // if(!(scanner_occupant == connected_scanner.occupant)) + // return + + // Turn the alias ("Mutation 1", "Mutation 35") into a mutation path + var/path = GET_MUTATION_TYPE_FROM_ALIAS(alias) + + // Check to see if this mutation is in the active mutation list. If it isn't, + // then the mutation isn't eligible for discovery. If it is but is scrambled, + // then the mutation isn't eligible for discovery. Finally, check if the + // mutation is in discovered mutations - If it isn't, add it to discover. + var/datum/mutation/human/M = scanner_occupant.dna.get_mutation(path) + if(!M) + return FALSE + if(M.scrambled) + return FALSE + if(stored_research && !(path in stored_research.discovered_mutations)) + var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(path) + stored_research.discovered_mutations += path + say("Successfully discovered [HM.name].") + return TRUE + + return FALSE + +/** + * Find a mutation from various storage locations via ATOM ref + * + * Takes an ATOM Ref and searches the appropriate mutation buffers and storage + * vars to try and find the associated mutation. + * + * Arguments: + * * ref - ATOM ref of the mutation to locate + * * target_flags - Flags for storage mediums to search, see #defines + */ +/obj/machinery/computer/scan_consolenew/proc/get_mut_by_ref(ref, target_flags) + var/mutation + + // Assume the occupant is valid and the check has been carried out before + // calling this proc with the relevant flags. + if(target_flags & SEARCH_OCCUPANT) + mutation = (locate(ref) in scanner_occupant.dna.mutations) + if(mutation) + return mutation + + if(target_flags & SEARCH_STORED) + mutation = (locate(ref) in stored_mutations) + if(mutation) + return mutation + + if(diskette && (target_flags & SEARCH_DISKETTE)) + mutation = (locate(ref) in diskette.mutations) + if(mutation) + return mutation + + if(injector_selection && (target_flags & SEARCH_ADV_INJ)) + for(var/I in injector_selection) + mutation = (locate(ref) in injector_selection["[I]"]) + if(mutation) + return mutation + + return null + +/** + * Creates a randomised accuracy value for the enzyme pulse functionality. + * + * Donor code from previous DNA Console iteration. + * + * Arguments: + * * position - Index of the intended enzyme element to pulse + * * radduration - Duration of intended radiation pulse + * * number_of_blocks - Number of individual data blocks in the pulsed enzyme + */ +/obj/machinery/computer/scan_consolenew/proc/randomize_radiation_accuracy(position, radduration, number_of_blocks) + var/val = round(gaussian(0, RADIATION_ACCURACY_MULTIPLIER/radduration) + position, 1) + return WRAP(val, 1, number_of_blocks+1) + +/** + * Scrambles an enzyme element value for the enzyme pulse functionality. + * + * Donor code from previous DNA Console iteration. + * + * Arguments: + * * input - Enzyme identity element to scramble, expected hex value + * * rs - Strength of radiation pulse, increases the range of possible outcomes + */ +/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs) var/length = length(input) var/ran = gaussian(0, rs*RADIATION_STRENGTH_MULTIPLIER) if(ran == 0) @@ -956,98 +1950,48 @@ ran = -round(-ran) //positive, so ceiling it return num2hex(WRAP(hex2num(input)+ran, 0, 16**length), length) -/obj/machinery/computer/scan_consolenew/proc/randomize_radiation_accuracy(position, radduration, number_of_blocks) - var/val = round(gaussian(0, RADIATION_ACCURACY_MULTIPLIER/radduration) + position, 1) - return WRAP(val, 1, number_of_blocks+1) + /** + * Performs the enzyme radiation pulse. + * + * Donor code from previous DNA Console iteration. Called from process() when + * there is a radiation pulse in progress. Ends processing. + */ +/obj/machinery/computer/scan_consolenew/proc/rad_pulse() + // GUARD CHECK - Can we genetically modify the occupant? Includes scanner + // operational guard checks. + // If we can't, abort the procedure. + if(!can_modify_occupant()) + rad_pulse_index = 0 + end_processing() + return + + var/len = length_char(scanner_occupant.dna.uni_identity) + var/num = randomize_radiation_accuracy(rad_pulse_index, radduration + (connected_scanner.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2 //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low + var/hex = copytext_char(scanner_occupant.dna.uni_identity, num, num+1) + hex = scramble(hex, radstrength, radduration) + + scanner_occupant.dna.uni_identity = copytext_char(scanner_occupant.dna.uni_identity, 1, num) + hex + copytext_char(scanner_occupant.dna.uni_identity, num + 1) + scanner_occupant.updateappearance(mutations_overlay_update=1) + + rad_pulse_index = 0 + end_processing() + return + +/** + * Sets the default state for the tgui interface. + */ +/obj/machinery/computer/scan_consolenew/proc/set_default_state() + tgui_view_state["consoleMode"] = "storage" + tgui_view_state["storageMode"] = "console" + tgui_view_state["storageConsSubMode"] = "mutations" + tgui_view_state["storageDiskSubMode"] = "mutations" -/obj/machinery/computer/scan_consolenew/proc/get_viable_occupant() - var/mob/living/carbon/viable_occupant = null - if(connected) - viable_occupant = connected.occupant - if(!istype(viable_occupant) || !viable_occupant.dna || HAS_TRAIT(viable_occupant, TRAIT_RADIMMUNE) || HAS_TRAIT(viable_occupant, TRAIT_BADDNA)) - viable_occupant = null - return viable_occupant - -/obj/machinery/computer/scan_consolenew/proc/apply_buffer(action,buffer_num) - buffer_num = clamp(buffer_num, 1, NUMBER_OF_BUFFERS) - var/list/buffer_slot = buffer[buffer_num] - var/mob/living/carbon/viable_occupant = get_viable_occupant() - if(istype(buffer_slot)) - viable_occupant.radiation += rand(100/(connected.damage_coeff ** 2),250/(connected.damage_coeff ** 2)) - //15 and 40 are just magic numbers that were here before so i didnt touch them, they are initial boundaries of damage - //Each laser level reduces damage by lvl^2, so no effect on 1 lvl, 4 times less damage on 2 and 9 times less damage on 3 - //Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove - switch(action) - if(SCANNER_ACTION_UI) - if(buffer_slot["UI"]) - viable_occupant.dna.uni_identity = buffer_slot["UI"] - viable_occupant.updateappearance(mutations_overlay_update=1) - if(SCANNER_ACTION_UE) - if(buffer_slot["name"] && buffer_slot["UE"] && buffer_slot["blood_type"]) - viable_occupant.real_name = buffer_slot["name"] - viable_occupant.name = buffer_slot["name"] - viable_occupant.dna.unique_enzymes = buffer_slot["UE"] - viable_occupant.dna.blood_type = buffer_slot["blood_type"] - if(SCANNER_ACTION_MIXED) - if(buffer_slot["UI"]) - viable_occupant.dna.uni_identity = buffer_slot["UI"] - viable_occupant.updateappearance(mutations_overlay_update=1) - if(buffer_slot["name"] && buffer_slot["UE"] && buffer_slot["blood_type"]) - viable_occupant.real_name = buffer_slot["name"] - viable_occupant.name = buffer_slot["name"] - viable_occupant.dna.unique_enzymes = buffer_slot["UE"] - viable_occupant.dna.blood_type = buffer_slot["blood_type"] -/obj/machinery/computer/scan_consolenew/proc/on_scanner_close() - if(delayed_action && get_viable_occupant()) - to_chat(connected.occupant, "[src] activates!") - apply_buffer(delayed_action["action"],delayed_action["buffer"]) - delayed_action = null //or make it stick + reset button ? - -/obj/machinery/computer/scan_consolenew/proc/get_valid_mutation(mutation) - var/mob/living/carbon/C = get_viable_occupant() - if(C) - var/datum/mutation/human/HM = C.dna.get_mutation(mutation) - if(HM) - return HM - for(var/datum/mutation/human/A in stored_mutations) - if(A.type == mutation) - return A - - -/obj/machinery/computer/scan_consolenew/proc/get_mutation_list(include_storage) //Returns a list of the mutation index types and any extra mutations - var/mob/living/carbon/viable_occupant = get_viable_occupant() - var/list/paths = list() - if(viable_occupant) - for(var/A in viable_occupant.dna.mutation_index) - paths += A - for(var/datum/mutation/human/A in viable_occupant.dna.mutations) - if(A.class == MUT_EXTRA) - paths += A.type - if(include_storage) - for(var/datum/mutation/human/A in stored_mutations) - paths += A.type - return paths - -/obj/machinery/computer/scan_consolenew/proc/get_valid_gene_string(mutation) - var/mob/living/carbon/C = get_viable_occupant() - if(C && (mutation in C.dna.mutation_index)) - return GET_GENE_STRING(mutation, C.dna) - else if(C && (LAZYLEN(C.dna.mutations))) - for(var/datum/mutation/human/A in C.dna.mutations) - if(A.type == mutation) - return GET_SEQUENCE(mutation) - for(var/datum/mutation/human/A in stored_mutations) - if(A.type == mutation) - return GET_SEQUENCE(mutation) - -/obj/machinery/computer/scan_consolenew/proc/discover(mutation) - if(stored_research && !(mutation in stored_research.discovered_mutations)) - stored_research.discovered_mutations += mutation - return TRUE -/////////////////////////// DNA MACHINES #undef INJECTOR_TIMEOUT #undef NUMBER_OF_BUFFERS +#undef SCRAMBLE_TIMEOUT +#undef JOKER_TIMEOUT +#undef JOKER_UPGRADE #undef RADIATION_STRENGTH_MAX #undef RADIATION_STRENGTH_MULTIPLIER @@ -1057,11 +2001,9 @@ #undef RADIATION_IRRADIATION_MULTIPLIER -#undef SCANNER_ACTION_SE -#undef SCANNER_ACTION_UI -#undef SCANNER_ACTION_UE -#undef SCANNER_ACTION_MIXED +#undef STATUS_TRANSFORMING -//#undef BAD_MUTATION_DIFFICULTY -//#undef GOOD_MUTATION_DIFFICULTY -//#undef OP_MUTATION_DIFFICULTY +#undef SEARCH_OCCUPANT +#undef SEARCH_STORED +#undef SEARCH_DISKETTE +#undef SEARCH_ADV_INJ diff --git a/code/game/machinery/computer/launchpad_control.dm b/code/game/machinery/computer/launchpad_control.dm index 26efe7c4ba0e..ed5a031dba2f 100644 --- a/code/game/machinery/computer/launchpad_control.dm +++ b/code/game/machinery/computer/launchpad_control.dm @@ -56,7 +56,7 @@ /obj/machinery/computer/launchpad/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "launchpad_console", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "LaunchpadConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/launchpad/ui_data(mob/user) diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index 29ef03f1fc09..8fe12fb4122a 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -41,7 +41,7 @@ circuit = /obj/item/circuitboard/computer/aiupload /obj/machinery/computer/upload/ai/interact(mob/user) - current = select_active_ai(user) + current = select_active_ai(user, z) if (!current) to_chat(user, "No active AIs detected!") diff --git a/code/game/machinery/computer/prisoner/gulag_teleporter.dm b/code/game/machinery/computer/prisoner/gulag_teleporter.dm index 9da901cd20e7..6cb6a1d34ed6 100644 --- a/code/game/machinery/computer/prisoner/gulag_teleporter.dm +++ b/code/game/machinery/computer/prisoner/gulag_teleporter.dm @@ -25,7 +25,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "gulag_console", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "GulagTeleporterConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_data(mob/user) diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 917d6f9f7afe..115b827e54e2 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -27,7 +27,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "robotics_control_console", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "RoboticsControlConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/robotics/ui_data(mob/user) diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 42a62cda3590..864172b0fed9 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -748,7 +748,7 @@ What a mess.*/ switch(href_list["choice"]) if("Change Rank") if(active1) - active1.fields["rank"] = href_list["rank"] + active1.fields["rank"] = strip_html(href_list["rank"]) if(href_list["rank"] in get_all_jobs()) active1.fields["real_rank"] = href_list["real_rank"] diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index 1cab2bd04569..ab42b16d8aed 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -22,7 +22,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "station_alert", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "StationAlertConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/station_alert/ui_data(mob/user) diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm index 4028a1347aa9..6be511f78518 100644 --- a/code/game/machinery/computer/teleporter.dm +++ b/code/game/machinery/computer/teleporter.dm @@ -37,7 +37,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "teleporter", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Teleporter", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/teleporter/ui_data(mob/user) @@ -90,16 +90,18 @@ say("Processing hub calibration to target...") calibrating = TRUE power_station.update_icon() - spawn(50 * (3 - power_station.teleporter_hub.accuracy)) //Better parts mean faster calibration - calibrating = FALSE - if(check_hub_connection()) - power_station.teleporter_hub.calibrated = TRUE - say("Calibration complete.") - else - say("Error: Unable to detect hub.") - power_station.update_icon() + addtimer(CALLBACK(src, .proc/finish_calibration), 50 * (3 - power_station.teleporter_hub.accuracy)) //Better parts mean faster calibration . = TRUE +/obj/machinery/computer/teleporter/proc/finish_calibration() + calibrating = FALSE + if(check_hub_connection()) + power_station.teleporter_hub.calibrated = TRUE + say("Calibration complete.") + else + say("Error: Unable to detect hub.") + power_station.update_icon() + /obj/machinery/computer/teleporter/proc/check_hub_connection() if(!power_station) return FALSE diff --git a/code/game/machinery/defibrillator_mount.dm b/code/game/machinery/defibrillator_mount.dm index 820b02491db2..898005f9beb7 100644 --- a/code/game/machinery/defibrillator_mount.dm +++ b/code/game/machinery/defibrillator_mount.dm @@ -9,7 +9,7 @@ density = FALSE use_power = IDLE_POWER_USE idle_power_usage = 0 - power_channel = EQUIP + power_channel = AREA_USAGE_EQUIP req_one_access = list(ACCESS_MEDICAL, ACCESS_HEADS, ACCESS_SECURITY) //used to control clamps /// The mount's defib var/obj/item/defibrillator/defib @@ -45,8 +45,9 @@ . += "defib" if(defib.powered) + var/obj/item/stock_parts/cell/C = get_cell() . += (defib.safety ? "online" : "emagged") - var/ratio = defib.cell.charge / defib.cell.maxcharge + var/ratio = C.charge / C.maxcharge ratio = CEILING(ratio * 4, 1) * 25 . += "charge[ratio]" @@ -75,10 +76,16 @@ if(HAS_TRAIT(I, TRAIT_NODROP) || !user.transferItemToLoc(I, src)) to_chat(user, "[I] is stuck to your hand!") return + var/obj/item/defibrillator/D = I + if(!D.get_cell()) + to_chat(user, "Only defibrilators containing a cell can be hooked up to [src]!") + return user.visible_message("[user] hooks up [I] to [src]!", \ "You press [I] into the mount, and it clicks into place.") playsound(src, 'sound/machines/click.ogg', 50, TRUE) + // Make sure the defib is set before processing begins. defib = I + begin_processing() update_icon() return else if(defib && I == defib.paddles) @@ -148,6 +155,8 @@ user.visible_message("[user] unhooks [defib] from [src].", \ "You slide out [defib] from [src] and unhook the charging cables.") playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + // Make sure processing ends before the defib is nulled + end_processing() defib = null update_icon() @@ -159,12 +168,12 @@ wallframe_type = /obj/item/wallframe/defib_mount/charging /obj/machinery/defibrillator_mount/charging/process() - if(defib?.cell?.charge < defib.cell.maxcharge && is_operational()) + var/obj/item/stock_parts/cell/C = get_cell() + if(C.charge < C.maxcharge && is_operational()) use_power(100) - defib.cell.give(80) + C.give(80) update_icon() - //wallframe, for attaching the mounts easily /obj/item/wallframe/defib_mount name = "unhooked defibrillator mount" diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index a9573e2a1eec..ed9d16a5e6ef 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -168,6 +168,7 @@ to_chat(user, "[src] is now in [mode] mode.") /obj/item/grenade/barrier/prime() + . = ..() new /obj/structure/barricade/security(get_turf(src.loc)) switch(mode) if(VERTICAL) diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm index d82f245714ee..49b6463b1c12 100644 --- a/code/game/machinery/dna_scanner.dm +++ b/code/game/machinery/dna_scanner.dm @@ -15,6 +15,7 @@ var/precision_coeff var/message_cooldown var/breakout_time = 1200 + var/obj/machinery/computer/scan_consolenew/linked_console = null /obj/machinery/dna_scannernew/RefreshParts() scan_level = 0 @@ -97,9 +98,8 @@ // DNA manipulators cannot operate on severed heads or brains if(iscarbon(occupant)) - var/obj/machinery/computer/scan_consolenew/console = locate_computer(/obj/machinery/computer/scan_consolenew) - if(console) - console.on_scanner_close() + if(linked_console) + linked_console.on_scanner_close() return TRUE @@ -109,6 +109,9 @@ ..() + if(linked_console) + linked_console.on_scanner_open() + return TRUE /obj/machinery/dna_scannernew/relaymove(mob/user as mob) @@ -142,14 +145,12 @@ return close_machine(target) -/* WaspStation Begin - Cloning - //Just for transferring between genetics machines. /obj/item/disk/data name = "DNA data disk" icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. - var/list/fields = list() + var/list/genetic_makeup_buffer = list() var/list/mutations = list() var/max_mutations = 6 var/read_only = FALSE //Well,it's still a floppy disk @@ -166,5 +167,3 @@ /obj/item/disk/data/examine(mob/user) . = ..() . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." - -WaspStation End*/ diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 7d1f559f3f09..a5639574107f 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1203,7 +1203,7 @@ var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src)) if(killthis) - killthis.ex_act(EXPLODE_HEAVY)//Smashin windows + SSexplosions.medobj += killthis operating = TRUE update_icon(AIRLOCK_CLOSING, 1) @@ -1435,7 +1435,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "ai_airlock", name, 500, 390, master_ui, state) + ui = new(user, src, ui_key, "AiAirlock", name, 500, 390, master_ui, state) ui.open() return TRUE diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index e1b826ffeee4..ae8a069b332f 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -2,10 +2,14 @@ name = "airlock electronics" req_access = list(ACCESS_MAINT_TUNNELS) custom_price = 50 - + /// A list of all granted accesses var/list/accesses = list() + /// If the airlock should require ALL or only ONE of the listed accesses var/one_access = 0 - var/unres_sides = 0 //unrestricted sides, or sides of the airlock that will open regardless of access + /// Unrestricted sides, or sides of the airlock that will open regardless of access + var/unres_sides = 0 + /// A holder of the electronics, in case of them working as an integrated part + var/holder /obj/item/electronics/airlock/examine(mob/user) . = ..() @@ -15,7 +19,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "airlock_electronics", name, 420, 485, master_ui, state) + ui = new(user, src, ui_key, "AirlockElectronics", name, 420, 485, master_ui, state) ui.open() /obj/item/electronics/airlock/ui_static_data(mob/user) @@ -44,7 +48,6 @@ data["accesses"] = accesses data["oneAccess"] = one_access data["unres_direction"] = unres_sides - return data /obj/item/electronics/airlock/ui_act(action, params) @@ -84,3 +87,8 @@ return accesses -= get_region_accesses(region) . = TRUE + +/obj/item/electronics/airlock/ui_host() + if(holder) + return holder + return src diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index 28d077ea58fa..c87ae870a835 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -310,8 +310,8 @@ /obj/machinery/door/airlock/titanium name = "shuttle airlock" assemblytype = /obj/structure/door_assembly/door_assembly_titanium - icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi' - overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi' + icon = 'waspstation/icons/obj/doors/airlocks/shuttle/shuttle.dmi' + overlays_file = 'waspstation/icons/obj/doors/airlocks/shuttle/overlays.dmi' normal_integrity = 400 has_hatch = FALSE @@ -456,8 +456,8 @@ /obj/machinery/door/airlock/shuttle name = "shuttle airlock" - icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi' - overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi' + icon = 'waspstation/icons/obj/doors/airlocks/shuttle/shuttle.dmi' //Wasp Edit - Classic Shuttle + overlays_file = 'waspstation/icons/obj/doors/airlocks/shuttle/overlays.dmi' assemblytype = /obj/structure/door_assembly/door_assembly_shuttle has_hatch = FALSE diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 4e5fa5f48ae4..315ef79b2894 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -146,7 +146,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "brig_timer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "BrigTimer", name, ui_x, ui_y, master_ui, state) ui.open() //icon update function diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 004af45df288..acb0ba691d5e 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -7,11 +7,12 @@ density = TRUE move_resist = MOVE_FORCE_VERY_STRONG layer = OPEN_DOOR_LAYER - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON max_integrity = 350 armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70) CanAtmosPass = ATMOS_PASS_DENSITY flags_1 = PREVENT_CLICK_UNDER_1 + ricochet_chance_mod = 0.8 damage_deflection = 10 interaction_flags_atom = INTERACT_ATOM_UI_INTERACT @@ -145,6 +146,9 @@ /obj/machinery/door/CanAllowThrough(atom/movable/mover, turf/target) . = ..() + if(.) + return + if(istype(mover) && (mover.pass_flags & PASSGLASS)) return !opacity diff --git a/code/game/machinery/embedded_controller/access_controller.dm b/code/game/machinery/embedded_controller/access_controller.dm index 08844f1adc67..50fc03a44329 100644 --- a/code/game/machinery/embedded_controller/access_controller.dm +++ b/code/game/machinery/embedded_controller/access_controller.dm @@ -5,7 +5,7 @@ #define CYCLE_INTERIOR 5 /obj/machinery/doorButtons - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 4 diff --git a/code/game/machinery/embedded_controller/airlock_controller.dm b/code/game/machinery/embedded_controller/airlock_controller.dm index 5afa98d3a5f9..493929381b45 100644 --- a/code/game/machinery/embedded_controller/airlock_controller.dm +++ b/code/game/machinery/embedded_controller/airlock_controller.dm @@ -201,7 +201,7 @@ density = FALSE frequency = FREQ_AIRLOCK_CONTROL - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON // Setup parameters only var/id_tag diff --git a/code/game/machinery/embedded_controller/simple_vent_controller.dm b/code/game/machinery/embedded_controller/simple_vent_controller.dm index b08a4dab2dd1..6de766090c2a 100644 --- a/code/game/machinery/embedded_controller/simple_vent_controller.dm +++ b/code/game/machinery/embedded_controller/simple_vent_controller.dm @@ -39,7 +39,7 @@ density = FALSE frequency = FREQ_ATMOS_CONTROL - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON // Setup parameters only var/airpump_tag diff --git a/code/game/machinery/fat_sucker.dm b/code/game/machinery/fat_sucker.dm index 182223b968c7..5e22ced2a64d 100644 --- a/code/game/machinery/fat_sucker.dm +++ b/code/game/machinery/fat_sucker.dm @@ -118,12 +118,12 @@ else . += "[icon_state]_door_off" if(occupant) - if(powered(EQUIP)) + if(powered()) . += "[icon_state]_stack" . += "[icon_state]_yellow" else . += "[icon_state]_red" - else if(powered(EQUIP)) + else if(powered()) . += "[icon_state]_red" if(panel_open) . += "[icon_state]_panel" @@ -131,7 +131,7 @@ /obj/machinery/fat_sucker/process() if(!processing) return - if(!powered(EQUIP) || !occupant || !iscarbon(occupant)) + if(!powered() || !occupant || !iscarbon(occupant)) open_machine() return @@ -152,7 +152,7 @@ use_power(500) /obj/machinery/fat_sucker/proc/start_extracting() - if(state_open || !occupant || processing || !powered(EQUIP)) + if(state_open || !occupant || processing || !powered()) return if(iscarbon(occupant)) var/mob/living/carbon/C = occupant diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index f863d73710f2..b8fd11102201 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -23,7 +23,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 6 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON resistance_flags = FIRE_PROOF light_power = 0 diff --git a/code/game/machinery/gulag_item_reclaimer.dm b/code/game/machinery/gulag_item_reclaimer.dm index c872fa661a7a..98f9a304f727 100644 --- a/code/game/machinery/gulag_item_reclaimer.dm +++ b/code/game/machinery/gulag_item_reclaimer.dm @@ -31,7 +31,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "gulag_item_reclaimer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "GulagItemReclaimer", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/gulag_item_reclaimer/ui_data(mob/user) diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm index 5b2543ff7eff..f236fcaee7ac 100644 --- a/code/game/machinery/harvester.dm +++ b/code/game/machinery/harvester.dm @@ -58,7 +58,7 @@ start_harvest() /obj/machinery/harvester/proc/can_harvest() - if(!powered(EQUIP) || state_open || !occupant || !iscarbon(occupant)) + if(!powered() || state_open || !occupant || !iscarbon(occupant)) return var/mob/living/carbon/C = occupant if(!allow_clothing) @@ -95,7 +95,7 @@ /obj/machinery/harvester/proc/harvest() warming_up = FALSE update_icon() - if(!harvesting || state_open || !powered(EQUIP) || !occupant || !iscarbon(occupant)) + if(!harvesting || state_open || !powered() || !occupant || !iscarbon(occupant)) return playsound(src, 'sound/machines/juicer.ogg', 20, TRUE) var/mob/living/carbon/C = occupant diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 8a06b52c5ceb..2e24be983025 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -41,27 +41,45 @@ Possible to do for anyone motivated enough: max_integrity = 300 armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0) circuit = /obj/item/circuitboard/machine/holopad - var/list/masters //List of living mobs that use the holopad - var/list/holorays //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/obj/item/disk/holodisk/disk //Record disk - var/replay_mode = FALSE //currently replaying a recording - var/loop_mode = FALSE //currently looping a recording - var/record_mode = FALSE //currently recording - var/record_start = 0 //recording start time - var/record_user //user that inititiated the recording - var/obj/effect/overlay/holo_pad_hologram/replay_holo //replay hologram - var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging + ui_x = 440 + ui_y = 245 + /// List of living mobs that use the holopad + var/list/masters + /// Holoray-mob link + var/list/holorays + /// To prevent request spam. ~Carn + var/last_request = 0 + /// Change to change how far the AI can move away from the holopad before deactivating + var/holo_range = 5 + /// Array of /datum/holocalls + var/list/holo_calls + /// Currently outgoing holocall, do not modify the datums only check and call the public procs + var/datum/holocall/outgoing_call + /// Record disk + var/obj/item/disk/holodisk/disk + /// Currently replaying a recording + var/replay_mode = FALSE + /// Currently looping a recording + var/loop_mode = FALSE + /// Currently recording + var/record_mode = FALSE + /// Recording start time + var/record_start = 0 + /// User that inititiated the recording + var/record_user + /// Replay hologram + var/obj/effect/overlay/holo_pad_hologram/replay_holo + /// Calls will be automatically answered after a couple rings, here for debugging + var/static/force_answer_call = FALSE var/static/list/holopads = list() var/obj/effect/overlay/holoray/ray var/ringing = FALSE var/offset = FALSE var/on_network = TRUE - var/secure = FALSE //for pads in secure areas; do not allow forced connecting + /// For pads in secure areas; do not allow forced connecting + var/secure = FALSE + /// If we are currently calling another holopad + var/calling = FALSE /obj/machinery/holopad/secure name = "secure holopad" @@ -179,171 +197,145 @@ obj/machinery/holopad/secure/Initialize() return to_chat(user,"You insert [P] into [src].") disk = P - updateDialog() return return ..() +/obj/machinery/holopad/ui_status(mob/user) + if(!is_operational()) + return UI_CLOSE + if(outgoing_call && !calling) + return UI_CLOSE + return ..() -/obj/machinery/holopad/ui_interact(mob/living/carbon/human/user) //Carn: Hologram requests. +/obj/machinery/holopad/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "Holopad", name, ui_x, ui_y, master_ui, state) + ui.open() + +/obj/machinery/holopad/ui_data(mob/user) + var/list/data = list() + data["calling"] = calling + data["on_network"] = on_network + data["on_cooldown"] = last_request + 200 < world.time ? FALSE : TRUE + data["allowed"] = allowed(user) + data["disk"] = disk ? TRUE : FALSE + data["disk_record"] = disk?.record ? TRUE : FALSE + data["replay_mode"] = replay_mode + data["loop_mode"] = loop_mode + data["record_mode"] = record_mode + data["holo_calls"] = list() + for(var/I in holo_calls) + var/datum/holocall/HC = I + var/list/call_data = list( + caller = HC.user, + connected = HC.connected_holopad == src ? TRUE : FALSE, + ref = REF(HC) + ) + data["holo_calls"] += list(call_data) + return data + +/obj/machinery/holopad/ui_act(action, list/params) . = ..() - if(!istype(user)) + if(.) return - if(outgoing_call || user.incapacitated() || !is_operational()) - return - - user.set_machine(src) - var/dat - if(temp) - dat = temp - else - if(on_network) - dat += "Request an AI's presence
" - if(allowed(user)) - dat += "Connect to another holopad
" + switch(action) + if("AIrequest") + if(last_request + 200 < world.time) + last_request = world.time + to_chat(usr, "You requested an AI's presence.") + var/area/area = get_area(src) + for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs) + if(!AI.client) + continue + to_chat(AI, "Your presence is requested at \the [area].") + return TRUE else - dat += "Call another holopad
" - if(disk) - if(disk.record) - //Replay - dat += "Replay disk recording
" - dat += "Loop disk recording
" - //Clear - dat += "Clear disk recording
" + to_chat(usr, "A request for AI presence was already sent recently.") + return + if("holocall") + if(outgoing_call) + return + 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/result = input(usr, "Choose an area to call", "Holocall") as null|anything in sortNames(callnames) + if(QDELETED(usr) || !result || outgoing_call) + return + if(usr.loc == loc) + var/input = text2num(params["headcall"]) + var/headcall = input == 1 ? TRUE : FALSE + new /datum/holocall(usr, src, callnames[result], headcall) + calling = TRUE + return TRUE + else + to_chat(usr, "You must stand on the holopad to make a call!") + if("connectcall") + var/datum/holocall/call_to_connect = locate(params["holopad"]) in holo_calls + if(!QDELETED(call_to_connect)) + call_to_connect.Answer(src) + return TRUE + if("disconnectcall") + var/datum/holocall/call_to_disconnect = locate(params["holopad"]) in holo_calls + if(!QDELETED(call_to_disconnect)) + call_to_disconnect.Disconnect(src) + return TRUE + if("disk_eject") + if(disk && !replay_mode) + disk.forceMove(drop_location()) + disk = null + return TRUE + if("replay_mode") + if(replay_mode) + replay_stop() + return TRUE + else + replay_start() + return TRUE + if("loop_mode") + loop_mode = !loop_mode + return TRUE + if("record_mode") + if(record_mode) + record_stop() + return TRUE + else + record_start(usr) + return TRUE + if("record_clear") + record_clear() + return TRUE + if("offset") + offset++ + if(offset > 4) + offset = FALSE + var/turf/new_turf + if(!offset) + new_turf = get_turf(src) else - //Record - dat += "Start new recording
" - //Eject - dat += "Eject disk
" - - if(LAZYLEN(holo_calls)) - dat += "=====================================================
" - - if(on_network) - 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/datum/browser/popup = new(user, "holopad", name, 300, 175) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - -//Stop ringing the AI!! + new_turf = get_step(src, GLOB.cardinals[offset]) + replay_holo.forceMove(new_turf) + return TRUE + if("hang_up") + if(outgoing_call) + outgoing_call.Disconnect(src) + return TRUE + +/** + * hangup_all_calls: Disconnects all current holocalls from the holopad + */ /obj/machinery/holopad/proc/hangup_all_calls() for(var/I in holo_calls) var/datum/holocall/HC = I HC.Disconnect(src) -/obj/machinery/holopad/Topic(href, href_list) - if(..() || isAI(usr)) - return - add_fingerprint(usr) - if(!is_operational()) - 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.silicon_mobs) - 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"] || href_list["Holoconnect"]) - if(outgoing_call) - return - var/head_call = FALSE - if(href_list["Holoconnect"]) - head_call = TRUE - - 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/result = input(usr, "Choose an area to call", "Holocall") as null|anything in sortNames(callnames) - if(QDELETED(usr) || !result || outgoing_call) - return - - if(usr.loc == loc) - temp = "Dialing...
" - temp += "Main Menu" - new /datum/holocall(usr, src, callnames[result], head_call) - - else if(href_list["connectcall"]) - var/datum/holocall/call_to_connect = locate(href_list["connectcall"]) in holo_calls - if(!QDELETED(call_to_connect)) - call_to_connect.Answer(src) - temp = "" - - else if(href_list["disconnectcall"]) - var/datum/holocall/call_to_disconnect = locate(href_list["disconnectcall"]) in holo_calls - if(!QDELETED(call_to_disconnect)) - call_to_disconnect.Disconnect(src) - temp = "" - - else if(href_list["mainmenu"]) - temp = "" - if(outgoing_call) - outgoing_call.Disconnect() - - else if(href_list["disk_eject"]) - if(disk && !replay_mode) - disk.forceMove(drop_location()) - disk = null - - else if(href_list["replay_stop"]) - replay_stop() - else if(href_list["replay_start"]) - replay_start() - else if(href_list["loop_start"]) - loop_mode = TRUE - replay_start() - else if(href_list["record_start"]) - record_start(usr) - else if(href_list["record_stop"]) - record_stop() - else if(href_list["record_clear"]) - record_clear() - else if(href_list["offset"]) - offset++ - if (offset > 4) - offset = FALSE - var/turf/new_turf - if (!offset) - new_turf = get_turf(src) - else - new_turf = get_step(src, GLOB.cardinals[offset]) - replay_holo.forceMove(new_turf) - updateDialog() - //do not allow AIs to answer calls or people will use it to meta the AI sattelite /obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user) if (!istype(user)) @@ -585,22 +577,15 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ if(!replay_mode) replay_mode = TRUE replay_holo = setup_replay_holo(disk.record) - temp = "Replaying...
" - temp += "Change offset
" - temp += "End replay" SetLightsAndPower() replay_entry(1) - return /obj/machinery/holopad/proc/replay_stop() if(replay_mode) replay_mode = FALSE - loop_mode = FALSE offset = FALSE - temp = null QDEL_NULL(replay_holo) SetLightsAndPower() - updateDialog() /obj/machinery/holopad/proc/record_start(mob/living/user) if(!user || !disk || disk.record) @@ -610,8 +595,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ record_start = world.time record_user = user disk.record.set_caller_image(user) - temp = "Recording...
" - temp += "End recording." /obj/machinery/holopad/proc/record_message(mob/living/speaker,message,language) if(!record_mode) @@ -677,14 +660,11 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ /obj/machinery/holopad/proc/record_stop() if(record_mode) record_mode = FALSE - temp = null record_user = null - updateDialog() /obj/machinery/holopad/proc/record_clear() if(disk && disk.record) QDEL_NULL(disk.record) - updateDialog() /obj/effect/overlay/holo_pad_hologram initial_language_holder = /datum/language_holder/universal diff --git a/code/game/machinery/hypnochair.dm b/code/game/machinery/hypnochair.dm index ff8f1698d659..670d8f758c37 100644 --- a/code/game/machinery/hypnochair.dm +++ b/code/game/machinery/hypnochair.dm @@ -37,7 +37,7 @@ /obj/machinery/hypnochair/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "hypnochair", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "HypnoChair", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/hypnochair/ui_data() diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm index 265832a6a74b..23e1b9dcc1c8 100644 --- a/code/game/machinery/launch_pad.dm +++ b/code/game/machinery/launch_pad.dm @@ -324,8 +324,7 @@ /obj/item/launchpad_remote/ui_interact(mob/user, ui_key = "launchpad_remote", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "launchpad_remote", "Briefcase Launchpad Remote", 300, 240, master_ui, state) //width, height - ui.set_style("syndicate") + ui = new(user, src, ui_key, "LaunchpadRemote", "Briefcase Launchpad Remote", 300, 240, master_ui, state) //width, height ui.open() ui.set_autoupdate(TRUE) diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index a2d565d59741..5841080ad235 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/power.dmi' icon_state = "light1" desc = "Make dark." - power_channel = LIGHT + power_channel = AREA_USAGE_LIGHT /// Set this to a string, path, or area instance to control that area /// instead of the switch's location. var/area/area = null diff --git a/code/game/machinery/medical_kiosk.dm b/code/game/machinery/medical_kiosk.dm index b71b7e59572c..f02e21d73ec1 100644 --- a/code/game/machinery/medical_kiosk.dm +++ b/code/game/machinery/medical_kiosk.dm @@ -172,7 +172,7 @@ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "medical_kiosk", name, 625, 550, master_ui, state) + ui = new(user, src, ui_key, "MedicalKiosk", name, 575, 420, master_ui, state) ui.open() icon_state = "kiosk_off" RefreshParts() @@ -215,8 +215,11 @@ blood_warning = " Patient has DANGEROUSLY low blood levels. Seek a blood transfusion, iron supplements, or saline glucose immedietly. Ignoring treatment may lead to death!" blood_status = "Patient blood levels are currently reading [blood_percent]%. Patient has [ blood_type] type blood. [blood_warning]" - var/rad_value = altPatient.radiation - var/rad_status = "Target within normal-low radiation levels." + var/rad_sickness_value = altPatient.radiation + var/rad_sickness_status = "Target within normal-low radiation levels." + var/rad_contamination_value = get_rad_contamination(altPatient) + var/rad_contamination_status = "Target clothes and person not radioactive" + var/trauma_status = "Patient is free of unique brain trauma." var/clone_loss = altPatient.getCloneLoss() var/brain_loss = altPatient.getOrganLoss(ORGAN_SLOT_BRAIN) @@ -236,29 +239,18 @@ trauma_text += trauma_desc trauma_status = "Cerebral traumas detected: patient appears to be suffering from [english_list(trauma_text)]." - var/chem_status = FALSE - var/chemical_list= list() - var/overdose_status = FALSE + var/chemical_list = list() var/overdose_list = list() - var/addict_status = FALSE var/addict_list = list() var/hallucination_status = "Patient is not hallucinating." - for(var/datum/reagent/R in altPatient.reagents.reagent_list) - if(R.overdosed) - overdose_status = TRUE - if(altPatient.reagents.reagent_list.len) //Chemical Analysis details. - chem_status = TRUE for(var/datum/reagent/R in altPatient.reagents.reagent_list) chemical_list += list(list("name" = R.name, "volume" = round(R.volume, 0.01))) if(R.overdosed == 1) overdose_list += list(list("name" = R.name)) - else - chemical_list = "Patient contains no reagents" if(altPatient.reagents.addiction_list.len) - addict_status = TRUE for(var/datum/reagent/R in altPatient.reagents.addiction_list) addict_list += list(list("name" = R.name)) if (altPatient.hallucinating()) @@ -284,19 +276,26 @@ else if((brain_loss) >= 1) brain_status = "Mild brain damage detected." //You may have a miiiild case of severe brain damage. - if(altPatient.radiation >=1000) // - rad_status = "Patient is suffering from extreme radiation poisoning. Suggested treatment: Isolation of patient, followed by repeated dosages of Pentetic Acid." - else if(altPatient.radiation >= 500) - rad_status = "Patient is suffering from alarming radiation poisoning. Suggested treatment: Heavy use of showers and decontamination of clothing. Take Pentetic Acid or Potassium Iodine." - else if(altPatient.radiation >= 100) - rad_status = "Patient has moderate radioactive signatures. Keep under showers until symptoms subside." + if(rad_sickness_value >= 1000) // + rad_sickness_status = "Patient is suffering from extreme radiation poisoning, high toxen damage expected. Suggested treatment: Repeated dosages of Pentetic Acid or high amounts of Cold Seiver and anti-toxen" + else if(rad_sickness_value >= 300) + rad_sickness_status = "Patient is suffering from alarming radiation poisoning. Suggested treatment: Take Cold Seiver or Potassium Iodine, watch the toxen levels." + else if(rad_sickness_value >= 100) + rad_sickness_status = "Patient has moderate radioactive signatures. Symptoms will subside in a few minutes" + + if(rad_contamination_value >= 400) // + rad_contamination_status = "Patient is wearing extremely radioactive clothing. Suggested treatment: Isolation of patient and shower, remove all clothing and objects immediatly and place in a washing machine" + else if(rad_contamination_value >= 150) + rad_contamination_status = "Patient is wearing alarming radioactive clothing. Suggested treatment: Scan for contaminated objects and wash them with soap and water" + else if(rad_contamination_value >= 50) + rad_contamination_status = "Patient has moderate radioactive clothing. Maintain a social distance for a few minutes" + if(pandemonium == TRUE) chaos_modifier = 1 else if (user.hallucinating()) chaos_modifier = 0.3 - data["kiosk_cost"] = active_price + (chaos_modifier * (rand(1,25))) data["patient_name"] = patient_name data["patient_health"] = round(((total_health - (chaos_modifier * (rand(1,50)))) / max_health) * 100, 0.001) @@ -308,26 +307,25 @@ data["brain_health"] = brain_status data["brain_damage"] = brain_loss+(chaos_modifier * (rand(1,30))) data["patient_status"] = patient_status - data["rad_value"] = rad_value+(chaos_modifier * (rand(1,500))) - data["rad_status"] = rad_status + data["rad_sickness_value"] = rad_sickness_value+(chaos_modifier * (rand(1,500))) + data["rad_sickness_status"] = rad_sickness_status + data["rad_contamination_value"] = rad_contamination_value+(chaos_modifier * (rand(1,500))) + data["rad_contamination_status"] = rad_contamination_status data["trauma_status"] = trauma_status data["patient_illness"] = sickness data["illness_info"] = sickness_data data["bleed_status"] = bleed_status data["blood_levels"] = blood_percent - (chaos_modifier * (rand(1,35))) data["blood_status"] = blood_status - data["are_chems_present"] = chem_status ? TRUE : FALSE data["chemical_list"] = chemical_list - data["are_overdoses_present"] = overdose_status ? TRUE : FALSE - data["overdose_status"] = overdose_list - data["are_addictions_present"] = addict_status ? TRUE : FALSE - data["addiction_status"] = addict_list + data["overdose_list"] = overdose_list + data["addict_list"] = addict_list data["hallucinating_status"] = hallucination_status - data["active_status_1"] = scan_active_1 ? FALSE : TRUE //General Scan Check - data["active_status_2"] = scan_active_2 ? FALSE : TRUE //Symptom Scan Check - data["active_status_3"] = scan_active_3 ? FALSE : TRUE //Radio-Neuro Scan Check - data["active_status_4"] = scan_active_4 ? FALSE : TRUE //Radio-Neuro Scan Check + data["active_status_1"] = scan_active_1 // General Scan Check + data["active_status_2"] = scan_active_2 // Symptom Scan Check + data["active_status_3"] = scan_active_3 // Radio-Neuro Scan Check + data["active_status_4"] = scan_active_4 // Radio-Neuro Scan Check return data /obj/machinery/medical_kiosk/ui_act(action,active) diff --git a/code/game/machinery/medipen_refiller.dm b/code/game/machinery/medipen_refiller.dm index 3dc2090260ed..4b11076e04a0 100644 --- a/code/game/machinery/medipen_refiller.dm +++ b/code/game/machinery/medipen_refiller.dm @@ -69,7 +69,7 @@ to_chat(user, "You start furiously plunging [name].") if(do_after(user, 30, target = src)) to_chat(user, "You finish plunging the [name].") - reagents.reaction(get_turf(src), TOUCH) + reagents.expose(get_turf(src), TOUCH) reagents.clear_reagents() /obj/machinery/medipen_refiller/wrench_act(mob/living/user, obj/item/I) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 469934310364..b61a5deb6bcc 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -862,6 +862,7 @@ GLOBAL_LIST_EMPTY(allCasters) righthand_file = 'icons/mob/inhands/misc/books_righthand.dmi' w_class = WEIGHT_CLASS_SMALL attack_verb = list("bapped") + resistance_flags = FLAMMABLE var/screen = 0 var/pages = 0 var/curr_page = 0 @@ -1008,7 +1009,10 @@ GLOBAL_LIST_EMPTY(allCasters) if(ismob(loc)) attack_self(loc) -/obj/item/newspaper/attackby(obj/item/W, mob/user, params) +/obj/item/newspaper/attackby(obj/item/W, mob/living/user, params) + if(burn_paper_product_attackby_check(W, user)) + return + if(istype(W, /obj/item/pen)) if(!user.is_literate()) to_chat(user, "You scribble illegibly on [src]!") @@ -1024,5 +1028,6 @@ GLOBAL_LIST_EMPTY(allCasters) scribble_page = curr_page scribble = s attack_self(user) + add_fingerprint(user) else return ..() diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 3e767d9682c8..66e3e19bd4de 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -25,7 +25,7 @@ idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power req_access = list(ACCESS_SEC_DOORS) - power_channel = EQUIP //drains power from the EQUIPMENT channel + power_channel = AREA_USAGE_EQUIP //drains power from the EQUIPMENT channel var/base_icon_state = "standard" var/scan_range = 7 diff --git a/code/game/machinery/prisonlabor.dm b/code/game/machinery/prisonlabor.dm index b76a62a6beaa..d425efeeee45 100644 --- a/code/game/machinery/prisonlabor.dm +++ b/code/game/machinery/prisonlabor.dm @@ -1,6 +1,6 @@ /obj/machinery/plate_press name = "license plate press" - desc = "You know, we're making a lot of license plates for a station with literaly no cars in it." + desc = "You know, we're making a lot of license plates for a station with literally no cars in it." icon = 'icons/obj/machines/prison.dmi' icon_state = "offline" use_power = IDLE_POWER_USE diff --git a/code/game/machinery/roulette_machine.dm b/code/game/machinery/roulette_machine.dm index 6407b0e8b628..76e502db17c3 100644 --- a/code/game/machinery/roulette_machine.dm +++ b/code/game/machinery/roulette_machine.dm @@ -65,7 +65,7 @@ return ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "roulette", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Roulette", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/roulette/ui_data(mob/user) diff --git a/code/game/machinery/scan_gate.dm b/code/game/machinery/scan_gate.dm index ab50f71a430d..e9dc66db6b76 100644 --- a/code/game/machinery/scan_gate.dm +++ b/code/game/machinery/scan_gate.dm @@ -187,7 +187,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "scanner_gate", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "ScannerGate", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/scanner_gate/ui_data() diff --git a/code/game/machinery/sheetifier.dm b/code/game/machinery/sheetifier.dm index 9c728d89adf0..4728050b6bb4 100644 --- a/code/game/machinery/sheetifier.dm +++ b/code/game/machinery/sheetifier.dm @@ -42,3 +42,13 @@ update_icon() var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) materials.retrieve_all() //Returns all as sheets + +/obj/machinery/sheetifier/attackby(obj/item/I, mob/user, params) + if(default_unfasten_wrench(user, I)) + return + if(default_deconstruction_screwdriver(user, I)) + update_icon() + return + if(default_deconstruction_crowbar(I)) + return + return ..() diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index 888e627482e9..b9632c9a6a66 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -174,7 +174,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "space_heater", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "SpaceHeater", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/space_heater/ui_data() diff --git a/code/game/machinery/stasis.dm b/code/game/machinery/stasis.dm index da8111f57147..db4459fe350f 100644 --- a/code/game/machinery/stasis.dm +++ b/code/game/machinery/stasis.dm @@ -21,8 +21,8 @@ /obj/machinery/stasis/Initialize() . = ..() - for(var/direction in GLOB.cardinals) - op_computer = locate(/obj/machinery/computer/operating, get_step(src, direction)) + for(var/direction in GLOB.alldirs) + op_computer = locate(/obj/machinery/computer/operating) in get_step(src, direction) if(op_computer) op_computer.sbed = src break @@ -126,12 +126,6 @@ chill_out(L) update_icon() -/obj/machinery/stasis/proc/check_patient() - if(occupant) - return TRUE - else - return FALSE - /obj/machinery/stasis/post_unbuckle_mob(mob/living/L) thaw_them(L) if(L == occupant) diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 4205c762c332..a2eef179a773 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -90,7 +90,7 @@ /obj/machinery/suit_storage_unit/cmo suit_type = /obj/item/clothing/suit/space/hardsuit/medical/cmo - mask_type = /obj/item/clothing/mask/breath + mask_type = /obj/item/clothing/mask/breath/medical storage_type = /obj/item/tank/internals/oxygen /obj/machinery/suit_storage_unit/rd @@ -421,7 +421,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "suit_storage_unit", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "SuitStorageUnit", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/suit_storage_unit/ui_data() diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm index 545e2d9ab633..c7b921b6b763 100644 --- a/code/game/machinery/syndicatebomb.dm +++ b/code/game/machinery/syndicatebomb.dm @@ -11,7 +11,8 @@ density = FALSE layer = BELOW_MOB_LAYER //so people can't hide it and it's REALLY OBVIOUS resistance_flags = FIRE_PROOF | ACID_PROOF - speed_process = TRUE + processing_flags = START_PROCESSING_MANUALLY + subsystem_type = /datum/controller/subsystem/processing/fastprocess interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_OFFLINE @@ -48,7 +49,7 @@ /obj/machinery/syndicatebomb/process() if(!active) - STOP_PROCESSING(SSfastprocess, src) + end_processing() detonation_timer = null next_beep = null countdown.stop() @@ -87,12 +88,12 @@ payload = new payload(src) update_icon() countdown = new(src) - STOP_PROCESSING(SSfastprocess, src) + end_processing() /obj/machinery/syndicatebomb/Destroy() QDEL_NULL(wires) QDEL_NULL(countdown) - STOP_PROCESSING(SSfastprocess, src) + end_processing() return ..() /obj/machinery/syndicatebomb/examine(mob/user) @@ -183,7 +184,7 @@ /obj/machinery/syndicatebomb/proc/activate() active = TRUE - START_PROCESSING(SSfastprocess, src) + begin_processing() countdown.start() next_beep = world.time + 10 detonation_timer = world.time + (timer_set * 10) diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm index ff9385d72ece..bf867c6e6d81 100644 --- a/code/game/machinery/telecomms/computers/message.dm +++ b/code/game/machinery/telecomms/computers/message.dm @@ -348,9 +348,8 @@ hacking = TRUE screen = MSG_MON_SCREEN_HACKED //Time it takes to bruteforce is dependant on the password length. - spawn(100*length(linkedServer.decryptkey)) - if(src && linkedServer && usr) - BruteForce(usr) + addtimer(CALLBACK(src, .proc/finish_bruteforce, usr), 100*length(linkedServer.decryptkey)) + //Delete the log. if (href_list["delete_logs"]) //Are they on the view logs screen? @@ -444,6 +443,13 @@ return attack_hand(usr) +/obj/machinery/computer/message_monitor/proc/finish_bruteforce(mob/user) + if(!QDELETED(user)) + BruteForce(user) + return + hacking = FALSE + screen = MSG_MON_SCREEN_MAIN + #undef MSG_MON_SCREEN_MAIN #undef MSG_MON_SCREEN_LOGS #undef MSG_MON_SCREEN_HACKED @@ -465,7 +471,6 @@ /obj/item/paper/monitorkey/proc/print(obj/machinery/telecomms/message_server/server) info = "

Daily Key Reset


The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one." - info_links = info add_overlay("paper_words") /obj/item/paper/monitorkey/LateInitialize() diff --git a/code/game/machinery/telecomms/machines/message_server.dm b/code/game/machinery/telecomms/machines/message_server.dm index e903356cd025..7137971c3740 100644 --- a/code/game/machinery/telecomms/machines/message_server.dm +++ b/code/game/machinery/telecomms/machines/message_server.dm @@ -60,7 +60,7 @@ icon_state = "blackbox" /obj/item/blackbox - name = "the blackbox" + name = "\proper the blackbox" desc = "A strange relic, capable of recording data on extradimensional vertices. It lives inside the blackbox recorder for safe keeping." icon = 'icons/obj/stationobjs.dmi' icon_state = "blackcube" diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index 01d4be65e783..655214a0164f 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -12,6 +12,7 @@ GLOBAL_LIST_INIT(dye_registry, list( DYE_RAINBOW = /obj/item/clothing/under/color/rainbow, DYE_MIME = /obj/item/clothing/under/rank/civilian/mime, DYE_CLOWN = /obj/item/clothing/under/rank/civilian/clown, + DYE_CHAP = /obj/item/clothing/under/rank/civilian/chaplain, DYE_QM = /obj/item/clothing/under/rank/cargo/qm, DYE_LAW = /obj/item/clothing/under/suit/black, DYE_CAPTAIN = /obj/item/clothing/under/rank/command/captain, @@ -20,7 +21,9 @@ GLOBAL_LIST_INIT(dye_registry, list( DYE_CE = /obj/item/clothing/under/rank/engineering/chief_engineer, DYE_RD = /obj/item/clothing/under/rank/rnd/research_director, DYE_CMO = /obj/item/clothing/under/rank/medical/chief_medical_officer, - DYE_REDCOAT = /obj/item/clothing/under/costume/redcoat + DYE_REDCOAT = /obj/item/clothing/under/costume/redcoat, + DYE_SYNDICATE = /obj/item/clothing/under/syndicate, + DYE_CENTCOM = /obj/item/clothing/under/rank/centcom/commander ), DYE_REGISTRY_JUMPSKIRT = list( DYE_RED = /obj/item/clothing/under/color/jumpskirt/red, @@ -31,7 +34,16 @@ GLOBAL_LIST_INIT(dye_registry, list( DYE_PURPLE = /obj/item/clothing/under/color/jumpskirt/lightpurple, DYE_BLACK = /obj/item/clothing/under/color/jumpskirt/black, DYE_WHITE = /obj/item/clothing/under/color/jumpskirt/white, - DYE_RAINBOW = /obj/item/clothing/under/color/jumpskirt/rainbow + DYE_RAINBOW = /obj/item/clothing/under/color/jumpskirt/rainbow, + DYE_MIME = /obj/item/clothing/under/rank/civilian/mime/skirt, + DYE_CHAP = /obj/item/clothing/under/rank/civilian/chaplain/skirt, + DYE_QM = /obj/item/clothing/under/rank/cargo/qm/skirt, + DYE_CAPTAIN = /obj/item/clothing/under/rank/command/captain/skirt, + DYE_HOP = /obj/item/clothing/under/rank/command/head_of_personnel/skirt, + DYE_HOS = /obj/item/clothing/under/rank/security/head_of_security/skirt, + DYE_CE = /obj/item/clothing/under/rank/engineering/chief_engineer/skirt, + DYE_RD = /obj/item/clothing/under/rank/rnd/research_director/skirt, + DYE_CMO = /obj/item/clothing/under/rank/medical/chief_medical_officer/skirt, ), DYE_REGISTRY_GLOVES = list( DYE_RED = /obj/item/clothing/gloves/color/red, @@ -52,7 +64,9 @@ GLOBAL_LIST_INIT(dye_registry, list( DYE_CE = /obj/item/clothing/gloves/color/black, DYE_RD = /obj/item/clothing/gloves/color/grey, DYE_CMO = /obj/item/clothing/gloves/color/latex/nitrile, - DYE_REDCOAT = /obj/item/clothing/gloves/color/white + DYE_REDCOAT = /obj/item/clothing/gloves/color/white, + DYE_SYNDICATE = /obj/item/clothing/gloves/combat, + DYE_CENTCOM = /obj/item/clothing/gloves/combat ), DYE_REGISTRY_SNEAKERS = list( DYE_RED = /obj/item/clothing/shoes/sneakers/red, @@ -65,12 +79,15 @@ GLOBAL_LIST_INIT(dye_registry, list( DYE_WHITE = /obj/item/clothing/shoes/sneakers/white, DYE_RAINBOW = /obj/item/clothing/shoes/sneakers/rainbow, DYE_MIME = /obj/item/clothing/shoes/sneakers/black, + DYE_CLOWN = /obj/item/clothing/shoes/sneakers/rainbow, DYE_QM = /obj/item/clothing/shoes/sneakers/brown, DYE_CAPTAIN = /obj/item/clothing/shoes/sneakers/brown, DYE_FO = /obj/item/clothing/shoes/sneakers/brown, DYE_CE = /obj/item/clothing/shoes/sneakers/brown, DYE_RD = /obj/item/clothing/shoes/sneakers/brown, - DYE_CMO = /obj/item/clothing/shoes/sneakers/brown + DYE_CMO = /obj/item/clothing/shoes/sneakers/brown, + DYE_SYNDICATE = /obj/item/clothing/shoes/combat, + DYE_CENTCOM = /obj/item/clothing/shoes/combat ), DYE_REGISTRY_FANNYPACK = list( DYE_RED = /obj/item/storage/belt/fannypack/red, @@ -80,7 +97,8 @@ GLOBAL_LIST_INIT(dye_registry, list( DYE_BLUE = /obj/item/storage/belt/fannypack/blue, DYE_PURPLE = /obj/item/storage/belt/fannypack/purple, DYE_BLACK = /obj/item/storage/belt/fannypack/black, - DYE_WHITE = /obj/item/storage/belt/fannypack/white + DYE_WHITE = /obj/item/storage/belt/fannypack/white, + DYE_SYNDICATE = /obj/item/storage/belt/military ), DYE_REGISTRY_BEDSHEET = list( DYE_RED = /obj/item/bedsheet/red, @@ -94,6 +112,7 @@ GLOBAL_LIST_INIT(dye_registry, list( DYE_RAINBOW = /obj/item/bedsheet/rainbow, DYE_MIME = /obj/item/bedsheet/mime, DYE_CLOWN = /obj/item/bedsheet/clown, + DYE_CHAP = /obj/item/bedsheet/chaplain, DYE_QM = /obj/item/bedsheet/qm, DYE_LAW = /obj/item/bedsheet/black, DYE_CAPTAIN = /obj/item/bedsheet/captain, @@ -102,7 +121,9 @@ GLOBAL_LIST_INIT(dye_registry, list( DYE_CE = /obj/item/bedsheet/ce, DYE_RD = /obj/item/bedsheet/rd, DYE_CMO = /obj/item/bedsheet/cmo, - DYE_COSMIC = /obj/item/bedsheet/cosmos + DYE_COSMIC = /obj/item/bedsheet/cosmos, + DYE_SYNDICATE = /obj/item/bedsheet/syndie, + DYE_CENTCOM = /obj/item/bedsheet/centcom ), DYE_LAWYER_SPECIAL = list( DYE_COSMIC = /obj/item/clothing/under/rank/civilian/lawyer/galaxy, diff --git a/code/game/mecha/combat/combat.dm b/code/game/mecha/combat/combat.dm index d977bb4f56d6..0834f28d0088 100644 --- a/code/game/mecha/combat/combat.dm +++ b/code/game/mecha/combat/combat.dm @@ -3,12 +3,12 @@ internals_req_access = list(ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY) internal_damage_threshold = 50 armor = list("melee" = 30, "bullet" = 30, "laser" = 15, "energy" = 20, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - mouse_pointer = 'icons/mecha/mecha_mouse.dmi' + mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse.dmi' destruction_sleep_duration = 40 exit_delay = 40 /obj/mecha/combat/restore_equipment() - mouse_pointer = 'icons/mecha/mecha_mouse.dmi' + mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse.dmi' . = ..() /obj/mecha/combat/proc/max_ammo() //Max the ammo stored for Nuke Ops mechs, or anyone else that calls this diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm index 7e2d8e8a2ddd..4486eb32549d 100644 --- a/code/game/mecha/equipment/tools/medical_tools.dm +++ b/code/game/mecha/equipment/tools/medical_tools.dm @@ -330,7 +330,7 @@ R += "[A.name] ([num2text(A.volume)]" mechsyringe.icon_state = initial(mechsyringe.icon_state) mechsyringe.icon = initial(mechsyringe.icon) - mechsyringe.reagents.reaction(M, INJECT) + mechsyringe.reagents.expose(M, INJECT) mechsyringe.reagents.trans_to(M, mechsyringe.reagents.total_volume, transfered_by = originaloccupant) M.take_bodypart_damage(2) log_combat(originaloccupant, M, "shot", "syringegun") diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm index 2776c1ed465f..e979996cc31f 100644 --- a/code/game/mecha/equipment/tools/other_tools.dm +++ b/code/game/mecha/equipment/tools/other_tools.dm @@ -11,12 +11,13 @@ equip_cooldown = 150 energy_drain = 1000 range = MECHA_RANGED + var/teleport_range = 7 /obj/item/mecha_parts/mecha_equipment/teleporter/action(atom/target) if(!action_checks(target) || is_centcom_level(loc.z)) return var/turf/T = get_turf(target) - if(T) + if(T && (loc.z == T.z) && (get_dist(loc, T) <= teleport_range)) do_teleport(chassis, T, 4, channel = TELEPORT_CHANNEL_BLUESPACE) return 1 @@ -279,7 +280,7 @@ energy_drain = 0 range = 0 var/coeff = 100 - var/list/use_channels = list(EQUIP,ENVIRON,LIGHT) + var/list/use_channels = list(AREA_USAGE_EQUIP,AREA_USAGE_ENVIRON,AREA_USAGE_LIGHT) selectable = 0 /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/Destroy() @@ -342,7 +343,7 @@ var/area/A = get_area(chassis) if(A) var/pow_chan - for(var/c in list(EQUIP,ENVIRON,LIGHT)) + for(var/c in use_channels) if(A.powered(c)) pow_chan = c break diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm index 9173d8b8fce3..86fe58c07b8c 100644 --- a/code/game/mecha/equipment/tools/work_tools.dm +++ b/code/game/mecha/equipment/tools/work_tools.dm @@ -230,9 +230,9 @@ if(!W) return var/turf/W_turf = get_turf(W) - W.reagents.reaction(W_turf) + W.reagents.expose(W_turf) for(var/atom/atm in W_turf) - W.reagents.reaction(atm) + W.reagents.expose(atm) if(W.loc == my_target) break sleep(2) diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm index b9d3912852c6..36f17f281c10 100644 --- a/code/game/mecha/mech_bay.dm +++ b/code/game/mecha/mech_bay.dm @@ -94,7 +94,7 @@ /obj/machinery/computer/mech_bay_power_console/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "mech_bay_power_console", "Mech Bay Power Control Console", ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "MechBayPowerConsole", "Mech Bay Power Control Console", ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/mech_bay_power_console/ui_act(action, params) diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index b7b5516c2fd4..b08cfb2aecb5 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -16,6 +16,7 @@ var/part_set var/datum/design/being_built var/list/queue = list() + var/list/datum/design/matching_designs var/processing_queue = 0 var/screen = "main" var/link_on_init = TRUE @@ -39,6 +40,7 @@ /obj/machinery/mecha_part_fabricator/Initialize(mapload) stored_research = new + matching_designs = list() rmat = AddComponent(/datum/component/remote_materials, "mechfab", mapload && link_on_init) RefreshParts() //Recalculating local material sizes if the fab isn't linked return ..() @@ -88,10 +90,10 @@ if(D.build_type & MECHFAB) if(!(set_name in D.category)) continue - output += "
[output_part_info(D)]
\[" + output += "
[output_part_info(D)]
" if(check_resources(D)) output += "Build | " - output += "Add to queue\]\[?\]
" + output += "Add to queue?
" return output /obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D) @@ -107,24 +109,23 @@ i++ return output -/obj/machinery/mecha_part_fabricator/proc/output_available_resources() +/obj/machinery/mecha_part_fabricator/proc/output_ui_header() var/output - var/datum/component/material_container/materials = rmat.mat_container - - if(materials) - for(var/mat_id in materials.materials) - var/datum/material/M = mat_id - var/amount = materials.materials[mat_id] - var/ref = REF(M) - output += "[M.name]: [amount] cm³" - if(amount >= MINERAL_MATERIAL_AMOUNT) - output += "- Remove \[1\]" - if(amount >= (MINERAL_MATERIAL_AMOUNT * 10)) - output += " | \[10\]" - output += " | \[50\]" - output += "
" + output += "
Mecha Fabricator
" + output += "Security protocols: [(obj_flags & EMAGGED)? "Disabled" : "Enabled"]
" + if (rmat.mat_container) + output += "Material Amount: [rmat.format_amount()]" else - output += "No material storage connected, please contact the quartermaster.
" + output += "No material storage connected, please contact the quartermaster." + output += "
Sync with R&D servers
" + output += "Main Screen" + output += "
" + output += "
\ + \ + \ + \ + \ +

" return output /obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D) @@ -142,6 +143,40 @@ return TRUE return FALSE +/obj/machinery/mecha_part_fabricator/proc/output_ui_materials() + var/output + output += "

Material Storage:

" + for(var/mat_id in rmat.mat_container.materials) + var/datum/material/M = mat_id + var/amount = rmat.mat_container.materials[mat_id] + var/ref = REF(M) + output += "* [amount] of [M.name]: " + if(amount >= MINERAL_MATERIAL_AMOUNT) output += "Eject" + if(amount >= MINERAL_MATERIAL_AMOUNT*5) output += "5x" + if(amount >= MINERAL_MATERIAL_AMOUNT) output += "All" + output += "
" + output += "
" + return output + +/obj/machinery/mecha_part_fabricator/proc/search(string) + matching_designs.Cut() + for(var/v in stored_research.researched_designs) + var/datum/design/D = SSresearch.techweb_design_by_id(v) + if(!(D.build_type & MECHFAB)) + continue + if(findtext(D.name,string)) + matching_designs.Add(D) + +/obj/machinery/mecha_part_fabricator/proc/output_ui_search() + var/output + output += "

Search Results:

" + for(var/datum/design/D in matching_designs) + output += "
[output_part_info(D)]
" + if(check_resources(D)) + output += "Build | " + output += "Add to queue?
" + return output + /obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D) var/list/res_coef = get_resources_w_coeff(D) @@ -239,7 +274,7 @@ output += "Remove" output += "" - output += "\[Process queue | Clear queue\]" + output += "Process queue | Clear queue" return output /obj/machinery/mecha_part_fabricator/proc/sync() @@ -271,18 +306,20 @@ left_part = temp else if(being_built) var/obj/I = being_built.build_path - left_part = {"Building [initial(I.name)].
- Please wait until completion...
"} + left_part = {"
Building [initial(I.name)].
+ Please wait until completion...
"} else + left_part = output_ui_header() 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\]
" + left_part += "[part_set] - Add all parts to queue
" if("parts") left_part += output_parts_list(part_set) - left_part += "
Return" + if("resources") + left_part += output_ui_materials() + if("search") + left_part += output_ui_search() dat = {" @@ -304,18 +341,22 @@ - -
+ [left_part] + [list_queue()]
"} - user << browse(dat, "window=mecha_fabricator;size=1000x430") - onclose(user, "mecha_fabricator") + + var/datum/browser/popup = new(user, "mecha_fabricator", name, 1000, 430) + popup.set_content(dat) + popup.open() + //user << browse(dat, "window=mecha_fabricator;size=1000x430") + //onclose(user, "mecha_fabricator") return /obj/machinery/mecha_part_fabricator/Topic(href, href_list) @@ -385,6 +426,9 @@ Return "} break + if(href_list["search"]) //Search for designs with name matching pattern + search(href_list["to_search"]) + screen = "search" if(href_list["remove_mat"] && href_list["material"]) var/datum/material/Mat = locate(href_list["material"]) diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index 3a9cb26f50e0..9c52797129e8 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -12,7 +12,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "exosuit_control_console", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "ExosuitControlConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/mecha/ui_data(mob/user) diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm index 334aaad8b636..d10b2e878a61 100644 --- a/code/game/mecha/mecha_defense.dm +++ b/code/game/mecha/mecha_defense.dm @@ -128,10 +128,22 @@ severity++ for(var/X in equipment) var/obj/item/mecha_parts/mecha_equipment/ME = X - ME.ex_act(severity,target) + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += ME + if(EXPLODE_HEAVY) + SSexplosions.medobj += ME + if(EXPLODE_LIGHT) + SSexplosions.lowobj += ME for(var/Y in trackers) var/obj/item/mecha_parts/mecha_tracking/MT = Y - MT.ex_act(severity, target) + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += MT + if(EXPLODE_HEAVY) + SSexplosions.medobj += MT + if(EXPLODE_LIGHT) + SSexplosions.lowobj += MT if(occupant) occupant.ex_act(severity,target) @@ -151,7 +163,7 @@ log_message("EMP detected", LOG_MECHA, color="red") if(istype(src, /obj/mecha/combat)) - mouse_pointer = 'icons/mecha/mecha_mouse-disable.dmi' + mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse-disable.dmi' occupant?.update_mouse_pointer() if(!equipment_disabled && occupant) //prevent spamming this message with back-to-back EMPs to_chat(occupant, "Error -- Connection to equipment control unit has been lost.") diff --git a/code/game/mecha/working/clarke.dm b/code/game/mecha/working/clarke.dm new file mode 100644 index 000000000000..6009a8c43523 --- /dev/null +++ b/code/game/mecha/working/clarke.dm @@ -0,0 +1,102 @@ +/* Wasp Edit - Removes Clarke +///Lavaproof, fireproof, fast mech with low armor and higher energy consumption, cannot strafe and has an internal ore box. +/obj/mecha/working/clarke + desc = "Combining man and machine for a better, stronger engineer. Can even resist lava!" + name = "\improper Clarke" + icon_state = "clarke" + max_temperature = 65000 + max_integrity = 200 + step_in = 1.25 + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + lights_power = 7 + deflect_chance = 10 + step_energy_drain = 15 //slightly higher energy drain since you movin those wheels FAST + armor = list("melee" = 20, "bullet" = 10, "laser" = 20, "energy" = 10, "bomb" = 60, "bio" = 0, "rad" = 70, "fire" = 100, "acid" = 100) //low armor to compensate for fire protection and speed + max_equip = 7 + wreckage = /obj/structure/mecha_wreckage/clarke + enter_delay = 40 + canstrafe = FALSE + /// Handles an internal ore box for Clarke + var/obj/structure/ore_box/box + +/obj/mecha/working/clarke/Initialize() + . = ..() + box = new /obj/structure/ore_box(src) + var/obj/item/mecha_parts/mecha_equipment/orebox_manager/ME = new(src) + ME.attach(src) + +/obj/mecha/working/clarke/Destroy() + box.dump_box_contents() + return ..() + +/obj/mecha/working/clarke/moved_inside(mob/living/carbon/human/H) + . = ..() + if(.) + var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_DIAGNOSTIC_ADVANCED] + hud.add_hud_to(H) + +/obj/mecha/working/clarke/go_out() + if(isliving(occupant)) + var/mob/living/L = occupant + var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_DIAGNOSTIC_ADVANCED] + hud.remove_hud_from(L) + return ..() + +/obj/mecha/working/clarke/mmi_moved_inside(obj/item/mmi/M, mob/user) + . = ..() + if(.) + var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_DIAGNOSTIC_ADVANCED] + var/mob/living/brain/B = M.brainmob + hud.add_hud_to(B) + +/obj/mecha/working/clarke/domove(direction) + . = ..() + if (.) + if(prob(30)) + crush_floor() + +/obj/mecha/working/clarke/mechturn(direction) + if(prob(80)) + crush_floor() + return ..() + +///Damages floor tiles and plating as the Clarke moves over them with its tracks. Damage is superficial, will not create hull breaches. +/obj/mecha/working/clarke/proc/crush_floor() + var/turf/open/floor/tile = get_turf(src) + if(!istype(tile, /turf/open/floor)) + return + tile.crush() + +//Ore Box Controls + +///Special equipment for the Clarke mech, handles moving ore without giving the mech a hydraulic clamp and cargo compartment. +/obj/item/mecha_parts/mecha_equipment/orebox_manager + name = "ore storage module" + desc = "An automated ore box management device." + icon = 'icons/obj/mining.dmi' + icon_state = "bin" + selectable = FALSE + detachable = FALSE + salvageable = FALSE + /// Var to avoid istype checking every time the topic button is pressed. This will only work inside Clarke mechs. + var/obj/mecha/working/clarke/hostmech + +/obj/item/mecha_parts/mecha_equipment/orebox_manager/attach(obj/mecha/M) + . = ..() + if(istype(M, /obj/mecha/working/clarke)) + hostmech = M + +/obj/item/mecha_parts/mecha_equipment/orebox_manager/detach() + hostmech = null //just in case + return ..() + +/obj/item/mecha_parts/mecha_equipment/orebox_manager/Topic(href,href_list) + . = ..() + if(!hostmech || !hostmech.box) + return + hostmech.box.dump_box_contents() + +/obj/item/mecha_parts/mecha_equipment/orebox_manager/get_equip_info() + return "[..()] [hostmech?.box ? "Unload Cargo" : "Error"]" + +Wasp End */ diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index e6f7584a8873..2a4c40f88a5b 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -319,7 +319,7 @@ if(target && !target.stat) O.throw_at(target, 7, 5) else - O.ex_act(EXPLODE_HEAVY) + SSexplosions.medobj += O /obj/effect/anomaly/bhole/proc/grav(r, ex_act_force, pull_chance, turf_removal_chance) for(var/t = -r, t < r, t++) @@ -338,7 +338,13 @@ if(prob(pull_chance)) for(var/obj/O in T.contents) if(O.anchored) - O.ex_act(ex_act_force) + switch(ex_act_force) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += O + if(EXPLODE_HEAVY) + SSexplosions.medobj += O + if(EXPLODE_LIGHT) + SSexplosions.lowobj += O else step_towards(O,src) for(var/mob/living/M in T.contents) @@ -346,4 +352,10 @@ //Damaging the turf if( T && prob(turf_removal_chance) ) - T.ex_act(ex_act_force) + switch(ex_act_force) + if(EXPLODE_DEVASTATE) + SSexplosions.highturf += T + if(EXPLODE_HEAVY) + SSexplosions.medturf += T + if(EXPLODE_LIGHT) + SSexplosions.lowturf += T diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm index 4d69febf860d..55ad097793cc 100644 --- a/code/game/objects/effects/contraband.dm +++ b/code/game/objects/effects/contraband.dm @@ -386,8 +386,8 @@ desc = "The POWER that gamers CRAVE! In partnership with Vlad's Salad." icon_state = "poster39" -/obj/structure/sign/poster/contraband/sun_kist - name = "Sun-kist" +/obj/structure/sign/poster/contraband/starkist + name = "Star-kist" desc = "Drink the stars!" icon_state = "poster40" diff --git a/code/game/objects/effects/decals/turfdecal/tilecoloring.dm b/code/game/objects/effects/decals/turfdecal/tilecoloring.dm index 93497837527a..459f4a32064a 100644 --- a/code/game/objects/effects/decals/turfdecal/tilecoloring.dm +++ b/code/game/objects/effects/decals/turfdecal/tilecoloring.dm @@ -4,6 +4,11 @@ layer = TURF_PLATING_DECAL_LAYER alpha = 110 +/obj/effect/turf_decal/tile/Initialize() + if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS]) + color = "#[random_short_color()]" + . = ..() + /obj/effect/turf_decal/tile/blue name = "blue corner" color = "#52B4E9" @@ -38,11 +43,25 @@ color = "#D4D4D4" alpha = 50 +/obj/effect/turf_decal/tile/random // so many colors + name = "colorful corner" + color = "#E300FF" //bright pink as default for mapping + +/obj/effect/turf_decal/tile/random/Initialize() + color = "#[random_short_color()]" + . = ..() + + /obj/effect/turf_decal/trimline layer = TURF_PLATING_DECAL_LAYER alpha = 110 icon_state = "trimline_box" +/obj/effect/turf_decal/trimline/Initialize() + if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS]) + color = "#[random_short_color()]" + . = ..() + /obj/effect/turf_decal/trimline/white color = "#FFFFFF" diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm index 1edd966ce0b0..0207633bf23e 100644 --- a/code/game/objects/effects/effect_system/effects_foam.dm +++ b/code/game/objects/effects/effect_system/effects_foam.dm @@ -142,7 +142,7 @@ if(O.type == src.type) continue if(lifetime % reagent_divisor) - reagents.reaction(O, VAPOR, fraction) + reagents.expose(O, VAPOR, fraction) var/hit = 0 for(var/mob/living/L in range(0,src)) hit += foam_mob(L) @@ -150,7 +150,7 @@ lifetime++ //this is so the decrease from mobs hit and the natural decrease don't cumulate. var/T = get_turf(src) if(lifetime % reagent_divisor) - reagents.reaction(T, VAPOR, fraction) + reagents.expose(T, VAPOR, fraction) if(--amount < 0) return @@ -163,7 +163,7 @@ return 0 var/fraction = 1/initial(reagent_divisor) if(lifetime % reagent_divisor) - reagents.reaction(L, VAPOR, fraction) + reagents.expose(L, VAPOR, fraction) lifetime-- return 1 diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm index e06605238114..80ff9c7e8aa9 100644 --- a/code/game/objects/effects/effect_system/effects_smoke.dm +++ b/code/game/objects/effects/effect_system/effects_smoke.dm @@ -232,9 +232,9 @@ for(var/atom/movable/AM in T) if(AM.type == src.type) continue - reagents.reaction(AM, TOUCH, fraction) + reagents.expose(AM, TOUCH, fraction) - reagents.reaction(T, TOUCH, fraction) + reagents.expose(T, TOUCH, fraction) return 1 /obj/effect/particle_effect/smoke/chem/smoke_mob(mob/living/carbon/M) @@ -247,7 +247,7 @@ return 0 var/fraction = 1/initial(lifetime) reagents.copy_to(C, fraction*reagents.total_volume) - reagents.reaction(M, INGEST, fraction) + reagents.expose(M, INGEST, fraction) return 1 diff --git a/code/game/objects/effects/effect_system/effects_water.dm b/code/game/objects/effects/effect_system/effects_water.dm index 31124381f6b1..44d9c485544b 100644 --- a/code/game/objects/effects/effect_system/effects_water.dm +++ b/code/game/objects/effects/effect_system/effects_water.dm @@ -21,7 +21,7 @@ /obj/effect/particle_effect/water/Bump(atom/A) if(reagents) - reagents.reaction(A) + reagents.expose(A) return ..() diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index 1193ce213c2a..119a0893d477 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -28,6 +28,7 @@ s.set_up(3, 1, src) s.start() mineEffect(victim) + SEND_SIGNAL(src, COMSIG_MINE_TRIGGERED) triggered = 1 qdel(src) @@ -42,11 +43,22 @@ /obj/effect/mine/explosive/mineEffect(mob/victim) explosion(loc, range_devastation, range_heavy, range_light, range_flash) - /obj/effect/mine/stun name = "stun mine" var/stun_time = 80 +/obj/effect/mine/shrapnel + name = "shrapnel mine" + var/shrapnel_type = /obj/projectile/bullet/shrapnel + var/shrapnel_magnitude = 3 + +/obj/effect/mine/shrapnel/mineEffect(mob/victim) + AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_magnitude) + +/obj/effect/mine/shrapnel/sting + name = "stinger mine" + shrapnel_type = /obj/projectile/bullet/pellet/stingball + /obj/effect/mine/stun/mineEffect(mob/living/victim) if(isliving(victim)) victim.Paralyze(stun_time) diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index be817e042bc5..427be3628fcc 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -199,30 +199,9 @@ linked = P break -/obj/effect/portal/permanent/teleport(atom/movable/M, force = FALSE) // Made perma portals always teleport even in noteleport areas since they are mapmaker only - if(!force && (!istype(M) || iseffect(M) || (ismecha(M) && !mech_sized) || (isobj(M) && !ismob(M)))) //Things that shouldn't teleport. - return - var/turf/real_target = get_link_target_turf() - if(!istype(real_target)) - return FALSE - if(!force && (!ismecha(M) && !istype(M, /obj/projectile) && M.anchored && !allow_anchored)) - return - if(ismegafauna(M)) - message_admins("[M] has used a portal at [ADMIN_VERBOSEJMP(src)] made by [usr].") - var/no_effect = FALSE - if(last_effect == world.time) - no_effect = TRUE - else - last_effect = world.time - if(do_teleport(M, real_target, innate_accuracy_penalty, no_effects = no_effect, channel = teleport_channel, forced = TRUE)) - if(istype(M, /obj/projectile)) - var/obj/projectile/P = M - P.ignore_source_check = TRUE - return TRUE - // try to search for a new one if something was var edited etc - set_linked() +/obj/effect/portal/permanent/teleport(atom/movable/M, force = FALSE) + set_linked() // update portal links . = ..() - return FALSE /obj/effect/portal/permanent/one_way // doesn't have a return portal, can have multiple exits, /obj/effect/landmark/portal_exit to mark them name = "one-way portal" diff --git a/code/game/objects/effects/proximity.dm b/code/game/objects/effects/proximity.dm index 677e2c3b256a..904022b1a89c 100644 --- a/code/game/objects/effects/proximity.dm +++ b/code/game/objects/effects/proximity.dm @@ -113,4 +113,5 @@ /obj/effect/abstract/proximity_checker/Crossed(atom/movable/AM) set waitfor = FALSE . = ..() - monitor.hasprox_receiver.HasProximity(AM) + if(monitor && monitor.hasprox_receiver) + monitor.hasprox_receiver.HasProximity(AM) diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm index 3a29b72ca34f..739e8237af67 100644 --- a/code/game/objects/effects/spawners/gibspawner.dm +++ b/code/game/objects/effects/spawners/gibspawner.dm @@ -1,5 +1,6 @@ /obj/effect/gibspawner + icon_state = "gibspawner"// For the map editor var/sparks = 0 //whether sparks spread var/virusProb = 20 //the chance for viruses to spread on the gibs var/gib_mob_type //generate a fake mob to transfer DNA from if we weren't passed a mob. @@ -31,7 +32,7 @@ var/list/dna_to_add //find the dna to pass to the spawned gibs. do note this can be null if the mob doesn't have blood. add_blood_DNA() has built in null handling. - if(source_mob) + if(source_mob && istype(source_mob)) dna_to_add = source_mob.get_blood_dna_list() //ez pz else if(gib_mob_type) var/mob/living/temp_mob = new gib_mob_type(src) //generate a fake mob so that we pull the right type of DNA for the gibs. diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index f94506815929..3ddcdd9730d0 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -148,6 +148,36 @@ else ..() +/obj/structure/spider/spiderling/proc/cancel_vent_move() + forceMove(entry_vent) + entry_vent = null + +/obj/structure/spider/spiderling/proc/vent_move(obj/machinery/atmospherics/components/unary/vent_pump/exit_vent) + if(QDELETED(exit_vent) || exit_vent.welded) + cancel_vent_move() + return + + forceMove(exit_vent) + var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) + addtimer(CALLBACK(src, .proc/do_vent_move, exit_vent, travel_time), travel_time) + +/obj/structure/spider/spiderling/proc/do_vent_move(obj/machinery/atmospherics/components/unary/vent_pump/exit_vent, travel_time) + if(QDELETED(exit_vent) || exit_vent.welded) + cancel_vent_move() + return + + if(prob(50)) + audible_message("You hear something scampering through the ventilation ducts.") + + addtimer(CALLBACK(src, .proc/finish_vent_move, exit_vent), travel_time) + +/obj/structure/spider/spiderling/proc/finish_vent_move(obj/machinery/atmospherics/components/unary/vent_pump/exit_vent) + if(QDELETED(exit_vent) || exit_vent.welded) + cancel_vent_move() + return + forceMove(exit_vent.loc) + entry_vent = null + /obj/structure/spider/spiderling/process() if(travelling_in_vent) if(isturf(loc)) @@ -167,34 +197,8 @@ visible_message("[src] scrambles into the ventilation ducts!", \ "You hear something scampering through the ventilation ducts.") - spawn(rand(20,60)) - if(!exit_vent || exit_vent.welded) - forceMove(entry_vent) - entry_vent = null - return - - forceMove(exit_vent) - var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) - spawn(travel_time) - - if(!exit_vent || exit_vent.welded) - forceMove(entry_vent) - entry_vent = null - return - - if(prob(50)) - audible_message("You hear something scampering through the ventilation ducts.") - sleep(travel_time) - - if(!exit_vent || exit_vent.welded) - forceMove(entry_vent) - entry_vent = null - return - forceMove(exit_vent.loc) - entry_vent = null - var/area/new_area = get_area(loc) - if(new_area) - new_area.Entered(src) + addtimer(CALLBACK(src, .proc/vent_move, exit_vent), rand(20,60)) + //================= else if(prob(33)) diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index a49355e9a742..fdd36b9aad2a 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -97,14 +97,20 @@ icon_state = "phaseout" /obj/effect/temp_visual/dir_setting/wraith - name = "blood" + name = "shadow" icon = 'icons/mob/mob.dmi' icon_state = "phase_shift2" - duration = 12 + duration = 6 + +/obj/effect/temp_visual/dir_setting/wraith/angelic + icon_state = "phase_shift2_angelic" /obj/effect/temp_visual/dir_setting/wraith/out icon_state = "phase_shift" +/obj/effect/temp_visual/dir_setting/wraith/out/angelic + icon_state = "phase_shift_angelic" + /obj/effect/temp_visual/dir_setting/tailsweep icon_state = "tailsweep" duration = 4 diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 71005eae75cb..54d89082f1d1 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -21,25 +21,28 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb ///Icon file for mob worn overlays. ///no var for state because it should *always* be the same as icon_state var/icon/mob_overlay_icon - //Forced mob worn layer instead of the standard preferred ssize. + ///Forced mob worn layer instead of the standard preferred ssize. var/alternate_worn_layer - //Dimensions of the icon file used when this item is worn, eg: hats.dmi - //eg: 32x32 sprite, 64x64 sprite, etc. - //allows inhands/worn sprites to be of any size, but still centered on a mob properly + ///Dimensions of the icon file used when this item is worn, eg: hats.dmi (32x32 sprite, 64x64 sprite, etc.). Allows inhands/worn sprites to be of any size, but still centered on a mob properly var/worn_x_dimension = 32 + ///Dimensions of the icon file used when this item is worn, eg: hats.dmi (32x32 sprite, 64x64 sprite, etc.). Allows inhands/worn sprites to be of any size, but still centered on a mob properly var/worn_y_dimension = 32 - //Same as above but for inhands, uses the lefthand_ and righthand_ file vars + ///Same as for [worn_x_dimension][/obj/item/var/worn_x_dimension] but for inhands, uses the lefthand_ and righthand_ file vars var/inhand_x_dimension = 32 + ///Same as for [worn_y_dimension][/obj/item/var/worn_y_dimension] but for inhands, uses the lefthand_ and righthand_ file vars var/inhand_y_dimension = 32 max_integrity = 200 obj_flags = NONE + ///Item flags for the item var/item_flags = NONE + ///Sound played when you hit something with the item var/hitsound + ///Played when the item is used, for example tools var/usesound ///Used when yate into a mob var/mob_throw_hit_sound @@ -50,71 +53,103 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb ///Sound uses when dropping the item, or when its thrown. var/drop_sound + ///How large is the object, used for stuff like whether it can fit in backpacks or not var/w_class = WEIGHT_CLASS_NORMAL - var/slot_flags = 0 //This is used to determine on which slots an item can fit. + ///This is used to determine on which slots an item can fit. + var/slot_flags = 0 pass_flags = PASSTABLE pressure_resistance = 4 var/obj/item/master = null - var/heat_protection = 0 //flags which determine which body parts are protected from heat. Use the HEAD, CHEST, GROIN, etc. flags. See setup.dm - var/cold_protection = 0 //flags which determine which body parts are protected from cold. Use the HEAD, CHEST, GROIN, etc. flags. See setup.dm - var/max_heat_protection_temperature //Set this variable to determine up to which temperature (IN KELVIN) the item protects against heat damage. Keep at null to disable protection. Only protects areas set by heat_protection flags - var/min_cold_protection_temperature //Set this variable to determine down to which temperature (IN KELVIN) the item protects against cold damage. 0 is NOT an acceptable number due to if(varname) tests!! Keep at null to disable protection. Only protects areas set by cold_protection flags + ///flags which determine which body parts are protected from heat. [See here][HEAD] + var/heat_protection = 0 + ///flags which determine which body parts are protected from cold. [See here][HEAD] + var/cold_protection = 0 + ///Set this variable to determine up to which temperature (IN KELVIN) the item protects against heat damage. Keep at null to disable protection. Only protects areas set by heat_protection flags + var/max_heat_protection_temperature + ///Set this variable to determine down to which temperature (IN KELVIN) the item protects against cold damage. 0 is NOT an acceptable number due to if(varname) tests!! Keep at null to disable protection. Only protects areas set by cold_protection flags + var/min_cold_protection_temperature - var/list/actions //list of /datum/action's that this item has. - var/list/actions_types //list of paths of action datums to give to the item on New(). + ///list of /datum/action's that this item has. + var/list/actions + ///list of paths of action datums to give to the item on New(). + var/list/actions_types //Since any item can now be a piece of clothing, this has to be put here so all items share it. - var/flags_inv //This flag is used to determine when items in someone's inventory cover others. IE helmets making it so you can't see glasses, etc. - var/transparent_protection = NONE //you can see someone's mask through their transparent visor, but you can't reach it + ///This flag is used to determine when items in someone's inventory cover others. IE helmets making it so you can't see glasses, etc. + var/flags_inv + ///you can see someone's mask through their transparent visor, but you can't reach it + var/transparent_protection = NONE + ///flags for what should be done when you click on the item, default is picking it up var/interaction_flags_item = INTERACT_ITEM_ATTACK_HAND_PICKUP - var/body_parts_covered = 0 //see setup.dm for appropriate bit flags - var/gas_transfer_coefficient = 1 // for leaking gas from turf to mask and vice-versa (for masks right now, but at some point, i'd like to include space helmets) - var/permeability_coefficient = 1 // for chemicals/diseases - var/siemens_coefficient = 1 // for electrical admittance/conductance (electrocution checks and shit) - var/slowdown = 0 // How much clothing is slowing you down. Negative values speeds you up - var/armour_penetration = 0 //percentage of armour effectiveness to remove - var/list/allowed = null //suit storage stuff. - var/equip_delay_self = 0 //In deciseconds, how long an item takes to equip; counts only for normal clothing slots, not pockets etc. - var/equip_delay_other = 20 //In deciseconds, how long an item takes to put on another person - var/strip_delay = 40 //In deciseconds, how long an item takes to remove from another person + ///What body parts are covered by the clothing when you wear it + var/body_parts_covered = 0 + ///Literally does nothing right now + var/gas_transfer_coefficient = 1 + /// How likely a disease or chemical is to get through a piece of clothing + var/permeability_coefficient = 1 + /// for electrical admittance/conductance (electrocution checks and shit) + var/siemens_coefficient = 1 + /// How much clothing is slowing you down. Negative values speeds you up + var/slowdown = 0 + ///percentage of armour effectiveness to remove + var/armour_penetration = 0 + ///What objects the suit storage can store + var/list/allowed = null + ///In deciseconds, how long an item takes to equip; counts only for normal clothing slots, not pockets etc. + var/equip_delay_self = 0 + ///In deciseconds, how long an item takes to put on another person + var/equip_delay_other = 20 + ///In deciseconds, how long an item takes to remove from another person + var/strip_delay = 40 + ///How long it takes to resist out of the item (cuffs and such) var/breakouttime = 0 - var/list/attack_verb //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]" - var/list/species_exception = null // list() of species types, if a species cannot put items in a certain slot, but species type is in list, it will be able to wear that item + ///Used in [atom/proc/attackby] to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]" + var/list/attack_verb + ///list() of species types, if a species cannot put items in a certain slot, but species type is in list, it will be able to wear that item + var/list/species_exception = null + ///Who threw the item var/mob/thrownby = null - mouse_drag_pointer = MOUSE_ACTIVE_POINTER //the icon to indicate this object is being dragged + ///the icon to indicate this object is being dragged + mouse_drag_pointer = MOUSE_ACTIVE_POINTER + ///Does it embed and if yes, what kind of embed var/list/embedding = NONE - var/flags_cover = 0 //for flags such as GLASSESCOVERSEYES + ///for flags such as [GLASSESCOVERSEYES] + var/flags_cover = 0 var/heat = 0 ///All items with sharpness of IS_SHARP or higher will automatically get the butchering component. var/sharpness = IS_BLUNT + ///How a tool acts when you use it on something, such as wirecutters cutting wires while multitools measure power var/tool_behaviour = NONE + ///How fast does the tool work var/toolspeed = 1 var/block_chance = 0 var/hit_reaction_chance = 0 //If you want to have something unrelated to blocking/armour piercing etc. Maybe not needed, but trying to think ahead/allow more freedom - var/reach = 1 //In tiles, how far this weapon can reach; 1 for adjacent, which is default + ///In tiles, how far this weapon can reach; 1 for adjacent, which is default + var/reach = 1 - //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 = null // for default list, see /mob/proc/equip_to_appropriate_slot() + ///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. For default list, see [/mob/proc/equip_to_appropriate_slot] + var/list/slot_equipment_priority = null - // Needs to be in /obj/item because corgis can wear a lot of - // non-clothing items + ///Reference to the datum that determines whether dogs can wear the item: Needs to be in /obj/item because corgis can wear a lot of non-clothing items var/datum/dog_fashion/dog_fashion = null //Tooltip vars - var/force_string //string form of an item's force. Edit this var only to set a custom force string + ///string form of an item's force. Edit this var only to set a custom force string + var/force_string var/last_force_string_check = 0 var/tip_timer + ///Determines who can shoot this var/trigger_guard = TRIGGER_GUARD_NONE ///Used as the dye color source in the washing machine only (at the moment). Can be a hex color or a key corresponding to a registry entry, see washing_machine.dm @@ -124,9 +159,10 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb ///What dye registry should be looked at when dying this item; see washing_machine.dm var/dying_key - //Grinder vars - var/list/grind_results //A reagent list containing the reagents this item produces when ground up in a grinder - this can be an empty list to allow for reagent transferring only - var/list/juice_results //A reagent list containing blah blah... but when JUICED in a grinder! + ///Grinder var:A reagent list containing the reagents this item produces when ground up in a grinder - this can be an empty list to allow for reagent transferring only + var/list/grind_results + //Grinder var:A reagent list containing blah blah... but when JUICED in a grinder! + var/list/juice_results var/canMouseDown = FALSE @@ -171,18 +207,16 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb /obj/item/ComponentInitialize() . = ..() - // this proc says it's for initializing components, but we're initializing elements too because it's you and me against the world >:) - if(embedding) - AddElement(/datum/element/embed, embedding) - else if(GLOB.embedpocalypse) - embedding = EMBED_POINTY - AddElement(/datum/element/embed, embedding) - name = "pointy [name]" - else if(GLOB.stickpocalypse) - embedding = EMBED_HARMLESS - AddElement(/datum/element/embed, embedding) - name = "sticky [name]" + if(!LAZYLEN(embedding)) + if(GLOB.embedpocalypse) + embedding = EMBED_POINTY + name = "pointy [name]" + else if(GLOB.stickpocalypse) + embedding = EMBED_HARMLESS + name = "sticky [name]" + + updateEmbedding() if(GLOB.rpg_loot_items) AddComponent(/datum/component/fantasy) @@ -190,14 +224,12 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb if(sharpness) //give sharp objects butchering functionality, for consistency AddComponent(/datum/component/butchering, 80 * toolspeed) - -//user: The mob that is suiciding -//damagetype: The type of damage the item will inflict on the user -//BRUTELOSS = 1 -//FIRELOSS = 2 -//TOXLOSS = 4 -//OXYLOSS = 8 -//Output a creative message and then return the damagetype done +/**Makes cool stuff happen when you suicide with an item + * + *Outputs a creative message and then return the damagetype done + * Arguments: + * * user: The mob that is suiciding + */ /obj/item/proc/suicide_act(mob/user) return @@ -238,10 +270,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb if(!user.research_scanner) return - // Research prospects, including boostable nodes and point values. - // Deliver to a console to know whether the boosts have already been used. + /// Research prospects, including boostable nodes and point values. Deliver to a console to know whether the boosts have already been used. var/list/research_msg = list("Research prospects: ") + ///Separator between the items on the list var/sep = "" + ///Nodes that can be boosted var/list/boostable_nodes = techweb_item_boost_check(src) if (boostable_nodes) for(var/id in boostable_nodes) @@ -416,21 +449,24 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE) user?.update_equipment_speed_mods() -// called just as an item is picked up (loc is not yet changed) +/// called just as an item is picked up (loc is not yet changed) /obj/item/proc/pickup(mob/user) SHOULD_CALL_PARENT(1) SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user) item_flags |= IN_INVENTORY -// called when "found" in pockets and storage items. Returns 1 if the search should end. +/// called when "found" in pockets and storage items. Returns 1 if the search should end. /obj/item/proc/on_found(mob/finder) return -// called after an item is placed in an equipment slot -// user is mob that equipped it -// slot uses the slot_X defines found in setup.dm -// for items that can be placed in multiple slots -// Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it +/** + *called after an item is placed in an equipment slot + + * Arguments: + * * user is mob that equipped it + * * slot uses the slot_X defines found in setup.dm for items that can be placed in multiple slots + * * Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it + */ /obj/item/proc/equipped(mob/user, slot, initial = FALSE) SHOULD_CALL_PARENT(1) SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot) @@ -446,21 +482,27 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb playsound(src, pickup_sound, PICKUP_SOUND_VOLUME, ignore_walls = FALSE) user.update_equipment_speed_mods() -//sometimes we only want to grant the item's action if it's equipped in a specific slot. +///sometimes we only want to grant the item's action if it's equipped in a specific slot. /obj/item/proc/item_action_slot_check(slot, mob/user) if(slot == ITEM_SLOT_BACKPACK || slot == ITEM_SLOT_LEGCUFFED) //these aren't true slots, so avoid granting actions there return FALSE return TRUE -//the mob M is attempting to equip this item into the slot passed through as 'slot'. Return 1 if it can do this and 0 if it can't. -//if this is being done by a mob other than M, it will include the mob equipper, who is trying to equip the item to mob M. equipper will be null otherwise. -//If you are making custom procs but would like to retain partial or complete functionality of this one, include a 'return ..()' to where you want this to happen. -//Set disable_warning to TRUE if you wish it to not give you outputs. -/obj/item/proc/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE) +/** + *the mob M is attempting to equip this item into the slot passed through as 'slot'. Return 1 if it can do this and 0 if it can't. + *if this is being done by a mob other than M, it will include the mob equipper, who is trying to equip the item to mob M. equipper will be null otherwise. + *If you are making custom procs but would like to retain partial or complete functionality of this one, include a 'return ..()' to where you want this to happen. + * Arguments: + * * disable_warning to TRUE if you wish it to not give you text outputs. + * * slot is the slot we are trying to equip to + * * equipper is the mob trying to equip the item + * * bypass_equip_delay_self for whether we want to bypass the equip delay + */ +/obj/item/proc/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, swap = FALSE) if(!M) return FALSE - return M.can_equip(src, slot, disable_warning, bypass_equip_delay_self) + return M.can_equip(src, slot, disable_warning, bypass_equip_delay_self, swap) /obj/item/verb/verb_pickup() set src in oview(1) @@ -592,10 +634,12 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE) return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum) -/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE) +/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE) + if(HAS_TRAIT(src, TRAIT_NODROP)) + return thrownby = thrower callback = CALLBACK(src, .proc/after_throw, callback) //replace their callback with our own - . = ..(target, range, speed, thrower, spin, diagonals_first, callback, force, gentle) + . = ..(target, range, speed, thrower, spin, diagonals_first, callback, force, gentle, quickstart = quickstart) /obj/item/proc/after_throw(datum/callback/callback) @@ -727,8 +771,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb /obj/item/proc/grind_requirements(obj/machinery/reagentgrinder/R) //Used to check for extra requirements for grinding an object return TRUE - //Called BEFORE the object is ground up - use this to change grind results based on conditions - //Use "return -1" to prevent the grinding from occurring +///Called BEFORE the object is ground up - use this to change grind results based on conditions. Use "return -1" to prevent the grinding from occurring /obj/item/proc/on_grind() /obj/item/proc/on_juice() @@ -770,8 +813,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb closeToolTip(usr) -// Called when a mob tries to use the item as a tool. -// Handles most checks. +/// Called when a mob tries to use the item as a tool.Handles most checks. /obj/item/proc/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks) // No delay means there is no start message, and no reason to call tool_start_check before use_tool. // Run the start check here so we wouldn't have to call it manually. @@ -820,23 +862,21 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb return TRUE -// Called before use_tool if there is a delay, or by use_tool if there isn't. -// Only ever used by welding tools and stacks, so it's not added on any other use_tool checks. +/// Called before [obj/item/proc/use_tool] if there is a delay, or by [obj/item/proc/use_tool] if there isn't. Only ever used by welding tools and stacks, so it's not added on any other [obj/item/proc/use_tool] checks. /obj/item/proc/tool_start_check(mob/living/user, amount=0) . = tool_use_check(user, amount) if(.) SEND_SIGNAL(src, COMSIG_TOOL_START_USE, user) -// A check called by tool_start_check once, and by use_tool on every tick of delay. +/// A check called by [/obj/item/proc/tool_start_check] once, and by use_tool on every tick of delay. /obj/item/proc/tool_use_check(mob/living/user, amount) return !amount -// Generic use proc. Depending on the item, it uses up fuel, charges, sheets, etc. -// Returns TRUE on success, FALSE on failure. +/// Generic use proc. Depending on the item, it uses up fuel, charges, sheets, etc. Returns TRUE on success, FALSE on failure. /obj/item/proc/use(used) return !used -// Plays item's usesound, if any. +/// Plays item's usesound, if any. /obj/item/proc/play_tool_sound(atom/target, volume=50) if(target && usesound && volume) var/played_sound = usesound @@ -853,7 +893,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb if(.) SEND_SIGNAL(src, COMSIG_TOOL_IN_USE, user) -// Returns a numeric value for sorting items used as parts in machines, so they can be replaced by the rped +/// Returns a numeric value for sorting items used as parts in machines, so they can be replaced by the rped /obj/item/proc/get_part_rating() return 0 @@ -872,16 +912,13 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb dropped(M, FALSE) return ..() -/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, datum/callback/callback, gentle = FALSE) - if(HAS_TRAIT(src, TRAIT_NODROP)) - return - return ..() - /obj/item/proc/embedded(mob/living/carbon/human/embedded_mob) return /obj/item/proc/unembedded() - return + if(item_flags & DROPDEL) + QDEL_NULL(src) + return TRUE /obj/item/proc/canStrip(mob/stripper, mob/owner) SHOULD_BE_PURE(TRUE) @@ -890,13 +927,17 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb /obj/item/proc/doStrip(mob/stripper, mob/owner) return owner.dropItemToGround(src) -/** - * Does the current embedding var meet the criteria for being harmless? Namely, does it explicitly define the pain multiplier and jostle pain mult to be 0? If so, return true. - */ -/obj/item/proc/is_embed_harmless() +///Does the current embedding var meet the criteria for being harmless? Namely, does it have a pain multiplier and jostle pain mult of 0? If so, return true. +/obj/item/proc/isEmbedHarmless() if(embedding) return !isnull(embedding["pain_mult"]) && !isnull(embedding["jostle_pain_mult"]) && embedding["pain_mult"] == 0 && embedding["jostle_pain_mult"] == 0 +///In case we want to do something special (like self delete) upon failing to embed in something, return true +/obj/item/proc/failedEmbed() + if(item_flags & DROPDEL) + QDEL_NULL(src) + return TRUE + ///Called by the carbon throw_item() proc. Returns null if the item negates the throw, or a reference to the thing to suffer the throw else. /obj/item/proc/on_thrown(mob/living/carbon/user, atom/target) if((item_flags & ABSTRACT) || HAS_TRAIT(src, TRAIT_NODROP)) @@ -906,3 +947,47 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb to_chat(user, "You set [src] down gently on the ground.") return return src + +/** + * tryEmbed() is for when you want to try embedding something without dealing with the damage + hit messages of calling hitby() on the item while targetting the target. + * + * Really, this is used mostly with projectiles with shrapnel payloads, from [/datum/element/embed/proc/checkEmbedProjectile], and called on said shrapnel. Mostly acts as an intermediate between different embed elements. + * + * Arguments: + * * target- Either a body part, a carbon, or a closed turf. What are we hitting? + * * forced- Do we want this to go through 100%? + */ +/obj/item/proc/tryEmbed(atom/target, forced=FALSE, silent=FALSE) + if(!isbodypart(target) && !iscarbon(target) && !isclosedturf(target)) + return + if(!forced && !LAZYLEN(embedding)) + return + + if(SEND_SIGNAL(src, COMSIG_EMBED_TRY_FORCE, target, forced, silent)) + return TRUE + failedEmbed() + +///For when you want to disable an item's embedding capabilities (like transforming weapons and such), this proc will detach any active embed elements from it. +/obj/item/proc/disableEmbedding() + SEND_SIGNAL(src, COMSIG_ITEM_DISABLE_EMBED) + return + +///For when you want to add/update the embedding on an item. Uses the vars in [/obj/item/embedding], and defaults to config values for values that aren't set. Will automatically detach previous embed elements on this item. +/obj/item/proc/updateEmbedding() + if(!islist(embedding) || !LAZYLEN(embedding)) + return + + AddElement(/datum/element/embed,\ + embed_chance = (!isnull(embedding["embed_chance"]) ? embedding["embed_chance"] : EMBED_CHANCE),\ + fall_chance = (!isnull(embedding["fall_chance"]) ? embedding["fall_chance"] : EMBEDDED_ITEM_FALLOUT),\ + pain_chance = (!isnull(embedding["pain_chance"]) ? embedding["pain_chance"] : EMBEDDED_PAIN_CHANCE),\ + pain_mult = (!isnull(embedding["pain_mult"]) ? embedding["pain_mult"] : EMBEDDED_PAIN_MULTIPLIER),\ + remove_pain_mult = (!isnull(embedding["remove_pain_mult"]) ? embedding["remove_pain_mult"] : EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER),\ + rip_time = (!isnull(embedding["rip_time"]) ? embedding["rip_time"] : EMBEDDED_UNSAFE_REMOVAL_TIME),\ + ignore_throwspeed_threshold = (!isnull(embedding["ignore_throwspeed_threshold"]) ? embedding["ignore_throwspeed_threshold"] : FALSE),\ + impact_pain_mult = (!isnull(embedding["impact_pain_mult"]) ? embedding["impact_pain_mult"] : EMBEDDED_IMPACT_PAIN_MULTIPLIER),\ + jostle_chance = (!isnull(embedding["jostle_chance"]) ? embedding["jostle_chance"] : EMBEDDED_JOSTLE_CHANCE),\ + jostle_pain_mult = (!isnull(embedding["jostle_pain_mult"]) ? embedding["jostle_pain_mult"] : EMBEDDED_JOSTLE_PAIN_MULTIPLIER),\ + pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\ + embed_chance_turf_mod = (!isnull(embedding["embed_chance_turf_mod"]) ? embedding["embed_chance_turf_mod"] : EMBED_CHANCE_TURF_MOD)) + return TRUE diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index 5c0d8f57e4f2..3f0288e88efb 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -202,10 +202,10 @@ RLD var/airlock_glass = FALSE // So the floor's rcd_act knows how much ammo to use var/window_type = /obj/structure/window/fulltile var/advanced_airlock_setting = 1 //Set to 1 if you want more paintjobs available - var/list/conf_access = null - var/use_one_access = 0 //If the airlock should require ALL or only ONE of the listed accesses. var/delay_mod = 1 var/canRturf = FALSE //Variable for R walls to deconstruct them + /// Integrated airlock electronics for setting access to a newly built airlocks + var/obj/item/electronics/airlock/airlock_electronics /obj/item/construction/rcd/suicide_act(mob/user) user.visible_message("[user] sets the RCD to 'Wall' and points it down [user.p_their()] throat! It looks like [user.p_theyre()] trying to commit suicide..") @@ -242,78 +242,6 @@ RLD else to_chat(user, "\the [src] dont have remote storage connection.") - -/obj/item/construction/rcd/proc/change_airlock_access(mob/user) - if (!ishuman(user) && !user.has_unlimited_silicon_privilege) - return - - var/t1 = "" - - if(use_one_access) - t1 += "Restriction Type: At least one access required
" - else - t1 += "Restriction Type: All accesses required
" - - t1 += "Remove All
" - - var/accesses = "" - accesses += "
Access
" - accesses += "" - accesses += "" - for(var/i = 1; i <= 7; i++) - accesses += "" - accesses += "" - for(var/i = 1; i <= 7; i++) - accesses += "" - accesses += "
[get_region_accesses_name(i)]:
" - for(var/A in get_region_accesses(i)) - if(A in conf_access) - accesses += "[replacetext(get_access_desc(A), " ", " ")] " - else - accesses += "[replacetext(get_access_desc(A), " ", " ")] " - accesses += "
" - accesses += "
" - t1 += "[accesses]" - - t1 += "

Close

\n" - - var/datum/browser/popup = new(user, "rcd_access", "Access Control", 900, 500) - popup.set_content(t1) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() - onclose(user, "rcd_access") - -/obj/item/construction/rcd/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained()) - return - - if (href_list["close"]) - usr << browse(null, "window=rcd_access") - return - - if (href_list["access"]) - toggle_access(href_list["access"]) - change_airlock_access(usr) - -/obj/item/construction/rcd/proc/toggle_access(acc) - if (acc == "all") - conf_access = null - else if(acc == "one") - use_one_access = !use_one_access - else - var/req = text2num(acc) - - 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/construction/rcd/proc/get_airlock_image(airlock_type) var/obj/machinery/door/airlock/proto = airlock_type var/ic = initial(proto.icon) @@ -495,9 +423,13 @@ RLD /obj/item/construction/rcd/Initialize() . = ..() + airlock_electronics = new(src) + airlock_electronics.name = "Access Control" + airlock_electronics.holder = src GLOB.rcd_list += src /obj/item/construction/rcd/Destroy() + QDEL_NULL(airlock_electronics) GLOB.rcd_list -= src . = ..() @@ -546,7 +478,7 @@ RLD change_computer_dir(user) return if("Change Access") - change_airlock_access(user) + airlock_electronics.ui_interact(user) return if("Change Airlock Type") change_airlock_setting(user) diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm index 2a9f93642462..625b671fa4de 100644 --- a/code/game/objects/items/RPD.dm +++ b/code/game/objects/items/RPD.dm @@ -250,7 +250,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/pipes) assets.send(user) - ui = new(user, src, ui_key, "rpd", name, 425, 515, master_ui, state) + ui = new(user, src, ui_key, "RapidPipeDispenser", name, 425, 515, master_ui, state) ui.open() /obj/item/pipe_dispenser/ui_data(mob/user) diff --git a/code/game/objects/items/RSF.dm b/code/game/objects/items/RSF.dm index 426d3ea61dac..f4f594cf6c92 100644 --- a/code/game/objects/items/RSF.dm +++ b/code/game/objects/items/RSF.dm @@ -3,11 +3,15 @@ CONTAINS: RSF */ +///Extracts the related object from a associated list of objects and values, or lists and objects. +#define OBJECT_OR_LIST_ELEMENT(from, input) (islist(input) ? from[input] : input) /obj/item/rsf name = "\improper Rapid-Service-Fabricator" desc = "A device used to rapidly deploy service items." icon = 'icons/obj/tools.dmi' icon_state = "rsf" + ///The icon state to revert to when the tool is empty + var/spent_icon_state = "rsf_empty" lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' opacity = 0 @@ -15,171 +19,186 @@ RSF anchored = FALSE item_flags = NOBLUDGEON armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + ///The current matter count var/matter = 0 - var/mode = 1 + ///The max amount of matter in the device + var/max_matter = 30 + ///The type of the object we are going to dispense + var/to_dispense + ///The cost of the object we are going to dispense + var/dispense_cost = 0 w_class = WEIGHT_CLASS_NORMAL + ///An associated list of atoms and charge costs. This can contain a seperate list, as long as it's associated item is an object + var/list/cost_by_item = list(/obj/item/reagent_containers/food/drinks/drinkingglass = 20, + /obj/item/paper = 10, + /obj/item/storage/pill_bottle/dice = 200, + /obj/item/pen = 50, + /obj/item/clothing/mask/cigarette = 10, + ) + ///An associated list of fuel and it's value + var/list/matter_by_item = list(/obj/item/rcd_ammo = 10,) + ///A list of surfaces that we are allowed to place things on. + var/list/allowed_surfaces = list(/turf/open/floor, /obj/structure/table) + ///The unit of mesure of the matter, for use in text + var/discriptor = "fabrication-units" + ///The verb that describes what we're doing, for use in text + var/action_type = "Dispensing" + +/obj/item/rsf/Initialize() + . = ..() + to_dispense = cost_by_item[1] /obj/item/rsf/examine(mob/user) . = ..() - . += "It currently holds [matter]/30 fabrication-units." + . += "It currently holds [matter]/[max_matter] [discriptor]." /obj/item/rsf/cyborg matter = 30 /obj/item/rsf/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/rcd_ammo)) - if((matter + 10) > 30) - to_chat(user, "The RSF can't hold any more matter!") + if(is_type_in_list(W,matter_by_item))//If the thing we got hit by is in our matter list + var/tempMatter = matter_by_item[W.type] + matter + if(tempMatter > max_matter) + to_chat(user, "\The [src] can't hold any more [discriptor]!") return qdel(W) - matter += 10 + matter = tempMatter //We add its value playsound(src.loc, 'sound/machines/click.ogg', 10, TRUE) - to_chat(user, "The RSF now holds [matter]/30 fabrication-units.") - icon_state = "rsf" + to_chat(user, "\The [src] now holds [matter]/[max_matter] [discriptor].") + icon_state = initial(icon_state)//and set the icon state to the initial value it had else return ..() /obj/item/rsf/attack_self(mob/user) playsound(src.loc, 'sound/effects/pop.ogg', 50, FALSE) - switch(mode) - if(5) - mode = 1 - to_chat(user, "Changed dispensing mode to 'Drinking Glass'.") - if(1) - mode = 2 - to_chat(user, "Changed dispensing mode to 'Paper'.") - if(2) - mode = 3 - to_chat(user, "Changed dispensing mode to 'Pen'.") - if(3) - mode = 4 - to_chat(user, "Changed dispensing mode to 'Dice Pack'.") - if(4) - mode = 5 - to_chat(user, "Changed dispensing mode to 'Cigarette'.") + var/target = cost_by_item + var/cost = 0 + //Warning, prepare for bodgecode + while(islist(target))//While target is a list we continue the loop + var/picked = show_radial_menu(user, src, formRadial(target), custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE) + if(!check_menu(user) || picked == null) + return + for(var/emem in target)//Back through target agian + var/atom/test = OBJECT_OR_LIST_ELEMENT(target, emem) + if(picked == initial(test.name))//We try and find the entry that matches the radial selection + cost = target[emem]//We cash the cost + target = emem + break + //If we found a list we start it all again, this time looking through its contents. + //This allows for sublists + to_dispense = target + dispense_cost = cost // Change mode +///Forms a radial menu based off an object in a list, or a list's associated object +/obj/item/rsf/proc/formRadial(from) + var/list/radial_list = list() + for(var/meme in from)//We iterate through all of targets entrys + var/atom/temp = OBJECT_OR_LIST_ELEMENT(from, meme) + //We then add their data into the radial menu + radial_list[initial(temp.name)] = image(icon = initial(temp.icon), icon_state = initial(temp.icon_state)) + return radial_list + +/obj/item/rsf/proc/check_menu(mob/user) + if(user.incapacitated() || !user.Adjacent(src)) + return FALSE + return TRUE + /obj/item/rsf/afterattack(atom/A, mob/user, proximity) . = ..() if(!proximity) return - if (!(istype(A, /obj/structure/table) || isfloorturf(A))) + if(!is_allowed(A)) return + if(use_matter(dispense_cost, user))//If we can charge that amount of charge, we do so and return true + playsound(loc, 'sound/machines/click.ogg', 10, TRUE) + var/atom/meme = new to_dispense(get_turf(A)) + to_chat(user, "[action_type] [meme.name]...") +///A helper proc. checks to see if we can afford the amount of charge that is passed, and if we can docs the charge from our base, and returns TRUE. If we can't we return FALSE +/obj/item/rsf/proc/use_matter(charge, mob/user) if(iscyborg(user)) var/mob/living/silicon/robot/R = user - if(!R.cell || R.cell.charge < 200) + var/end_charge = R.cell.charge - charge + if(end_charge < 0) to_chat(user, "You do not have enough power to use [src].") - icon_state = "rsf_empty" - return - else if (matter < 1) - to_chat(user, "\The [src] doesn't have enough matter left.") - icon_state = "rsf_empty" - return - - var/turf/T = get_turf(A) - playsound(src.loc, 'sound/machines/click.ogg', 10, TRUE) - switch(mode) - if(1) - to_chat(user, "Dispensing Drinking Glass...") - new /obj/item/reagent_containers/food/drinks/drinkingglass(T) - use_matter(20, user) - if(2) - to_chat(user, "Dispensing Paper Sheet...") - new /obj/item/paper(T) - use_matter(10, user) - if(3) - to_chat(user, "Dispensing Pen...") - new /obj/item/pen(T) - use_matter(50, user) - if(4) - to_chat(user, "Dispensing Dice Pack...") - new /obj/item/storage/pill_bottle/dice(T) - use_matter(200, user) - if(5) - to_chat(user, "Dispensing Cigarette...") - new /obj/item/clothing/mask/cigarette(T) - use_matter(10, user) - -/obj/item/rsf/proc/use_matter(charge, mob/user) - if (iscyborg(user)) - var/mob/living/silicon/robot/R = user - R.cell.charge -= charge + icon_state = spent_icon_state + return FALSE + R.cell.charge = end_charge + return TRUE else + if(matter - 1 < 0) + to_chat(user, "\The [src] doesn't have enough [discriptor] left.") + icon_state = spent_icon_state + return FALSE matter-- - to_chat(user, "The RSF now holds [matter]/30 fabrication-units.") + to_chat(user, "\The [src] now holds [matter]/[max_matter] [discriptor].") + return TRUE + +///Helper proc that iterates through all the things we are allowed to spawn on, and sees if the passed atom is one of them +/obj/item/rsf/proc/is_allowed(atom/to_check) + for(var/sort in allowed_surfaces) + if(istype(to_check, sort)) + return TRUE + return FALSE -/obj/item/cookiesynth +/obj/item/rsf/cookiesynth name = "Cookie Synthesizer" desc = "A self-recharging device used to rapidly deploy cookies." - icon = 'icons/obj/tools.dmi' icon_state = "rcd" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - var/matter = 10 - var/toxin = 0 + spent_icon_state = "rcd" + max_matter = 10 + cost_by_item = list() + dispense_cost = 100 + discriptor = "cookie-units" + action_type = "Fabricates" + ///Tracks whether or not the cookiesynth is about to print a poisoned cookie + var/toxin = FALSE //This might be better suited to some initialize fuckery, but I don't have a good "poisoned" sprite + ///Holds a copy of world.time taken the last time the synth gained a charge. Used with cooldowndelay to track when the next charge should be gained var/cooldown = 0 + ///The period between recharges var/cooldowndelay = 10 - w_class = WEIGHT_CLASS_NORMAL -/obj/item/cookiesynth/examine(mob/user) +/obj/item/rsf/cookiesynth/Initialize() . = ..() - . += "It currently holds [matter]/10 cookie-units." + START_PROCESSING(SSprocessing, src) -/obj/item/cookiesynth/attackby() +/obj/item/rsf/cookiesynth/Destroy() + STOP_PROCESSING(SSprocessing, src) + return ..() + +/obj/item/rsf/cookiesynth/attackby() return -/obj/item/cookiesynth/emag_act(mob/user) +/obj/item/rsf/cookiesynth/emag_act(mob/user) obj_flags ^= EMAGGED if(obj_flags & EMAGGED) to_chat(user, "You short out [src]'s reagent safety checker!") else to_chat(user, "You reset [src]'s reagent safety checker!") - toxin = 0 -/obj/item/cookiesynth/attack_self(mob/user) +/obj/item/rsf/cookiesynth/attack_self(mob/user) var/mob/living/silicon/robot/P = null if(iscyborg(user)) P = user - if((obj_flags & EMAGGED)&&!toxin) - toxin = 1 - to_chat(user, "Cookie Synthesizer hacked.") - else if(P.emagged&&!toxin) - toxin = 1 + if(((obj_flags & EMAGGED) || (P && P.emagged)) && !toxin) + toxin = TRUE + to_dispense = /obj/item/reagent_containers/food/snacks/cookie/sleepy to_chat(user, "Cookie Synthesizer hacked.") else - toxin = 0 + toxin = FALSE + to_dispense = /obj/item/reagent_containers/food/snacks/cookie to_chat(user, "Cookie Synthesizer reset.") -/obj/item/cookiesynth/process() - if(matter < 10) - matter++ +/obj/item/rsf/cookiesynth/process() + matter = min(matter + 1, max_matter) //We add 1 up to a point + if(matter >= max_matter) + STOP_PROCESSING(SSprocessing, src) -/obj/item/cookiesynth/afterattack(atom/A, mob/user, proximity) - . = ..() +/obj/item/rsf/cookiesynth/afterattack(atom/A, mob/user, proximity) if(cooldown > world.time) return - if(!proximity) - return - if (!(istype(A, /obj/structure/table) || isfloorturf(A))) - return - if(matter < 1) - to_chat(user, "[src] doesn't have enough matter left. Wait for it to recharge!") - return - if(iscyborg(user)) - var/mob/living/silicon/robot/R = user - if(!R.cell || R.cell.charge < 400) - to_chat(user, "You do not have enough power to use [src].") - return - var/turf/T = get_turf(A) - playsound(src.loc, 'sound/machines/click.ogg', 10, TRUE) - to_chat(user, "Fabricating Cookie...") - var/obj/item/reagent_containers/food/snacks/cookie/S = new /obj/item/reagent_containers/food/snacks/cookie(T) - if(toxin) - S.reagents.add_reagent(/datum/reagent/toxin/chloralhydrate, 10) - if (iscyborg(user)) - var/mob/living/silicon/robot/R = user - R.cell.charge -= 100 - else - matter-- + . = ..() cooldown = world.time + cooldowndelay + if(!(datum_flags & DF_ISPROCESSING)) + START_PROCESSING(SSprocessing, src) diff --git a/code/game/objects/items/airlock_painter.dm b/code/game/objects/items/airlock_painter.dm index 5bf569d9cb07..14f222580852 100644 --- a/code/game/objects/items/airlock_painter.dm +++ b/code/game/objects/items/airlock_painter.dm @@ -87,7 +87,7 @@ // make some colorful reagent, and apply it to the lungs L.create_reagents(10) L.reagents.add_reagent(/datum/reagent/colorful_reagent, 10) - L.reagents.reaction(L, TOUCH, 1) + L.reagents.expose(L, TOUCH, 1) // TODO maybe add some colorful vomit? @@ -100,7 +100,7 @@ else if(can_use(user) && !L) user.visible_message("[user] is spraying toner on [user.p_them()]self from [src]! It looks like [user.p_theyre()] trying to commit suicide.") user.reagents.add_reagent(/datum/reagent/colorful_reagent, 1) - user.reagents.reaction(user, TOUCH, 1) + user.reagents.expose(user, TOUCH, 1) return TOXLOSS else @@ -202,7 +202,7 @@ /obj/item/airlock_painter/decal/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "decal_painter", name, 500, 400, master_ui, state) + ui = new(user, src, ui_key, "DecalPainter", name, 500, 400, master_ui, state) ui.open() /obj/item/airlock_painter/decal/ui_data(mob/user) diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index d15ac2d6cabc..1558b9f04e56 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -33,6 +33,10 @@ if(href_list["create_area"]) if(in_use) return + var/area/A = get_area(usr) + if(A.noteleport) + to_chat(usr, "You cannot edit restricted areas.") + return in_use = TRUE create_area(usr) in_use = FALSE diff --git a/code/game/objects/items/cardboard_cutouts.dm b/code/game/objects/items/cardboard_cutouts.dm index 020c60b99bfa..37a9b4e314ad 100644 --- a/code/game/objects/items/cardboard_cutouts.dm +++ b/code/game/objects/items/cardboard_cutouts.dm @@ -6,17 +6,36 @@ icon_state = "cutout_basic" w_class = WEIGHT_CLASS_BULKY resistance_flags = FLAMMABLE - // Possible restyles for the cutout; - // add an entry in change_appearance() if you add to here - var/list/possible_appearances = list("Assistant", "Clown", "Mime", - "Traitor", "Nuke Op", "Cultist", "Clockwork Cultist", - "Revolutionary", "Wizard", "Shadowling", "Xenomorph", "Xenomorph Maid", "Swarmer", - "Ash Walker", "Deathsquad Officer", "Ian", "Slaughter Demon", - "Laughter Demon", "Private Security Officer") - var/pushed_over = FALSE //If the cutout is pushed over and has to be righted - var/deceptive = FALSE //If the cutout actually appears as what it portray and not a discolored version + /// Possible restyles for the cutout, add an entry in change_appearance() if you add to here + var/list/possible_appearances = list() + /// If the cutout is pushed over and has to be righted + var/pushed_over = FALSE + /// If the cutout actually appears as what it portray and not a discolored version + var/deceptive = FALSE - var/lastattacker = null +/obj/item/cardboard_cutout/Initialize() + . = ..() + possible_appearances = sortList(list( + "Assistant" = image(icon = src.icon, icon_state = "cutout_greytide"), + "Clown" = image(icon = src.icon, icon_state = "cutout_clown"), + "Mime" = image(icon = src.icon, icon_state = "cutout_mime"), + "Traitor" = image(icon = src.icon, icon_state = "cutout_traitor"), + "Nuke Op" = image(icon = src.icon, icon_state = "cutout_fluke"), + "Cultist" = image(icon = src.icon, icon_state = "cutout_cultist"), + "Clockwork Cultist" = image(icon = src.icon, icon_state = "cutout_servant"), + "Revolutionary" = image(icon = src.icon, icon_state = "cutout_viva"), + "Wizard" = image(icon = src.icon, icon_state = "cutout_wizard"), + "Shadowling" = image(icon = src.icon, icon_state = "cutout_shadowling"), + "Xenomorph" = image(icon = src.icon, icon_state = "cutout_fukken_xeno"), + "Xenomorph Maid" = image(icon = src.icon, icon_state = "cutout_lusty"), + "Swarmer" = image(icon = src.icon, icon_state = "cutout_swarmer"), + "Ash Walker" = image(icon = src.icon, icon_state = "cutout_free_antag"), + "Deathsquad Officer" = image(icon = src.icon, icon_state = "cutout_deathsquad"), + "Ian" = image(icon = src.icon, icon_state = "cutout_ian"), + "Slaughter Demon" = image(icon = 'icons/mob/mob.dmi', icon_state = "daemon"), + "Laughter Demon" = image(icon = 'icons/mob/mob.dmi', icon_state = "bowmon"), + "Private Security Officer" = image(icon = src.icon, icon_state = "cutout_ntsec") + )) //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/cardboard_cutout/attack_hand(mob/living/user) @@ -74,22 +93,21 @@ push_over() return BULLET_ACT_HIT +/** + * change_appearance: Changes a skin of the cardboard cutout based on a user's choice + * + * Arguments: + * * crayon The crayon used to change and recolor the cardboard cutout + * * user The mob choosing a skin of the cardboard cutout + */ /obj/item/cardboard_cutout/proc/change_appearance(obj/item/toy/crayon/crayon, mob/living/user) - if(!crayon || !user) - return - if(pushed_over) - to_chat(user, "Right [src] first!") - return - if(crayon.check_empty(user)) - return - if(crayon.is_capped) - to_chat(user, "Take the cap off first!") - return - var/new_appearance = input(user, "Choose a new appearance for [src].", "26th Century Deception") as null|anything in sortList(possible_appearances) - if(!new_appearance || !crayon || !user.canUseTopic(src, BE_CLOSE)) - return + var/new_appearance = show_radial_menu(user, src, possible_appearances, custom_check = CALLBACK(src, .proc/check_menu, user, crayon), radius = 36, require_near = TRUE) + if(!new_appearance) + return FALSE if(!do_after(user, 10, FALSE, src, TRUE)) - return + return FALSE + if(!check_menu(user, crayon)) + return FALSE user.visible_message("[user] gives [src] a new look.", "Voila! You give [src] a new look.") crayon.use_charges(1) crayon.check_empty(user) @@ -178,7 +196,33 @@ name = "Private Security Officer" desc = "A cardboard cutout of a private security officer." icon_state = "cutout_ntsec" - return 1 + else + return FALSE + return TRUE + +/** + * check_menu: Checks if we are allowed to interact with a radial menu + * + * Arguments: + * * user The mob interacting with a menu + * * crayon The crayon used to interact with a menu + */ +/obj/item/cardboard_cutout/proc/check_menu(mob/living/user, obj/item/toy/crayon/crayon) + if(!istype(user)) + return FALSE + if(user.incapacitated()) + return FALSE + if(pushed_over) + to_chat(user, "Right [src] first!") + return FALSE + if(!crayon || !user.is_holding(crayon)) + return FALSE + if(crayon.check_empty(user)) + return FALSE + if(crayon.is_capped) + to_chat(user, "Take the cap off first!") + return FALSE + return TRUE /obj/item/cardboard_cutout/setDir(newdir) newdir = SOUTH diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index d624f16563e9..e72dec01d7aa 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -119,6 +119,7 @@ var/obj/machinery/paystand/my_store var/uses_overlays = TRUE var/icon/cached_flat_icon + var/registered_age = 13 // default age for ss13 players /obj/item/card/id/Initialize(mapload) . = ..() @@ -135,14 +136,17 @@ /obj/item/card/id/attack_self(mob/user) if(Adjacent(user)) - user.visible_message("[user] shows you: [icon2html(src, viewers(user))] [src.name].", "You show \the [src.name].") + var/minor + if(registered_name && registered_age && registered_age < AGE_MINOR) + minor = " (MINOR)" + user.visible_message("[user] shows you: [icon2html(src, viewers(user))] [src.name][minor].", "You show \the [src.name][minor].") add_fingerprint(user) /obj/item/card/id/vv_edit_var(var_name, var_value) . = ..() if(.) switch(var_name) - if("assignment","registered_name") + if("assignment","registered_name","registered_age") update_label() /obj/item/card/id/attackby(obj/item/W, mob/user, params) @@ -178,6 +182,8 @@ return registered_account.adjust_money(cash_money) + SSblackbox.record_feedback("amount", "credits_inserted", cash_money) + log_econ("[cash_money] credits were inserted into [src] owned by [src.registered_name]") if(physical_currency) to_chat(user, "You stuff [I] into [src]. It disappears in a small puff of bluespace smoke, adding [cash_money] credits to the linked account.") else @@ -198,7 +204,8 @@ total += cash_money registered_account.adjust_money(cash_money) - + SSblackbox.record_feedback("amount", "credits_inserted", total) + log_econ("[total] credits were inserted into [src] owned by [src.registered_name]") QDEL_LIST(money) return total @@ -267,6 +274,8 @@ var/obj/item/holochip/holochip = new (user.drop_location(), amount_to_remove) user.put_in_hands(holochip) to_chat(user, "You withdraw [amount_to_remove] credits into a holochip.") + SSblackbox.record_feedback("amount", "credits_removed", amount_to_remove) + log_econ("[amount_to_remove] credits were removed from [src] owned by [src.registered_name]") return else var/difference = amount_to_remove - registered_account.account_balance @@ -274,20 +283,31 @@ /obj/item/card/id/examine(mob/user) . = ..() - if(mining_points) - . += "There's [mining_points] mining equipment redemption point\s loaded onto this card." if(registered_account) . += "The account linked to the ID belongs to '[registered_account.account_holder]' and reports a balance of [registered_account.account_balance] cr." + . += "There's more information below, you can look again to take a closer look..." + +/obj/item/card/id/examine_more(mob/user) + var/list/msg = list("You examine [src] closer, and note the following...") + + if(registered_age) + msg += "The card indicates that the holder is [registered_age] years old. [(registered_age < AGE_MINOR) ? "There's a holographic stripe that reads 'MINOR: DO NOT SERVE ALCOHOL OR TOBACCO' along the bottom of the card." : ""]" + if(mining_points) + msg += "There's [mining_points] mining equipment redemption point\s loaded onto this card." + if(registered_account) + msg += "The account linked to the ID belongs to '[registered_account.account_holder]' and reports a balance of [registered_account.account_balance] cr." if(registered_account.account_job) var/datum/bank_account/D = SSeconomy.get_dep_account(registered_account.account_job.paycheck_department) if(D) - . += "The [D.account_holder] reports a balance of [D.account_balance] cr." - . += "Alt-Click the ID to pull money from the linked account in the form of holochips." - . += "You can insert credits into the linked account by pressing holochips, cash, or coins against the ID." + msg += "The [D.account_holder] reports a balance of [D.account_balance] cr." + msg += "Alt-Click the ID to pull money from the linked account in the form of holochips." + msg += "You can insert credits into the linked account by pressing holochips, cash, or coins against the ID." if(registered_account.account_holder == user.real_name) - . += "If you lose this ID card, you can reclaim your account by Alt-Clicking a blank ID card while holding it and entering your account ID number." + msg += "If you lose this ID card, you can reclaim your account by Alt-Clicking a blank ID card while holding it and entering your account ID number." else - . += "There is no registered account linked to this card. Alt-Click to add one." + msg += "There is no registered account linked to this card. Alt-Click to add one." + + return msg /obj/item/card/id/GetAccess() return access @@ -412,6 +432,11 @@ update_label() var/target_occupation = stripped_input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than Maintenance.", "Agent card job assignment", assignment ? assignment : "Assistant", MAX_MESSAGE_LEN) if(!target_occupation) return + + var/newAge = input(user, "Choose the ID's age:\n([AGE_MIN]-[AGE_MAX])", "Agent card age") as num|null + if(newAge) + registered_age = max(round(text2num(newAge)), 0) + registered_name = input_name assignment = target_occupation update_label() @@ -460,6 +485,27 @@ update_label() icon_state = "syndie" access = list(ACCESS_SYNDICATE) uses_overlays = FALSE + registered_age = null + +/obj/item/card/id/syndicate_command/crew_id + name = "syndicate ID card" + id_type_name = "syndicate ID card" + desc = "An ID straight from the Syndicate." + registered_name = "Syndicate" + assignment = "Syndicate Operative" + icon_state = "syndie" + access = list(ACCESS_SYNDICATE, ACCESS_ROBOTICS) + uses_overlays = FALSE + +/obj/item/card/id/syndicate_command/captain_id + name = "syndicate captain ID card" + id_type_name = "syndicate captain ID card" + desc = "An ID straight from the Syndicate." + registered_name = "Syndicate" + assignment = "Syndicate Ship Captain" + icon_state = "syndie" + access = list(ACCESS_SYNDICATE, ACCESS_ROBOTICS) + uses_overlays = FALSE /obj/item/card/id/syndicate_command/crew_id name = "syndicate ID card" @@ -491,6 +537,7 @@ update_label() righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' registered_name = "Captain" assignment = "Captain" + registered_age = null /obj/item/card/id/captains_spare/Initialize() var/datum/job/captain/J = new/datum/job/captain @@ -513,6 +560,7 @@ update_label() registered_name = "Central Command" assignment = "Central Command" uses_overlays = FALSE + registered_age = null /obj/item/card/id/centcom/Initialize() access = get_all_centcom_access() @@ -526,6 +574,7 @@ update_label() registered_name = "Emergency Response Team Commander" assignment = "Emergency Response Team Commander" uses_overlays = FALSE + registered_age = null /obj/item/card/id/ert/Initialize() access = get_all_accesses()+get_ert_access("commander")-ACCESS_CHANGE_IDS @@ -619,6 +668,7 @@ update_label() uses_overlays = FALSE var/goal = 0 //How far from freedom? var/points = 0 + registered_age = null /obj/item/card/id/prisoner/attack_self(mob/user) to_chat(usr, "You have accumulated [points] out of the [goal] points you need for freedom.") @@ -663,11 +713,12 @@ update_label() access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MECH_MINING, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) /obj/item/card/id/away - name = "a perfectly generic identification card" + name = "\proper a perfectly generic identification card" desc = "A perfectly generic identification card. Looks like it could use some flavor." access = list(ACCESS_AWAY_GENERAL) icon_state = "retro" uses_overlays = FALSE + registered_age = null /obj/item/card/id/away/hotel name = "Staff ID" @@ -679,7 +730,7 @@ update_label() access = list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_MAINT, ACCESS_AWAY_SEC) /obj/item/card/id/away/old - name = "a perfectly generic identification card" + name = "\proper a perfectly generic identification card" desc = "A perfectly generic identification card. Looks like it could use some flavor." /obj/item/card/id/away/old/sec @@ -715,6 +766,7 @@ update_label() uses_overlays = FALSE var/department_ID = ACCOUNT_CIV var/department_name = ACCOUNT_CIV_NAME + registered_age = null /obj/item/card/id/departmental_budget/Initialize() . = ..() diff --git a/code/game/objects/items/chromosome.dm b/code/game/objects/items/chromosome.dm index d3a77080bf34..3acf3cfe5cb3 100644 --- a/code/game/objects/items/chromosome.dm +++ b/code/game/objects/items/chromosome.dm @@ -53,32 +53,32 @@ /obj/item/chromosome/stabilizer name = "stabilizer chromosome" - desc = "A chromosome that adjusts to the body to reduce genetic damage by 20%." + desc = "A chromosome that reduces mutation instability by 20%." icon_state = "stabilizer" stabilizer_coeff = 0.8 weight = 1 /obj/item/chromosome/synchronizer name = "synchronizer chromosome" - desc = "A chromosome that gives the mind more controle over the mutation, reducing knockback and downsides by 50%." + desc = "A chromosome that reduces mutation knockback and downsides by 50%." icon_state = "synchronizer" synchronizer_coeff = 0.5 /obj/item/chromosome/power name = "power chromosome" - desc = "A power chromosome for boosting certain mutation's power by 50%." + desc = "A chromosome that increases mutation power by 50%." icon_state = "power" power_coeff = 1.5 /obj/item/chromosome/energy name = "energetic chromosome" - desc = "A chromosome that reduces cooldown on action based mutations by 50%." + desc = "A chromosome that reduces action based mutation cooldowns by by 50%." icon_state = "energy" energy_coeff = 0.5 /obj/item/chromosome/reinforcer name = "reinforcement chromosome" - desc = "Renders the mutation immune to mutadone." + desc = "A chromosome that renders mutations immune to mutadone." icon_state = "reinforcer" weight = 3 diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index 5b477ac582fc..934009e181bf 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -251,7 +251,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM if (smoke_all) to_smoke = reagents.total_volume/((smoketime * 2) / (dragtime / 10)) - reagents.reaction(C, INGEST, fraction) + reagents.expose(C, INGEST, fraction) var/obj/item/organ/lungs/L = C.getorganslot(ORGAN_SLOT_LUNGS) if(L && !(L.organ_flags & ORGAN_SYNTHETIC)) C.adjustOrganLoss(ORGAN_SLOT_LUNGS, lung_harm) @@ -901,12 +901,12 @@ CIGARETTE PACKETS ARE IN FANCY.DM var/mob/living/carbon/C = loc if (src == C.wear_mask) // if it's in the human/monkey mouth, transfer reagents to the mob var/fraction = min(REAGENTS_METABOLISM/reagents.total_volume, 1) //this will react instantly, making them a little more dangerous than cigarettes - reagents.reaction(C, INGEST, fraction) + reagents.expose(C, INGEST, fraction) if(!reagents.trans_to(C, REAGENTS_METABOLISM)) reagents.remove_any(REAGENTS_METABOLISM) if(reagents.get_reagent_amount(/datum/reagent/fuel)) //HOT STUFF - C.fire_stacks = 2 + C.adjust_fire_stacks(2) C.IgniteMob() if(reagents.get_reagent_amount(/datum/reagent/toxin/plasma)) // the plasma explodes when exposed to fire diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm index 40721defdb4f..9d072a8ec78c 100644 --- a/code/game/objects/items/circuitboards/computer_circuitboards.dm +++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm @@ -334,20 +334,6 @@ name = "R&D Console Production Only (Computer Board)" build_path = /obj/machinery/computer/rdconsole/production - -/obj/item/circuitboard/computer/rdconsole/attackby(obj/item/I, mob/user, params) - if(I.tool_behaviour == TOOL_SCREWDRIVER) - if(build_path == /obj/machinery/computer/rdconsole/core) - name = "R&D Console - Robotics (Computer Board)" - build_path = /obj/machinery/computer/rdconsole/robotics - to_chat(user, "Access protocols successfully updated.") - else - name = "R&D Console (Computer Board)" - build_path = /obj/machinery/computer/rdconsole/core - to_chat(user, "Defaulting access protocols.") - else - return ..() - /obj/item/circuitboard/computer/rdservercontrol name = "R&D Server Control (Computer Board)" icon_state = "science" diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index a13fcb965645..f9b351faadb6 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -1184,6 +1184,7 @@ WaspStation End */ req_components = list( /obj/item/stock_parts/manipulator = 2, /obj/item/stock_parts/matter_bin = 2) + needs_anchored = FALSE /obj/item/circuitboard/machine/abductor name = "alien board (Report This)" diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm index 14b8545843a9..b12cbd9995f9 100644 --- a/code/game/objects/items/control_wand.dm +++ b/code/game/objects/items/control_wand.dm @@ -93,7 +93,7 @@ icon_state = "gangtool-blue" region_access = 3 -/obj/item/door_remote/civillian +/obj/item/door_remote/civilian name = "civilian door remote" icon_state = "gangtool-white" region_access = 1 diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index fe97b402b55a..547c3099b3ad 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -88,6 +88,11 @@ refill() +/obj/item/toy/crayon/examine(mob/user) + . = ..() + if(can_change_colour) + . += "Ctrl-click [src] while it's on your person to quickly recolour it." + /obj/item/toy/crayon/proc/refill() if(charges == -1) charges_left = 100 @@ -144,7 +149,7 @@ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "crayon", name, 600, 600, + ui = new(user, src, ui_key, "Crayon", name, 600, 600, master_ui, state) ui.open() @@ -155,6 +160,12 @@ to_chat(user, "The cap on [src] is now [is_capped ? "on" : "off"].") update_icon() +/obj/item/toy/crayon/CtrlClick(mob/user) + if(can_change_colour && !isturf(loc) && user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + select_colour(user) + else + return ..() + /obj/item/toy/crayon/proc/staticDrawables() . = list() @@ -232,14 +243,7 @@ else paint_mode = PAINT_NORMAL if("select_colour") - if(can_change_colour) - var/chosen_colour = input(usr,"","Choose Color",paint_color) as color|null - - if (!isnull(chosen_colour)) - paint_color = chosen_colour - . = TRUE - else - . = FALSE + . = can_change_colour && select_colour(usr) if("enter_text") var/txt = stripped_input(usr,"Choose what to write.", "Scribbles",default = text_buffer) @@ -249,6 +253,13 @@ drawtype = "a" update_icon() +/obj/item/toy/crayon/proc/select_colour(mob/user) + var/chosen_colour = input(user, "", "Choose Color", paint_color) as color|null + if (!isnull(chosen_colour) && user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + paint_color = chosen_colour + return TRUE + return FALSE + /obj/item/toy/crayon/proc/crayon_text_strip(text) var/static/regex/crayon_r = new /regex(@"[^\w!?,.=%#&+\/\-]") return replacetext(lowertext(text), crayon_r, "") @@ -414,7 +425,7 @@ if(affected_turfs.len) fraction /= affected_turfs.len for(var/t in affected_turfs) - reagents.reaction(t, TOUCH, fraction * volume_multiplier) + reagents.expose(t, TOUCH, fraction * volume_multiplier) reagents.trans_to(t, ., volume_multiplier, transfered_by = user) check_empty(user) @@ -435,7 +446,7 @@ if(check_empty(user)) //Prevents divsion by zero return var/fraction = min(eaten / reagents.total_volume, 1) - reagents.reaction(M, INGEST, fraction * volume_multiplier) + reagents.expose(M, INGEST, fraction * volume_multiplier) reagents.trans_to(M, eaten, volume_multiplier, transfered_by = user) // check_empty() is called during afterattack else @@ -658,7 +669,7 @@ H.update_body() var/used = use_charges(user, 10, FALSE) var/fraction = min(1, used / reagents.maximum_volume) - reagents.reaction(user, VAPOR, fraction * volume_multiplier) + reagents.expose(user, VAPOR, fraction * volume_multiplier) reagents.trans_to(user, used, volume_multiplier, transfered_by = user) return (OXYLOSS) @@ -714,7 +725,7 @@ . = use_charges(user, 10, FALSE) var/fraction = min(1, . / reagents.maximum_volume) - reagents.reaction(C, VAPOR, fraction * volume_multiplier) + reagents.expose(C, VAPOR, fraction * volume_multiplier) return @@ -734,7 +745,7 @@ . = use_charges(user, 2) var/fraction = min(1, . / reagents.maximum_volume) - reagents.reaction(target, TOUCH, fraction * volume_multiplier) + reagents.expose(target, TOUCH, fraction * volume_multiplier) reagents.trans_to(target, ., volume_multiplier, transfered_by = user) if(pre_noise || post_noise) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 06b6fd8ea000..68c3d4a9a2e8 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -1063,9 +1063,11 @@ GLOBAL_LIST_EMPTY(PDAs) for(var/atom/A in src) A.emp_act(severity) if (!(. & EMP_PROTECT_SELF)) - emped += 1 - spawn(200 * severity) - emped -= 1 + emped++ + addtimer(CALLBACK(src, .proc/emp_end), 200 * severity) + +/obj/item/pda/proc/emp_end() + emped-- /proc/get_viewable_pdas(sort_by_job = FALSE) . = list() diff --git a/code/game/objects/items/devices/PDA/PDA_types.dm b/code/game/objects/items/devices/PDA/PDA_types.dm index 3ed864edfa14..8756ab9f54bf 100644 --- a/code/game/objects/items/devices/PDA/PDA_types.dm +++ b/code/game/objects/items/devices/PDA/PDA_types.dm @@ -10,7 +10,7 @@ /obj/item/pda/clown/ComponentInitialize() . = ..() - AddComponent(/datum/component/slippery, 5SECONDS, NO_SLIP_WHEN_WALKING, CALLBACK(src, .proc/AfterSlip), 5SECONDS) + AddComponent(/datum/component/slippery/clowning, 120, NO_SLIP_WHEN_WALKING, CALLBACK(src, .proc/AfterSlip)) /obj/item/pda/clown/proc/AfterSlip(mob/living/carbon/human/M) if (istype(M) && (M.real_name != owner)) diff --git a/code/game/objects/items/devices/PDA/virus_cart.dm b/code/game/objects/items/devices/PDA/virus_cart.dm index eb1598465200..7b528a3e993d 100644 --- a/code/game/objects/items/devices/PDA/virus_cart.dm +++ b/code/game/objects/items/devices/PDA/virus_cart.dm @@ -95,6 +95,18 @@ hidden_uplink.hidden_crystals += hidden_uplink.telecrystals //Temporarially hide the PDA's crystals, so you can't steal telecrystals. hidden_uplink.telecrystals = telecrystals telecrystals = 0 + hidden_uplink.locked = FALSE hidden_uplink.active = TRUE else to_chat(U, "PDA not found.") + +/obj/item/cartridge/virus/frame/attackby(obj/item/I, mob/user, params) + . = ..() + if(istype(I, /obj/item/stack/telecrystal)) + if(!charges) + to_chat(user, "[src] is out of charges, it's refusing to accept [I].") + return + var/obj/item/stack/telecrystal/telecrystalStack = I + telecrystals += telecrystalStack.amount + to_chat(user, "You slot [telecrystalStack] into [src]. The next time it's used, it will also give telecrystals.") + telecrystalStack.use(telecrystalStack.amount) diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index 5500bcd1ab1c..ee9730d8601e 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -58,7 +58,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "intellicard", name, 500, 500, master_ui, state) + ui = new(user, src, ui_key, "Intellicard", name, 500, 500, master_ui, state) ui.open() /obj/item/aicard/ui_data() diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm index e6920d17fffb..e6d95129350b 100644 --- a/code/game/objects/items/devices/geiger_counter.dm +++ b/code/game/objects/items/devices/geiger_counter.dm @@ -143,14 +143,7 @@ return TRUE /obj/item/geiger_counter/proc/scan(atom/A, mob/user) - var/rad_strength = 0 - for(var/i in get_rad_contents(A)) // Yes it's intentional that you can't detect radioactive things under rad protection. Gives traitors a way to hide their glowing green rocks. - var/atom/thing = i - if(!thing) - continue - var/datum/component/radioactive/radiation = thing.GetComponent(/datum/component/radioactive) - if(radiation) - rad_strength += radiation.strength + var/rad_strength = get_rad_contamination(A) if(isliving(A)) var/mob/living/M = A diff --git a/code/game/objects/items/devices/instruments.dm b/code/game/objects/items/devices/instruments.dm index a188e804228d..985a372d99a7 100644 --- a/code/game/objects/items/devices/instruments.dm +++ b/code/game/objects/items/devices/instruments.dm @@ -136,7 +136,7 @@ /obj/item/instrument/guitar name = "guitar" - desc = "It's made of wood and has bronze strings." + desc = "It's made out of wood and has bronze strings." icon_state = "guitar" item_state = "guitar" instrumentExt = "ogg" diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 5e8db504704e..fc14fcb3a699 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -14,7 +14,7 @@ var/energy = 10 var/max_energy = 10 var/effectchance = 30 - var/recharging = 0 + var/recharging = FALSE var/recharge_locked = FALSE var/obj/item/stock_parts/micro_laser/diode //used for upgrading! @@ -177,7 +177,7 @@ energy -= 1 if(energy <= max_energy) if(!recharging) - recharging = 1 + recharging = TRUE START_PROCESSING(SSobj, src) if(energy <= 0) to_chat(user, "[src]'s battery is overused, it needs time to recharge!") @@ -187,10 +187,13 @@ icon_state = "pointer" /obj/item/laser_pointer/process() + if(!diode) + recharging = FALSE + return PROCESS_KILL if(prob(20 + diode.rating*20 - recharge_locked*2)) //t1 is 20, 2 40 energy += 1 if(energy >= max_energy) energy = max_energy - recharging = 0 + recharging = FALSE recharge_locked = FALSE - ..() + return ..() diff --git a/code/game/objects/items/devices/ocd.dm b/code/game/objects/items/devices/ocd.dm new file mode 100644 index 000000000000..52bb7352c886 --- /dev/null +++ b/code/game/objects/items/devices/ocd.dm @@ -0,0 +1,11 @@ +/obj/item/devices/ocd_device + name = "Occupational Corruption Device" + desc = "When you need to make the lives of new-hires that much more confusing, think OCD." + icon = 'icons/obj/device.dmi' + icon_state = "gangtool-white" + +/obj/item/devices/ocd_device/attack_self(mob/user) + var/datum/round_event/bureaucratic_error/event = new() + event.start() + deadchat_broadcast(" An OCD has been activated! ") + qdel(src) diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index de4623298cb8..f574aaea16bf 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -89,7 +89,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "electropack", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Electropack", name, ui_x, ui_y, master_ui, state) ui.open() /obj/item/electropack/ui_data(mob/user) diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index f5225c054f86..08ce36bceda6 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -6,49 +6,44 @@ anchored = TRUE w_class = WEIGHT_CLASS_BULKY canhear_range = 2 - var/number = 0 - var/anyai = 1 - var/mob/living/silicon/ai/ai = list() - var/last_tick //used to delay the powercheck dog_fashion = null - var/unfastened = FALSE + unscrewed = FALSE /obj/item/radio/intercom/unscrewed - unfastened = TRUE + unscrewed = TRUE /obj/item/radio/intercom/Initialize(mapload, ndir, building) . = ..() if(building) setDir(ndir) - START_PROCESSING(SSobj, src) - -/obj/item/radio/intercom/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() + var/area/current_area = get_area(src) + if(!current_area) + return + RegisterSignal(current_area, COMSIG_AREA_POWER_CHANGE, .proc/AreaPowerCheck) /obj/item/radio/intercom/examine(mob/user) . = ..() . += "Use [MODE_TOKEN_INTERCOM] when nearby to speak into it." - if(!unfastened) + if(!unscrewed) . += "It's screwed and secured to the wall." else . += "It's unscrewed from the wall, and can be detached." /obj/item/radio/intercom/attackby(obj/item/I, mob/living/user, params) if(I.tool_behaviour == TOOL_SCREWDRIVER) - if(unfastened) + if(unscrewed) user.visible_message("[user] starts tightening [src]'s screws...", "You start screwing in [src]...") if(I.use_tool(src, user, 30, volume=50)) user.visible_message("[user] tightens [src]'s screws!", "You tighten [src]'s screws.") - unfastened = FALSE + unscrewed = FALSE else user.visible_message("[user] starts loosening [src]'s screws...", "You start unscrewing [src]...") if(I.use_tool(src, user, 40, volume=50)) user.visible_message("[user] loosens [src]'s screws!", "You unscrew [src], loosening it from the wall.") - unfastened = TRUE + unscrewed = TRUE return else if(I.tool_behaviour == TOOL_WRENCH) - if(!unfastened) + if(!unscrewed) to_chat(user, "You need to unscrew [src] from the wall first!") return user.visible_message("[user] starts unsecuring [src]...", "You start unsecuring [src]...") @@ -83,46 +78,53 @@ var/turf/position = get_turf(src) if(isnull(position) || !(position.z in level)) return FALSE - if(!src.listening) + if(!listening) return FALSE if(freq == FREQ_SYNDICATE) - if(!(src.syndie)) + if(!(syndie)) return FALSE//Prevents broadcast of messages over devices lacking the encryption return TRUE /obj/item/radio/intercom/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, message_mode) - if (message_mode == MODE_INTERCOM) + if(message_mode == MODE_INTERCOM) return // Avoid hearing the same thing twice - if(!anyai && !(speaker in ai)) - return - ..() + return ..() -/obj/item/radio/intercom/process() - if(((world.timeofday - last_tick) > 30) || ((world.timeofday - last_tick) < 0)) - last_tick = world.timeofday +/obj/item/radio/intercom/emp_act(severity) + . = ..() // Parent call here will set `on` to FALSE. + update_icon() - var/area/A = get_area(src) - if(!A || emped) - on = FALSE - else - on = A.powered(EQUIP) // set "on" to the power status +/obj/item/radio/intercom/end_emp_effect(curremp) + . = ..() + AreaPowerCheck() // Make sure the area/local APC is powered first before we actually turn back on. - if(!on) - icon_state = "intercom-p" - else - icon_state = initial(icon_state) +/obj/item/radio/intercom/update_icon() + . = ..() + if(on) + icon_state = initial(icon_state) + else + icon_state = "intercom-p" + +/** + * Proc called whenever the intercom's area loses or gains power. Responsible for setting the `on` variable and calling `update_icon()`. + * + * Normally called after the intercom's area recieves the `COMSIG_AREA_POWER_CHANGE` signal, but it can also be called directly. + * Arguments: + * * source - the area that just had a power change. + */ +/obj/item/radio/intercom/proc/AreaPowerCheck(datum/source) + var/area/current_area = get_area(src) + if(!current_area) + on = FALSE + else + on = current_area.powered(AREA_USAGE_EQUIP) // set "on" to the equipment power status of our area. + update_icon() /obj/item/radio/intercom/add_blood_DNA(list/blood_dna) return FALSE -/obj/item/radio/intercom/end_emp_effect(curremp) - . = ..() - if(!.) - return - on = FALSE - //Created through the autolathe or through deconstructing intercoms. Can be applied to wall to make a new intercom on it! /obj/item/wallframe/intercom name = "intercom frame" diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 14801f6f496b..ea218cd5146a 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -118,7 +118,7 @@ ui_height += 6 + channels.len * 21 else ui_height += 24 - ui = new(user, src, ui_key, "radio", name, ui_width, ui_height, master_ui, state) + ui = new(user, src, ui_key, "Radio", name, ui_width, ui_height, master_ui, state) ui.open() /obj/item/radio/ui_data(mob/user) diff --git a/code/game/objects/items/devices/spyglasses.dm b/code/game/objects/items/devices/spyglasses.dm index a69f22c62c3a..563e4d76d021 100644 --- a/code/game/objects/items/devices/spyglasses.dm +++ b/code/game/objects/items/devices/spyglasses.dm @@ -46,7 +46,7 @@ desc = "an advanced peice of espionage equipment in the shape of a pocket protector. it has a built in 360 degree camera for all your nefarious needs. Microphone not included." var/obj/item/clothing/glasses/regular/spy/linked_glasses - var/obj/screen/cam_screen + var/obj/screen/map_view/cam_screen var/obj/screen/plane_master/lighting/cam_plane_master // Ranges higher than one can be used to see through walls. var/cam_range = 1 diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm index 1be9024bcb27..c5b09edaf1e4 100644 --- a/code/game/objects/items/devices/traitordevices.dm +++ b/code/game/objects/items/devices/traitordevices.dm @@ -88,20 +88,19 @@ effective or pretty fucking useless. var/cooldown = get_cooldown() used = TRUE icon_state = "health1" - handle_cooldown(cooldown) // splits off to handle the cooldown while handling wavelength + addtimer(VARSET_CALLBACK(src, used, FALSE), cooldown) + addtimer(VARSET_CALLBACK(src, icon_state, "health"), cooldown) to_chat(user, "Successfully irradiated [M].") - spawn((wavelength+(intensity*4))*5) - if(M) - if(intensity >= 5) - M.apply_effect(round(intensity/0.075), EFFECT_UNCONSCIOUS) - M.rad_act(intensity*10) + addtimer(CALLBACK(src, .proc/radiation_aftereffect, M), (wavelength+(intensity*4))*5) else to_chat(user, "The radioactive microlaser is still recharging.") -/obj/item/healthanalyzer/rad_laser/proc/handle_cooldown(cooldown) - spawn(cooldown) - used = FALSE - icon_state = "health" +/obj/item/healthanalyzer/rad_laser/proc/radiation_aftereffect(mob/living/M) + if(QDELETED(M)) + return + if(intensity >= 5) + M.apply_effect(round(intensity/0.075), EFFECT_UNCONSCIOUS) + M.rad_act(intensity*10) /obj/item/healthanalyzer/rad_laser/proc/get_cooldown() return round(max(10, (stealth*30 + intensity*5 - wavelength/4))) @@ -116,7 +115,7 @@ effective or pretty fucking useless. datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "radioactive_microlaser", "Radioactive Microlaser", ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "RadioactiveMicrolaser", "Radioactive Microlaser", ui_x, ui_y, master_ui, state) ui.open() /obj/item/healthanalyzer/rad_laser/ui_data(mob/user) diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 6258d0d41c63..a800a9e49fbd 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -197,7 +197,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "transfer_valve", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "TransferValve", name, ui_x, ui_y, master_ui, state) ui.open() /obj/item/transfer_valve/ui_data(mob/user) diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm index 2032a93f58d2..a93f24a18c69 100644 --- a/code/game/objects/items/dna_injector.dm +++ b/code/game/objects/items/dna_injector.dm @@ -254,14 +254,6 @@ name = "\improper DNA injector (Mute)" add_mutations = list(MUT_MUTE) -/obj/item/dnainjector/antismile - name = "\improper DNA injector (Anti-Smile)" - remove_mutations = list(SMILE) - -/obj/item/dnainjector/smilemut - name = "\improper DNA injector (Smile)" - add_mutations = list(SMILE) - /obj/item/dnainjector/unintelligiblemut name = "\improper DNA injector (Unintelligible)" add_mutations = list(UNINTELLIGIBLE) diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm index 210c08463079..775fdd044683 100644 --- a/code/game/objects/items/eightball.dm +++ b/code/game/objects/items/eightball.dm @@ -196,7 +196,7 @@ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "eightball", name, 400, 600, master_ui, state) + ui = new(user, src, ui_key, "EightBallVote", name, 400, 600, master_ui, state) ui.open() /obj/item/toy/eightball/haunted/ui_data(mob/user) diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm index 28276168b308..2b4e1111e425 100644 --- a/code/game/objects/items/extinguisher.dm +++ b/code/game/objects/items/extinguisher.dm @@ -192,9 +192,9 @@ step_towards(W,my_target) if(!W.reagents) continue - W.reagents.reaction(get_turf(W)) + W.reagents.expose(get_turf(W)) for(var/A in get_turf(W)) - W.reagents.reaction(A) + W.reagents.expose(A) if(W.loc == my_target) particles -= W if(repetition < power) diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm index bf1a0f704695..bec7b165019b 100644 --- a/code/game/objects/items/granters.dm +++ b/code/game/objects/items/granters.dm @@ -200,12 +200,13 @@ user.blind_eyes(10) /obj/item/book/granter/spell/mindswap - spell = /obj/effect/proc_holder/spell/targeted/mind_transfer + spell = /obj/effect/proc_holder/spell/pointed/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 //Used in used book recoils to store an identity for mindswaps remarks = list("If you mindswap from a mouse, they will be helpless when you recover...", "Wait, where am I...?", "This book is giving me a horrible headache...", "This page is blank, but I feel words popping into my head...", "GYNU... GYRO... Ugh...", "The voices in my head need to stop, I'm trying to read here...", "I don't think anyone will be happy when I cast this spell...") + /// Mob used in book recoils to store an identity for mindswaps + var/mob/living/stored_swap /obj/item/book/granter/spell/mindswap/onlearned() spellname = pick("fireball","smoke","blind","forcewall","knock","barnyard","charge") @@ -224,8 +225,8 @@ 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 - if(swapper.cast(list(stored_swap), user, TRUE, TRUE)) + var/obj/effect/proc_holder/spell/pointed/mind_transfer/swapper = new + if(swapper.cast(list(stored_swap), user, TRUE)) to_chat(user,"You're suddenly somewhere else... and someone else?!") to_chat(stored_swap,"Suddenly you're staring at [src] again... where are you, who are you?!") else @@ -259,7 +260,7 @@ user.Paralyze(40) /obj/item/book/granter/spell/barnyard - spell = /obj/effect/proc_holder/spell/targeted/barnyardcurse + spell = /obj/effect/proc_holder/spell/pointed/barnyardcurse spellname = "barnyard" icon_state ="bookhorses" desc = "This book is more horse than your mind has room for." diff --git a/code/game/objects/items/grenades/antigravity.dm b/code/game/objects/items/grenades/antigravity.dm index a4bc207be033..313b91acd71b 100644 --- a/code/game/objects/items/grenades/antigravity.dm +++ b/code/game/objects/items/grenades/antigravity.dm @@ -8,10 +8,11 @@ var/duration = 300 /obj/item/grenade/antigravity/prime() + . = ..() update_mob() for(var/turf/T in view(range,src)) T.AddElement(/datum/element/forced_gravity, forced_value) addtimer(CALLBACK(T, /datum/.proc/_RemoveElement, list(forced_value)), duration) - qdel(src) + resolve() diff --git a/code/game/objects/items/grenades/chem_grenade.dm b/code/game/objects/items/grenades/chem_grenade.dm index ae5fc2dc426b..c39d9783710e 100644 --- a/code/game/objects/items/grenades/chem_grenade.dm +++ b/code/game/objects/items/grenades/chem_grenade.dm @@ -178,6 +178,7 @@ if(stage != GRENADE_READY) return + . = ..() var/list/datum/reagents/reactants = list() for(var/obj/item/reagent_containers/glass/G in beakers) reactants += G.reagents diff --git a/code/game/objects/items/grenades/clusterbuster.dm b/code/game/objects/items/grenades/clusterbuster.dm index ed61b41c397d..6e4687d72c32 100644 --- a/code/game/objects/items/grenades/clusterbuster.dm +++ b/code/game/objects/items/grenades/clusterbuster.dm @@ -15,6 +15,7 @@ var/segment_chance = 35 /obj/item/grenade/clusterbuster/prime() + . = ..() update_mob() var/numspawned = rand(min_spawned,max_spawned) var/again = 0 @@ -29,13 +30,13 @@ new payload_spawner(drop_location(), payload, numspawned)//Launches payload playsound(src, prime_sound, 75, TRUE, -3) - qdel(src) + resolve() ////////////////////// //Clusterbang segment ////////////////////// /obj/item/grenade/clusterbuster/segment - desc = "A smaller segment of a clusterbang. Better run." + desc = "A smaller segment of a clusterbang. Better run!" name = "clusterbang segment" icon = 'icons/obj/grenade.dmi' icon_state = "clusterbang_segment" @@ -62,7 +63,7 @@ /obj/item/grenade/clusterbuster/segment/prime() new payload_spawner(drop_location(), payload, rand(min_spawned,max_spawned)) playsound(src, prime_sound, 75, TRUE, -3) - qdel(src) + resolve() ////////////////////////////////// //The payload spawner effect diff --git a/code/game/objects/items/grenades/emgrenade.dm b/code/game/objects/items/grenades/emgrenade.dm index 4c8ea1ce30d0..afb0ab48c574 100644 --- a/code/game/objects/items/grenades/emgrenade.dm +++ b/code/game/objects/items/grenades/emgrenade.dm @@ -5,9 +5,10 @@ item_state = "emp" /obj/item/grenade/empgrenade/prime() + . = ..() update_mob() for(var/obj/machinery/light/L in range(10, src)) L.on = 1 L.break_light_tube() empulse(src, 4, 10) - qdel(src) + resolve() diff --git a/code/game/objects/items/grenades/festive.dm b/code/game/objects/items/grenades/festive.dm index a7ae03bd125d..af68f7566a3e 100644 --- a/code/game/objects/items/grenades/festive.dm +++ b/code/game/objects/items/grenades/festive.dm @@ -109,9 +109,10 @@ obj/item/grenade/firecracker/preprime(mob/user, delayoverride, msg = TRUE, volum addtimer(CALLBACK(src, .proc/prime), isnull(delayoverride)? det_time : delayoverride) /obj/item/grenade/firecracker/prime() + . = ..() update_mob() var/explosion_loc = get_turf(src) - qdel(src) + resolve() explosion(explosion_loc,-1,-1,2) diff --git a/code/game/objects/items/grenades/flashbang.dm b/code/game/objects/items/grenades/flashbang.dm index 0be2f717907f..cbce3cff51f1 100644 --- a/code/game/objects/items/grenades/flashbang.dm +++ b/code/game/objects/items/grenades/flashbang.dm @@ -7,6 +7,7 @@ var/flashbang_range = 7 //how many tiles away the mob will be stunned. /obj/item/grenade/flashbang/prime() + . = ..() update_mob() var/flashbang_turf = get_turf(src) if(!flashbang_turf) @@ -16,7 +17,7 @@ new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_WHITE, (flashbang_range + 2), 4, 2) for(var/mob/living/M in get_hearers_in_view(flashbang_range, flashbang_turf)) bang(get_turf(M), M) - qdel(src) + resolve() /obj/item/grenade/flashbang/proc/bang(turf/T , mob/living/M) if(M.stat == DEAD) //They're dead! @@ -39,3 +40,91 @@ M.Paralyze(5) M.Knockdown(30) M.soundbang_act(1, max(200/max(1,distance), 60), rand(0, 5)) + +/obj/item/grenade/stingbang + name = "stingbang" + icon_state = "timeg" + item_state = "flashbang" + lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' + var/flashbang_range = 1 //how many tiles away the mob will be stunned. + shrapnel_type = /obj/projectile/bullet/pellet/stingball + shrapnel_radius = 5 + custom_premium_price = 700 // mostly gotten through cargo, but throw in one for the sec vendor ;) + +/obj/item/grenade/stingbang/mega + name = "mega stingbang" + shrapnel_type = /obj/projectile/bullet/pellet/stingball/mega + shrapnel_radius = 12 + +/obj/item/grenade/stingbang/prime() + if(iscarbon(loc)) + var/mob/living/carbon/C = loc + var/obj/item/bodypart/B = C.get_holding_bodypart_of_item(src) + if(B) + C.visible_message("[src] goes off in [C]'s hand, blowing [C.p_their()] [B.name] to bloody shreds!", "[src] goes off in your hand, blowing your [B.name] to bloody shreds!") + B.dismember() + + . = ..() + update_mob() + var/flashbang_turf = get_turf(src) + if(!flashbang_turf) + return + do_sparks(rand(5, 9), FALSE, src) + playsound(flashbang_turf, 'sound/weapons/flashbang.ogg', 50, TRUE, 8, 0.9) + new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_WHITE, (flashbang_range + 2), 2, 1) + for(var/mob/living/M in get_hearers_in_view(flashbang_range, flashbang_turf)) + pop(get_turf(M), M) + resolve() + +/obj/item/grenade/stingbang/proc/pop(turf/T , mob/living/M) + if(M.stat == DEAD) //They're dead! + return + M.show_message("POP", MSG_AUDIBLE) + var/distance = max(0,get_dist(get_turf(src),T)) +//Flash + if(M.flash_act(affect_silicon = 1)) + M.Paralyze(max(10/max(1,distance), 5)) + M.Knockdown(max(100/max(1,distance), 60)) + +//Bang + if(!distance || loc == M || loc == M.loc) //Stop allahu akbarring rooms with this. + M.Paralyze(20) + M.Knockdown(200) + M.soundbang_act(1, 200, 10, 15) + if(M.apply_damages(10, 10)) + to_chat(M, "The blast from \the [src] bruises and burns you!") + + // only checking if they're on top of the tile, cause being one tile over will be its own punishment + +// Grenade that releases more shrapnel the more times you use it in hand between priming and detonation (sorta like the 9bang from MW3), for admin goofs +/obj/item/grenade/primer + name = "rotfrag grenade" + desc = "A grenade that generates more shrapnel the more you rotate it in your hand after pulling the pin. This one releases shrapnel shards." + icon_state = "timeg" + item_state = "flashbang" + lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' + var/rots_per_mag = 3 /// how many times we need to "rotate" the charge in hand per extra tile of magnitude + shrapnel_type = /obj/projectile/bullet/shrapnel + var/rots = 1 /// how many times we've "rotated" the charge + +/obj/item/grenade/primer/attack_self(mob/user) + . = ..() + if(active) + user.playsound_local(user, 'sound/misc/box_deploy.ogg', 50, TRUE) + rots++ + user.changeNext_move(CLICK_CD_RAPID) + +/obj/item/grenade/primer/prime() + shrapnel_radius = round(rots / rots_per_mag) + . = ..() + resolve() + +/obj/item/grenade/primer/stingbang + name = "rotsting" + desc = "A grenade that generates more shrapnel the more you rotate it in your hand after pulling the pin. This one releases stingballs." + lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' + rots_per_mag = 2 + shrapnel_type = /obj/projectile/bullet/pellet/stingball diff --git a/code/game/objects/items/grenades/ghettobomb.dm b/code/game/objects/items/grenades/ghettobomb.dm index e7e134606680..7eb71d05db78 100644 --- a/code/game/objects/items/grenades/ghettobomb.dm +++ b/code/game/objects/items/grenades/ghettobomb.dm @@ -64,9 +64,10 @@ preprime(user, null, FALSE) /obj/item/grenade/iedcasing/prime() //Blowing that can up + . = ..() update_mob() explosion(src.loc,-1,-1,2, flame_range = 4) // small explosion, plus a very large fireball. - qdel(src) + resolve() /obj/item/grenade/iedcasing/change_det_time() return //always be random. diff --git a/code/game/objects/items/grenades/grenade.dm b/code/game/objects/items/grenades/grenade.dm index 4d4277aef9a3..641b0f656272 100644 --- a/code/game/objects/items/grenades/grenade.dm +++ b/code/game/objects/items/grenades/grenade.dm @@ -18,6 +18,22 @@ var/display_timer = 1 var/clumsy_check = GRENADE_CLUMSY_FUMBLE var/sticky = FALSE + // I moved the explosion vars and behavior to base grenades because we want all grenades to call [/obj/item/grenade/proc/prime] so we can send COMSIG_GRENADE_PRIME + ///how big of a devastation explosion radius on prime + var/ex_dev = 0 + ///how big of a heavy explosion radius on prime + var/ex_heavy = 0 + ///how big of a light explosion radius on prime + var/ex_light = 0 + ///how big of a flame explosion radius on prime + var/ex_flame = 0 + + // dealing with creating a [/datum/component/pellet_cloud] on prime + /// if set, will spew out projectiles of this type + var/shrapnel_type + /// the higher this number, the more projectiles are created as shrapnel + var/shrapnel_radius + var/shrapnel_initialized /obj/item/grenade/suicide_act(mob/living/carbon/user) user.visible_message("[user] primes [src], then eats it! It looks like [user.p_theyre()] trying to commit suicide!") @@ -80,12 +96,23 @@ add_fingerprint(user) if(msg) to_chat(user, "You prime [src]! [capitalize(DisplayTimeText(det_time))]!") + if(shrapnel_type && shrapnel_radius) + shrapnel_initialized = TRUE + AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_radius) playsound(src, 'sound/weapons/armbomb.ogg', volume, TRUE) active = TRUE icon_state = initial(icon_state) + "_active" + SEND_SIGNAL(src, COMSIG_GRENADE_ARMED, det_time, delayoverride) addtimer(CALLBACK(src, .proc/prime), isnull(delayoverride)? det_time : delayoverride) /obj/item/grenade/proc/prime() + if(shrapnel_type && shrapnel_radius && !shrapnel_initialized) // add a second check for adding the component in case whatever triggered the grenade went straight to prime (badminnery for example) + shrapnel_initialized = TRUE + AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_radius) + + SEND_SIGNAL(src, COMSIG_GRENADE_PRIME) + if(ex_dev || ex_heavy || ex_light || ex_flame) + explosion(loc, ex_dev, ex_heavy, ex_light, flame_range = ex_flame) /obj/item/grenade/proc/update_mob() if(ismob(loc)) @@ -143,3 +170,10 @@ . = ..() if(active) user.throw_item(target) + +/// Don't call qdel() directly on the grenade after it booms, call this instead so it can still resolve its pellet_cloud component if it has shrapnel, then the component will qdel it +/obj/item/grenade/proc/resolve() + if(shrapnel_type) + moveToNullspace() + else + qdel(src) diff --git a/code/game/objects/items/grenades/hypno.dm b/code/game/objects/items/grenades/hypno.dm index 5d7652bb303b..d9c954fb92d4 100644 --- a/code/game/objects/items/grenades/hypno.dm +++ b/code/game/objects/items/grenades/hypno.dm @@ -8,6 +8,7 @@ var/flashbang_range = 7 /obj/item/grenade/hypnotic/prime() + . = ..() update_mob() var/flashbang_turf = get_turf(src) if(!flashbang_turf) @@ -17,7 +18,7 @@ new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_PURPLE, (flashbang_range + 2), 4, 2) for(var/mob/living/M in get_hearers_in_view(flashbang_range, flashbang_turf)) bang(get_turf(M), M) - qdel(src) + resolve() /obj/item/grenade/hypnotic/proc/bang(turf/T, mob/living/M) if(M.stat == DEAD) //They're dead! diff --git a/code/game/objects/items/grenades/plastic.dm b/code/game/objects/items/grenades/plastic.dm index b0e3d45c9cb6..a1b169189d3f 100644 --- a/code/game/objects/items/grenades/plastic.dm +++ b/code/game/objects/items/grenades/plastic.dm @@ -44,6 +44,8 @@ /obj/item/grenade/c4/prime() if(QDELETED(src)) return + + . = ..() var/turf/location if(target) if(!QDELETED(target)) @@ -59,7 +61,7 @@ explosion(get_step(T, aim_dir), boom_sizes[1], boom_sizes[2], boom_sizes[3]) else explosion(location, boom_sizes[1], boom_sizes[2], boom_sizes[3]) - qdel(src) + resolve() //assembly stuff /obj/item/grenade/c4/receive_signal() @@ -102,7 +104,7 @@ I.throw_range = max(1, (I.throw_range - 3)) if(I.embedding) I.embedding["embed_chance"] = 0 - I.AddElement(/datum/element/embed, I.embedding) + I.updateEmbedding() else if(istype(AM, /mob/living)) plastic_overlay.layer = FLOAT_LAYER @@ -147,7 +149,7 @@ shout_syndicate_crap(user) explosion(user,0,2,0) //Cheap explosion imitation because putting prime() here causes runtimes user.gib(1, 1) - qdel(src) + resolve() // X4 is an upgraded directional variant of c4 which is relatively safe to be standing next to. And much less safe to be standing on the other side of. // C4 is intended to be used for infiltration, and destroying tech. X4 is intended to be used for heavy breaching and tight spaces. diff --git a/code/game/objects/items/grenades/smokebomb.dm b/code/game/objects/items/grenades/smokebomb.dm index 3bcfee09384a..590312772efc 100644 --- a/code/game/objects/items/grenades/smokebomb.dm +++ b/code/game/objects/items/grenades/smokebomb.dm @@ -20,6 +20,7 @@ ///Here we generate some smoke and also damage blobs??? for some reason. Honestly not sure why we do that. /obj/item/grenade/smokebomb/prime() + . = ..() update_mob() playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3) var/datum/effect_system/smoke_spread/bad/smoke = new @@ -29,4 +30,4 @@ for(var/obj/structure/blob/B in view(8,src)) var/damage = round(30/(get_dist(B,src)+1)) B.take_damage(damage, BURN, "melee", 0) - qdel(src) + resolve() diff --git a/code/game/objects/items/grenades/spawnergrenade.dm b/code/game/objects/items/grenades/spawnergrenade.dm index dcefb5c5e188..dfa0b6b63d18 100644 --- a/code/game/objects/items/grenades/spawnergrenade.dm +++ b/code/game/objects/items/grenades/spawnergrenade.dm @@ -1,5 +1,5 @@ /obj/item/grenade/spawnergrenade - desc = "It will unleash an unspecified anomaly into the vicinity." + desc = "It will unleash an unspecified anomaly in the surrounding vicinity." name = "delivery grenade" icon = 'icons/obj/grenade.dmi' icon_state = "delivery" @@ -8,6 +8,7 @@ var/deliveryamt = 1 // amount of type to deliver /obj/item/grenade/spawnergrenade/prime() // Prime now just handles the two loops that query for people in lockers and people who can see it. + . = ..() update_mob() if(spawner_type && deliveryamt) // Make a quick flash @@ -20,7 +21,7 @@ var/list/spawned = spawn_and_random_walk(spawner_type, T, deliveryamt, walk_chance=50, admin_spawn=((flags_1 & ADMIN_SPAWNED_1) ? TRUE : FALSE)) afterspawn(spawned) - qdel(src) + resolve() /obj/item/grenade/spawnergrenade/proc/afterspawn(list/mob/spawned) return diff --git a/code/game/objects/items/grenades/syndieminibomb.dm b/code/game/objects/items/grenades/syndieminibomb.dm index f7c683167414..446b27d5a404 100644 --- a/code/game/objects/items/grenades/syndieminibomb.dm +++ b/code/game/objects/items/grenades/syndieminibomb.dm @@ -4,27 +4,44 @@ icon = 'icons/obj/grenade.dmi' icon_state = "syndicate" item_state = "flashbang" - + ex_dev = 1 + ex_heavy = 2 + ex_light = 4 + ex_flame = 2 /obj/item/grenade/syndieminibomb/prime() + . = ..() update_mob() - explosion(src.loc,1,2,4,flame_range = 2) - qdel(src) + resolve() /obj/item/grenade/syndieminibomb/concussion name = "HE Grenade" desc = "A compact shrapnel grenade meant to devastate nearby organisms and cause some damage in the process. Pull pin and throw opposite direction." icon_state = "concussion" + ex_heavy = 2 + ex_light = 3 + ex_flame = 3 -/obj/item/grenade/syndieminibomb/concussion/prime() - update_mob() - explosion(src.loc,0,2,3,flame_range = 3) - qdel(src) - -/obj/item/grenade/syndieminibomb/concussion/frag +/obj/item/grenade/frag name = "frag grenade" - desc = "Fire in the hole." + desc = "An anti-personnel fragmentation grenade, this weapon excels at killing soft targets by shredding them with metal shrapnel." icon_state = "frag" + shrapnel_type = /obj/projectile/bullet/shrapnel + shrapnel_radius = 4 + ex_heavy = 1 + ex_light = 3 + ex_flame = 4 + +/obj/item/grenade/frag/mega + name = "FRAG grenade" + desc = "An anti-everything fragmentation grenade, this weapon excels at killing anything any everything by shredding them with metal shrapnel." + shrapnel_type = /obj/projectile/bullet/shrapnel/mega + shrapnel_radius = 12 + +/obj/item/grenade/frag/prime() + . = ..() + update_mob() + resolve() /obj/item/grenade/gluon desc = "An advanced grenade that releases a harmful stream of gluons inducing radiation in those nearby. These gluon streams will also make victims feel exhausted, and induce shivering. This extreme coldness will also likely wet any nearby floors." @@ -37,6 +54,7 @@ var/stamina_damage = 30 /obj/item/grenade/gluon/prime() + . = ..() update_mob() playsound(loc, 'sound/effects/empulse.ogg', 50, TRUE) radiation_pulse(src, rad_damage) @@ -47,4 +65,4 @@ for(var/mob/living/carbon/L in T) L.adjustStaminaLoss(stamina_damage) L.adjust_bodytemperature(-230) - qdel(src) + resolve() diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm index 073bfaff862d..6b3c14dcf09d 100644 --- a/code/game/objects/items/handcuffs.dm +++ b/code/game/objects/items/handcuffs.dm @@ -336,7 +336,7 @@ gender = NEUTER var/knockdown = 0 -/obj/item/restraints/legcuffs/bola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, gentle = FALSE) +/obj/item/restraints/legcuffs/bola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, gentle = FALSE, quickstart = TRUE) if(!..()) return playsound(src.loc,'sound/weapons/bolathrow.ogg', 75, TRUE) diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 23f99f3b7e35..57780c60c7be 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -216,24 +216,30 @@ if(user.mind && (user.mind.holy_role) && !reskinned) reskin_holy_weapon(user) +/** + * reskin_holy_weapon: Shows a user a list of all available nullrod reskins and based on his choice replaces the nullrod with the reskinned version + * + * Arguments: + * * M The mob choosing a nullrod reskin + */ /obj/item/nullrod/proc/reskin_holy_weapon(mob/M) if(GLOB.holy_weapon_type) return - var/obj/item/nullrod/holy_weapon - var/list/holy_weapons_list = typesof(/obj/item/nullrod) var/list/display_names = list() - for(var/V in holy_weapons_list) + var/list/nullrod_icons = list() + for(var/V in typesof(/obj/item/nullrod)) var/obj/item/nullrod/rodtype = V - if (initial(rodtype.chaplain_spawnable)) + if(initial(rodtype.chaplain_spawnable)) display_names[initial(rodtype.name)] = rodtype + nullrod_icons += list(initial(rodtype.name) = image(icon = initial(rodtype.icon), icon_state = initial(rodtype.icon_state))) - var/choice = input(M,"What theme would you like for your holy weapon?","Holy Weapon Theme") as null|anything in sortList(display_names, /proc/cmp_typepaths_asc) - if(QDELETED(src) || !choice || M.stat || !in_range(M, src) || M.incapacitated() || reskinned) + nullrod_icons = sortList(nullrod_icons) + var/choice = show_radial_menu(M, src , nullrod_icons, custom_check = CALLBACK(src, .proc/check_menu, M), radius = 42, require_near = TRUE) + if(!choice || !check_menu(M)) return var/A = display_names[choice] // This needs to be on a separate var as list member access is not allowed for new - holy_weapon = new A - + var/obj/item/nullrod/holy_weapon = new A GLOB.holy_weapon_type = holy_weapon.type SSblackbox.record_feedback("tally", "chaplain_weapon", 1, "[choice]") @@ -243,6 +249,21 @@ qdel(src) M.put_in_active_hand(holy_weapon) +/** + * check_menu: Checks if we are allowed to interact with a radial menu + * + * Arguments: + * * user The mob interacting with a menu + */ +/obj/item/nullrod/proc/check_menu(mob/user) + if(!istype(user)) + return FALSE + if(QDELETED(src) || reskinned) + return FALSE + if(user.incapacitated() || !user.is_holding(src)) + return FALSE + return TRUE + /obj/item/nullrod/godhand icon_state = "disintegrate" item_state = "disintegrate" diff --git a/code/game/objects/items/implants/implant_stealth.dm b/code/game/objects/items/implants/implant_stealth.dm index 74f04c679695..d225e7180db8 100644 --- a/code/game/objects/items/implants/implant_stealth.dm +++ b/code/game/objects/items/implants/implant_stealth.dm @@ -24,7 +24,7 @@ go_invisible() -/obj/structure/closet/cardboard/agent/open() +/obj/structure/closet/cardboard/agent/open(mob/living/user, force = FALSE) . = ..() qdel(src) diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm index d6193f1d1eed..e679ed5636b4 100644 --- a/code/game/objects/items/kitchen.dm +++ b/code/game/objects/items/kitchen.dm @@ -28,6 +28,7 @@ attack_verb = list("attacked", "stabbed", "poked") hitsound = 'sound/weapons/bladeslice.ogg' armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) + item_flags = EYE_STAB var/datum/reagent/forkload //used to eat omelette /obj/item/kitchen/fork/suicide_act(mob/living/carbon/user) @@ -48,11 +49,6 @@ M.reagents.add_reagent(forkload.type, 1) icon_state = "fork" forkload = null - - else if(user.zone_selected == BODY_ZONE_PRECISE_EYES) - if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) - M = user - return eyestab(M,user) else return ..() @@ -89,6 +85,7 @@ attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") sharpness = IS_SHARP_ACCURATE armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + item_flags = EYE_STAB var/bayonet = FALSE //Can this be attached to a gun? custom_price = 250 @@ -100,14 +97,6 @@ /obj/item/kitchen/knife/proc/set_butchering() AddComponent(/datum/component/butchering, 80 - force, 100, force - 10) //bonus chance increases depending on force -/obj/item/kitchen/knife/attack(mob/living/carbon/M, mob/living/carbon/user) - if(user.zone_selected == BODY_ZONE_PRECISE_EYES) - if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) - M = user - return eyestab(M,user) - else - return ..() - /obj/item/kitchen/knife/suicide_act(mob/user) user.visible_message(pick("[user] is slitting [user.p_their()] wrists with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.", \ "[user] is slitting [user.p_their()] throat with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.", \ diff --git a/code/game/objects/items/manuals.dm b/code/game/objects/items/manuals.dm index be7915f67f01..0bee29268729 100644 --- a/code/game/objects/items/manuals.dm +++ b/code/game/objects/items/manuals.dm @@ -172,7 +172,7 @@ It can cook multiple items at once.

Processor:

- Use it to process certain ingredients (meat into faggot, doughslice into spaghetti, potato into fries,etc...) + Use it to process certain ingredients (meat into meatballs, doughslice into spaghetti, potato into fries,etc...)

Gibber:

Stuff an animal in it to grind it into meat. diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index 1741b010fe1a..7648cff1b311 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -31,7 +31,7 @@ /obj/item/melee/synthetic_arm_blade name = "synthetic arm blade" - desc = "A grotesque blade that on closer inspection seems made of synthetic flesh, it still feels like it would hurt very badly as a weapon." + desc = "A grotesque blade that on closer inspection seems to be made out of synthetic flesh, it still feels like it would hurt very badly as a weapon." icon = 'icons/obj/changeling_items.dmi' icon_state = "arm_blade" item_state = "arm_blade" diff --git a/code/game/objects/items/melee/transforming.dm b/code/game/objects/items/melee/transforming.dm index 0bf164d81ea7..a2e104267841 100644 --- a/code/game/objects/items/melee/transforming.dm +++ b/code/game/objects/items/melee/transforming.dm @@ -23,7 +23,7 @@ if(attack_verb_off.len) attack_verb = attack_verb_off if(embedding) - RemoveElement(/datum/element/embed, embedding) + updateEmbedding() if(sharpness) AddComponent(/datum/component/butchering, 50, 100, 0, hitsound) @@ -56,7 +56,7 @@ icon_state = icon_state_on w_class = w_class_on if(embedding) - AddElement(/datum/element/embed, embedding) + updateEmbedding() else force = initial(force) throwforce = initial(throwforce) @@ -67,7 +67,7 @@ icon_state = initial(icon_state) w_class = initial(w_class) if(embedding) - RemoveElement(/datum/element/embed, embedding) + disableEmbedding() transform_messages(user, supress_message_text) add_fingerprint(user) diff --git a/code/game/objects/items/miscellaneous.dm b/code/game/objects/items/miscellaneous.dm index a243347c8f60..4ff2fcc9addb 100644 --- a/code/game/objects/items/miscellaneous.dm +++ b/code/game/objects/items/miscellaneous.dm @@ -80,6 +80,7 @@ /obj/item/storage/box/hero name = "Courageous Tomb Raider - 1940's." + desc = "This legendary figure of still dubious historical accuracy is thought to have been a world-famous archeologist who embarked on countless adventures in far away lands, along with his trademark whip and fedora hat." /obj/item/storage/box/hero/PopulateContents() new /obj/item/clothing/head/fedora/curator(src) @@ -90,6 +91,7 @@ /obj/item/storage/box/hero/astronaut name = "First Man on the Moon - 1960's." + desc = "One small step for a man, one giant leap for mankind. Relive the beginnings of space exploration with this fully functional set of vintage EVA equipment." /obj/item/storage/box/hero/astronaut/PopulateContents() new /obj/item/clothing/suit/space/nasavoid(src) @@ -99,6 +101,7 @@ /obj/item/storage/box/hero/scottish name = "Braveheart, the Scottish rebel - 1300's." + desc = "Seemingly a legendary figure in the battle for Scottish independence, this historical figure is closely associated with blue facepaint, big swords, strange man skirts, and his ever enduring catchphrase: 'FREEDOM!!'" /obj/item/storage/box/hero/scottish/PopulateContents() new /obj/item/clothing/under/costume/kilt(src) @@ -108,6 +111,7 @@ /obj/item/storage/box/hero/carphunter name = "Carp Hunter, Wildlife Expert - 2506." + desc = "Despite his nickname, this wildlife expert was mainly known as a passionate enviromentalist and conservationist, often coming in contact with dangerous wildlife to teach about the beauty of nature." /obj/item/storage/box/hero/carphunter/PopulateContents() new /obj/item/clothing/suit/space/hardsuit/carp/old(src) @@ -156,7 +160,7 @@ return MANUAL_SUICIDE /obj/item/virgin_mary - name = "A picture of the virgin mary" + name = "\proper a picture of the virgin mary" desc = "A small, cheap icon depicting the virgin mother." icon = 'icons/obj/blackmarket.dmi' icon_state = "madonna" @@ -169,14 +173,10 @@ #define NICKNAME_CAP (MAX_NAME_LEN/2) /obj/item/virgin_mary/attackby(obj/item/W, mob/user, params) . = ..() - var/ignition_msg = W.ignition_effect(src, user) - if(!ignition_msg) - return if(resistance_flags & ON_FIRE) return - user.dropItemToGround(src) - user.visible_message("[user] lights [src] ablaze with [W]!", "You light [src] on fire!") - fire_act() + if(!burn_paper_product_attackby_check(W, user, TRUE)) + return if(used_up) return if(!isliving(user) || !user.mind) //A sentient mob needs to be burning it, ya cheezit. @@ -201,7 +201,7 @@ joe.real_name = new_name used_up = TRUE mob_mobs += joe - joe.say("My soul will burn like this saint if I betray my familiy. I enter alive and I will have to get out dead.", forced = /obj/item/virgin_mary) + joe.say("My soul will burn like this saint if I betray my family. I enter alive and I will have to get out dead.", forced = /obj/item/virgin_mary) to_chat(joe, "Being inducted into the mafia does not grant antagonist status.") #undef NICKNAME_CAP diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm index f4496ea19313..7550e053ad24 100644 --- a/code/game/objects/items/mop.dm +++ b/code/game/objects/items/mop.dm @@ -32,7 +32,7 @@ var/obj/effect/decal/cleanable/C = O cleaner?.mind.adjust_experience(/datum/skill/cleaning, max(round(C.beauty/CLEAN_SKILL_BEAUTY_ADJUSTMENT,1),0)) //it is intentional that the mop rounds xp but soap does not, USE THE SACRED TOOL qdel(O) - reagents.reaction(A, TOUCH, 10) //Needed for proper floor wetting. + reagents.expose(A, TOUCH, 10) //Needed for proper floor wetting. var/val2remove = 1 if(cleaner?.mind) val2remove = round(cleaner.mind.get_skill_modifier(/datum/skill/cleaning, SKILL_SPEED_MODIFIER),0.1) diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index 954a0ea657e2..59741ae2c4ba 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -630,6 +630,10 @@ . = ..() host = loc +/obj/item/borg/projectile_dampen/cyborg_unequip(mob/user) + deactivate_field() + . = ..() + /obj/item/borg/projectile_dampen/on_mob_death() deactivate_field() . = ..() diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index e0c1d70f7617..9e04802698b0 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -384,7 +384,7 @@ created_name = "" else if(href_list["Master"]) - forced_ai = select_active_ai(usr) + forced_ai = select_active_ai(usr, z) if(!forced_ai) to_chat(usr, "No active AIs detected.") diff --git a/code/game/objects/items/shrapnel.dm b/code/game/objects/items/shrapnel.dm new file mode 100644 index 000000000000..cd3436846e6c --- /dev/null +++ b/code/game/objects/items/shrapnel.dm @@ -0,0 +1,68 @@ +/obj/item/shrapnel // frag grenades + name = "shrapnel shard" + embedding = list(embed_chance=70, ignore_throwspeed_threshold=TRUE, fall_chance=4, embed_chance_turf_mod=-100) + custom_materials = list(/datum/material/iron=50) + armour_penetration = -20 + icon = 'icons/obj/shards.dmi' + icon_state = "large" + w_class = WEIGHT_CLASS_TINY + item_flags = DROPDEL + +/obj/item/shrapnel/stingball // stingbang grenades + name = "stingball" + embedding = list(embed_chance=90, fall_chance=3, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.7, pain_mult=5, jostle_pain_mult=6, rip_time=15, embed_chance_turf_mod=-100) + icon_state = "tiny" + +/obj/item/shrapnel/bullet // bullets + name = "bullet" + icon = 'icons/obj/ammo.dmi' + icon_state = "s-casing" + item_flags = NONE + +/obj/item/shrapnel/bullet/c38 // .38 round + name = "\improper .38 bullet" + +/obj/item/shrapnel/bullet/c38/dumdum // .38 DumDum round + name = "\improper .38 DumDum bullet" + embedding = list(embed_chance=70, fall_chance=7, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10, embed_chance_turf_mod=-100) + +/obj/projectile/bullet/shrapnel + name = "flying shrapnel shard" + damage = 9 + range = 10 + armour_penetration = -30 + dismemberment = 5 + ricochets_max = 2 + ricochet_chance = 40 + shrapnel_type = /obj/item/shrapnel + ricochet_incidence_leeway = 60 + hit_stunned_targets = TRUE + +/obj/projectile/bullet/shrapnel/mega + name = "flying shrapnel hunk" + range = 25 + dismemberment = 10 + ricochets_max = 4 + ricochet_chance = 90 + ricochet_decay_chance = 0.9 + +/obj/projectile/bullet/pellet/stingball + name = "stingball pellet" + damage = 3 + stamina = 8 + ricochets_max = 4 + ricochet_chance = 66 + ricochet_decay_chance = 1 + ricochet_decay_damage = 0.9 + ricochet_auto_aim_angle = 10 + ricochet_auto_aim_range = 2 + ricochet_incidence_leeway = 0 + shrapnel_type = /obj/item/shrapnel/stingball + +/obj/projectile/bullet/pellet/stingball/mega + name = "megastingball pellet" + ricochets_max = 6 + ricochet_chance = 110 + +/obj/projectile/bullet/pellet/stingball/on_ricochet(atom/A) + hit_stunned_targets = TRUE // ducking will save you from the first wave, but not the rebounds diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm index d33556894c8f..b210a1e27399 100644 --- a/code/game/objects/items/stacks/rods.dm +++ b/code/game/objects/items/stacks/rods.dm @@ -9,7 +9,9 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \ new/datum/stack_recipe("fore starboard spacepod frame", /obj/item/pod_parts/pod_frame/fore_starboard, 15, time = 30, one_per_turf = 0), \ new/datum/stack_recipe("aft port spacepod frame", /obj/item/pod_parts/pod_frame/aft_port, 15, time = 30, one_per_turf = 0), \ new/datum/stack_recipe("aft starboard spacepod frame", /obj/item/pod_parts/pod_frame/aft_starboard, 15, time = 30, one_per_turf = 0), \ + null, \ // Wasp end + new/datum/stack_recipe("railing", /obj/structure/railing, 3, time = 18, window_checks = TRUE), \ )) /obj/item/stack/rods diff --git a/code/game/objects/items/stacks/sheets/hot_ice.dm b/code/game/objects/items/stacks/sheets/hot_ice.dm index bb64f5e6cb79..2e5d299fec82 100644 --- a/code/game/objects/items/stacks/sheets/hot_ice.dm +++ b/code/game/objects/items/stacks/sheets/hot_ice.dm @@ -5,7 +5,7 @@ singular_name = "hot ice" icon = 'icons/obj/stack_objects.dmi' custom_materials = list(/datum/material/hot_ice=MINERAL_MATERIAL_AMOUNT) - grind_results = list(/datum/reagent/toxin/plasma = 200) + grind_results = list(/datum/reagent/toxin/plasma = 300) material_type = /datum/material/hot_ice /obj/item/stack/sheet/hot_ice/suicide_act(mob/living/carbon/user) diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 2b593f85c76a..5ddf9d2963a0 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -38,10 +38,11 @@ GLOBAL_LIST_INIT(sandstone_recipes, list ( \ item_state = "sheet-sandstone" throw_speed = 3 throw_range = 5 - custom_materials = list(/datum/material/glass=MINERAL_MATERIAL_AMOUNT) + custom_materials = list(/datum/material/sandstone=MINERAL_MATERIAL_AMOUNT) sheettype = "sandstone" merge_type = /obj/item/stack/sheet/mineral/sandstone walltype = /turf/closed/wall/mineral/sandstone + material_type = /datum/material/sandstone /obj/item/stack/sheet/mineral/sandstone/get_main_recipes() . = ..() @@ -237,6 +238,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \ GLOBAL_LIST_INIT(gold_recipes, list ( \ new/datum/stack_recipe("golden door", /obj/structure/mineral_door/gold, 10, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("gold tile", /obj/item/stack/tile/mineral/gold, 1, 4, 20), \ + new/datum/stack_recipe("blank plaque", /obj/item/plaque, 1), \ new/datum/stack_recipe("HoS Statue", /obj/structure/statue/gold/hos, 5, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("HOP Statue", /obj/structure/statue/gold/head_of_personnel, 5, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("CE Statue", /obj/structure/statue/gold/ce, 5, one_per_turf = 1, on_floor = 1), \ @@ -346,6 +348,7 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \ GLOBAL_LIST_INIT(titanium_recipes, list ( \ new/datum/stack_recipe("titanium tile", /obj/item/stack/tile/mineral/titanium, 1, 4, 20), \ + new/datum/stack_recipe("shuttle seat", /obj/structure/chair/comfy/shuttle, 2, one_per_turf = TRUE, on_floor = TRUE), \ )) /obj/item/stack/sheet/mineral/titanium/get_main_recipes() @@ -398,12 +401,14 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list ( \ name = "snow" icon_state = "sheet-snow" item_state = "sheet-snow" + custom_materials = list(/datum/material/snow = MINERAL_MATERIAL_AMOUNT) singular_name = "snow block" force = 1 throwforce = 2 grind_results = list(/datum/reagent/consumable/ice = 20) merge_type = /obj/item/stack/sheet/mineral/snow walltype = /turf/closed/wall/mineral/snow + material_type = /datum/material/snow GLOBAL_LIST_INIT(snow_recipes, list ( \ new/datum/stack_recipe("Snow wall", /turf/closed/wall/mineral/snow, 5, one_per_turf = 1, on_floor = 1), \ diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 1e18aa8dec3b..bf3e503d3b9f 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -261,11 +261,13 @@ GLOBAL_LIST_INIT(bamboo_recipes, list ( \ icon_state = "sheet-bamboo" item_state = "sheet-bamboo" icon = 'icons/obj/stack_objects.dmi' + custom_materials = list(/datum/material/bamboo = MINERAL_MATERIAL_AMOUNT) throwforce = 15 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0) resistance_flags = FLAMMABLE merge_type = /obj/item/stack/sheet/mineral/bamboo grind_results = list(/datum/reagent/cellulose = 10) + material_type = /datum/material/bamboo /obj/item/stack/sheet/mineral/bamboo/get_main_recipes() . = ..() @@ -442,12 +444,14 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \ singular_name = "cardboard sheet" icon_state = "sheet-card" item_state = "sheet-card" + custom_materials = list(/datum/material/cardboard = MINERAL_MATERIAL_AMOUNT) resistance_flags = FLAMMABLE force = 0 throwforce = 0 merge_type = /obj/item/stack/sheet/cardboard novariants = TRUE grind_results = list(/datum/reagent/cellulose = 10) + material_type = /datum/material/cardboard /obj/item/stack/sheet/cardboard/get_main_recipes() . = ..() @@ -464,6 +468,12 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \ to_chat(user, "You stamp the cardboard! It's a clown box! Honk!") if (amount >= 0) new/obj/item/storage/box/clown(droploc) //bugfix + if(istype(I, /obj/item/stamp/chameleon) && !istype(loc, /obj/item/storage)) + var/atom/droploc = drop_location() + if(use(1)) + to_chat(user, "You stamp the cardboard in a sinister way.") + if (amount >= 0) + new/obj/item/storage/box/syndie_kit(droploc) else . = ..() @@ -488,10 +498,12 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \ icon_state = "sheet-runed" item_state = "sheet-runed" icon = 'icons/obj/stack_objects.dmi' + custom_materials = list(/datum/material/runedmetal = MINERAL_MATERIAL_AMOUNT) sheettype = "runed" merge_type = /obj/item/stack/sheet/runed_metal novariants = TRUE grind_results = list(/datum/reagent/iron = 5, /datum/reagent/blood = 15) + material_type = /datum/material/runedmetal /obj/item/stack/sheet/runed_metal/attack_self(mob/living/user) if(!iscultist(user)) @@ -543,6 +555,7 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ icon_state = "sheet-brass" item_state = "sheet-brass" icon = 'icons/obj/stack_objects.dmi' + custom_materials = list(/datum/material/bronze = MINERAL_MATERIAL_AMOUNT) lefthand_file = 'icons/mob/inhands/misc/sheets_lefthand.dmi' righthand_file = 'icons/mob/inhands/misc/sheets_righthand.dmi' resistance_flags = FIRE_PROOF | ACID_PROOF @@ -556,6 +569,7 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ grind_results = list(/datum/reagent/iron = 5, /datum/reagent/copper = 3) //we have no "tin" reagent so this is the closest thing merge_type = /obj/item/stack/tile/bronze tableVariant = /obj/structure/table/bronze + material_type = /datum/material/bronze /obj/item/stack/tile/bronze/get_main_recipes() . = ..() @@ -597,6 +611,7 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ icon = 'icons/obj/mining.dmi' icon_state = "bone" item_state = "sheet-bone" + custom_materials = list(/datum/material/bone = MINERAL_MATERIAL_AMOUNT) singular_name = "bone" desc = "Someone's been drinking their milk." force = 7 @@ -607,6 +622,7 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ throw_range = 3 grind_results = list(/datum/reagent/carbon = 10) merge_type = /obj/item/stack/sheet/bone + material_type = /datum/material/bone GLOBAL_LIST_INIT(plastic_recipes, list( new /datum/stack_recipe("plastic floor tile", /obj/item/stack/tile/plastic, 1, 4, 20), \ @@ -616,7 +632,7 @@ GLOBAL_LIST_INIT(plastic_recipes, list( new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/food/drinks/waterbottle/large/empty, 3), \ new /datum/stack_recipe("colo cups", /obj/item/reagent_containers/food/drinks/colocup, 1), \ new /datum/stack_recipe("wet floor sign", /obj/item/clothing/suit/caution, 2), \ - new /datum/stack_recipe("blank sign", /obj/item/sign_backing, 1))) + new /datum/stack_recipe("blank wall sign", /obj/item/sign, 1))) /obj/item/stack/sheet/plastic name = "plastic" @@ -649,9 +665,11 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra singular_name = "paper frame" icon_state = "sheet-paper" item_state = "sheet-paper" + custom_materials = list(/datum/material/paper = MINERAL_MATERIAL_AMOUNT) merge_type = /obj/item/stack/sheet/paperframes resistance_flags = FLAMMABLE grind_results = list(/datum/reagent/cellulose = 20) + material_type = /datum/material/paper /obj/item/stack/sheet/paperframes/get_main_recipes() . = ..() @@ -676,8 +694,8 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra merge_type = /obj/item/stack/sheet/stalinium /obj/item/stack/sheet/meat - name = "meat sheet" - desc = "Something's bloody meat compressed into a nice solid sheet" + name = "meat sheets" + desc = "Something's bloody meat compressed into a nice solid sheet." singular_name = "meat sheet" icon_state = "sheet-meat" material_flags = MATERIAL_COLOR @@ -685,3 +703,44 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra merge_type = /obj/item/stack/sheet/meat material_type = /datum/material/meat material_modifier = 1 //None of that wussy stuff + +/obj/item/stack/sheet/meat/fifty + amount = 50 +/obj/item/stack/sheet/meat/twenty + amount = 20 +/obj/item/stack/sheet/meat/five + amount = 5 + +/obj/item/stack/sheet/pizza + name = "pepperoni sheetzzas" + desc = "It's a delicious pepperoni sheetzza!" + singular_name = "pepperoni sheetzza" + icon_state = "sheet-pizza" + custom_materials = list(/datum/material/pizza = MINERAL_MATERIAL_AMOUNT) + merge_type = /obj/item/stack/sheet/pizza + material_type = /datum/material/pizza + material_modifier = 1 + +/obj/item/stack/sheet/pizza/fifty + amount = 50 +/obj/item/stack/sheet/pizza/twenty + amount = 20 +/obj/item/stack/sheet/pizza/five + amount = 5 + +/obj/item/stack/sheet/sandblock + name = "blocks of sand" + desc = "You're too old to be playing with sandcastles. Now you build... sandstations." + singular_name = "block of sand" + icon_state = "sheet-sandstone" + custom_materials = list(/datum/material/sand = MINERAL_MATERIAL_AMOUNT) + merge_type = /obj/item/stack/sheet/sandblock + material_type = /datum/material/sand + material_modifier = 1 + +/obj/item/stack/sheet/sandblock/fifty + amount = 50 +/obj/item/stack/sheet/sandblock/twenty + amount = 20 +/obj/item/stack/sheet/sandblock/five + amount = 5 diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 92f8c6a691db..e80db0804e4a 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -65,7 +65,7 @@ var/datum/material/M = SSmaterials.GetMaterialRef(material_type) //First/main material for(var/i in M.categories) switch(i) - if(MAT_CATEGORY_RIGID) + if(MAT_CATEGORY_RIGID && MAT_CATEGORY_BASE_RECIPES) var/list/temp = SSmaterials.rigid_stack_recipes.Copy() recipes += temp update_weight() diff --git a/code/game/objects/items/stacks/tape.dm b/code/game/objects/items/stacks/tape.dm index 37084a8d886c..f7760622c2cd 100644 --- a/code/game/objects/items/stacks/tape.dm +++ b/code/game/objects/items/stacks/tape.dm @@ -36,7 +36,7 @@ return I.embedding = conferred_embed - I.AddElement(/datum/element/embed, I.embedding) + I.updateEmbedding() to_chat(user, "You finish wrapping [I] with [src].") I.name = "[prefix] [I.name]" diff --git a/code/game/objects/items/stacks/telecrystal.dm b/code/game/objects/items/stacks/telecrystal.dm index 21fc5bd8c957..0a5740681ffe 100644 --- a/code/game/objects/items/stacks/telecrystal.dm +++ b/code/game/objects/items/stacks/telecrystal.dm @@ -21,19 +21,6 @@ else return ..() -/obj/item/stack/telecrystal/afterattack(obj/item/I, mob/user, proximity) - . = ..() - if(!proximity) - return - if(istype(I, /obj/item/cartridge/virus/frame)) - var/obj/item/cartridge/virus/frame/cart = I - if(!cart.charges) - to_chat(user, "[cart] is out of charges, it's refusing to accept [src].") - return - cart.telecrystals += amount - use(amount) - to_chat(user, "You slot [src] into [cart]. The next time it's used, it will also give telecrystals.") - /obj/item/stack/telecrystal/five amount = 5 diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index ad1f2fec3ec4..26abd46e607e 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -14,12 +14,32 @@ var/turf_type = null var/mineralType = null novariants = TRUE + var/human_maxHealth = 100 /obj/item/stack/tile/Initialize(mapload, amount) . = ..() pixel_x = rand(-3, 3) pixel_y = rand(-3, 3) //randomize a little +/obj/item/stack/tile/examine(mob/user) + . = ..() + if(throwforce && !is_cyborg) //do not want to divide by zero or show the message to borgs who can't throw + var/verb + switch(CEILING(human_maxHealth / throwforce, 1)) //throws to crit a human + if(1 to 3) + verb = "superb" + if(4 to 6) + verb = "great" + if(7 to 9) + verb = "good" + if(10 to 12) + verb = "fairly decent" + if(13 to 15) + verb = "mediocre" + if(!verb) + return + . += "Those could work as a [verb] throwing weapon." + /obj/item/stack/tile/attackby(obj/item/W, mob/user, params) if (W.tool_behaviour == TOOL_WELDER) @@ -308,7 +328,7 @@ /obj/item/stack/tile/plasteel name = "floor tile" singular_name = "floor tile" - desc = "Those could work as a pretty decent throwing weapon." + desc = "The ground you walk on." icon_state = "tile" item_state = "tile" force = 6 @@ -321,7 +341,6 @@ resistance_flags = FIRE_PROOF /obj/item/stack/tile/plasteel/cyborg - desc = "The ground you walk on." //Not the usual floor tile desc as that refers to throwing, Cyborgs can't do that - RR custom_materials = null // All other Borg versions of items have no Metal or Glass - RR is_cyborg = 1 cost = 125 @@ -335,9 +354,10 @@ turf_type = /turf/open/floor/plastic /obj/item/stack/tile/material - name = "tile" + name = "floor tile" singular_name = "floor tile" - desc = "A tile of flooring." + desc = "The ground you walk on." + throwforce = 10 icon_state = "material_tile" turf_type = /turf/open/floor/material material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index a0fda96a6954..e33aa961ac36 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -18,6 +18,7 @@ // Generic non-item /obj/item/storage/bag slot_flags = ITEM_SLOT_BELT + w_class = WEIGHT_CLASS_BULKY /obj/item/storage/bag/ComponentInitialize() . = ..() @@ -38,8 +39,6 @@ item_state = "trashbag" lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi' - - w_class = WEIGHT_CLASS_BULKY var/insertable = TRUE /obj/item/storage/bag/trash/ComponentInitialize() @@ -194,8 +193,8 @@ //WaspStation Begin - Better bag sprites icon = 'waspstation/icons/obj/bags.dmi' icon_state = "plantbag" - //WaspStation end w_class = WEIGHT_CLASS_TINY + //WaspStation end resistance_flags = FLAMMABLE /obj/item/storage/bag/plants/ComponentInitialize() @@ -240,7 +239,6 @@ icon_state = "sheetsnatcher" var/capacity = 300; //the number of sheets it can carry. - w_class = WEIGHT_CLASS_NORMAL component_type = /datum/component/storage/concrete/stack /obj/item/storage/bag/sheetsnatcher/ComponentInitialize() @@ -280,7 +278,6 @@ desc = "A bag for books." icon = 'icons/obj/library.dmi' icon_state = "bookbag" - w_class = WEIGHT_CLASS_BULKY //Bigger than a book because physics resistance_flags = FLAMMABLE /obj/item/storage/bag/books/ComponentInitialize() @@ -308,7 +305,6 @@ throwforce = 10 throw_speed = 3 throw_range = 5 - w_class = WEIGHT_CLASS_BULKY flags_1 = CONDUCT_1 custom_materials = list(/datum/material/iron=3000) @@ -375,7 +371,6 @@ icon_state = "chembag" //WaspStation end desc = "A bag for storing pills, patches, and bottles." - w_class = WEIGHT_CLASS_TINY resistance_flags = FLAMMABLE /obj/item/storage/bag/chemistry/ComponentInitialize() @@ -406,7 +401,6 @@ icon_state = "virobag" //WaspStation end desc = "A bag for the safe transportation and disposal of biowaste and other biological materials." - w_class = WEIGHT_CLASS_TINY resistance_flags = FLAMMABLE /obj/item/storage/bag/bio/ComponentInitialize() @@ -440,7 +434,6 @@ icon_state = "engbag" //WaspStation end desc = "A bag for storing small construction components." - w_class = WEIGHT_CLASS_TINY resistance_flags = FLAMMABLE /obj/item/storage/bag/construction/ComponentInitialize() diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index b985233e5e44..2ca1440f79d4 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -481,7 +481,7 @@ /obj/item/grenade/smokebomb = 4, /obj/item/grenade/empgrenade = 1, /obj/item/grenade/empgrenade = 1, - /obj/item/grenade/syndieminibomb/concussion/frag = 10, + /obj/item/grenade/frag = 10, /obj/item/grenade/gluon = 4, /obj/item/grenade/chem_grenade/incendiary = 2, /obj/item/grenade/chem_grenade/facid = 1, diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm index 36312a5198af..fbdf250682cc 100644 --- a/code/game/objects/items/storage/book.dm +++ b/code/game/objects/items/storage/book.dm @@ -17,11 +17,11 @@ /obj/item/storage/book/attack_self(mob/user) to_chat(user, "The pages of [title] have been cut out!") -GLOBAL_LIST_INIT(biblenames, list("Bible", "Quran", "Scrapbook", "Burning Bible", "Clown Bible", "Banana Bible", "Creeper Bible", "White Bible", "Holy Light", "The God Delusion", "Tome", "The King in Yellow", "Ithaqua", "Scientology", "Melted Bible", "Necronomicon","Insulationism")) +GLOBAL_LIST_INIT(biblenames, list("Bible", "Quran", "Scrapbook", "Burning Bible", "Clown Bible", "Banana Bible", "Creeper Bible", "White Bible", "Holy Light", "The God Delusion", "Tome", "The King in Yellow", "Ithaqua", "Scientology", "Melted Bible", "Necronomicon", "Insulationism", "Guru Granth Sahib")) //If you get these two lists not matching in size, there will be runtimes and I will hurt you in ways you couldn't even begin to imagine // if your bible has no custom itemstate, use one of the existing ones -GLOBAL_LIST_INIT(biblestates, list("bible", "koran", "scrapbook", "burning", "honk1", "honk2", "creeper", "white", "holylight", "atheist", "tome", "kingyellow", "ithaqua", "scientology", "melted", "necronomicon","insuls")) -GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "burning", "honk1", "honk2", "creeper", "white", "holylight", "atheist", "tome", "kingyellow", "ithaqua", "scientology", "melted", "necronomicon", "kingyellow")) +GLOBAL_LIST_INIT(biblestates, list("bible", "koran", "scrapbook", "burning", "honk1", "honk2", "creeper", "white", "holylight", "atheist", "tome", "kingyellow", "ithaqua", "scientology", "melted", "necronomicon", "insuls", "gurugranthsahib")) +GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "burning", "honk1", "honk2", "creeper", "white", "holylight", "atheist", "tome", "kingyellow", "ithaqua", "scientology", "melted", "necronomicon", "kingyellow", "gurugranthsahib")) /mob/proc/bible_check() //The bible, if held, might protect against certain things var/obj/item/storage/book/bible/B = locate() in src diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index d4eff0157e08..f91a4f7f975d 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -172,6 +172,10 @@ ..() // we want the regular stuff too new /obj/item/radio/off(src) +// Medical survival box +/obj/item/storage/box/survival/medical + mask_type = /obj/item/clothing/mask/breath/medical + /obj/item/storage/box/gloves name = "box of latex gloves" desc = "Contains sterile latex gloves." @@ -205,7 +209,6 @@ /obj/item/storage/box/syringes/variety/PopulateContents() new /obj/item/reagent_containers/syringe(src) new /obj/item/reagent_containers/syringe/lethal(src) - new /obj/item/reagent_containers/syringe/noreact(src) new /obj/item/reagent_containers/syringe/piercing(src) new /obj/item/reagent_containers/syringe/bluespace(src) @@ -307,6 +310,16 @@ for(var/i in 1 to 7) new /obj/item/grenade/flashbang(src) +/obj/item/storage/box/stingbangs + name = "box of stingbangs (WARNING)" + desc = "WARNING: These devices are extremely dangerous and can cause severe injuries or death in repeated use." + icon_state = "secbox" + illustration = "flashbang" + +/obj/item/storage/box/stingbangs/PopulateContents() + for(var/i in 1 to 5) + new /obj/item/grenade/stingbang(src) + /obj/item/storage/box/flashes name = "box of flashbulbs" desc = "WARNING: Flashes can cause serious eye damage, protective eyewear is required." @@ -452,19 +465,16 @@ for(var/i in 1 to 7) new /obj/item/reagent_containers/food/drinks/sillycup( src ) -/obj/item/storage/box/donkpockets - var/donktype = /obj/item/reagent_containers/food/snacks/donkpocket - -/obj/item/storage/box/donkpockets/PopulateContents() - for(var/i in 1 to 6) - new donktype(src) - /obj/item/storage/box/donkpockets name = "box of donk-pockets" desc = "Instructions: Heat in microwave. Product will cool if not eaten within seven minutes." icon_state = "donkpocketbox" illustration=null - donktype = /obj/item/reagent_containers/food/snacks/donkpocket + var/donktype = /obj/item/reagent_containers/food/snacks/donkpocket + +/obj/item/storage/box/donkpockets/PopulateContents() + for(var/i in 1 to 6) + new donktype(src) /obj/item/storage/box/donkpockets/ComponentInitialize() . = ..() @@ -874,7 +884,7 @@ /obj/item/storage/box/beanbag name = "box of beanbags" desc = "A box full of beanbag shells." - illustration = "rubbershot_box" + icon_state = "rubbershot_box" illustration = null /obj/item/storage/box/beanbag/PopulateContents() @@ -891,12 +901,6 @@ var/randomFigure = pick(subtypesof(/obj/item/toy/figure)) new randomFigure(src) -#define NODESIGN "None" -#define NANOTRASEN "NanotrasenStandard" -#define SYNDI "SyndiSnacks" -#define HEART "Heart" -#define SMILEY "SmileyFace" - /obj/item/storage/box/papersack name = "paper sack" desc = "A sack neatly crafted out of paper." @@ -905,7 +909,18 @@ illustration = null resistance_flags = FLAMMABLE foldable = null - var/design = NODESIGN + /// A list of all available papersack reskins + var/list/papersack_designs = list() + +/obj/item/storage/box/papersack/Initialize(mapload) + . = ..() + papersack_designs = sortList(list( + "None" = image(icon = src.icon, icon_state = "paperbag_None"), + "NanotrasenStandard" = image(icon = src.icon, icon_state = "paperbag_NanotrasenStandard"), + "SyndiSnacks" = image(icon = src.icon, icon_state = "paperbag_SyndiSnacks"), + "Heart" = image(icon = src.icon, icon_state = "paperbag_Heart"), + "SmileyFace" = image(icon = src.icon, icon_state = "paperbag_SmileyFace") + )) /obj/item/storage/box/papersack/update_icon_state() if(contents.len == 0) @@ -915,48 +930,62 @@ /obj/item/storage/box/papersack/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/pen)) - //if a pen is used on the sack, dialogue to change its design appears - if(contents.len) - to_chat(user, "You can't modify [src] with items still inside!") - return - var/list/designs = list(NODESIGN, NANOTRASEN, SYNDI, HEART, SMILEY, "Cancel") - var/switchDesign = input("Select a Design:", "Paper Sack Design", designs[1]) in sortList(designs) - if(get_dist(usr, src) > 1) - to_chat(usr, "You have moved too far away!") - return - var/choice = designs.Find(switchDesign) - if(design == designs[choice] || designs[choice] == "Cancel") - return 0 - to_chat(usr, "You make some modifications to [src] using your pen.") - design = designs[choice] - icon_state = "paperbag_[design]" - item_state = "paperbag_[design]" - switch(designs[choice]) - if(NODESIGN) + var/choice = show_radial_menu(user, src , papersack_designs, custom_check = CALLBACK(src, .proc/check_menu, user, W), radius = 36, require_near = TRUE) + if(!choice) + return FALSE + if(icon_state == "paperbag_[choice]") + return FALSE + switch(choice) + if("None") desc = "A sack neatly crafted out of paper." - if(NANOTRASEN) + if("NanotrasenStandard") desc = "A standard Nanotrasen paper lunch sack for loyal employees on the go." - if(SYNDI) + if("SyndiSnacks") desc = "The design on this paper sack is a remnant of the notorious 'SyndieSnacks' program." - if(HEART) + if("Heart") desc = "A paper sack with a heart etched onto the side." - if(SMILEY) + if("SmileyFace") desc = "A paper sack with a crude smile etched onto the side." - return 0 + else + return FALSE + to_chat(user, "You make some modifications to [src] using your pen.") + icon_state = "paperbag_[choice]" + item_state = "paperbag_[choice]" + return FALSE else if(W.get_sharpness()) if(!contents.len) if(item_state == "paperbag_None") user.show_message("You cut eyeholes into [src].", MSG_VISUAL) new /obj/item/clothing/head/papersack(user.loc) qdel(src) - return 0 + return FALSE else if(item_state == "paperbag_SmileyFace") user.show_message("You cut eyeholes into [src] and modify the design.", MSG_VISUAL) new /obj/item/clothing/head/papersack/smiley(user.loc) qdel(src) - return 0 + return FALSE return ..() +/** + * check_menu: Checks if we are allowed to interact with a radial menu + * + * Arguments: + * * user The mob interacting with a menu + * * P The pen used to interact with a menu + */ +/obj/item/storage/box/papersack/proc/check_menu(mob/user, obj/item/pen/P) + if(!istype(user)) + return FALSE + if(user.incapacitated()) + return FALSE + if(contents.len) + to_chat(user, "You can't modify [src] with items still inside!") + return FALSE + if(!P || !user.is_holding(P)) + to_chat(user, "You need a pen to modify [src]!") + return FALSE + return TRUE + /obj/item/storage/box/papersack/meat desc = "It's slightly moist and smells like a slaughterhouse." @@ -964,12 +993,6 @@ for(var/i in 1 to 7) new /obj/item/reagent_containers/food/snacks/meat/slab(src) -#undef NODESIGN -#undef NANOTRASEN -#undef SYNDI -#undef HEART -#undef SMILEY - /obj/item/storage/box/ingredients //This box is for the randomely chosen version the chef spawns with, it shouldn't actually exist. name = "ingredients box" illustration = "fruit" @@ -1019,7 +1042,7 @@ /obj/item/storage/box/ingredients/italian/PopulateContents() for(var/i in 1 to 3) new /obj/item/reagent_containers/food/snacks/grown/tomato(src) - new /obj/item/reagent_containers/food/snacks/faggot(src) + new /obj/item/reagent_containers/food/snacks/meatball(src) new /obj/item/reagent_containers/food/drinks/bottle/wine(src) /obj/item/storage/box/ingredients/vegetarian @@ -1042,7 +1065,7 @@ new /obj/item/reagent_containers/food/snacks/grown/potato(src) new /obj/item/reagent_containers/food/snacks/grown/tomato(src) new /obj/item/reagent_containers/food/snacks/grown/corn(src) - new /obj/item/reagent_containers/food/snacks/faggot(src) + new /obj/item/reagent_containers/food/snacks/meatball(src) /obj/item/storage/box/ingredients/fruity theme_name = "fruity" @@ -1098,7 +1121,7 @@ new /obj/item/reagent_containers/food/snacks/carpmeat(src) new /obj/item/reagent_containers/food/snacks/meat/slab/xeno(src) new /obj/item/reagent_containers/food/snacks/meat/slab/corgi(src) - new /obj/item/reagent_containers/food/snacks/faggot(src) + new /obj/item/reagent_containers/food/snacks/meatball(src) /obj/item/storage/box/ingredients/exotic theme_name = "exotic" @@ -1277,7 +1300,7 @@ new/obj/item/sparkler(src) new/obj/item/grenade/firecracker(src) if(prob(20)) - new /obj/item/grenade/syndieminibomb/concussion/frag(src) + new /obj/item/grenade/frag(src) else new /obj/item/toy/snappop(src) @@ -1312,7 +1335,7 @@ /obj/item/storage/box/gum/ComponentInitialize() . = ..() var/datum/component/storage/STR = GetComponent(/datum/component/storage) - STR.set_holdable(list(/obj/item/storage/box/gum)) + STR.set_holdable(list(/obj/item/reagent_containers/food/snacks/chewable/bubblegum)) STR.max_items = 4 /obj/item/storage/box/gum/PopulateContents() @@ -1345,6 +1368,15 @@ for(var/i in 1 to 4) new/obj/item/reagent_containers/food/snacks/chewable/bubblegum/happiness(src) +/obj/item/storage/box/gum/bubblegum + name = "bubblegum gum packet" + desc = "The packaging is entirely in Demonic, apparently. You feel like even opening this would be a sin." + icon_state = "bubblegum_bubblegum" + +/obj/item/storage/box/gum/bubblegum/PopulateContents() + for(var/i in 1 to 4) + new/obj/item/reagent_containers/food/snacks/chewable/bubblegum/bubblegum(src) + /obj/item/storage/box/shipping name = "box of shipping supplies" desc = "Contains several scanners and labelers for shipping things. Wrapping Paper not included." diff --git a/code/game/objects/items/storage/fancy.dm b/code/game/objects/items/storage/fancy.dm index 797ae7cfbcc7..9086dc0ec5f4 100644 --- a/code/game/objects/items/storage/fancy.dm +++ b/code/game/objects/items/storage/fancy.dm @@ -16,9 +16,6 @@ /obj/item/storage/fancy icon = 'icons/obj/food/containers.dmi' - icon_state = "donutbox6" - name = "donut box" - desc = "Mmm. Donuts." resistance_flags = FLAMMABLE var/icon_type = "donut" var/spawn_type = null @@ -58,17 +55,21 @@ fancy_open = TRUE update_icon() +#define DONUT_INBOX_SPRITE_WIDTH 3 + /* * Donut Box */ /obj/item/storage/fancy/donut_box - icon = 'icons/obj/food/containers.dmi' - icon_state = "donutbox6" - icon_type = "donut" name = "donut box" + desc = "Mmm. Donuts." + icon = 'icons/obj/food/donuts.dmi' + icon_state = "donutbox_inner" + icon_type = "donut" spawn_type = /obj/item/reagent_containers/food/snacks/donut fancy_open = TRUE + appearance_flags = KEEP_TOGETHER /obj/item/storage/fancy/donut_box/ComponentInitialize() . = ..() @@ -76,6 +77,36 @@ STR.max_items = 6 STR.set_holdable(list(/obj/item/reagent_containers/food/snacks/donut)) +/obj/item/storage/fancy/donut_box/PopulateContents() + . = ..() + update_icon() + +/obj/item/storage/fancy/donut_box/update_icon_state() + if(fancy_open) + icon_state = "donutbox_inner" + else + icon_state = "donutbox" + +/obj/item/storage/fancy/donut_box/update_overlays() + . = ..() + + if (!fancy_open) + return + + var/donuts = 0 + + for (var/_donut in contents) + var/obj/item/reagent_containers/food/snacks/donut/donut = _donut + if (!istype(donut)) + continue + + . += image(icon = initial(icon), icon_state = donut.in_box_sprite(), pixel_x = donuts * DONUT_INBOX_SPRITE_WIDTH) + donuts += 1 + + . += image(icon = initial(icon), icon_state = "donutbox_top") + +#undef DONUT_INBOX_SPRITE_WIDTH + /* * Egg Box */ @@ -137,6 +168,7 @@ spawn_type = /obj/item/clothing/mask/cigarette/space_cigarette var/candy = FALSE //for cigarette overlay custom_price = 75 + age_restricted = TRUE /obj/item/storage/fancy/cigarettes/ComponentInitialize() . = ..() @@ -253,6 +285,7 @@ icon_type = "candy cigarette" spawn_type = /obj/item/clothing/mask/cigarette/candy candy = TRUE + age_restricted = FALSE /obj/item/storage/fancy/cigarettes/cigpack_candy/Initialize() . = ..() diff --git a/code/game/objects/items/storage/firstaid.dm b/code/game/objects/items/storage/firstaid.dm index ce663a47b462..72632db9cbf0 100644 --- a/code/game/objects/items/storage/firstaid.dm +++ b/code/game/objects/items/storage/firstaid.dm @@ -115,7 +115,7 @@ generate_items_inside(items_inside,src) /obj/item/storage/firstaid/ancient - icon_state = "firstaid" + icon_state = "oldfirstaid" desc = "A first aid kit with the ability to heal common types of injuries." /obj/item/storage/firstaid/ancient/PopulateContents() @@ -127,6 +127,10 @@ /obj/item/stack/medical/ointment= 3) generate_items_inside(items_inside,src) +/obj/item/storage/firstaid/ancient/heirloom + desc = "A first aid kit with the ability to heal common types of injuries. You start thinking of the good old days just by looking at it." + empty = TRUE // long since been ransacked by hungry powergaming assistants breaking into med storage + /obj/item/storage/firstaid/fire name = "burn treatment kit" desc = "A specialized medical kit for when the toxins lab -spontaneously- burns down." @@ -470,4 +474,29 @@ for(var/i in 1 to 7) new /obj/item/reagent_containers/pill/floorpill(src) +///////////////////////////////////////// Psychologist inventory pillbottles +/obj/item/storage/pill_bottle/happinesspsych + name = "happiness pills" + desc = "Contains pills used as a last resort means to temporarily stabilize depression and anxiety. WARNING: side effects may include slurred speech, drooling, and severe addiction." + +/obj/item/storage/pill_bottle/happinesspsych/PopulateContents() + for(var/i in 1 to 5) + new /obj/item/reagent_containers/pill/happinesspsych(src) + +/obj/item/storage/pill_bottle/lsdpsych + name = "mindbreaker toxin pills" + desc = "!FOR THERAPEUTIC USE ONLY! Contains pills used to alleviate the symptoms of Reality Dissociation Syndrome." + +/obj/item/storage/pill_bottle/lsdpsych/PopulateContents() + for(var/i in 1 to 5) + new /obj/item/reagent_containers/pill/lsdpsych(src) + +/obj/item/storage/pill_bottle/paxpsych + name = "pax pills" + desc = "Contains pills used to temporarily pacify patients that are deemed a harm to themselves or others." + +/obj/item/storage/pill_bottle/paxpsych/PopulateContents() + for(var/i in 1 to 5) + new /obj/item/reagent_containers/pill/paxpsych(src) + WaspStation End */ diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm index d39a58af8dba..ee3bc568bac3 100644 --- a/code/game/objects/items/storage/storage.dm +++ b/code/game/objects/items/storage/storage.dm @@ -20,8 +20,13 @@ /obj/item/storage/contents_explosion(severity, target) for(var/atom/A in contents) - A.ex_act(severity, target) - CHECK_TICK + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += A + if(EXPLODE_HEAVY) + SSexplosions.medobj += A + if(EXPLODE_LIGHT) + SSexplosions.lowobj += A /obj/item/storage/canStrip(mob/who) . = ..() diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 4840ce15a42f..e73bc7f2c39a 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -9,8 +9,8 @@ new /obj/item/binoculars(src) // 2 tc? new /obj/item/encryptionkey/syndicate(src) // 2 tc new /obj/item/storage/box/syndie_kit/space(src) //4 tc - new /obj/item/grenade/syndieminibomb/concussion/frag(src) // ~2 tc each? - new /obj/item/grenade/syndieminibomb/concussion/frag(src) + new /obj/item/grenade/frag(src) // ~2 tc each? + new /obj/item/grenade/frag(src) new /obj/item/flashlight/emp(src) if("bloodyspai") @@ -165,7 +165,7 @@ new /obj/item/assembly/signaler(src) // 0 tc new /obj/item/storage/toolbox/syndicate(src) // 1 tc new /obj/item/pen/edagger(src) - new /obj/item/gun/energy/wormhole_projector(src) + new /obj/item/gun/energy/wormhole_projector/core_inserted(src) new /obj/item/gun/energy/decloner/unrestricted(src) if("bee") @@ -367,6 +367,13 @@ for(var/i in 1 to 7) new /obj/item/reagent_containers/syringe/bioterror(src) +/obj/item/storage/box/syndie_kit/clownpins + name = "ultra hilarious firing pin box" + +/obj/item/storage/box/syndie_kit/clownpins/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/firing_pin/clown/ultra(src) + /obj/item/storage/box/syndie_kit/imp_adrenal name = "adrenal implant box" diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index 96480efd8b15..79c92c471265 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -253,12 +253,13 @@ /// After the initial stun period, we check to see if the target needs to have the stun applied. /obj/item/melee/baton/proc/apply_stun_effect_end(mob/living/target) var/trait_check = HAS_TRAIT(target, TRAIT_STUNRESISTANCE) //var since we check it in out to_chat as well as determine stun duration + if(!target.IsKnockdown()) + to_chat(target, "Your muscles seize, making you collapse[trait_check ? ", but your body quickly recovers..." : "!"]") + if(trait_check) target.Knockdown(stun_time * 0.1) else target.Knockdown(stun_time) - if(!target.IsKnockdown()) - to_chat(target, "Your muscles seize, making you collapse[trait_check ? ", but your body quickly recovers..." : "!"]") /obj/item/melee/baton/emp_act(severity) . = ..() @@ -318,7 +319,7 @@ convertible = FALSE custom_materials = list(/datum/material/iron = 10000, /datum/material/glass = 4000, /datum/material/silver = 10000, /datum/material/gold = 2000) -/obj/item/melee/baton/boomerang/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE) +/obj/item/melee/baton/boomerang/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE) if(turned_on) if(ishuman(thrower)) var/mob/living/carbon/human/H = thrower diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm index eb9123e5ceaf..ab3da28f2202 100644 --- a/code/game/objects/items/tanks/jetpack.dm +++ b/code/game/objects/items/tanks/jetpack.dm @@ -1,275 +1,275 @@ -/obj/item/tank/jetpack - name = "jetpack (empty)" - desc = "A tank of compressed gas for use as propulsion in zero-gravity areas. Use with caution." - icon_state = "jetpack" - item_state = "jetpack" - lefthand_file = 'icons/mob/inhands/equipment/jetpacks_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/jetpacks_righthand.dmi' - w_class = WEIGHT_CLASS_BULKY - distribute_pressure = ONE_ATMOSPHERE * O2STANDARD - actions_types = list(/datum/action/item_action/set_internals, /datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) - var/gas_type = /datum/gas/oxygen - var/on = FALSE - var/stabilizers = FALSE - var/full_speed = TRUE // If the jetpack will have a speedboost in space/nograv or not - var/datum/effect_system/trail_follow/ion/ion_trail - -/obj/item/tank/jetpack/Initialize() - . = ..() - ion_trail = new - ion_trail.auto_process = FALSE - ion_trail.set_up(src) - -/obj/item/tank/jetpack/populate_gas() - if(gas_type) - air_contents.assert_gas(gas_type) - air_contents.gases[gas_type][MOLES] = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C)) - -/obj/item/tank/jetpack/ui_action_click(mob/user, action) - if(istype(action, /datum/action/item_action/toggle_jetpack)) - cycle(user) - else if(istype(action, /datum/action/item_action/jetpack_stabilization)) - if(on) - stabilizers = !stabilizers - to_chat(user, "You turn the jetpack stabilization [stabilizers ? "on" : "off"].") - else - toggle_internals(user) - - -/obj/item/tank/jetpack/proc/cycle(mob/user) - if(user.incapacitated()) - return - - if(!on) - turn_on(user) - to_chat(user, "You turn the jetpack on.") - else - turn_off(user) - to_chat(user, "You turn the jetpack off.") - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - - -/obj/item/tank/jetpack/proc/turn_on(mob/user) - if(!allow_thrust(0.01, user)) - return - on = TRUE - icon_state = "[initial(icon_state)]-on" - ion_trail.start() - RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/move_react) - RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, .proc/pre_move_react) - if(full_speed) - user.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) - -/obj/item/tank/jetpack/proc/turn_off(mob/user) - on = FALSE - stabilizers = FALSE - icon_state = initial(icon_state) - ion_trail.stop() - UnregisterSignal(user, COMSIG_MOVABLE_MOVED) - UnregisterSignal(user, COMSIG_MOVABLE_PRE_MOVE) - user.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) - -/obj/item/tank/jetpack/proc/move_react(mob/user) - if(!on)//If jet dont work, it dont work - return - if(!user)//Don't allow jet self using - return - if(!isturf(user.loc))//You can't use jet in nowhere or from mecha/closet - return - if(!(user.is_flying() || user.is_floating()) || user.buckled)//You don't want use jet in gravity or while buckled. - return - if(user.pulledby)//You don't must use jet if someone pull you - return - if(user.throwing)//You don't must use jet if you thrown - return - if(length(user.client.keys_held & user.client.movement_keys))//You use jet when press keys. yes. - allow_thrust(0.01, user) - -/obj/item/tank/jetpack/proc/pre_move_react(mob/user) - ion_trail.oldposition = get_turf(src) - -/obj/item/tank/jetpack/proc/allow_thrust(num, mob/living/user) - if((num < 0.005 || air_contents.total_moles() < num)) - turn_off(user) - return - - var/datum/gas_mixture/removed = air_contents.remove(num) - if(removed.total_moles() < 0.005) - turn_off(user) - return - - var/turf/T = get_turf(user) - T.assume_air(removed) - ion_trail.generate_effect() - - return TRUE - -/obj/item/tank/jetpack/suicide_act(mob/user) - if (istype(user, /mob/living/carbon/human/)) - var/mob/living/carbon/human/H = user - H.forcesay("WHAT THE FUCK IS CARBON DIOXIDE?") - H.visible_message("[user] is suffocating [user.p_them()]self with [src]! It looks like [user.p_they()] didn't read what that jetpack says!") - return (OXYLOSS) - else - ..() - -/obj/item/tank/jetpack/improvised - name = "improvised jetpack" - desc = "A jetpack made from two air tanks, a fire extinguisher and some atmospherics equipment. It doesn't look like it can hold much." - icon_state = "jetpack-improvised" - item_state = "jetpack-sec" - volume = 20 //normal jetpacks have 70 volume - gas_type = null //it starts empty - full_speed = FALSE //moves at hardsuit jetpack speeds - -/obj/item/tank/jetpack/improvised/allow_thrust(num, mob/living/user) - if(!on) - return - if((num < 0.005 || air_contents.total_moles() < num)) - turn_off(user) - return - if(rand(0,250) == 0) - to_chat(user, "You feel your jetpack's engines cut out.") - turn_off(user) - return - - var/datum/gas_mixture/removed = air_contents.remove(num) - if(removed.total_moles() < 0.005) - turn_off(user) - return - - var/turf/T = get_turf(user) - T.assume_air(removed) - ion_trail.generate_effect() - - return TRUE - -/obj/item/tank/jetpack/void - name = "void jetpack (oxygen)" - desc = "It works well in a void." - icon_state = "jetpack-void" - item_state = "jetpack-void" - -/obj/item/tank/jetpack/oxygen - name = "jetpack (oxygen)" - desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." - icon_state = "jetpack" - item_state = "jetpack" - -/obj/item/tank/jetpack/oxygen/harness - name = "jet harness (oxygen)" - desc = "A lightweight tactical harness, used by those who don't want to be weighed down by traditional jetpacks." - icon_state = "jetpack-mini" - item_state = "jetpack-mini" - volume = 40 - throw_range = 7 - w_class = WEIGHT_CLASS_NORMAL - -/obj/item/tank/jetpack/oxygen/captain - name = "captain's jetpack" - desc = "A compact, lightweight jetpack containing a high amount of compressed oxygen." - icon_state = "jetpack-captain" - item_state = "jetpack-captain" - w_class = WEIGHT_CLASS_NORMAL - volume = 90 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF //steal objective items are hard to destroy. - -/obj/item/tank/jetpack/oxygen/security - name = "security jetpack (oxygen)" - desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas by security forces." - icon_state = "jetpack-sec" - item_state = "jetpack-sec" - - - -/obj/item/tank/jetpack/carbondioxide - name = "jetpack (carbon dioxide)" - desc = "A tank of compressed carbon dioxide for use as propulsion in zero-gravity areas. Painted black to indicate that it should not be used as a source for internals." - icon_state = "jetpack-black" - item_state = "jetpack-black" - distribute_pressure = 0 - gas_type = /datum/gas/carbon_dioxide - - -/obj/item/tank/jetpack/suit - name = "hardsuit jetpack upgrade" - desc = "A modular, compact set of thrusters designed to integrate with a hardsuit. It is fueled by a tank inserted into the suit's storage compartment." - icon_state = "jetpack-mining" - item_state = "jetpack-black" - w_class = WEIGHT_CLASS_NORMAL - actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) - volume = 1 - slot_flags = null - gas_type = null - full_speed = FALSE - var/datum/gas_mixture/temp_air_contents - var/obj/item/tank/internals/tank = null - var/mob/living/carbon/human/cur_user - -/obj/item/tank/jetpack/suit/Initialize() - . = ..() - STOP_PROCESSING(SSobj, src) - temp_air_contents = air_contents - -/obj/item/tank/jetpack/suit/attack_self() - return - -/obj/item/tank/jetpack/suit/cycle(mob/user) - if(!istype(loc, /obj/item/clothing/suit/space/hardsuit)) - to_chat(user, "\The [src] must be connected to a hardsuit!") - return - - var/mob/living/carbon/human/H = user - if(!istype(H.s_store, /obj/item/tank/internals)) - to_chat(user, "You need a tank in your suit storage!") - return - ..() - -/obj/item/tank/jetpack/suit/turn_on(mob/user) - if(!istype(loc, /obj/item/clothing/suit/space/hardsuit) || !ishuman(loc.loc) || loc.loc != user) - return - var/mob/living/carbon/human/H = user - tank = H.s_store - air_contents = tank.air_contents - START_PROCESSING(SSobj, src) - cur_user = user - ..() - -/obj/item/tank/jetpack/suit/turn_off(mob/user) - tank = null - air_contents = temp_air_contents - STOP_PROCESSING(SSobj, src) - cur_user = null - ..() - -/obj/item/tank/jetpack/suit/process() - if(!istype(loc, /obj/item/clothing/suit/space/hardsuit) || !ishuman(loc.loc)) - turn_off(cur_user) - return - var/mob/living/carbon/human/H = loc.loc - if(!tank || tank != H.s_store) - turn_off(cur_user) - return - ..() - - -//Return a jetpack that the mob can use -//Back worn jetpacks, hardsuit internal packs, and so on. -//Used in Process_Spacemove() and wherever you want to check for/get a jetpack - -/mob/proc/get_jetpack() - return - -/mob/living/carbon/get_jetpack() - var/obj/item/tank/jetpack/J = back - if(istype(J)) - return J - -/mob/living/carbon/human/get_jetpack() - var/obj/item/tank/jetpack/J = ..() - if(!istype(J) && istype(wear_suit, /obj/item/clothing/suit/space/hardsuit)) - var/obj/item/clothing/suit/space/hardsuit/C = wear_suit - J = C.jetpack - return J +/obj/item/tank/jetpack + name = "jetpack (empty)" + desc = "A tank of compressed gas for use as propulsion in zero-gravity areas. Use with caution." + icon_state = "jetpack" + item_state = "jetpack" + lefthand_file = 'icons/mob/inhands/equipment/jetpacks_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/jetpacks_righthand.dmi' + w_class = WEIGHT_CLASS_BULKY + distribute_pressure = ONE_ATMOSPHERE * O2STANDARD + actions_types = list(/datum/action/item_action/set_internals, /datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) + var/gas_type = /datum/gas/oxygen + var/on = FALSE + var/stabilizers = FALSE + var/full_speed = TRUE // If the jetpack will have a speedboost in space/nograv or not + var/datum/effect_system/trail_follow/ion/ion_trail + +/obj/item/tank/jetpack/Initialize() + . = ..() + ion_trail = new + ion_trail.auto_process = FALSE + ion_trail.set_up(src) + +/obj/item/tank/jetpack/populate_gas() + if(gas_type) + air_contents.assert_gas(gas_type) + air_contents.gases[gas_type][MOLES] = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C)) + +/obj/item/tank/jetpack/ui_action_click(mob/user, action) + if(istype(action, /datum/action/item_action/toggle_jetpack)) + cycle(user) + else if(istype(action, /datum/action/item_action/jetpack_stabilization)) + if(on) + stabilizers = !stabilizers + to_chat(user, "You turn the jetpack stabilization [stabilizers ? "on" : "off"].") + else + toggle_internals(user) + + +/obj/item/tank/jetpack/proc/cycle(mob/user) + if(user.incapacitated()) + return + + if(!on) + turn_on(user) + to_chat(user, "You turn the jetpack on.") + else + turn_off(user) + to_chat(user, "You turn the jetpack off.") + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + + +/obj/item/tank/jetpack/proc/turn_on(mob/user) + if(!allow_thrust(0.01, user)) + return + on = TRUE + icon_state = "[initial(icon_state)]-on" + ion_trail.start() + RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/move_react) + RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, .proc/pre_move_react) + if(full_speed) + user.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) + +/obj/item/tank/jetpack/proc/turn_off(mob/user) + on = FALSE + stabilizers = FALSE + icon_state = initial(icon_state) + ion_trail.stop() + UnregisterSignal(user, COMSIG_MOVABLE_MOVED) + UnregisterSignal(user, COMSIG_MOVABLE_PRE_MOVE) + user.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) + +/obj/item/tank/jetpack/proc/move_react(mob/user) + if(!on)//If jet dont work, it dont work + return + if(!user)//Don't allow jet self using + return + if(!isturf(user.loc))//You can't use jet in nowhere or from mecha/closet + return + if(!(user.is_flying() || user.is_floating()) || user.buckled)//You don't want use jet in gravity or while buckled. + return + if(user.pulledby)//You don't must use jet if someone pull you + return + if(user.throwing)//You don't must use jet if you thrown + return + if(length(user.client.keys_held & user.client.movement_keys))//You use jet when press keys. yes. + allow_thrust(0.01, user) + +/obj/item/tank/jetpack/proc/pre_move_react(mob/user) + ion_trail.oldposition = get_turf(src) + +/obj/item/tank/jetpack/proc/allow_thrust(num, mob/living/user) + if((num < 0.005 || air_contents.total_moles() < num)) + turn_off(user) + return + + var/datum/gas_mixture/removed = air_contents.remove(num) + if(removed.total_moles() < 0.005) + turn_off(user) + return + + var/turf/T = get_turf(user) + T.assume_air(removed) + ion_trail.generate_effect() + + return TRUE + +/obj/item/tank/jetpack/suicide_act(mob/user) + if (istype(user, /mob/living/carbon/human/)) + var/mob/living/carbon/human/H = user + H.forcesay("WHAT THE FUCK IS CARBON DIOXIDE?") + H.visible_message("[user] is suffocating [user.p_them()]self with [src]! It looks like [user.p_they()] didn't read what that jetpack says!") + return (OXYLOSS) + else + ..() + +/obj/item/tank/jetpack/improvised + name = "improvised jetpack" + desc = "A jetpack made from two air tanks, a fire extinguisher and some atmospherics equipment. It doesn't look like it can hold much." + icon_state = "jetpack-improvised" + item_state = "jetpack-sec" + volume = 20 //normal jetpacks have 70 volume + gas_type = null //it starts empty + full_speed = FALSE //moves at hardsuit jetpack speeds + +/obj/item/tank/jetpack/improvised/allow_thrust(num, mob/living/user) + if(!on) + return + if((num < 0.005 || air_contents.total_moles() < num)) + turn_off(user) + return + if(rand(0,250) == 0) + to_chat(user, "You feel your jetpack's engines cut out.") + turn_off(user) + return + + var/datum/gas_mixture/removed = air_contents.remove(num) + if(removed.total_moles() < 0.005) + turn_off(user) + return + + var/turf/T = get_turf(user) + T.assume_air(removed) + ion_trail.generate_effect() + + return TRUE + +/obj/item/tank/jetpack/void + name = "void jetpack (oxygen)" + desc = "It works well in a void." + icon_state = "jetpack-void" + item_state = "jetpack-void" + +/obj/item/tank/jetpack/oxygen + name = "jetpack (oxygen)" + desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." + icon_state = "jetpack" + item_state = "jetpack" + +/obj/item/tank/jetpack/oxygen/harness + name = "jet harness (oxygen)" + desc = "A lightweight tactical harness, used by those who don't want to be weighed down by traditional jetpacks." + icon_state = "jetpack-mini" + item_state = "jetpack-mini" + volume = 40 + throw_range = 7 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/tank/jetpack/oxygen/captain + name = "captain's jetpack" + desc = "A compact, lightweight jetpack containing a high amount of compressed oxygen." + icon_state = "jetpack-captain" + item_state = "jetpack-captain" + w_class = WEIGHT_CLASS_NORMAL + volume = 90 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF //steal objective items are hard to destroy. + +/obj/item/tank/jetpack/oxygen/security + name = "security jetpack (oxygen)" + desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas by security forces." + icon_state = "jetpack-sec" + item_state = "jetpack-sec" + + + +/obj/item/tank/jetpack/carbondioxide + name = "jetpack (carbon dioxide)" + desc = "A tank of compressed carbon dioxide for use as propulsion in zero-gravity areas. Painted black to indicate that it should not be used as a source for internals." + icon_state = "jetpack-black" + item_state = "jetpack-black" + distribute_pressure = 0 + gas_type = /datum/gas/carbon_dioxide + + +/obj/item/tank/jetpack/suit + name = "hardsuit jetpack upgrade" + desc = "A modular, compact set of thrusters designed to integrate with a hardsuit. It is fueled by a tank inserted into the suit's storage compartment." + icon_state = "jetpack-mining" + item_state = "jetpack-black" + w_class = WEIGHT_CLASS_NORMAL + actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) + volume = 1 + slot_flags = null + gas_type = null + full_speed = FALSE + var/datum/gas_mixture/temp_air_contents + var/obj/item/tank/internals/tank = null + var/mob/living/carbon/human/cur_user + +/obj/item/tank/jetpack/suit/Initialize() + . = ..() + STOP_PROCESSING(SSobj, src) + temp_air_contents = air_contents + +/obj/item/tank/jetpack/suit/attack_self() + return + +/obj/item/tank/jetpack/suit/cycle(mob/user) + if(!istype(loc, /obj/item/clothing/suit/space/hardsuit)) + to_chat(user, "\The [src] must be connected to a hardsuit!") + return + + var/mob/living/carbon/human/H = user + if(!istype(H.s_store, /obj/item/tank/internals)) + to_chat(user, "You need a tank in your suit storage!") + return + ..() + +/obj/item/tank/jetpack/suit/turn_on(mob/user) + if(!istype(loc, /obj/item/clothing/suit/space/hardsuit) || !ishuman(loc.loc) || loc.loc != user) + return + var/mob/living/carbon/human/H = user + tank = H.s_store + air_contents = tank.air_contents + START_PROCESSING(SSobj, src) + cur_user = user + ..() + +/obj/item/tank/jetpack/suit/turn_off(mob/user) + tank = null + air_contents = temp_air_contents + STOP_PROCESSING(SSobj, src) + cur_user = null + ..() + +/obj/item/tank/jetpack/suit/process() + if(!istype(loc, /obj/item/clothing/suit/space/hardsuit) || !ishuman(loc.loc)) + turn_off(cur_user) + return + var/mob/living/carbon/human/H = loc.loc + if(!tank || tank != H.s_store) + turn_off(cur_user) + return + ..() + + +//Return a jetpack that the mob can use +//Back worn jetpacks, hardsuit internal packs, and so on. +//Used in Process_Spacemove() and wherever you want to check for/get a jetpack + +/mob/proc/get_jetpack() + return + +/mob/living/carbon/get_jetpack() + var/obj/item/tank/jetpack/J = back + if(istype(J)) + return J + +/mob/living/carbon/human/get_jetpack() + var/obj/item/tank/jetpack/J = ..() + if(!istype(J) && istype(wear_suit, /obj/item/clothing/suit/space/hardsuit)) + var/obj/item/clothing/suit/space/hardsuit/C = wear_suit + J = C.jetpack + return J diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm index b39f240205e8..b1c42c0fcd60 100644 --- a/code/game/objects/items/tanks/tanks.dm +++ b/code/game/objects/items/tanks/tanks.dm @@ -152,7 +152,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "tanks", name, 400, 120, master_ui, state) + ui = new(user, src, ui_key, "Tank", name, 400, 120, master_ui, state) ui.open() /obj/item/tank/ui_data(mob/user) diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm index 184a117460fe..ccc14a8dddf4 100644 --- a/code/game/objects/items/tanks/watertank.dm +++ b/code/game/objects/items/tanks/watertank.dm @@ -410,7 +410,7 @@ return var/used_amount = injection_amount/usage_ratio - reagents.reaction(user, INJECT,injection_amount,0) + reagents.expose(user, INJECT,injection_amount,0) reagents.trans_to(user,used_amount,multiplier=usage_ratio) update_icon() user.update_inv_back() //for overlays update diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm index 31b0dee6b11f..1d3c412f0b27 100644 --- a/code/game/objects/items/teleportation.dm +++ b/code/game/objects/items/teleportation.dm @@ -1,4 +1,3 @@ - #define SOURCE_PORTAL 1 #define DESTINATION_PORTAL 2 @@ -25,91 +24,75 @@ throw_speed = 3 throw_range = 7 custom_materials = list(/datum/material/iron=400) + var/tracking_range = 20 -/obj/item/locator/attack_self(mob/user) - user.set_machine(src) - var/dat - if (temp) - dat = "[temp]

Clear" - else - dat = {" -Persistent Signal Locator
-Refresh"} - user << browse(dat, "window=radio") - onclose(user, "radio") - return - -/obj/item/locator/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained()) - return - var/turf/current_location = get_turf(usr)//What turf is the user on? - if(!current_location || is_centcom_level(current_location.z))//If turf was not found or they're on CentCom - to_chat(usr, "[src] is malfunctioning.") - return - if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc))) - usr.set_machine(src) - if (href_list["refresh"]) - temp = "Persistent Signal Locator
" - var/turf/sr = get_turf(src) - - if (sr) - temp += "Beacon Signals:
" - for(var/obj/item/beacon/W in GLOB.teleportbeacons) - if (!W.renamed) - continue - var/turf/tr = get_turf(W) - if (tr.z == sr.z && tr) - var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) - if (direct < 5) - direct = "very strong" - else - if (direct < 10) - direct = "strong" - else - if (direct < 20) - direct = "weak" - else - direct = "very weak" - temp += "[W.name]-[dir2text(get_dir(sr, tr))]-[direct]
" - - temp += "Implant Signals:
" - for (var/obj/item/implant/tracking/W in GLOB.tracked_implants) - if (!W.imp_in || !isliving(W.loc)) - continue - else - var/mob/living/M = W.loc - if (M.stat == DEAD) - if (M.timeofdeath + W.lifespan_postmortem < world.time) - continue - - var/turf/tr = get_turf(W) - if (tr.z == sr.z && tr) - var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) - if (direct < 20) - if (direct < 5) - direct = "very strong" - else - if (direct < 10) - direct = "strong" - else - direct = "weak" - temp += "[W.imp_in.name]-[dir2text(get_dir(sr, tr))]-[direct]
" - - temp += "You are at \[[sr.x],[sr.y],[sr.z]\] in orbital coordinates.

Refresh
" +/obj/item/locator/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "BluespaceLocator", name, 300, 300, master_ui, state) + ui.open() + +/obj/item/locator/ui_data(mob/user) + var/list/data = list() + + data["trackingrange"] = tracking_range; + + // Get our current turf location. + var/turf/sr = get_turf(src) + + if (sr) + // Check every teleport beacon. + var/list/tele_beacons = list() + for(var/obj/item/beacon/W in GLOB.teleportbeacons) + + // Get the tracking beacon's turf location. + var/turf/tr = get_turf(W) + + // Make sure it's on a turf and that its Z-level matches the tracker's Z-level + if (tr && tr.z == sr.z) + // Get the distance between the beacon's turf and our turf + var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) + + // If the target is too far away, skip over this beacon. + if(distance > tracking_range) + continue + + var/beacon_name + + if(W.renamed) + beacon_name = W.name + else + var/area/A = get_area(W) + beacon_name = A.name + + var/D = dir2text(get_dir(sr, tr)) + tele_beacons += list(list(name = beacon_name, direction = D, distance = distance)) + + data["telebeacons"] = tele_beacons + + var/list/track_implants = list() + + for (var/obj/item/implant/tracking/W in GLOB.tracked_implants) + if (!W.imp_in || !isliving(W.loc)) + continue else - temp += "Processing Error: Unable to locate orbital position.
" - else - if (href_list["temp"]) - temp = null - if (ismob(src.loc)) - attack_self(src.loc) - else - for(var/mob/M in viewers(1, src)) - if (M.client) - src.attack_self(M) - return + var/mob/living/M = W.loc + if (M.stat == DEAD) + if (M.timeofdeath + W.lifespan_postmortem < world.time) + continue + var/turf/tr = get_turf(W) + var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) + + if(distance > tracking_range) + continue + + var/D = dir2text(get_dir(sr, tr)) + track_implants += list(list(name = W.imp_in.name, direction = D, distance = distance)) + data["trackimplants"] = track_implants + return data +/obj/machinery/my_machine/ui_act(action, params) + if(..()) return /* * Hand-tele diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index d4595e5bbb0c..613d3be39dc8 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -22,6 +22,7 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) drop_sound = 'sound/items/handling/screwdriver_drop.ogg' pickup_sound = 'sound/items/handling/screwdriver_pickup.ogg' + item_flags = EYE_STAB var/random_color = TRUE //if the screwdriver uses random coloring var/static/list/screwdriver_colors = list( "blue" = rgb(24, 97, 213), @@ -72,18 +73,6 @@ else return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', icon_state) -/obj/item/screwdriver/attack(mob/living/carbon/M, mob/living/carbon/user) - if(!istype(M)) - return ..() - if(user.zone_selected != BODY_ZONE_PRECISE_EYES && user.zone_selected != BODY_ZONE_HEAD) - return ..() - if(HAS_TRAIT(user, TRAIT_PACIFISM)) - to_chat(user, "You don't want to harm [M]!") - return - if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) - M = user - return eyestab(M,user) - /obj/item/screwdriver/abductor name = "alien screwdriver" desc = "An ultrasonic screwdriver." diff --git a/code/game/objects/items/toy_mechs.dm b/code/game/objects/items/toy_mechs.dm new file mode 100644 index 000000000000..7f39b8f8869c --- /dev/null +++ b/code/game/objects/items/toy_mechs.dm @@ -0,0 +1,632 @@ +/** + * Mech prizes + MECHA COMBAT!! + */ + +/// Mech battle special attack types. +#define SPECIAL_ATTACK_HEAL 1 +#define SPECIAL_ATTACK_DAMAGE 2 +#define SPECIAL_ATTACK_UTILITY 3 +#define SPECIAL_ATTACK_OTHER 4 + +/// Max length of a mech battle +#define MAX_BATTLE_LENGTH 50 + +/obj/item/toy/prize + icon = 'icons/obj/toy.dmi' + icon_state = "ripleytoy" + verb_say = "beeps" + verb_ask = "beeps" + verb_exclaim = "beeps" + verb_yell = "beeps" + w_class = WEIGHT_CLASS_SMALL + /// Timer when it'll be off cooldown + var/timer = 0 + /// Cooldown between play sessions + var/cooldown = 1.5 SECONDS + /// Cooldown multiplier after a battle (by default: battle cooldowns are 30 seconds) + var/cooldown_multiplier = 20 + /// If it makes noise when played with + var/quiet = FALSE + /// TRUE = Offering battle to someone || FALSE = Not offering battle + var/wants_to_battle = FALSE + /// TRUE = in combat currently || FALSE = Not in combat + var/in_combat = FALSE + /// The mech's health in battle + var/combat_health = 0 + /// The mech's max combat health + var/max_combat_health = 0 + /// TRUE = the special attack is charged || FALSE = not charged + var/special_attack_charged = FALSE + /// What type of special attack they use - SPECIAL_ATTACK_DAMAGE, SPECIAL_ATTACK_HEAL, SPECIAL_ATTACK_UTILITY, SPECIAL_ATTACK_OTHER + var/special_attack_type = 0 + /// What message their special move gets on examining + var/special_attack_type_message = "" + /// The battlecry when using the special attack + var/special_attack_cry = "*flip" + /// Current cooldown of their special attack + var/special_attack_cooldown = 0 + /// This mech's win count in combat + var/wins = 0 + /// ...And their loss count in combat + var/losses = 0 + +/obj/item/toy/prize/Initialize() + . = ..() + desc = "Mini-Mecha action figure! Collect them all! Attack your friends or another mech with one to initiate epic mech combat! [desc]." + combat_health = max_combat_health + switch(special_attack_type) + if(SPECIAL_ATTACK_DAMAGE) + special_attack_type_message = "an aggressive move, which deals bonus damage." + if(SPECIAL_ATTACK_HEAL) + special_attack_type_message = "a defensive move, which grants bonus healing." + if(SPECIAL_ATTACK_UTILITY) + special_attack_type_message = "a utility move, which heals the user and damages the opponent." + if(SPECIAL_ATTACK_OTHER) + special_attack_type_message = "a special move, which [special_attack_type_message]" + else + special_attack_type_message = "a mystery move, even I don't know." + +/** + * this proc combines "sleep" while also checking for if the battle should continue + * + * this goes through some of the checks - the toys need to be next to each other to fight! + * if it's player vs themself: They need to be able to "control" both mechs (either must be adjacent or using TK) + * if it's player vs player: Both players need to be able to "control" their mechs (either must be adjacent or using TK) + * if it's player vs mech (suicide): the mech needs to be in range of the player + * if all the checks are TRUE, it does the sleeps, and returns TRUE. Otherwise, it returns FALSE. + * Arguments: + * * delay - the amount of time the sleep at the end of the check will sleep for + * * attacker - the attacking toy in the battle. + * * attacker_controller - the controller of the attacking toy. there should ALWAYS be an attacker_controller + * * opponent - (optional) the defender controller in the battle, for PvP + */ +/obj/item/toy/prize/proc/combat_sleep(var/delay, obj/item/toy/prize/attacker, mob/living/carbon/attacker_controller, mob/living/carbon/opponent) + if(!attacker_controller) + return FALSE + + if(!attacker) //if there's no attacker, then attacker_controller IS the attacker + if(!in_range(src, attacker_controller)) + attacker_controller.visible_message("[attacker_controller] is running from [src]! The coward!") + return FALSE + else // if there's an attacker, we can procede as normal + if(!in_range(src, attacker)) //and the two toys aren't next to each other, the battle ends + attacker_controller.visible_message(" [attacker] and [src] separate, ending the battle. ", \ + " [attacker] and [src] separate, ending the battle. ") + return FALSE + + //dead men tell no tales, incapacitated men fight no fights + if(attacker_controller.incapacitated()) + return FALSE + //if the attacker_controller isn't next to the attacking toy (and doesn't have telekinesis), the battle ends + if(!in_range(attacker, attacker_controller) && !(attacker_controller.dna.check_mutation(TK))) + attacker_controller.visible_message(" [attacker_controller.name] seperates from [attacker], ending the battle.", \ + " You separate from [attacker], ending the battle. ") + return FALSE + + //if it's PVP and the opponent is not next to the defending(src) toy (and doesn't have telekinesis), the battle ends + if(opponent) + if(opponent.incapacitated()) + return FALSE + if(!in_range(src, opponent) && !(opponent.dna.check_mutation(TK))) + opponent.visible_message(" [opponent.name] seperates from [src], ending the battle.", \ + " You separate from [src], ending the battle. ") + return FALSE + //if it's not PVP and the attacker_controller isn't next to the defending toy (and doesn't have telekinesis), the battle ends + else + if (!in_range(src, attacker_controller) && !(attacker_controller.dna.check_mutation(TK))) + attacker_controller.visible_message(" [attacker_controller.name] seperates from [src] and [attacker], ending the battle.", \ + " You separate [attacker] and [src], ending the battle. ") + return FALSE + + //if all that is good, then we can sleep peacefully + sleep(delay) + return TRUE + +//all credit to skasi for toy mech fun ideas +/obj/item/toy/prize/attack_self(mob/user) + if(timer < world.time) + to_chat(user, "You play with [src].") + timer = world.time + cooldown + if(!quiet) + playsound(user, 'sound/mecha/mechstep.ogg', 20, TRUE) + else + . = ..() + +/obj/item/toy/prize/attack_hand(mob/user) + . = ..() + if(.) + return + if(loc == user) + attack_self(user) + +/** + * If you attack a mech with a mech, initiate combat between them + */ +/obj/item/toy/prize/attackby(obj/item/user_toy, mob/living/user) + if(istype(user_toy, /obj/item/toy/prize)) + var/obj/item/toy/prize/P = user_toy + if(check_battle_start(user, P)) + mecha_brawl(P, user) + ..() + +/** + * Attack is called from the user's toy, aimed at target(another human), checking for target's toy. + */ +/obj/item/toy/prize/attack(mob/living/carbon/human/target, mob/living/carbon/human/user) + if(target == user) + to_chat(user, "Target another toy mech if you want to start a battle with yourself.") + return + else if(user.a_intent != INTENT_HARM) + if(wants_to_battle) //prevent spamming someone with offers + to_chat(user, "You already are offering battle to someone!") + return + if(!check_battle_start(user)) //if the user's mech isn't ready, don't bother checking + return + + for(var/obj/item/I in target.held_items) + if(istype(I, /obj/item/toy/prize)) //if you attack someone with a mech who's also holding a mech, offer to battle them + var/obj/item/toy/prize/P = I + if(!P.check_battle_start(target, null, user)) //check if the attacker mech is ready + break + + //slap them with the metaphorical white glove + if(P.wants_to_battle) //if the target mech wants to battle, initiate the battle from their POV + mecha_brawl(P, target, user) //P = defender's mech / SRC = attacker's mech / target = defender / user = attacker + P.wants_to_battle = FALSE + return + + //extend the offer of battle to the other mech + to_chat(user, "You offer battle to [target.name]!") + to_chat(target, "[user.name] wants to battle with [user.p_their()] [name]! Attack them with a toy mech to initiate combat.") + wants_to_battle = TRUE + addtimer(CALLBACK(src, .proc/withdraw_offer, user), 6 SECONDS) + return + + ..() + +/** + * Overrides attack_tk - Sorry, you have to be face to face to initiate a battle, it's good sportsmanship + */ +/obj/item/toy/prize/attack_tk(mob/user) + if(timer < world.time) + to_chat(user, "You telekinetically play with [src].") + timer = world.time + cooldown + if(!quiet) + playsound(user, 'sound/mecha/mechstep.ogg', 20, TRUE) + +/** + * Resets the request for battle. + * + * For use in a timer, this proc resets the wants_to_battle variable after a short period. + * Arguments: + * * user - the user wanting to do battle + */ +/obj/item/toy/prize/proc/withdraw_offer(mob/living/carbon/user) + if(wants_to_battle) + wants_to_battle = FALSE + to_chat(user, "You get the feeling they don't want to battle.") +/** + * Starts a battle, toy mech vs player. Player... doesn't win. + */ +/obj/item/toy/prize/suicide_act(mob/living/carbon/user) + if(in_combat) + to_chat(user, "[src] is in battle, let it finish first.") + return + + user.visible_message("[user] begins a fight [user.p_they()] can't win with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + + in_combat = TRUE + sleep(1.5 SECONDS) + for(var/i in 1 to 4) + switch(i) + if(1, 3) + SpinAnimation(5, 0) + playsound(src, 'sound/mecha/mechstep.ogg', 30, TRUE) + user.adjustBruteLoss(25) + user.adjustStaminaLoss(50) + if(2) + user.SpinAnimation(5, 0) + playsound(user, 'sound/weapons/smash.ogg', 20, TRUE) + combat_health-- //we scratched it! + if(4) + say(special_attack_cry + "!!") + user.adjustStaminaLoss(25) + + if(!combat_sleep(1 SECONDS, null, user)) + say("PATHETIC.") + combat_health = max_combat_health + in_combat = FALSE + return SHAME + + sleep(0.5 SECONDS) + user.adjustBruteLoss(450) + + in_combat = FALSE + say("AN EASY WIN. MY POWER INCREASES.") // steal a soul, become swole + add_atom_colour(rgb(255, 115, 115), ADMIN_COLOUR_PRIORITY) + max_combat_health = round(max_combat_health*1.5 + 0.1) + combat_health = max_combat_health + wins++ + return BRUTELOSS + +/obj/item/toy/prize/examine() + . = ..() + . += "This toy's special attack is [special_attack_cry], [special_attack_type_message] " + if(in_combat) + . += "This toy has a maximum health of [max_combat_health]. Currently, it's [combat_health]." + . += "Its special move light is [special_attack_cooldown? "flashing red." : "green and is ready!"]" + else + . += "This toy has a maximum health of [max_combat_health]." + + if(wins || losses) + . += "This toy has [wins] wins, and [losses] losses." + +/** + * Override the say proc if they're mute + */ +/obj/item/toy/prize/say() + if(!quiet) + . = ..() + +/** + * The 'master' proc of the mech battle. Processes the entire battle's events and makes sure it start and finishes correctly. + * + * src is the defending toy, and the battle proc is called on it to begin the battle. + * After going through a few checks at the beginning to ensure the battle can start properly, the battle begins a loop that lasts + * until either toy has no more health. During this loop, it also ensures the mechs stay in combat range of each other. + * It will then randomly decide attacks for each toy, occasionally making one or the other use their special attack. + * When either mech has no more health, the loop ends, and it displays the victor and the loser while updating their stats and resetting them. + * Arguments: + * * attacker - the attacking toy, the toy in the attacker_controller's hands + * * attacker_controller - the user, the one who is holding the toys / controlling the fight + * * opponent - optional arg used in Mech PvP battles: the other person who is taking part in the fight (controls src) + */ +/obj/item/toy/prize/proc/mecha_brawl(obj/item/toy/prize/attacker, mob/living/carbon/attacker_controller, mob/living/carbon/opponent) + //A GOOD DAY FOR A SWELL BATTLE! + attacker_controller.visible_message(" [attacker_controller.name] collides [attacker] with [src]! Looks like they're preparing for a brawl! ", \ + " You collide [attacker] into [src], sparking a fierce battle! ", \ + " You hear hard plastic smacking into hard plastic.", COMBAT_MESSAGE_RANGE) + + /// Who's in control of the defender (src)? + var/mob/living/carbon/src_controller = (opponent)? opponent : attacker_controller + /// How long has the battle been going? + var/battle_length = 0 + + in_combat = TRUE + attacker.in_combat = TRUE + + //1.5 second cooldown * 20 = 30 second cooldown after a fight + timer = world.time + cooldown*cooldown_multiplier + attacker.timer = world.time + attacker.cooldown*attacker.cooldown_multiplier + + sleep(1 SECONDS) + //--THE BATTLE BEGINS-- + while(combat_health > 0 && attacker.combat_health > 0 && battle_length < MAX_BATTLE_LENGTH) + if(!combat_sleep(0.5 SECONDS, attacker, attacker_controller, opponent)) //combat_sleep checks everything we need to have checked for combat to continue + break + + //before we do anything - deal with charged attacks + if(special_attack_charged) + src_controller.visible_message(" [src] unleashes its special attack!! ", \ + " You unleash [src]'s special attack! ") + special_attack_move(attacker) + else if(attacker.special_attack_charged) + + attacker_controller.visible_message(" [attacker] unleashes its special attack!! ", \ + " You unleash [attacker]'s special attack! ") + attacker.special_attack_move(src) + else + //process the cooldowns + if(special_attack_cooldown > 0) + special_attack_cooldown-- + if(attacker.special_attack_cooldown > 0) + attacker.special_attack_cooldown-- + + //combat commences + switch(rand(1,8)) + if(1 to 3) //attacker wins + if(attacker.special_attack_cooldown == 0 && attacker.combat_health <= round(attacker.max_combat_health/3)) //if health is less than 1/3 and special off CD, use it + attacker.special_attack_charged = TRUE + attacker_controller.visible_message(" [attacker] begins charging its special attack!! ", \ + " You begin charging [attacker]'s special attack! ") + else //just attack + attacker.SpinAnimation(5, 0) + playsound(attacker, 'sound/mecha/mechstep.ogg', 30, TRUE) + combat_health-- + attacker_controller.visible_message(" [attacker] devastates [src]! ", \ + " You ram [attacker] into [src]! ", \ + " You hear hard plastic smacking hard plastic.", COMBAT_MESSAGE_RANGE) + if(prob(5)) + combat_health-- + playsound(src, 'sound/effects/meteorimpact.ogg', 20, TRUE) + attacker_controller.visible_message(" ...and lands a CRIPPLING BLOW! ", \ + " ...and you land a CRIPPLING blow on [src]! ", null, COMBAT_MESSAGE_RANGE) + + if(4) //both lose + attacker.SpinAnimation(5, 0) + SpinAnimation(5, 0) + combat_health-- + attacker.combat_health-- + do_sparks(2, FALSE, src) + do_sparks(2, FALSE, attacker) + if(prob(50)) + attacker_controller.visible_message(" [attacker] and [src] clash dramatically, causing sparks to fly! ", \ + " [attacker] and [src] clash dramatically, causing sparks to fly! ", \ + " You hear hard plastic rubbing against hard plastic.", COMBAT_MESSAGE_RANGE) + else + src_controller.visible_message(" [src] and [attacker] clash dramatically, causing sparks to fly! ", \ + " [src] and [attacker] clash dramatically, causing sparks to fly! ", \ + " You hear hard plastic rubbing against hard plastic.", COMBAT_MESSAGE_RANGE) + if(5) //both win + playsound(attacker, 'sound/weapons/parry.ogg', 20, TRUE) + if(prob(50)) + attacker_controller.visible_message(" [src]'s attack deflects off of [attacker]. ", \ + " [src]'s attack deflects off of [attacker]. ", \ + " You hear hard plastic bouncing off hard plastic.", COMBAT_MESSAGE_RANGE) + else + src_controller.visible_message(" [attacker]'s attack deflects off of [src]. ", \ + " [attacker]'s attack deflects off of [src]. ", \ + " You hear hard plastic bouncing off hard plastic.", COMBAT_MESSAGE_RANGE) + + if(6 to 8) //defender wins + if(special_attack_cooldown == 0 && combat_health <= round(max_combat_health/3)) //if health is less than 1/3 and special off CD, use it + special_attack_charged = TRUE + src_controller.visible_message(" [src] begins charging its special attack!! ", \ + " You begin charging [src]'s special attack! ") + else //just attack + SpinAnimation(5, 0) + playsound(src, 'sound/mecha/mechstep.ogg', 30, TRUE) + attacker.combat_health-- + src_controller.visible_message(" [src] smashes [attacker]! ", \ + " You smash [src] into [attacker]! ", \ + " You hear hard plastic smashing hard plastic.", COMBAT_MESSAGE_RANGE) + if(prob(5)) + attacker.combat_health-- + playsound(attacker, 'sound/effects/meteorimpact.ogg', 20, TRUE) + src_controller.visible_message(" ...and lands a CRIPPLING BLOW! ", \ + " ...and you land a CRIPPLING blow on [attacker]! ", null, COMBAT_MESSAGE_RANGE) + else + attacker_controller.visible_message(" [src] and [attacker] stand around awkwardly.", \ + " You don't know what to do next.") + + battle_length++ + sleep(0.5 SECONDS) + + /// Lines chosen for the winning mech + var/list/winlines = list("YOU'RE NOTHING BUT SCRAP!", "I'LL YIELD TO NONE!", "GLORY IS MINE!", "AN EASY FIGHT.", "YOU SHOULD HAVE NEVER FACED ME.", "ROCKED AND SOCKED.") + + if(attacker.combat_health <= 0 && combat_health <= 0) //both lose + playsound(src, 'sound/machines/warning-buzzer.ogg', 20, TRUE) + attacker_controller.visible_message(" MUTUALLY ASSURED DESTRUCTION!! [src] and [attacker] both end up destroyed!", \ + " Both [src] and [attacker] are destroyed!") + else if(attacker.combat_health <= 0) //src wins + wins++ + attacker.losses++ + playsound(attacker, 'sound/effects/light_flicker.ogg', 20, TRUE) + attacker_controller.visible_message(" [attacker] falls apart!", \ + " [attacker] falls apart!", null, COMBAT_MESSAGE_RANGE) + say("[pick(winlines)]") + src_controller.visible_message(" [src] destroys [attacker] and walks away victorious!", \ + " You raise up [src] victoriously over [attacker]!") + else if (combat_health <= 0) //attacker wins + attacker.wins++ + losses++ + playsound(src, 'sound/effects/light_flicker.ogg', 20, TRUE) + src_controller.visible_message(" [src] collapses!", \ + " [src] collapses!", null, COMBAT_MESSAGE_RANGE) + attacker.say("[pick(winlines)]") + attacker_controller.visible_message(" [attacker] demolishes [src] and walks away victorious!", \ + " You raise up [attacker] proudly over [src]!") + else //both win? + say("NEXT TIME.") + //don't want to make this a one sided conversation + quiet? attacker.say("I WENT EASY ON YOU.") : attacker.say("OF COURSE.") + + in_combat = FALSE + attacker.in_combat = FALSE + + combat_health = max_combat_health + attacker.combat_health = attacker.max_combat_health + + return + +/** + * This proc checks if a battle can be initiated between src and attacker. + * + * Both SRC and attacker (if attacker is included) timers are checked if they're on cooldown, and + * both SRC and attacker (if attacker is included) are checked if they are in combat already. + * If any of the above are true, the proc returns FALSE and sends a message to user (and target, if included) otherwise, it returns TRUE + * Arguments: + * * user: the user who is initiating the battle + * * attacker: optional arg for checking two mechs at once + * * target: optional arg used in Mech PvP battles (if used, attacker is target's toy) + */ +/obj/item/toy/prize/proc/check_battle_start(mob/living/carbon/user, obj/item/toy/prize/attacker, mob/living/carbon/target) + if(attacker && attacker.in_combat) + to_chat(user, "[target?target.p_their() : "Your" ] [attacker.name] is in combat.") + target?.to_chat(target, "Your [attacker.name] is in combat.") + return FALSE + if(in_combat) + to_chat(user, "Your [name] is in combat.") + target?.to_chat(target, "[target.p_their()] [name] is in combat.") + return FALSE + if(attacker && attacker.timer > world.time) + to_chat(user, "[target?target.p_their() : "Your" ] [attacker.name] isn't ready for battle.") + target?.to_chat(target, "Your [attacker.name] isn't ready for battle.") + return FALSE + if(timer > world.time) + to_chat(user, "Your [name] isn't ready for battle.") + target?.to_chat(target, "[target.p_their()] [name] isn't ready for battle.") + return FALSE + + return TRUE + +/** + * Processes any special attack moves that happen in the battle (called in the mechaBattle proc). + * + * Makes the toy shout their special attack cry and updates its cooldown. Then, does the special attack. + * Arguments: + * * victim - the toy being hit by the special move + */ +/obj/item/toy/prize/proc/special_attack_move(obj/item/toy/prize/victim) + say(special_attack_cry + "!!") + + special_attack_charged = FALSE + special_attack_cooldown = 3 + + switch(special_attack_type) + if(SPECIAL_ATTACK_DAMAGE) //+2 damage + victim.combat_health-=2 + playsound(src, 'sound/weapons/marauder.ogg', 20, TRUE) + if(SPECIAL_ATTACK_HEAL) //+2 healing + combat_health+=2 + playsound(src, 'sound/mecha/mech_shield_raise.ogg', 20, TRUE) + if(SPECIAL_ATTACK_UTILITY) //+1 heal, +1 damage + victim.combat_health-- + combat_health++ + playsound(src, 'sound/mecha/mechmove01.ogg', 30, TRUE) + if(SPECIAL_ATTACK_OTHER) //other + super_special_attack(victim) + else + say("I FORGOT MY SPECIAL ATTACK...") + +/** + * Base proc for 'other' special attack moves. + * + * This one is only for inheritance, each mech with an 'other' type move has their procs below. + * Arguments: + * * victim - the toy being hit by the super special move (doesn't necessarily need to be used) + */ +/obj/item/toy/prize/proc/super_special_attack(obj/item/toy/prize/victim) + visible_message(" [src] does a cool flip.") + +/obj/item/toy/prize/ripley + name = "toy Ripley" + desc = "1/13" + max_combat_health = 4 //200 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "GIGA DRILL BREAK" + +/obj/item/toy/prize/fireripley //rip + name = "toy Firefighting Ripley" + desc = "2/13" + icon_state = "fireripleytoy" + max_combat_health = 5 //250 integrity? + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "FIRE SHIELD" + +/obj/item/toy/prize/deathripley + name = "toy Deathsquad Ripley" + desc = "3/13" + icon_state = "deathripleytoy" + max_combat_health = 5 //250 integrity + special_attack_type = SPECIAL_ATTACK_OTHER + special_attack_type_message = "instantly destroys the opposing mech if its health is less than this mech's health." + special_attack_cry = "KILLER CLAMP" + +/obj/item/toy/prize/deathripley/super_special_attack(obj/item/toy/prize/victim) + playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 20, TRUE) + if(victim.combat_health < combat_health) //Instantly kills the other mech if it's health is below our's. + say("EXECUTE!!") + victim.combat_health = 0 + else //Otherwise, just deal one damage. + victim.combat_health-- + +/obj/item/toy/prize/gygax + name = "toy Gygax" + desc = "4/13" + icon_state = "gygaxtoy" + max_combat_health = 5 //250 integrity + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "SUPER SERVOS" + +/obj/item/toy/prize/durand + name = "toy Durand" + desc = "5/13" + icon_state = "durandtoy" + max_combat_health = 6 //400 integrity + special_attack_type = SPECIAL_ATTACK_HEAL + special_attack_cry = "SHIELD OF PROTECTION" + +/obj/item/toy/prize/honk + name = "toy H.O.N.K." + desc = "6/13" + icon_state = "honktoy" + max_combat_health = 4 //140 integrity + special_attack_type = SPECIAL_ATTACK_OTHER + special_attack_type_message = "puts the opposing mech's special move on cooldown and heals this mech." + special_attack_cry = "MEGA HORN" + +/obj/item/toy/prize/honk/super_special_attack(obj/item/toy/prize/victim) + playsound(src, 'sound/machines/honkbot_evil_laugh.ogg', 20, TRUE) + victim.special_attack_cooldown += 3 //Adds cooldown to the other mech and gives a minor self heal + combat_health++ + +/obj/item/toy/prize/marauder + name = "toy Marauder" + desc = "7/13" + icon_state = "maraudertoy" + max_combat_health = 7 //500 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "BEAM BLAST" + +/obj/item/toy/prize/seraph + name = "toy Seraph" + desc = "8/13" + icon_state = "seraphtoy" + max_combat_health = 8 //550 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "ROCKET BARRAGE" + +/obj/item/toy/prize/mauler + name = "toy Mauler" + desc = "9/13" + icon_state = "maulertoy" + max_combat_health = 7 //500 integrity + special_attack_type = SPECIAL_ATTACK_DAMAGE + special_attack_cry = "BULLET STORM" + +/obj/item/toy/prize/odysseus + name = "toy Odysseus" + desc = "10/13" + icon_state = "odysseustoy" + max_combat_health = 4 //120 integrity + special_attack_type = SPECIAL_ATTACK_HEAL + special_attack_cry = "MECHA BEAM" + +/obj/item/toy/prize/phazon + name = "toy Phazon" + desc = "11/13" + icon_state = "phazontoy" + max_combat_health = 6 //200 integrity + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "NO-CLIP" + +/obj/item/toy/prize/reticence + name = "toy Reticence" + desc = "12/13" + icon_state = "reticencetoy" + quiet = TRUE + max_combat_health = 4 //100 integrity + special_attack_type = SPECIAL_ATTACK_OTHER + special_attack_type_message = "has a lower cooldown than normal special moves, increases the opponent's cooldown, and deals damage." + special_attack_cry = "*wave" + +/obj/item/toy/prize/reticence/super_special_attack(obj/item/toy/prize/victim) + special_attack_cooldown-- //Has a lower cooldown... + victim.special_attack_cooldown++ //and increases the opponent's cooldown by 1... + victim.combat_health-- //and some free damage. + +/obj/item/toy/prize/clarke + name = "toy Clarke" + desc = "13/13" + icon_state = "clarketoy" + max_combat_health = 4 //200 integrity + special_attack_type = SPECIAL_ATTACK_UTILITY + special_attack_cry = "ROLL OUT" + +#undef SPECIAL_ATTACK_HEAL +#undef SPECIAL_ATTACK_DAMAGE +#undef SPECIAL_ATTACK_UTILITY +#undef SPECIAL_ATTACK_OTHER +#undef MAX_BATTLE_LENGTH diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 6a49738d3c80..462de62488fb 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -7,7 +7,6 @@ * Toy swords * Crayons * Snap pops - * Mech prizes * AI core prizes * Toy codex gigas * Skeleton toys @@ -26,7 +25,6 @@ * Broken Radio */ - /obj/item/toy throwforce = 0 throw_speed = 3 @@ -101,9 +99,9 @@ else T = get_turf(src) T.visible_message("[src] bursts!","You hear a pop and a splash.") - reagents.reaction(T) + reagents.expose(T) for(var/atom/A in T) - reagents.reaction(A) + reagents.expose(A) icon_state = "burst" qdel(src) @@ -180,6 +178,51 @@ icon = 'icons/obj/singularity.dmi' icon_state = "singularity_s1" +/obj/item/toy/spinningtoy/suicide_act(mob/living/carbon/human/user) + var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD) + if(!myhead) + user.visible_message("[user] tries consuming [src]... but [user.p_they()] [user.p_have()] no mouth!") // and i must scream + return SHAME + user.visible_message("[user] consumes [src]! It looks like [user.p_theyre()] trying to commit suicicide!") + playsound(user, 'sound/items/eatfood.ogg', 50, TRUE) + user.adjust_nutrition(50) // mmmm delicious + addtimer(CALLBACK(src, .proc/manual_suicide, user), (3SECONDS)) + return MANUAL_SUICIDE + +/** + * Internal function used in the toy singularity suicide + * + * Cavity implants the toy singularity into the body of the user (arg1), and kills the user. + * Makes the user vomit and receive 120 suffocation damage if there already is a cavity implant in the user. + * Throwing the singularity away will cause the user to start choking themself to death. + * Arguments: + * * user - Whoever is doing the suiciding + */ +/obj/item/toy/spinningtoy/proc/manual_suicide(mob/living/carbon/human/user) + if(!user) + return + if(!user.is_holding(src)) // Half digestion? Start choking to death + user.visible_message("[user] panics and starts choking [user.p_them()]self to death!") + user.adjustOxyLoss(200) + user.death(FALSE) // unfortunately you have to handle the suiciding yourself with a manual suicide + user.ghostize(FALSE) // get the fuck out of our body + return + var/obj/item/bodypart/chest/CH = user.get_bodypart(BODY_ZONE_CHEST) + if(CH.cavity_item) // if he's (un)bright enough to have a round and full belly... + user.visible_message("[user] regurgitates [src]!") // I swear i dont have a fetish + user.vomit(100, TRUE, distance = 0) + user.adjustOxyLoss(120) + user.dropItemToGround(src) // incase the crit state doesn't drop the singulo to the floor + user.set_suicide(FALSE) + return + user.transferItemToLoc(src, user, TRUE) + CH.cavity_item = src // The mother came inside and found Andy, dead with a HUGE belly full of toys + user.adjustOxyLoss(200) // You know how most small toys in the EU have that 3+ onion head icon and a warning that says "Unsuitable for children under 3 years of age due to small parts - choking hazard"? This is why. + user.death(FALSE) + user.ghostize(FALSE) + + + /* * Toy gun: Why isnt this an /obj/item/gun? */ @@ -472,96 +515,6 @@ new /obj/item/toy/snappop/phoenix(get_turf(src)) qdel(src) - -/* - * Mech prizes - */ -/obj/item/toy/prize - icon = 'icons/obj/toy.dmi' - icon_state = "ripleytoy" - var/timer = 0 - var/cooldown = 30 - var/quiet = 0 - w_class = WEIGHT_CLASS_SMALL - -//all credit to skasi for toy mech fun ideas -/obj/item/toy/prize/attack_self(mob/user) - if(timer < world.time) - to_chat(user, "You play with [src].") - timer = world.time + cooldown - if(!quiet) - playsound(user, 'sound/mecha/mechstep.ogg', 20, TRUE) - else - . = ..() - -/obj/item/toy/prize/attack_hand(mob/user) - . = ..() - if(.) - return - if(loc == user) - attack_self(user) - -/obj/item/toy/prize/ripley - name = "toy Ripley" - desc = "Mini-Mecha action figure! Collect them all! 1/12." - -/obj/item/toy/prize/fireripley - name = "toy firefighting Ripley" - desc = "Mini-Mecha action figure! Collect them all! 2/12." - icon_state = "fireripleytoy" - -/obj/item/toy/prize/deathripley - name = "toy deathsquad Ripley" - desc = "Mini-Mecha action figure! Collect them all! 3/12." - icon_state = "deathripleytoy" - -/obj/item/toy/prize/gygax - name = "toy Gygax" - desc = "Mini-Mecha action figure! Collect them all! 4/12." - icon_state = "gygaxtoy" - -/obj/item/toy/prize/durand - name = "toy Durand" - desc = "Mini-Mecha action figure! Collect them all! 5/12." - icon_state = "durandprize" - -/obj/item/toy/prize/honk - name = "toy H.O.N.K." - desc = "Mini-Mecha action figure! Collect them all! 6/12." - icon_state = "honkprize" - -/obj/item/toy/prize/marauder - name = "toy Marauder" - desc = "Mini-Mecha action figure! Collect them all! 7/12." - icon_state = "marauderprize" - -/obj/item/toy/prize/seraph - name = "toy Seraph" - desc = "Mini-Mecha action figure! Collect them all! 8/12." - icon_state = "seraphprize" - -/obj/item/toy/prize/mauler - name = "toy Mauler" - desc = "Mini-Mecha action figure! Collect them all! 9/12." - icon_state = "maulerprize" - -/obj/item/toy/prize/odysseus - name = "toy Odysseus" - desc = "Mini-Mecha action figure! Collect them all! 10/12." - icon_state = "odysseusprize" - -/obj/item/toy/prize/phazon - name = "toy Phazon" - desc = "Mini-Mecha action figure! Collect them all! 11/12." - icon_state = "phazonprize" - -/obj/item/toy/prize/reticence - name = "toy Reticence" - desc = "Mini-Mecha action figure! Collect them all! 12/12." - icon_state = "reticenceprize" - quiet = 1 - - /obj/item/toy/talking name = "talking action figure" desc = "A generic action figure modeled after nothing in particular." @@ -746,6 +699,7 @@ user.put_in_hands(H) user.visible_message("[user] draws a card from the deck.", "You draw a card from the deck.") update_icon() + return H /obj/item/toy/cards/deck/update_icon_state() switch(cards.len) @@ -817,7 +771,7 @@ name = "hand of cards" desc = "A number of cards not in a deck, customarily held in ones hand." icon = 'icons/obj/toy.dmi' - icon_state = "nanotrasen_hand2" + icon_state = "none" w_class = WEIGHT_CLASS_TINY var/list/currenthand = list() var/choice = null @@ -827,6 +781,7 @@ user.set_machine(src) interact(user) + /obj/item/toy/cards/cardhand/ui_interact(mob/user) . = ..() var/dat = "You have:
" @@ -838,7 +793,6 @@ popup.set_content(dat) popup.open() - /obj/item/toy/cards/cardhand/Topic(href, href_list) if(..()) return @@ -861,12 +815,7 @@ cardUser.visible_message("[cardUser] draws a card from [cardUser.p_their()] hand.", "You take the [C.cardname] from your hand.") interact(cardUser) - if(src.currenthand.len < 3) - src.icon_state = "[deckstyle]_hand2" - else if(src.currenthand.len < 4) - src.icon_state = "[deckstyle]_hand3" - else if(src.currenthand.len < 5) - src.icon_state = "[deckstyle]_hand4" + update_sprite() if(src.currenthand.len == 1) var/obj/item/toy/cards/singlecard/N = new/obj/item/toy/cards/singlecard(src.loc) N.parentdeck = src.parentdeck @@ -886,12 +835,7 @@ user.visible_message("[user] adds a card to [user.p_their()] hand.", "You add the [C.cardname] to your hand.") qdel(C) interact(user) - if(currenthand.len > 4) - src.icon_state = "[deckstyle]_hand5" - else if(currenthand.len > 3) - src.icon_state = "[deckstyle]_hand4" - else if(currenthand.len > 2) - src.icon_state = "[deckstyle]_hand3" + update_sprite(src) else to_chat(user, "You can't mix cards from other decks!") else @@ -900,7 +844,7 @@ /obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/toy/cards/sourceobj) ..() newobj.deckstyle = sourceobj.deckstyle - newobj.icon_state = "[deckstyle]_hand2" // Another dumb hack, without this the hand is invisible (or has the default deckstyle) until another card is added. + update_sprite() newobj.card_hitsound = sourceobj.card_hitsound newobj.card_force = sourceobj.card_force newobj.card_throwforce = sourceobj.card_throwforce @@ -909,6 +853,18 @@ newobj.card_attack_verb = sourceobj.card_attack_verb newobj.resistance_flags = sourceobj.resistance_flags +/** + * This proc updates the sprite for when you create a hand of cards + */ +/obj/item/toy/cards/cardhand/proc/update_sprite() + cut_overlays() + var/overlay_cards = currenthand.len + + var/k = overlay_cards == 2 ? 1 : overlay_cards - 2 + for(var/i = k; i <= overlay_cards; i++) + var/card_overlay = image(icon=src.icon,icon_state="sc_[currenthand[i]]_[deckstyle]",pixel_x=(1-i+k)*3,pixel_y=(1-i+k)*3) + add_overlay(card_overlay) + /obj/item/toy/cards/singlecard name = "card" desc = "A playing card used to play card games like poker." @@ -975,12 +931,7 @@ user.visible_message("[user] adds a card to [user.p_their()] hand.", "You add the [cardname] to your hand.") qdel(src) H.interact(user) - if(H.currenthand.len > 4) - H.icon_state = "[deckstyle]_hand5" - else if(H.currenthand.len > 3) - H.icon_state = "[deckstyle]_hand4" - else if(H.currenthand.len > 2) - H.icon_state = "[deckstyle]_hand3" + H.update_sprite() else to_chat(user, "You can't mix cards from other decks!") else @@ -1008,7 +959,6 @@ newobj.card_attack_verb = sourceobj.card_attack_verb newobj.attack_verb = newobj.card_attack_verb - /* || Syndicate playing cards, for pretending you're Gambit and playing poker for the nuke disk. || */ diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index af4bec927897..f1573dbfd4bd 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -280,8 +280,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 force = 2 throwforce = 20 //20 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = 28 damage on hit due to guaranteed embedding throw_speed = 4 - embedding = list("pain_mult" = 4, "embed_chance" = 100, "fall_chance" = 0) - + embedding = list("pain_mult" = 4, "embed_chance" = 100, "fall_chance" = 0, "embed_chance_turf_mod" = 15) + armour_penetration = 40 w_class = WEIGHT_CLASS_SMALL sharpness = IS_SHARP @@ -604,7 +604,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 if(homerun_ready) user.visible_message("It's a home run!") target.throw_at(throw_target, rand(8,10), 14, user) - target.ex_act(EXPLODE_HEAVY) + SSexplosions.medturf += throw_target playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, TRUE) homerun_ready = 0 return @@ -675,6 +675,92 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 item_flags = DROPDEL | ABSTRACT attack_verb = list("bopped") +/obj/item/circlegame/Initialize() + . = ..() + var/mob/living/owner = loc + if(!istype(owner)) + return + RegisterSignal(owner, COMSIG_PARENT_EXAMINE, .proc/ownerExamined) + +/obj/item/circlegame/Destroy() + var/mob/owner = loc + if(!istype(owner)) + return + UnregisterSignal(owner, COMSIG_PARENT_EXAMINE) + . = ..() + +/// Stage 1: The mistake is made +/obj/item/circlegame/proc/ownerExamined(mob/living/owner, mob/living/sucker) + if(!istype(sucker) || !in_range(owner, sucker)) + return + addtimer(CALLBACK(src, .proc/waitASecond, owner, sucker), 4) + +/// Stage 2: Fear sets in +/obj/item/circlegame/proc/waitASecond(mob/living/owner, mob/living/sucker) + if(QDELETED(sucker) || QDELETED(src) || QDELETED(owner)) + return + + if(owner == sucker) // big mood + to_chat(owner, "Wait a second... you just looked at your own [src.name]!") + addtimer(CALLBACK(src, .proc/selfGottem, owner), 10) + else + to_chat(sucker, "Wait a second... was that a-") + addtimer(CALLBACK(src, .proc/GOTTEM, owner, sucker), 6) + +/// Stage 3A: We face our own failures +/obj/item/circlegame/proc/selfGottem(mob/living/owner) + if(QDELETED(src) || QDELETED(owner)) + return + + playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1) + owner.visible_message("[owner] shamefully bops [owner.p_them()]self with [owner.p_their()] [src.name].", "You shamefully bop yourself with your [src.name].", \ + "You hear a dull thud!") + log_combat(owner, owner, "bopped", src.name, "(self)") + owner.do_attack_animation(owner) + owner.apply_damage(100, STAMINA) + owner.Knockdown(10) + qdel(src) + +/// Stage 3B: We face our reckoning (unless we moved away or they're incapacitated) +/obj/item/circlegame/proc/GOTTEM(mob/living/owner, mob/living/sucker) + if(QDELETED(sucker)) + return + + if(QDELETED(src) || QDELETED(owner)) + to_chat(sucker, "Nevermind... must've been your imagination...") + return + + if(!in_range(owner, sucker) || !(owner.mobility_flags & MOBILITY_USE)) + to_chat(sucker, "Phew... you moved away before [owner] noticed you saw [owner.p_their()] [src.name]...") + return + + to_chat(owner, "[sucker] looks down at your [src.name] before trying to avert [sucker.p_their()] eyes, but it's too late!") + to_chat(sucker, "[owner] sees the fear in your eyes as you try to look away from [owner.p_their()] [src.name]!") + + owner.face_atom(sucker) + if(owner.client) + owner.client.give_award(/datum/award/achievement/misc/gottem, owner) // then everybody clapped + + playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1) + owner.do_attack_animation(sucker) + + if(HAS_TRAIT(owner, TRAIT_HULK)) + owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name] much harder than intended, sending [sucker.p_them()] flying!", \ + "You bop [sucker] with your [src.name] much harder than intended, sending [sucker.p_them()] flying!", "You hear a sickening sound of flesh hitting flesh!", ignored_mobs=list(sucker)) + to_chat(sucker, "[owner] bops you incredibly hard with [owner.p_their()] [src.name], sending you flying!") + sucker.apply_damage(50, STAMINA) + sucker.Knockdown(50) + log_combat(owner, sucker, "bopped", src.name, "(setup- Hulk)") + var/atom/throw_target = get_edge_target_turf(sucker, owner.dir) + sucker.throw_at(throw_target, 6, 3, owner) + else + owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name]!", "You bop [sucker] with your [src.name]!", \ + "You hear a dull thud!", ignored_mobs=list(sucker)) + sucker.apply_damage(15, STAMINA) + log_combat(owner, sucker, "bopped", src.name, "(setup)") + to_chat(sucker, "[owner] bops you with [owner.p_their()] [src.name]!") + qdel(src) + /obj/item/slapper name = "slapper" desc = "This is how real men fight." diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 867bd6bbd742..e8022409a77a 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -71,7 +71,8 @@ /obj/bullet_act(obj/projectile/P) . = ..() playsound(src, P.hitsound, 50, TRUE) - visible_message("[src] is hit by \a [P]!", null, null, COMBAT_MESSAGE_RANGE) + if(P.suppressed != SUPPRESSED_VERY) + visible_message("[src] is hit by \a [P]!", null, null, COMBAT_MESSAGE_RANGE) if(!QDELETED(src)) //Bullet on_hit effect might have already destroyed this object take_damage(P.damage, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration) @@ -154,7 +155,7 @@ return take_damage(M.force*3, mech_damtype, "melee", play_soundeffect, get_dir(src, M)) // multiplied by 3 so we can hit objs hard but not be overpowered against mobs. /obj/singularity_act() - ex_act(EXPLODE_DEVASTATE) + SSexplosions.highobj += src if(src && !QDELETED(src)) qdel(src) return 2 @@ -204,6 +205,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e SSfire_burning.processing[src] = src add_overlay(GLOB.fire_overlay, TRUE) return 1 + return ..() ///called when the obj is destroyed by fire /obj/proc/burn() diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 06921570865e..bbfb0b15641b 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -28,6 +28,8 @@ var/req_access_txt = "0" var/list/req_one_access var/req_one_access_txt = "0" + /// Custom fire overlay icon + var/custom_fire_overlay var/renamedByPlayer = FALSE //set when a player uses a pen on a renamable object @@ -84,7 +86,7 @@ SEND_SIGNAL(src, COMSIG_OBJ_SETANCHORED, anchorvalue) anchored = anchorvalue -/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE) +/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE) ..() if(obj_flags & FROZEN) visible_message("[src] shatters into a million pieces!") @@ -326,3 +328,24 @@ // Should move all contained objects to it's location. /obj/proc/dump_contents() CRASH("Unimplemented.") + +/obj/handle_ricochet(obj/projectile/P) + . = ..() + if(. && ricochet_damage_mod) + take_damage(P.damage * ricochet_damage_mod, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration) // pass along ricochet_damage_mod damage to the structure for the ricochet + +/obj/update_overlays() + . = ..() + if(acid_level) + . += GLOB.acid_overlay + if(resistance_flags & ON_FIRE) + . += custom_fire_overlay ? custom_fire_overlay : GLOB.fire_overlay + +/// Handles exposing an object to reagents. +/obj/expose_reagents(list/reagents, datum/reagents/source, method=TOUCH, volume_modifier=1, show_message=TRUE) + if((. = ..()) & COMPONENT_NO_EXPOSE_REAGENTS) + return + + for(var/reagent in reagents) + var/datum/reagent/R = reagent + . |= R.expose_obj(src, reagents[R]) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index dba1d540e436..b391c86016a1 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -4,6 +4,8 @@ max_integrity = 300 interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT layer = BELOW_OBJ_LAYER + flags_ricochet = RICOCHET_HARD + ricochet_chance_mod = 0.5 var/climb_time = 20 var/climbable = FALSE diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm index a0c96718c097..8e5631904945 100644 --- a/code/game/objects/structures/artstuff.dm +++ b/code/game/objects/structures/artstuff.dm @@ -80,7 +80,7 @@ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "canvas", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Canvas", name, ui_x, ui_y, master_ui, state) ui.set_autoupdate(FALSE) ui.open() diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm index dd887c061ca5..5275a5d2cb63 100644 --- a/code/game/objects/structures/barsigns.dm +++ b/code/game/objects/structures/barsigns.dm @@ -7,7 +7,7 @@ max_integrity = 500 integrity_failure = 0.5 armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - buildable_sign = 0 + buildable_sign = FALSE var/panel_open = FALSE var/datum/barsign/chosen_sign diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index 2d14c78d526f..eecdeae030d5 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -202,9 +202,10 @@ name = "shuttle seat" desc = "A comfortable, secure seat. It has a more sturdy looking buckling system, for smoother flights." icon_state = "shuttle_chair" + buildstacktype = /obj/item/stack/sheet/mineral/titanium /obj/structure/chair/comfy/shuttle/GetArmrest() - return mutable_appearance('icons/obj/chairs.dmi', "shuttle_chair_armrest") + return mutable_appearance('waspstation/icons/obj/chairs.dmi', "shuttle_chair_armrest") /obj/structure/chair/office anchored = FALSE diff --git a/code/game/objects/structures/beds_chairs/pew.dm b/code/game/objects/structures/beds_chairs/pew.dm index 65440fb5d8c5..b7aa1f65d2bd 100644 --- a/code/game/objects/structures/beds_chairs/pew.dm +++ b/code/game/objects/structures/beds_chairs/pew.dm @@ -41,7 +41,7 @@ update_leftpewarmrest() /obj/structure/chair/pew/right - name = "left wooden pew end" + name = "right wooden pew end" icon_state = "pewend_right" var/mutable_appearance/rightpewarmrest diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index c38b51beac7c..020b9829ce7b 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -175,6 +175,13 @@ LINEN BINS item_state = "sheetqm" dream_messages = list("a grey ID", "a shuttle", "a crate", "a sloth", "the quartermaster") +/obj/item/bedsheet/chaplain + name = "chaplain's blanket" + desc = "A blanket woven with the hearts of gods themselves... Wait, that's just linen." + icon_state = "sheetchap" + item_state = "sheetchap" + dream_messages = list("a grey ID", "the gods", "a fulfilled prayer", "a cult", "the chaplain") + /obj/item/bedsheet/brown icon_state = "sheetbrown" item_state = "sheetbrown" diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 1b5fc1961cad..a54d42f989d2 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -115,7 +115,9 @@ if(wall_mounted) return TRUE -/obj/structure/closet/proc/can_open(mob/living/user) +/obj/structure/closet/proc/can_open(mob/living/user, force = FALSE) + if(force) + return TRUE if(welded || locked) return FALSE var/turf/T = get_turf(src) @@ -153,9 +155,13 @@ if(AM != src && insert(AM) == -1) // limit reached break -/obj/structure/closet/proc/open(mob/living/user) - if(opened || !can_open(user)) +/obj/structure/closet/proc/open(mob/living/user, force = FALSE) + if(!can_open(user, force)) + return + if(opened) return + welded = FALSE + locked = FALSE playsound(loc, open_sound, open_sound_volume, TRUE, -3) opened = TRUE if(!dense_when_open) @@ -163,7 +169,7 @@ climb_time *= 0.5 //it's faster to climb onto an open thing dump_contents() update_icon() - return 1 + return TRUE /obj/structure/closet/proc/insert(atom/movable/AM) if(contents.len >= storage_capacity) @@ -491,8 +497,13 @@ /obj/structure/closet/contents_explosion(severity, target) for(var/atom/A in contents) - A.ex_act(severity, target) - CHECK_TICK + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += A + if(EXPLODE_HEAVY) + SSexplosions.medobj += A + if(EXPLODE_LIGHT) + SSexplosions.lowobj += A /obj/structure/closet/singularity_act() dump_contents() diff --git a/code/game/objects/structures/crates_lockers/closets/bodybag.dm b/code/game/objects/structures/crates_lockers/closets/bodybag.dm index ef054006ce25..81cc66b9e7aa 100644 --- a/code/game/objects/structures/crates_lockers/closets/bodybag.dm +++ b/code/game/objects/structures/crates_lockers/closets/bodybag.dm @@ -54,7 +54,7 @@ if(tagged) . += "bodybag_label" -/obj/structure/closet/body_bag/open(mob/living/user) +/obj/structure/closet/body_bag/open(mob/living/user, force = FALSE) . = ..() if(.) mouse_drag_pointer = MOUSE_INACTIVE_POINTER diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index b509d6ccd0fd..ca236a07a500 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -34,9 +34,9 @@ /obj/structure/closet/cardboard/proc/ResetMoveDelay() move_delay = FALSE -/obj/structure/closet/cardboard/open() - if(opened || !can_open()) - return 0 +/obj/structure/closet/cardboard/open(mob/living/user, force = TRUE) + if(opened || !can_open(user, force)) + return FALSE var/list/alerted = null if(egged < world.time) var/mob/living/Snake = null diff --git a/code/game/objects/structures/crates_lockers/closets/infinite.dm b/code/game/objects/structures/crates_lockers/closets/infinite.dm index 0256d08674a2..ef471d66db75 100644 --- a/code/game/objects/structures/crates_lockers/closets/infinite.dm +++ b/code/game/objects/structures/crates_lockers/closets/infinite.dm @@ -23,7 +23,7 @@ if(replicating_type && !opened && (length(contents) < stop_replicating_at)) new replicating_type(src) -/obj/structure/closet/infinite/open() +/obj/structure/closet/infinite/open(mob/living/user, force = FALSE) . = ..() if(. && auto_close_time) addtimer(CALLBACK(src, .proc/close_on_my_own), auto_close_time, TIMER_OVERRIDE) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm index dced7418af61..6507e5efea10 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm @@ -10,8 +10,8 @@ . = ..() recursive_organ_check(src) -/obj/structure/closet/secure_closet/freezer/open(mob/living/user) - if(opened || !can_open(user)) //dupe check just so we don't let the organs decay when someone fails to open the locker +/obj/structure/closet/secure_closet/freezer/open(mob/living/user, force = TRUE) + if(opened || !can_open(user, force)) //dupe check just so we don't let the organs decay when someone fails to open the locker return FALSE recursive_organ_check(src) return ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 0d7b9290b276..f5eea5076783 100755 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -67,7 +67,7 @@ new /obj/item/gun/energy/e_gun/mini(src) //WaspStation Edit - Gives HoP a mini egun new /obj/item/clothing/neck/petcollar(src) new /obj/item/pet_carrier(src) - new /obj/item/door_remote/civillian(src) + new /obj/item/door_remote/civilian(src) new /obj/item/circuitboard/machine/techfab/department/service(src) new /obj/item/storage/photo_album/HoP(src) @@ -206,18 +206,6 @@ /obj/structure/closet/secure_closet/detective/PopulateContents() ..() - new /obj/item/clothing/under/rank/security/detective(src) - new /obj/item/clothing/under/rank/security/detective/skirt(src) - new /obj/item/clothing/suit/det_suit(src) - new /obj/item/clothing/head/fedora/det_hat(src) - new /obj/item/clothing/gloves/color/black(src) - new /obj/item/clothing/under/rank/security/detective/grey(src) - new /obj/item/clothing/under/rank/security/detective/grey/skirt(src) - new /obj/item/clothing/accessory/waistcoat(src) - new /obj/item/clothing/suit/det_suit/grey(src) - new /obj/item/clothing/suit/det_suit/noir(src) - new /obj/item/clothing/head/fedora(src) - new /obj/item/clothing/shoes/laceup(src) new /obj/item/storage/box/evidence(src) new /obj/item/radio/headset/headset_sec(src) new /obj/item/detective_scanner(src) @@ -232,6 +220,7 @@ new /obj/item/clothing/neck/tie/black(src) new /obj/item/clothing/neck/tie/detective(src) new /obj/item/storage/box/rxglasses/spyglasskit(src) + /obj/structure/closet/secure_closet/injection name = "lethal injections" req_access = list(ACCESS_HOS) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index ea62ffde613f..e09367109413 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -50,7 +50,7 @@ if(manifest) tear_manifest(user) -/obj/structure/closet/crate/open(mob/living/user) +/obj/structure/closet/crate/open(mob/living/user, force = FALSE) . = ..() if(. && manifest) to_chat(user, "The manifest is torn off [src].") @@ -115,7 +115,7 @@ //Snowflake organ freezer code //Order is important, since we check source, we need to do the check whenever we have all the organs in the crate -/obj/structure/closet/crate/freezer/open() +/obj/structure/closet/crate/freezer/open(mob/living/user, force = FALSE) recursive_organ_check(src) ..() diff --git a/code/game/objects/structures/door_assembly_types.dm b/code/game/objects/structures/door_assembly_types.dm index 5fb454b5bc2f..67ebf91e38d6 100644 --- a/code/game/objects/structures/door_assembly_types.dm +++ b/code/game/objects/structures/door_assembly_types.dm @@ -226,9 +226,9 @@ /obj/structure/door_assembly/door_assembly_titanium name = "titanium airlock assembly" - icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi' + icon = 'waspstation/icons/obj/doors/airlocks/shuttle/shuttle.dmi' base_name = "shuttle airlock" - overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi' + overlays_file = 'waspstation/icons/obj/doors/airlocks/shuttle/overlays.dmi' glass_type = /obj/machinery/door/airlock/titanium/glass airlock_type = /obj/machinery/door/airlock/titanium mineral = "titanium" diff --git a/code/game/objects/structures/electricchair.dm b/code/game/objects/structures/electricchair.dm index 0c339b4904e7..8004d5733fe3 100644 --- a/code/game/objects/structures/electricchair.dm +++ b/code/game/objects/structures/electricchair.dm @@ -29,9 +29,9 @@ var/area/A = get_area(src) if(!isarea(A)) return - if(!A.powered(EQUIP)) + if(!A.powered(AREA_USAGE_EQUIP)) return - A.use_power(EQUIP, 5000) + A.use_power(AREA_USAGE_EQUIP, 5000) flick("echair_shock", src) var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index b248edfc1a2a..faee61e18ea7 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -33,7 +33,13 @@ /obj/structure/extinguisher_cabinet/contents_explosion(severity, target) if(stored_extinguisher) - stored_extinguisher.ex_act(severity, target) + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += stored_extinguisher + if(EXPLODE_HEAVY) + SSexplosions.medobj += stored_extinguisher + if(EXPLODE_LIGHT) + SSexplosions.lowobj += stored_extinguisher /obj/structure/extinguisher_cabinet/handle_atom_del(atom/A) if(A == stored_extinguisher) diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index e0e741d561ee..8a77d0e8a3fc 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -296,7 +296,7 @@ /obj/structure/falsewall/titanium name = "wall" desc = "A light-weight titanium wall used in shuttles." - icon = 'icons/turf/walls/shuttle_wall.dmi' + icon = 'waspstation/icons/turf/walls/shuttle_wall.dmi' icon_state = "shuttle" mineral = /obj/item/stack/sheet/mineral/titanium walltype = /turf/closed/wall/mineral/titanium diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index acbcac4579c3..0daf7d7e05fc 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -653,6 +653,13 @@ flavour_text = "Your ship docks after a long time somewhere in hostile space, reporting a malfunction. You are stuck here, with Nanotrasen station nearby. Fix the ship, find a way to power it and follow your captain's orders." important_info = "Obey orders given by your captain. DO NOT let the ship fall into enemy hands." outfit = /datum/outfit/syndicatespace/syndicrew + assignedrole = "Cybersun Crewmember" + +/obj/effect/mob_spawn/human/syndicatespace/Initialize(mapload) + . = ..() + var/policy = get_policy(ROLE_SYNDICATE_CYBERSUN) + if(policy) + important_info = policy /datum/outfit/syndicatespace/syndicrew/post_equip(mob/living/carbon/human/H) H.faction |= ROLE_SYNDICATE @@ -660,11 +667,6 @@ /obj/effect/mob_spawn/human/syndicatespace/special(mob/living/new_spawn) new_spawn.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MIND) -/obj/effect/mob_spawn/human/syndicatespace/Destroy() - new/obj/structure/fluff/empty_sleeper/syndicate(get_turf(src)) - return ..() - - /obj/effect/mob_spawn/human/syndicatespace/captain name = "Syndicate Ship Captain" roundstart = FALSE @@ -674,8 +676,14 @@ short_desc = "You are the captain of an old ship, stuck in hostile space." flavour_text = "Your ship docks after a long time somewhere in hostile space, reporting a malfunction. You are stuck here, with Nanotrasen station nearby. Command your crew and turn your ship into the most protected fortress." important_info = "Protect the ship and secret documents in your backpack with your own life. DO NOT let the ship fall into enemy hands." - mob_gender = "male" outfit = /datum/outfit/syndicatespace/syndicaptain + assignedrole = "Cybersun Captain" + +/obj/effect/mob_spawn/human/syndicatespace/syndicaptain/Initialize(mapload) + . = ..() + var/policy = get_policy(ROLE_SYNDICATE_CYBERSUN_CAPTAIN) + if(policy) + important_info = policy /datum/outfit/syndicatespace/syndicaptain/post_equip(mob/living/carbon/human/H) H.faction |= ROLE_SYNDICATE @@ -706,7 +714,7 @@ glasses = /obj/item/clothing/glasses/night mask = /obj/item/clothing/mask/gas/syndicate head = /obj/item/clothing/head/HoS/beret/syndicate - ears = /obj/item/radio/headset/syndicate/alt + ears = /obj/item/radio/headset/syndicate/alt/leader shoes = /obj/item/clothing/shoes/combat gloves = /obj/item/clothing/gloves/combat back = /obj/item/storage/backpack diff --git a/code/game/objects/structures/guncase.dm b/code/game/objects/structures/guncase.dm index e65ca19a5d02..d826cc9034db 100644 --- a/code/game/objects/structures/guncase.dm +++ b/code/game/objects/structures/guncase.dm @@ -96,8 +96,13 @@ /obj/structure/guncase/contents_explosion(severity, target) for(var/atom/A in contents) - A.ex_act(severity++, target) - CHECK_TICK + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += A + if(EXPLODE_HEAVY) + SSexplosions.medobj += A + if(EXPLODE_LIGHT) + SSexplosions.lowobj += A /obj/structure/guncase/shotgun name = "shotgun locker" diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index adeeec5affea..48da6f8126e6 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -89,7 +89,7 @@ user.visible_message("[user] begins to empty the contents of [src].", "You begin to empty the contents of [src]...") if(I.use_tool(src, user, 30)) to_chat(usr, "You empty the contents of [src]'s bucket onto the floor.") - reagents.reaction(src.loc) + reagents.expose(src.loc) src.reagents.clear_reagents() else return ..() diff --git a/code/game/objects/structures/plaques/_plaques.dm b/code/game/objects/structures/plaques/_plaques.dm new file mode 100644 index 000000000000..a2da8f6d07ea --- /dev/null +++ b/code/game/objects/structures/plaques/_plaques.dm @@ -0,0 +1,183 @@ +/obj/structure/plaque //This is a plaque you can craft with gold, then permanently engrave a title and description on, with a fountain pen. + icon = 'icons/obj/decals.dmi' + icon_state = "blankplaque" + name = "blank plaque" + desc = "A blank plaque, use a fancy pen to engrave it. It can be detatched from the wall with a wrench." + anchored = TRUE + opacity = FALSE + density = FALSE + layer = SIGN_LAYER + custom_materials = list(/datum/material/gold = 2000) + max_integrity = 200 //Twice as durable as regular signs. + armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE + ///Custom plaque structures and items both start "unengraved", once engraved with a fountain pen their text can't be altered again. Static plaques are already engraved. + var/engraved = FALSE + +/obj/item/plaque //The item version of the above. + icon = 'icons/obj/decals.dmi' + icon_state = "blankplaque" + name = "blank plaque" + desc = "A blank plaque, use a fancy pen to engrave it. It can be placed on a wall." + w_class = WEIGHT_CLASS_NORMAL + custom_materials = list(/datum/material/gold = 2000) + max_integrity = 200 + armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + ///This points the item to make the proper structure when placed on a wall. + var/plaque_path = /obj/structure/plaque + ///Custom plaque structures and items both start "unengraved", once engraved with a fountain pen their text can't be altered again. + var/engraved = FALSE + +/obj/structure/plaque/attack_hand(mob/user) + . = ..() + if(.) + return + user.examinate(src) + +/obj/structure/plaque/wrench_act(mob/living/user, obj/item/wrench/I) + . = ..() + user.visible_message("[user] starts removing [src]...", \ + "You start unfastening [src].") + I.play_tool_sound(src) + if(!I.use_tool(src, user, 4 SECONDS)) + return TRUE + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + user.visible_message("[user] unfastens [src].", \ + "You unfasten [src].") + var/obj/item/plaque/unwrenched_plaque = new (get_turf(user)) + if(engraved) //If it's still just a basic unengraved plaque, we can (and should) skip some of the below variable transfers. + unwrenched_plaque.name = name //Copy over the plaque structure variables to the plaque item we're creating when we unwrench it. + unwrenched_plaque.desc = desc + unwrenched_plaque.engraved = engraved + unwrenched_plaque.obj_integrity = obj_integrity + unwrenched_plaque.setDir(dir) + qdel(src) //The plaque structure on the wall goes poof and only the plaque item from unwrenching remains. + return TRUE + +/obj/structure/plaque/welder_act(mob/living/user, obj/item/I) + . = ..() + if(user.a_intent == INTENT_HARM) + return FALSE + if(obj_integrity == max_integrity) + to_chat(user, "This plaque is already in perfect condition.") + return TRUE + if(!I.tool_start_check(user, amount=0)) + return TRUE + user.visible_message("[user] starts repairing [src]...", \ + "You start repairing [src].") + if(!I.use_tool(src, user, 4 SECONDS, volume = 50)) + return TRUE + user.visible_message("[user] finishes repairing [src].", \ + "You finish repairing [src].") + obj_integrity = max_integrity + return TRUE + +/obj/item/plaque/welder_act(mob/living/user, obj/item/I) + . = ..() + if(user.a_intent == INTENT_HARM) + return FALSE + if(obj_integrity == max_integrity) + to_chat(user, "This plaque is already in perfect condition.") + return TRUE + if(!I.tool_start_check(user, amount=0)) + return TRUE + user.visible_message("[user] starts repairing [src]...", \ + "You start repairing [src].") + if(!I.use_tool(src, user, 4 SECONDS, volume = 50)) + return TRUE + user.visible_message("[user] finishes repairing [src].", \ + "You finish repairing [src].") + obj_integrity = max_integrity + return TRUE + +/obj/structure/plaque/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/pen/fountain)) + if(engraved) + to_chat(user, "This plaque has already been engraved.") + return + var/namechoice = input(user, "Title this plaque. (e.g. 'Best HoP Award', 'Great Ashwalker War Memorial')", "Plaque Customization") + if(!namechoice) + return + var/descriptionchoice = input(user, "Engrave this plaque's text.", "Plaque Customization") + if(!descriptionchoice) + return + if(!Adjacent(user)) //Make sure user is adjacent still + to_chat(user, "You need to stand next to the plaque to engrave it!") + return + user.visible_message("[user] begins engraving [src].", \ + "You begin engraving [src].") + if(!do_after(user, 4 SECONDS, target = src)) //This spits out a visible message that somebody is engraving a plaque, then has a delay. + return + name = "\improper [namechoice]" //We want improper here so examine doesn't get weird if somebody capitalizes the plaque title. + desc = "The plaque reads: '[descriptionchoice]'" + engraved = TRUE //The plaque now has a name, description, and can't be altered again. + user.visible_message("[user] engraves [src].", \ + "You engrave [src].") + return + if(istype(I, /obj/item/pen)) + if(engraved) + to_chat(user, "This plaque has already been engraved, and your pen isn't fancy enough to engrave it anyway! Find a fountain pen.") + return + to_chat(user, "Your pen isn't fancy enough to engrave this! Find a fountain pen.") //Go steal the Curator's. + return + return ..() + +/obj/item/plaque/attackby(obj/item/I, mob/user, params) //Same as part of the above, except for the item in hand instead of the structure. + if(istype(I, /obj/item/pen/fountain)) + if(engraved) + to_chat(user, "This plaque has already been engraved.") + return + var/namechoice = input(user, "Title this plaque. (e.g. 'Best HoP Award', 'Great Ashwalker War Memorial')", "Plaque Customization") + if(!namechoice) + return + var/descriptionchoice = input(user, "Engrave this plaque's text.", "Plaque Customization") + if(!descriptionchoice) + return + if(!Adjacent(user)) //Make sure user is adjacent still + to_chat(user, "You need to stand next to the plaque to engrave it!") + return + user.visible_message("[user] begins engraving [src].", \ + "You begin engraving [src].") + if(!do_after(user, 40, target = src)) //This spits out a visible message that somebody is engraving a plaque, then has a delay. + return + name = "\improper [namechoice]" //We want improper here so examine doesn't get weird if somebody capitalizes the plaque title. + desc = "The plaque reads: '[descriptionchoice]'" + engraved = TRUE //The plaque now has a name, description, and can't be altered again. + user.visible_message("[user] engraves [src].", \ + "You engrave [src].") + return + if(istype(I, /obj/item/pen)) + if(engraved) + to_chat(user, "This plaque has already been engraved, and your pen isn't fancy enough to engrave it anyway! Find a fountain pen.") + return + to_chat(user, "Your pen isn't fancy enough to engrave this! Find a fountain pen.") //Go steal the Curator's. + return + return ..() + +/obj/item/plaque/afterattack(atom/target, mob/user, proximity) + . = ..() + if(!iswallturf(target) || !proximity) + return + var/turf/target_turf = target + var/turf/user_turf = get_turf(user) + var/obj/structure/plaque/placed_plaque = new plaque_path(user_turf) //We place the plaque on the turf the user is standing, and pixel shift it to the target wall, as below. + //This is to mimic how signs and other wall objects are usually placed by mappers, and so they're only visible from one side of a wall. + var/dir = get_dir(user_turf, target_turf) + if(dir & NORTH) + placed_plaque.pixel_y = 32 + else if(dir & SOUTH) + placed_plaque.pixel_y = -32 + if(dir & EAST) + placed_plaque.pixel_x = 32 + else if(dir & WEST) + placed_plaque.pixel_x = -32 + user.visible_message("[user] fastens [src] to [target_turf].", \ + "You attach [src] to [target_turf].") + playsound(target_turf, 'sound/items/deconstruct.ogg', 50, TRUE) + if(engraved) + placed_plaque.name = name + placed_plaque.desc = desc + placed_plaque.engraved = engraved + placed_plaque.obj_integrity = obj_integrity + placed_plaque.setDir(dir) + qdel(src) diff --git a/code/game/objects/structures/signs/signs_plaques.dm b/code/game/objects/structures/plaques/static_plaques.dm similarity index 78% rename from code/game/objects/structures/signs/signs_plaques.dm rename to code/game/objects/structures/plaques/static_plaques.dm index 317f556e8d70..1ac3ec7546da 100644 --- a/code/game/objects/structures/signs/signs_plaques.dm +++ b/code/game/objects/structures/plaques/static_plaques.dm @@ -1,29 +1,30 @@ -//plaques and memorials +//These are static plaques, they are made of gold and start engraved with a title and description. -/obj/structure/sign/plaques - name = "plaque" - desc = "A plaque commemorating an event." - icon_state = "atmosplaque" +/obj/structure/plaque/static_plaque + engraved = TRUE -/obj/structure/sign/plaques/atmos +/obj/structure/plaque/static_plaque/atmos name = "\improper FEA Atmospherics Division plaque" desc = "This plaque commemorates the fall of the Atmos FEA division. For all the charred, dizzy, and brittle men who have died in its hands." -/obj/structure/sign/plaques/thunderdome +/obj/structure/plaque/static_plaque/thunderdome name = "Thunderdome Plaque" desc = "This plaque commemorates those who have fallen in glorious combat. For all the charred, dizzy, and beaten men who have died in its hands." -/obj/structure/sign/plaques/golden +/obj/structure/plaque/static_plaque/golden name = "The Most Robust Men Award for Robustness" desc = "To be Robust is not an action or a way of life, but a mental state. Only those with the force of Will strong enough to act during a crisis, saving friend from foe, are truly Robust. Stay Robust my friends." icon_state = "goldenplaque" -/obj/structure/sign/plaques/golden/captain +/obj/structure/plaque/static_plaque/golden/captain name = "The Most Robust Captain Award for Robustness" +//These are plaques that aren't made of metal, so we'll just consider them signs. Those are made of plastic, not gold. +//See: code>game>objects>structures>signs>_signs.dm + /obj/structure/sign/plaques/kiddie name = "\improper AI developers plaque" - desc = "Next to the extremely long list of names and job titles, there is a drawing of a little child. The child appears to be retarded. Beneath the image, someone has scratched the word \"PACKETS\"." + desc = "Next to the extremely long list of names and job titles, there is a drawing of a little child. The child appears to be disabled. Beneath the image, someone has scratched the word \"PACKETS\"." icon_state = "kiddieplaque" /obj/structure/sign/plaques/kiddie/badger @@ -31,7 +32,7 @@ desc = "A plaque commemorating the fallen, may they rest in peace, forever asleep amongst the stars. Someone has drawn a picture of a crying badger at the bottom." /obj/structure/sign/plaques/kiddie/library - name = "Library Rules Sign" + name = "\improper Library Rules Sign" desc = "A long list of rules to be followed when in the library, extolling the virtues of being quiet at all times and threatening those who would dare eat hot food inside." /obj/structure/sign/plaques/kiddie/perfect_man @@ -43,6 +44,6 @@ desc = "A guide to the drone shell dispenser, detailing the constructive and destructive applications of modern repair drones, as well as the development of the incorruptible cyborg servants of tomorrow, available today." /obj/structure/sign/plaques/deempisi - name = "Mr. Deempisi portrait" + name = "\improper Mr. Deempisi portrait" desc = "Under the painting a plaque reads: 'While the meat grinder may not have spared you, fear not. Not one part of you has gone to waste... You were delicious.'" icon_state = "monkey_painting" diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm index 570f26cf71d3..d7b0958e8c5b 100644 --- a/code/game/objects/structures/railings.dm +++ b/code/game/objects/structures/railings.dm @@ -6,12 +6,22 @@ density = TRUE anchored = TRUE climbable = TRUE + ///Initial direction of the railing. + var/ini_dir /obj/structure/railing/corner //aesthetic corner sharp edges hurt oof ouch icon_state = "railing_corner" density = FALSE climbable = FALSE +/obj/structure/railing/ComponentInitialize() + . = ..() + AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS ,null,CALLBACK(src, .proc/can_be_rotated),CALLBACK(src,.proc/after_rotation)) + +/obj/structure/railing/Initialize() + . = ..() + ini_dir = dir + /obj/structure/railing/attackby(obj/item/I, mob/living/user, params) ..() add_fingerprint(user) @@ -29,6 +39,22 @@ to_chat(user, "[src] is already in good condition!") return +/obj/structure/railing/wirecutter_act(mob/living/user, obj/item/I) + . = ..() + if(!anchored) + to_chat(user, "You cut apart the railing.") + I.play_tool_sound(src, 100) + deconstruct() + return TRUE + +/obj/structure/railing/deconstruct(disassembled) + . = ..() + if(!loc) //quick check if it's qdeleted already. + return + if(!(flags_1 & NODECONSTRUCT_1)) + var/obj/item/stack/rods/rod = new /obj/item/stack/rods(drop_location(), 3) + transfer_fingerprints_to(rod) + qdel(src) ///Implements behaviour that makes it possible to unanchor the railing. /obj/structure/railing/wrench_act(mob/living/user, obj/item/I) . = ..() @@ -40,25 +66,44 @@ to_chat(user, "You [anchored ? "fasten the railing to":"unfasten the railing from"] the floor.") return TRUE -/obj/structure/railing/proc/check_anchored(checked_anchored) - if(anchored == checked_anchored) - return TRUE - /obj/structure/railing/CanPass(atom/movable/mover, turf/target) - ..() + . = ..() if(get_dir(loc, target) & dir) - return !density + var/checking = FLYING | FLOATING + return . || mover.movement_type & checking return TRUE /obj/structure/railing/corner/CanPass() ..() return TRUE -/obj/structure/railing/CheckExit(atom/movable/O, turf/target) +/obj/structure/railing/CheckExit(atom/movable/mover, turf/target) ..() if(get_dir(loc, target) & dir) - return FALSE + var/checking = UNSTOPPABLE | FLYING | FLOATING + return !density || mover.movement_type & checking || mover.move_force >= MOVE_FORCE_EXTREMELY_STRONG return TRUE /obj/structure/railing/corner/CheckExit() return TRUE + +/obj/structure/railing/proc/can_be_rotated(mob/user,rotation_type) + if(anchored) + to_chat(user, "[src] cannot be rotated while it is fastened to the floor!") + return FALSE + + var/target_dir = turn(dir, rotation_type == ROTATION_CLOCKWISE ? -90 : 90) + + if(!valid_window_location(loc, target_dir)) //Expanded to include rails, as well! + to_chat(user, "[src] cannot be rotated in that direction!") + return FALSE + return TRUE + +/obj/structure/railing/proc/check_anchored(checked_anchored) + if(anchored == checked_anchored) + return TRUE + +/obj/structure/railing/proc/after_rotation(mob/user,rotation_type) + air_update_turf(1) + ini_dir = dir + add_fingerprint(user) diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm index 20e48a7efbb7..b49c68b528c3 100644 --- a/code/game/objects/structures/shower.dm +++ b/code/game/objects/structures/shower.dm @@ -99,7 +99,7 @@ /obj/machinery/shower/proc/wash_atom(atom/A) A.washed(src) - reagents.reaction(A, TOUCH, reaction_volume) + reagents.expose(A, TOUCH, reaction_volume) if(isliving(A)) check_heat(A) diff --git a/code/game/objects/structures/signs/_signs.dm b/code/game/objects/structures/signs/_signs.dm index ed3a506d5fb7..5b9b29bafb50 100644 --- a/code/game/objects/structures/signs/_signs.dm +++ b/code/game/objects/structures/signs/_signs.dm @@ -1,28 +1,44 @@ /obj/structure/sign icon = 'icons/obj/decals.dmi' + icon_state = "backing" + name = "sign backing" + desc = "A plastic sign backing, use a pen to change the decal. It can be detached from the wall with a wrench." anchored = TRUE opacity = 0 density = FALSE layer = SIGN_LAYER + custom_materials = list(/datum/material/plastic = 2000) max_integrity = 100 armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - var/buildable_sign = 1 //unwrenchable and modifiable + ///Determines if a sign is unwrenchable. + var/buildable_sign = TRUE rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE + resistance_flags = FLAMMABLE + ///This determines if you can select this sign type when using a pen on a sign backing. False by default, set to true per sign type to override. + var/is_editable = FALSE + ///sign_change_name is used to make nice looking, alphebetized and categorized names when you use a pen on a sign backing. + var/sign_change_name = "Sign - Blank" //If this is ever seen in game, something went wrong. -/obj/structure/sign/basic - name = "blank sign" - desc = "How can signs be real if our eyes aren't real? Use a pen to change the decal." +/obj/item/sign + name = "sign backing" + desc = "A plastic sign backing, use a pen to change the decal. It can be placed on a wall." + icon = 'icons/obj/decals.dmi' icon_state = "backing" + w_class = WEIGHT_CLASS_NORMAL + custom_materials = list(/datum/material/plastic = 2000) + armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + resistance_flags = FLAMMABLE + max_integrity = 100 + ///The type of sign structure that will be created when placed on a turf, the default looks just like a sign backing item. + var/sign_path = /obj/structure/sign + ///This determines if you can select this sign type when using a pen on a sign backing. False by default, set to true per sign type to override. + var/is_editable = TRUE -/obj/structure/sign/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src.loc, 'sound/weapons/slash.ogg', 80, TRUE) - else - playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(loc, 'sound/items/welder.ogg', 80, TRUE) +/obj/item/sign/Initialize() //Signs not attached to walls are always rotated so they look like they're laying horizontal. + . = ..() + var/matrix/M = matrix() + M.Turn(90) + transform = M /obj/structure/sign/attack_hand(mob/user) . = ..() @@ -30,125 +46,164 @@ return user.examinate(src) -/obj/structure/sign/attackby(obj/item/I, mob/user, params) - if(I.tool_behaviour == TOOL_WRENCH && buildable_sign) - user.visible_message("[user] starts removing [src]...", \ - "You start unfastening [src].") - I.play_tool_sound(src) - if(I.use_tool(src, user, 40)) - playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) - user.visible_message("[user] unfastens [src].", \ - "You unfasten [src].") - var/obj/item/sign_backing/SB = new (get_turf(user)) - SB.icon_state = icon_state - SB.sign_path = type - SB.setDir(dir) - qdel(src) - return - else if(istype(I, /obj/item/pen) && buildable_sign) - var/list/sign_types = list("Secure Area", "Biohazard", "High Voltage", "Radiation", "Hard Vacuum Ahead", "Disposal: Leads To Space", "Danger: Fire", "No Smoking", "Medbay", "Science", "Chemistry", \ - "Hydroponics", "Xenobiology", "Test Chamber","Firing Range", "Extreme Cold", "Extreme Heat", "Gas Mask", "Nanites Lab", "Maintenance", "Reactive Chemicals") - var/obj/structure/sign/sign_type - switch(input(user, "Select a sign type.", "Sign Customization") as null|anything in sortList(sign_types)) - if("Blank") - sign_type = /obj/structure/sign/basic - if("Secure Area") - sign_type = /obj/structure/sign/warning/securearea - if("Biohazard") - sign_type = /obj/structure/sign/warning/biohazard - if("High Voltage") - sign_type = /obj/structure/sign/warning/electricshock - if("Radiation") - sign_type = /obj/structure/sign/warning/radiation - if("Hard Vacuum Ahead") - sign_type = /obj/structure/sign/warning/vacuum - if("Disposal: Leads To Space") - sign_type = /obj/structure/sign/warning/deathsposal - if("Danger: Fire") - sign_type = /obj/structure/sign/warning/fire - if("No Smoking") - sign_type = /obj/structure/sign/warning/nosmoking/circle - if("Test Chamber") - sign_type = /obj/structure/sign/warning/testchamber - if("Firing Range") - sign_type = /obj/structure/sign/warning/firingrange - if("Extreme Cold") - sign_type = /obj/structure/sign/warning/coldtemp - if("Extreme Heat") - sign_type = /obj/structure/sign/warning/hottemp - if("Gas Mask") - sign_type = /obj/structure/sign/warning/gasmask - if("Reactive Chemicals") - sign_type = /obj/structure/sign/warning/chemdiamond - if("Medbay") - sign_type = /obj/structure/sign/departments/medbay/alt - if("Science") - sign_type = /obj/structure/sign/departments/science - if("Chemistry") - sign_type = /obj/structure/sign/departments/chemistry - if("Hydroponics") - sign_type = /obj/structure/sign/departments/botany - if("Xenobiology") - sign_type = /obj/structure/sign/departments/xenobio - if("Nanites Lab") - sign_type = /obj/structure/sign/departments/nanites - if("Maintenance") - sign_type = /obj/structure/sign/departments/mait - - //Make sure user is adjacent still - if(!Adjacent(user)) - return - - if(!sign_type) - return +/** + * This proc populates GLOBAL_LIST_EMPTY(editable_sign_types) + * + * The first time a pen is used on any sign, this populates GLOBAL_LIST_EMPTY(editable_sign_types), creating a global list of all the signs that you can set a sign backing to with a pen. + */ +/proc/populate_editable_sign_types() + for(var/s in subtypesof(/obj/structure/sign)) + var/obj/structure/sign/potential_sign = s + if(!initial(potential_sign.is_editable)) + continue + GLOB.editable_sign_types[initial(potential_sign.sign_change_name)] = potential_sign + GLOB.editable_sign_types = sortList(GLOB.editable_sign_types) //Alphabetizes the results. - //It's import to clone the pixel layout information - //Otherwise signs revert to being on the turf and - //move jarringly - var/obj/structure/sign/newsign = new sign_type(get_turf(src)) - newsign.pixel_x = pixel_x - newsign.pixel_y = pixel_y - qdel(src) - else - return ..() +/obj/structure/sign/wrench_act(mob/living/user, obj/item/wrench/I) + . = ..() + user.visible_message("[user] starts removing [src]...", \ + "You start unfastening [src].") + I.play_tool_sound(src) + if(!I.use_tool(src, user, 4 SECONDS)) + return TRUE + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + user.visible_message("[user] unfastens [src].", \ + "You unfasten [src].") + var/obj/item/sign/unwrenched_sign = new (get_turf(user)) + if(type != /obj/structure/sign) //If it's still just a basic sign backing, we can (and should) skip some of the below variable transfers. + unwrenched_sign.name = name //Copy over the sign structure variables to the sign item we're creating when we unwrench a sign. + unwrenched_sign.desc = "[desc] It can be placed on a wall." + unwrenched_sign.icon_state = icon_state + unwrenched_sign.sign_path = type + unwrenched_sign.obj_integrity = obj_integrity //Transfer how damaged it is. + unwrenched_sign.setDir(dir) + qdel(src) //The sign structure on the wall goes poof and only the sign item from unwrenching remains. + return TRUE -/obj/item/sign_backing - name = "sign backing" - desc = "A sign with adhesive backing. Use a pen to change the decal once installed." - icon = 'icons/obj/decals.dmi' - icon_state = "backing" - w_class = WEIGHT_CLASS_NORMAL - custom_materials = list(/datum/material/plastic = 2000) - resistance_flags = FLAMMABLE - var/sign_path = /obj/structure/sign/basic //the type of sign that will be created when placed on a turf +/obj/structure/sign/welder_act(mob/living/user, obj/item/I) + . = ..() + if(user.a_intent == INTENT_HARM) + return FALSE + if(obj_integrity == max_integrity) + to_chat(user, "This sign is already in perfect condition.") + return TRUE + if(!I.tool_start_check(user, amount=0)) + return TRUE + user.visible_message("[user] starts repairing [src]...", \ + "You start repairing [src].") + if(!I.use_tool(src, user, 4 SECONDS, volume =50 )) + return TRUE + user.visible_message("[user] finishes repairing [src].", \ + "You finish repairing [src].") + obj_integrity = max_integrity + return TRUE -/obj/item/sign_backing/afterattack(atom/target, mob/user, proximity) +/obj/item/sign/welder_act(mob/living/user, obj/item/I) . = ..() - if(isturf(target) && proximity) - var/turf/T = target - user.visible_message("[user] fastens [src] to [T].", \ - "You attach the sign to [T].") - playsound(T, 'sound/items/deconstruct.ogg', 50, TRUE) - var/obj/structure/sign/S = new sign_path(T) - S.setDir(dir) + if(user.a_intent == INTENT_HARM) + return FALSE + if(obj_integrity == max_integrity) + to_chat(user, "This sign is already in perfect condition.") + return TRUE + if(!I.tool_start_check(user, amount=0)) + return TRUE + user.visible_message("[user] starts repairing [src]...", \ + "You start repairing [src].") + if(!I.use_tool(src, user, 4 SECONDS, volume =50 )) + return TRUE + user.visible_message("[user] finishes repairing [src].", \ + "You finish repairing [src].") + obj_integrity = max_integrity + return TRUE + +/obj/structure/sign/attackby(obj/item/I, mob/user, params) + if(is_editable && istype(I, /obj/item/pen)) + if(!length(GLOB.editable_sign_types)) + populate_editable_sign_types() + if(!length(GLOB.editable_sign_types)) + CRASH("GLOB.editable_sign_types failed to populate") + var/choice = input(user, "Select a sign type.", "Sign Customization") as null|anything in GLOB.editable_sign_types + if(!choice) + return + if(!Adjacent(user)) //Make sure user is adjacent still. + to_chat(user, "You need to stand next to the sign to change it!") + return + user.visible_message("[user] begins changing [src].", \ + "You begin changing [src].") + if(!do_after(user, 4 SECONDS, target = src)) //Small delay for changing signs instead of it being instant, so somebody could be shoved or stunned to prevent them from doing so. + return + var/sign_type = GLOB.editable_sign_types[choice] + //It's import to clone the pixel layout information. + //Otherwise signs revert to being on the turf and + //move jarringly. + var/obj/structure/sign/changedsign = new sign_type(get_turf(src)) + changedsign.pixel_x = pixel_x + changedsign.pixel_y = pixel_y + changedsign.obj_integrity = obj_integrity qdel(src) + user.visible_message("[user] finishes changing the sign.", \ + "You finish changing the sign.") + return + return ..() -/obj/item/sign_backing/Move(atom/new_loc, direct = 0) - // pulling, throwing, or conveying a sign backing does not rotate it - var/old_dir = dir - . = ..() - setDir(old_dir) +/obj/item/sign/attackby(obj/item/I, mob/user, params) + if(is_editable && istype(I, /obj/item/pen)) + if(!length(GLOB.editable_sign_types)) + populate_editable_sign_types() + if(!length(GLOB.editable_sign_types)) + CRASH("GLOB.editable_sign_types failed to populate") + var/choice = input(user, "Select a sign type.", "Sign Customization") as null|anything in GLOB.editable_sign_types + if(!choice) + return + if(!Adjacent(user)) //Make sure user is adjacent still. + to_chat(user, "You need to stand next to the sign to change it!") + return + if(!choice) + return + user.visible_message("You begin changing [src].") + if(!do_after(user, 4 SECONDS, target = src)) + return + var/obj/structure/sign/sign_type = GLOB.editable_sign_types[choice] + name = initial(sign_type.name) + desc = "[initial(sign_type.desc)] It can be placed on a wall." + icon_state = initial(sign_type.icon_state) + sign_path = sign_type + user.visible_message("You finish changing the sign.") + return + return ..() -/obj/item/sign_backing/attack_self(mob/user) +/obj/item/sign/afterattack(atom/target, mob/user, proximity) . = ..() - setDir(turn(dir, 90)) + if(!iswallturf(target) || !proximity) + return + var/turf/target_turf = target + var/turf/user_turf = get_turf(user) + var/obj/structure/sign/placed_sign = new sign_path(user_turf) //We place the sign on the turf the user is standing, and pixel shift it to the target wall, as below. + //This is to mimic how signs and other wall objects are usually placed by mappers, and so they're only visible from one side of a wall. + var/dir = get_dir(user_turf, target_turf) + if(dir & NORTH) + placed_sign.pixel_y = 32 + else if(dir & SOUTH) + placed_sign.pixel_y = -32 + if(dir & EAST) + placed_sign.pixel_x = 32 + else if(dir & WEST) + placed_sign.pixel_x = -32 + user.visible_message("[user] fastens [src] to [target_turf].", \ + "You attach the sign to [target_turf].") + playsound(target_turf, 'sound/items/deconstruct.ogg', 50, TRUE) + placed_sign.obj_integrity = obj_integrity + placed_sign.setDir(dir) + qdel(src) /obj/structure/sign/nanotrasen - name = "\improper Nanotrasen logo" + name = "\improper Nanotrasen logo sign" + sign_change_name = "Corporate Logo - Nanotrasen" desc = "A sign with the Nanotrasen logo on it. Glory to Nanotrasen!" icon_state = "nanotrasen" + is_editable = TRUE /obj/structure/sign/logo - name = "\improper Nanotrasen logo" + name = "\improper Nanotrasen logo sign" desc = "The Nanotrasen corporate logo." icon_state = "nanotrasen_sign1" diff --git a/code/game/objects/structures/signs/signs_departments.dm b/code/game/objects/structures/signs/signs_departments.dm index 907ad30fe3f4..0de5621ae0a8 100644 --- a/code/game/objects/structures/signs/signs_departments.dm +++ b/code/game/objects/structures/signs/signs_departments.dm @@ -1,96 +1,163 @@ //departmental signs + +///////MEDBAY + +/obj/structure/sign/departments/medbay + name = "\improper Medbay sign" + sign_change_name = "Department - Medbay" + desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." + icon_state = "bluecross" + is_editable = TRUE + +/obj/structure/sign/departments/medbay/alt + name = "\improper Medbay sign" + sign_change_name = "Department - Medbay Alt" + desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." + icon_state = "bluecross2" + is_editable = TRUE + /obj/structure/sign/departments/examroom - name = "\improper EXAM ROOM" - desc = "A guidance sign which reads 'EXAM ROOM'." + name = "\improper Exam Room sign" + sign_change_name = "Department - Medbay: Exam Room" + desc = "A guidance sign which reads 'Exam Room'." icon_state = "examroom" - -/obj/structure/sign/departments/science //These 3 have multiple types, just var-edit the icon_state to whatever one you want on the map - name = "\improper SCIENCE" - desc = "A sign labelling an area where research and science is performed." - icon_state = "science1" + is_editable = TRUE /obj/structure/sign/departments/chemistry - name = "\improper CHEMISTRY" + name = "\improper Chemistry sign" + sign_change_name = "Department - Medbay: Chemistry" desc = "A sign labelling an area containing chemical equipment." icon_state = "chemistry1" + is_editable = TRUE /obj/structure/sign/departments/chemistry/pharmacy - name = "\improper PHARMACY" + name = "\improper Pharmacy sign" + sign_change_name = "Department - Medbay: Pharmacy" desc = "A sign labelling an area containing pharmacy equipment." icon_state = "pharmacy" + is_editable = TRUE + +/obj/structure/sign/departments/psychology + name = "\improper Psychology sign" + sign_change_name = "Department - Medbay: Psychology" + desc = "A sign labelling where the Psychologist works, they can probably help you get your head straight." + icon_state = "psychology" + is_editable = TRUE -/obj/structure/sign/departments/botany - name = "\improper HYDROPONICS" - desc = "A sign labelling an area as a place where plants are grown." - icon_state = "hydro1" +///////ENGINEERING + +/obj/structure/sign/departments/engineering + name = "\improper Engineering sign" + sign_change_name = "Department - Engineering" + desc = "A sign labelling an area where engineers work." + icon_state = "engine" + is_editable = TRUE + +///////SCIENCE + +/obj/structure/sign/departments/science + name = "\improper Science sign" + sign_change_name = "Department - Science" + desc = "A sign labelling an area where research and science is performed." + icon_state = "science1" + is_editable = TRUE + +/obj/structure/sign/departments/science/alt + name = "\improper Science sign" + sign_change_name = "Department - Science Alt" + desc = "A sign labelling an area where research and science is performed." + icon_state = "science2" + is_editable = TRUE /obj/structure/sign/departments/xenobio - name = "\improper XENOBIOLOGY" + name = "\improper Xenobiolgoy sign" + sign_change_name = "Department - Science: Xenobiology" desc = "A sign labelling an area as a place where xenobiological entities are researched." icon_state = "xenobio" + is_editable = TRUE -/obj/structure/sign/departments/evac - name = "\improper EVACUATION" - desc = "A sign labelling an area where evacuation procedures take place." - icon_state = "evac" +/obj/structure/sign/departments/nanites + name = "\improper Nanite Lab sign" + sign_change_name = "Department - Science: Nanites" + desc = "A sign labelling an area where testing and development of nanites is performed." + icon_state = "nanites" + is_editable = TRUE -/obj/structure/sign/departments/drop - name = "\improper DROP PODS" - desc = "A sign labelling an area where drop pod loading procedures take place." - icon_state = "drop" +///////SERVICE + +/obj/structure/sign/departments/botany + name = "\improper Botany sign" + sign_change_name = "Department - Botany" + desc = "A sign labelling an area as a place where plants are grown." + icon_state = "hydro1" + is_editable = TRUE /obj/structure/sign/departments/custodian - name = "\improper CUSTODIAN" - desc = "A sign labelling an area where the custodian works." + name = "\improper Janitor sign" + sign_change_name = "Department - Janitor" + desc = "A sign labelling an area where the janitor works." icon_state = "custodian" + is_editable = TRUE -/obj/structure/sign/departments/engineering - name = "\improper ENGINEERING" - desc = "A sign labelling an area where engineers work." - icon_state = "engine" +/obj/structure/sign/departments/holy + name = "\improper Chapel sign" + sign_change_name = "Department - Chapel" + desc = "A sign labelling a religious area." + icon_state = "holy" + is_editable = TRUE + +///////SUPPLY /obj/structure/sign/departments/cargo - name = "\improper CARGO" + name = "\improper Cargo sign" + sign_change_name = "Department - Cargo" desc = "A sign labelling an area where cargo ships dock." icon_state = "cargo" + is_editable = TRUE + +///////SECURITY /obj/structure/sign/departments/security - name = "\improper SECURITY" + name = "\improper Security sign" + sign_change_name = "Department - Security" desc = "A sign labelling an area where the law is law." icon_state = "security" + is_editable = TRUE -/obj/structure/sign/departments/holy - name = "\improper HOLY" - desc = "A sign labelling a religious area." - icon_state = "holy" +////MISC LOCATIONS /obj/structure/sign/departments/restroom - name = "\improper RESTROOM" + name = "\improper Restroom sign" + sign_change_name = "Location - Restroom" desc = "A sign labelling a restroom." icon_state = "restroom" - -/obj/structure/sign/departments/medbay - name = "\improper MEDBAY" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon_state = "bluecross" - -/obj/structure/sign/departments/medbay/alt - icon_state = "bluecross2" + is_editable = TRUE /obj/structure/sign/departments/mait - name = "\improper MAINTENANCE TUNNEL" + name = "\improper Maintenance Tunnel sign" + sign_change_name = "Location - Maintenance" desc = "A sign labelling an area where the departments of the station are linked together." icon_state = "mait1" + is_editable = TRUE /obj/structure/sign/departments/mait/alt + name = "\improper Maintenance Tunnel sign" + sign_change_name = "Location - Maintenance Alt" + desc = "A sign labelling an area where the departments of the station are linked together." icon_state = "mait2" + is_editable = TRUE -/obj/structure/sign/departments/nanites - name = "\improper NANITE LAB" - desc = "A sign labelling an area where testing and development of nanites is performed." - icon_state = "nanites" -/obj/structure/sign/departments/psychology - name = "\improper PSYCHOLOGY" - desc = "A sign labelling where the Psychologist works, they can probably help you get your head straight." - icon_state = "psychology" +/obj/structure/sign/departments/evac + name = "\improper Evacuation sign" + sign_change_name = "Location - Evacuation" + desc = "A sign labelling an area where evacuation procedures take place." + icon_state = "evac" + is_editable = TRUE + +/obj/structure/sign/departments/drop + name = "\improper Drop Pods sign" + sign_change_name = "Location - Drop Pods" + desc = "A sign labelling an area where drop pod loading procedures take place." + icon_state = "drop" + is_editable = TRUE diff --git a/code/game/objects/structures/signs/signs_maps.dm b/code/game/objects/structures/signs/signs_maps.dm index 63de37f2e8c1..ba79b6ff93f5 100644 --- a/code/game/objects/structures/signs/signs_maps.dm +++ b/code/game/objects/structures/signs/signs_maps.dm @@ -2,7 +2,7 @@ /obj/structure/sign/map name = "station map" - desc = "A framed picture of the station." + desc = "A navigational chart of the station." max_integrity = 500 /obj/structure/sign/map/left @@ -12,47 +12,47 @@ icon_state = "map-right" /obj/structure/sign/directions/science - name = "science department" + name = "science department sign" desc = "A direction sign, pointing out which way the Science department is." icon_state = "direction_sci" /obj/structure/sign/directions/engineering - name = "engineering department" + name = "engineering department sign" desc = "A direction sign, pointing out which way the Engineering department is." icon_state = "direction_eng" /obj/structure/sign/directions/security - name = "security department" + name = "security department sign" desc = "A direction sign, pointing out which way the Security department is." icon_state = "direction_sec" /obj/structure/sign/directions/medical - name = "medical bay" - desc = "A direction sign, pointing out which way the Medical Bay is." + name = "medbay sign" + desc = "A direction sign, pointing out which way the Medbay is." icon_state = "direction_med" /obj/structure/sign/directions/evac - name = "escape arm" + name = "evacuation sign" desc = "A direction sign, pointing out which way the escape shuttle dock is." icon_state = "direction_evac" /obj/structure/sign/directions/supply - name = "cargo bay" + name = "cargo sign" desc = "A direction sign, pointing out which way the Cargo Bay is." icon_state = "direction_supply" /obj/structure/sign/directions/command - name = "command department" + name = "command department sign" desc = "A direction sign, pointing out which way the Command department is." icon_state = "direction_bridge" /obj/structure/sign/directions/vault - name = "vault directions" + name = "vault sign" desc = "A direction sign, pointing out which way the station's Vault is." icon_state = "direction_vault" /obj/structure/sign/directions/upload - name = "upload directions" + name = "upload sign" desc = "A direction sign, pointing out which way the station's AI Upload is." icon_state = "direction_upload" diff --git a/code/game/objects/structures/signs/signs_warning.dm b/code/game/objects/structures/signs/signs_warning.dm index 3a35f2d26ea7..9b0867b55206 100644 --- a/code/game/objects/structures/signs/signs_warning.dm +++ b/code/game/objects/structures/signs/signs_warning.dm @@ -1,122 +1,181 @@ +//warning signs + + +///////DANGEROUS THINGS + /obj/structure/sign/warning - name = "\improper WARNING" + name = "\improper WARNING sign" + sign_change_name = "Warning" desc = "A warning sign." icon_state = "securearea" + is_editable = TRUE /obj/structure/sign/warning/securearea - name = "\improper SECURE AREA" + name = "\improper SECURE AREA sign" + sign_change_name = "Warning - Secure Area" desc = "A warning sign which reads 'SECURE AREA'." + is_editable = TRUE /obj/structure/sign/warning/docking - name = "\improper KEEP CLEAR: DOCKING AREA" + name = "\improper KEEP CLEAR: DOCKING AREA sign" + sign_change_name = "Warning - Docking Area" desc = "A warning sign which reads 'KEEP CLEAR OF DOCKING AREA'." + is_editable = TRUE /obj/structure/sign/warning/biohazard - name = "\improper BIOHAZARD" + name = "\improper BIOHAZARD sign" + sign_change_name = "Warning - Biohazard" desc = "A warning sign which reads 'BIOHAZARD'." icon_state = "bio" + is_editable = TRUE /obj/structure/sign/warning/electricshock - name = "\improper HIGH VOLTAGE" + name = "\improper HIGH VOLTAGE sign" + sign_change_name = "Warning - High Voltage" desc = "A warning sign which reads 'HIGH VOLTAGE'." icon_state = "shock" + is_editable = TRUE /obj/structure/sign/warning/vacuum - name = "\improper HARD VACUUM AHEAD" + name = "\improper HARD VACUUM AHEAD sign" + sign_change_name = "Warning - Hard Vacuum" desc = "A warning sign which reads 'HARD VACUUM AHEAD'." icon_state = "space" + is_editable = TRUE /obj/structure/sign/warning/vacuum/external - name = "\improper EXTERNAL AIRLOCK" + name = "\improper EXTERNAL AIRLOCK sign" + sign_change_name = "Warning - External Airlock" desc = "A warning sign which reads 'EXTERNAL AIRLOCK'." layer = MOB_LAYER + is_editable = TRUE /obj/structure/sign/warning/deathsposal - name = "\improper DISPOSAL: LEADS TO SPACE" + name = "\improper DISPOSAL: LEADS TO SPACE sign" + sign_change_name = "Warning - Disposals: Leads to Space" desc = "A warning sign which reads 'DISPOSAL: LEADS TO SPACE'." icon_state = "deathsposal" + is_editable = TRUE /obj/structure/sign/warning/bodysposal - name = "\improper DISPOSAL: LEADS TO MORGUE" + name = "\improper DISPOSAL: LEADS TO MORGUE sign" + sign_change_name = "Warning - Disposals: Leads to Morgue" desc = "A warning sign which reads 'DISPOSAL: LEADS TO MORGUE'." icon_state = "bodysposal" - -/obj/structure/sign/warning/pods - name = "\improper ESCAPE PODS" - desc = "A warning sign which reads 'ESCAPE PODS'." - icon_state = "pods" + is_editable = TRUE /obj/structure/sign/warning/fire - name = "\improper DANGER: FIRE" + name = "\improper DANGER: FIRE sign" + sign_change_name = "Warning - Fire Hazard" desc = "A warning sign which reads 'DANGER: FIRE'." icon_state = "fire" resistance_flags = FIRE_PROOF + is_editable = TRUE /obj/structure/sign/warning/nosmoking - name = "\improper NO SMOKING" + name = "\improper NO SMOKING sign" + sign_change_name = "Warning - No Smoking" desc = "A warning sign which reads 'NO SMOKING'." icon_state = "nosmoking2" resistance_flags = FLAMMABLE + is_editable = TRUE /obj/structure/sign/warning/nosmoking/circle + name = "\improper NO SMOKING sign" + sign_change_name = "Warning - No Smoking Alt" + desc = "A warning sign which reads 'NO SMOKING'." icon_state = "nosmoking" + is_editable = TRUE /obj/structure/sign/warning/radiation - name = "\improper HAZARDOUS RADIATION" + name = "\improper HAZARDOUS RADIATION sign" + sign_change_name = "Warning - Radiation" desc = "A warning sign alerting the user of potential radiation hazards." icon_state = "radiation" + is_editable = TRUE /obj/structure/sign/warning/radiation/rad_area - name = "\improper RADIOACTIVE AREA" + name = "\improper RADIOACTIVE AREA sign" + sign_change_name = "Warning - Radioactive Area" desc = "A warning sign which reads 'RADIOACTIVE AREA'." + is_editable = TRUE /obj/structure/sign/warning/xeno_mining - name = "\improper DANGEROUS ALIEN LIFE" + name = "\improper DANGEROUS ALIEN LIFE sign" + sign_change_name = "Warning - Xenos" desc = "A sign that warns would-be travellers of hostile alien life in the vicinity." icon = 'icons/obj/mining.dmi' icon_state = "xeno_warning" + is_editable = TRUE /obj/structure/sign/warning/enginesafety - name = "\improper ENGINEERING SAFETY" + name = "\improper ENGINEERING SAFETY sign" + sign_change_name = "Warning - Engineering Safety Protocols" desc = "A sign detailing the various safety protocols when working on-site to ensure a safe shift." icon_state = "safety" + is_editable = TRUE /obj/structure/sign/warning/explosives - name = "\improper HIGH EXPLOSIVES" + name = "\improper HIGH EXPLOSIVES sign" + sign_change_name = "Warning - Explosives" desc = "A warning sign which reads 'HIGH EXPLOSIVES'." icon_state = "explosives" + is_editable = TRUE /obj/structure/sign/warning/explosives/alt - name = "\improper HIGH EXPLOSIVES" + name = "\improper HIGH EXPLOSIVES sign" + sign_change_name = "Warning - Explosives Alt" desc = "A warning sign which reads 'HIGH EXPLOSIVES'." icon_state = "explosives2" + is_editable = TRUE /obj/structure/sign/warning/testchamber - name = "\improper TESTING AREA" - desc = "A sign that warns of high-power testing equipment in the area. That's either a really powerful laser... or a satellite landing on some person's head." + name = "\improper TESTING AREA sign" + sign_change_name = "Warning - Testing Area" + desc = "A sign that warns of high-power testing equipment in the area." icon_state = "testchamber" + is_editable = TRUE /obj/structure/sign/warning/firingrange - name = "\improper FIRING RANGE" + name = "\improper FIRING RANGE sign" + sign_change_name = "Warning - Firing Range" desc = "A sign reminding you to remain behind the firing line, and to wear ear protection." icon_state = "firingrange" + is_editable = TRUE /obj/structure/sign/warning/coldtemp - name = "\improper FREEZING AIR" + name = "\improper FREEZING AIR sign" + sign_change_name = "Warning - Temp: Cold" desc = "A sign that warns of extremely cold air in the vicinity." icon_state = "cold" + is_editable = TRUE /obj/structure/sign/warning/hottemp - name = "\improper SUPERHEATED AIR" + name = "\improper SUPERHEATED AIR sign" + sign_change_name = "Warning - Temp: Hot" desc = "A sign that warns of extremely hot air in the vicinity." icon_state = "heat" + is_editable = TRUE /obj/structure/sign/warning/gasmask - name = "\improper CONTAMINATED AIR" - desc = "A sign that warns of dangerous particulates in the air, instructing you to wear a filtration device." + name = "\improper CONTAMINATED AIR sign" + sign_change_name = "Warning - Contaminated Air" + desc = "A sign that warns of dangerous particulates or gasses in the air, instructing you to wear a filtration device." icon_state = "gasmask" + is_editable = TRUE /obj/structure/sign/warning/chemdiamond name = "\improper REACTIVE CHEMICALS" + sign_change_name = "Warning - Hazardous Chemicals sign" desc = "A sign that warns of potentially reactive chemicals nearby, be they explosive, flamable, or acidic." icon_state = "chemdiamond" + is_editable = TRUE + +////MISC LOCATIONS + +/obj/structure/sign/warning/pods + name = "\improper ESCAPE PODS sign" + sign_change_name = "Location - Escape Pods" + desc = "A warning sign which reads 'ESCAPE PODS'." + icon_state = "pods" + is_editable = TRUE diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index d46caf30fc73..664947df1884 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -138,8 +138,8 @@ /obj/structure/table/proc/tableheadsmash(mob/living/user, mob/living/pushed_mob) pushed_mob.Knockdown(30) - pushed_mob.apply_damage(40, BRUTE, BODY_ZONE_HEAD) - pushed_mob.apply_damage(60, STAMINA) + pushed_mob.apply_damage(30, BRUTE, BODY_ZONE_HEAD) + pushed_mob.apply_damage(40, STAMINA) take_damage(50) if(user.mind?.martial_art.smashes_tables && user.mind?.martial_art.can_use(user)) deconstruct(FALSE) @@ -526,8 +526,8 @@ /obj/structure/table/optable/Initialize() . = ..() - for(var/direction in GLOB.cardinals) - computer = locate(/obj/machinery/computer/operating, get_step(src, direction)) + for(var/direction in GLOB.alldirs) + computer = locate(/obj/machinery/computer/operating) in get_step(src, direction) if(computer) computer.table = src break @@ -541,17 +541,23 @@ pushed_mob.forceMove(loc) pushed_mob.set_resting(TRUE, TRUE) visible_message("[user] lays [pushed_mob] on [src].") - check_patient() + get_patient() -/obj/structure/table/optable/proc/check_patient() - var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, loc) +/obj/structure/table/optable/proc/get_patient() + var/mob/living/carbon/M = locate(/mob/living/carbon) in loc if(M) if(M.resting) patient = M - return TRUE else patient = null + +/obj/structure/table/optable/proc/check_eligible_patient() + get_patient() + if(!patient) return FALSE + if(ishuman(patient) || ismonkey(patient)) + return TRUE + return FALSE /* * Racks diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index cc76a9975e3b..1988510b7b80 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -71,7 +71,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "tank_dispenser", name, 275, 103, master_ui, state) + ui = new(user, src, ui_key, "TankDispenser", name, 275, 103, master_ui, state) ui.open() /obj/structure/tank_dispenser/ui_data(mob/user) diff --git a/code/game/objects/structures/transit_tubes/station.dm b/code/game/objects/structures/transit_tubes/station.dm index a2380aee8f5b..b915879539d4 100644 --- a/code/game/objects/structures/transit_tubes/station.dm +++ b/code/game/objects/structures/transit_tubes/station.dm @@ -109,21 +109,22 @@ if(open_status == STATION_TUBE_CLOSED) icon_state = "opening_[base_icon]" open_status = STATION_TUBE_OPENING - spawn(OPEN_DURATION) - if(open_status == STATION_TUBE_OPENING) - icon_state = "open_[base_icon]" - open_status = STATION_TUBE_OPEN + addtimer(CALLBACK(src, .proc/finish_animation), OPEN_DURATION) +/obj/structure/transit_tube/station/proc/finish_animation() + switch(open_status) + if(STATION_TUBE_OPENING) + icon_state = "open_[base_icon]" + open_status = STATION_TUBE_OPEN + if(STATION_TUBE_CLOSING) + icon_state = "closed_[base_icon]" + open_status = STATION_TUBE_CLOSED /obj/structure/transit_tube/station/proc/close_animation() if(open_status == STATION_TUBE_OPEN) icon_state = "closing_[base_icon]" open_status = STATION_TUBE_CLOSING - spawn(CLOSE_DURATION) - if(open_status == STATION_TUBE_CLOSING) - icon_state = "closed_[base_icon]" - open_status = STATION_TUBE_CLOSED - + addtimer(CALLBACK(src, .proc/finish_animation), CLOSE_DURATION) /obj/structure/transit_tube/station/proc/launch_pod() if(launch_cooldown >= world.time) @@ -145,19 +146,25 @@ /obj/structure/transit_tube/station/pod_stopped(obj/structure/transit_tube_pod/pod, from_dir) pod_moving = TRUE - spawn(5) - if(reverse_launch) - pod.setDir(tube_dirs[1]) //turning the pod around for next launch. - launch_cooldown = world.time + cooldown_delay - open_animation() - sleep(OPEN_DURATION + 2) - pod_moving = FALSE - if(!QDELETED(pod)) - var/datum/gas_mixture/floor_mixture = loc.return_air() - floor_mixture.archive() - pod.air_contents.archive() - pod.air_contents.share(floor_mixture, 1) //mix the pod's gas mixture with the tile it's on - air_update_turf() + addtimer(CALLBACK(src, .proc/start_stopped, pod), 5) + +/obj/structure/transit_tube/station/proc/start_stopped(obj/structure/transit_tube_pod/pod) + if(QDELETED(pod)) + return + if(reverse_launch) + pod.setDir(tube_dirs[1]) //turning the pod around for next launch. + launch_cooldown = world.time + cooldown_delay + open_animation() + addtimer(CALLBACK(src, .proc/finish_stopped, pod), OPEN_DURATION + 2) + +/obj/structure/transit_tube/station/proc/finish_stopped(obj/structure/transit_tube_pod/pod) + pod_moving = FALSE + if(!QDELETED(pod)) + var/datum/gas_mixture/floor_mixture = loc.return_air() + floor_mixture.archive() + pod.air_contents.archive() + pod.air_contents.share(floor_mixture, 1) //mix the pod's gas mixture with the tile it's on + air_update_turf() /obj/structure/transit_tube/station/init_tube_dirs() switch(dir) diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm index 3bc0e8e0a6ad..b371c0f5436b 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm @@ -59,7 +59,13 @@ /obj/structure/transit_tube_pod/contents_explosion(severity, target) for(var/atom/movable/AM in contents) - AM.ex_act(severity, target) + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += AM + if(EXPLODE_HEAVY) + SSexplosions.medobj += AM + if(EXPLODE_LIGHT) + SSexplosions.lowobj += AM /obj/structure/transit_tube_pod/singularity_pull(S, current_size) ..() diff --git a/code/game/objects/structures/votingbox.dm b/code/game/objects/structures/votingbox.dm index d3e4b67ae9e4..6e33f024355d 100644 --- a/code/game/objects/structures/votingbox.dm +++ b/code/game/objects/structures/votingbox.dm @@ -114,7 +114,7 @@ to_chat(user,"You cast your vote.") /obj/structure/votebox/proc/valid_vote(obj/item/paper/I) - if(length_char(text) > VOTE_TEXT_LIMIT || findtext(text,"

Voting Results:


    ")) + if(length_char(I.info) > VOTE_TEXT_LIMIT || findtext(I.info,"

    Voting Results:


      ")) return FALSE return TRUE @@ -174,28 +174,30 @@ var/obj/item/paper/P = new(drop_location()) var/list/tally = list() + tally += {" + + "} + tally += "

      Voting Results:


        " for(var/option in results) - tally += "
      1. \"
        [option]
        \" - [results[option]] Vote[results[option] > 1 ? "s" : ""].
      2. " + tally += "
      3. \"
        [option]
        \" - [results[option]] Vote[results[option] > 1 ? "s" : ""].
      4. " tally += "
      " - P.extra_headers = {" - - "} + P.info = tally.Join() P.name = "Voting Results" P.update_icon() diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 2036ee603e72..934f13c01ef9 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -336,6 +336,12 @@ to_chat(user, "You tear off a strip of gauze and make a rag.") G.use(1) return + + if(istype(O, /obj/item/stack/ore/glass)) + new /obj/item/stack/sheet/sandblock(loc) + to_chat(user, "You wet the sand in the sink and form it into a block.") + O.use(1) + return if(!istype(O)) return @@ -353,7 +359,7 @@ O.acid_level = 0 create_reagents(5) reagents.add_reagent(dispensedreagent, 5) - reagents.reaction(O, TOUCH) + reagents.expose(O, TOUCH) user.visible_message("[user] washes [O] using [src].", \ "You wash [O] using [src].") return 1 @@ -416,9 +422,11 @@ alpha = 200 //Mappers can also just set this to 255 if they want curtains that can't be seen through layer = SIGN_LAYER anchored = TRUE - opacity = 0 + opacity = FALSE density = FALSE var/open = TRUE + /// if it can be seen through when closed + var/opaque_closed = FALSE /obj/structure/curtain/proc/toggle() open = !open @@ -430,12 +438,14 @@ layer = WALL_OBJ_LAYER density = TRUE open = FALSE - + if(opaque_closed) + opacity = TRUE else icon_state = "[icon_type]-open" layer = SIGN_LAYER density = FALSE open = TRUE + opacity = FALSE /obj/structure/curtain/attackby(obj/item/W, mob/user) if (istype(W, /obj/item/toy/crayon)) @@ -490,3 +500,18 @@ icon_state = "bounty-open" color = null alpha = 255 + opaque_closed = TRUE + +/obj/structure/curtain/cloth/ + color = null + alpha = 255 + opaque_closed = TRUE + +/obj/structure/curtain/cloth/deconstruct(disassembled = TRUE) + new /obj/item/stack/sheet/cloth (loc, 4) + new /obj/item/stack/rods (loc, 1) + qdel(src) + +/obj/structure/curtain/cloth/fancy + icon_type = "cur_fancy" + icon_state = "cur_fancy-open" diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index e9dc4cee0045..a8b63eee6448 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -27,6 +27,8 @@ var/real_explosion_block //ignore this, just use explosion_block var/breaksound = "shatter" var/hitsound = 'sound/effects/Glasshit.ogg' + flags_ricochet = RICOCHET_HARD + ricochet_chance_mod = 0.4 /obj/structure/window/examine(mob/user) @@ -370,6 +372,7 @@ state = RWINDOW_SECURE glass_type = /obj/item/stack/sheet/rglass rad_insulation = RAD_HEAVY_INSULATION + ricochet_chance_mod = 0.8 //this is shitcode but all of construction is shitcode and needs a refactor, it works for now //If you find this like 4 years later and construction still hasn't been refactored, I'm so sorry for this @@ -660,7 +663,7 @@ /obj/structure/window/shuttle name = "shuttle window" desc = "A reinforced, air-locked pod window." - icon = 'icons/obj/smooth_structures/shuttle_window.dmi' + icon = 'waspstation/icons/obj/smooth_structures/shuttle_window.dmi' icon_state = "shuttle_window" dir = FULLTILE_WINDOW_DIR max_integrity = 150 @@ -675,6 +678,7 @@ explosion_block = 3 glass_type = /obj/item/stack/sheet/titaniumglass glass_amount = 2 + ricochet_chance_mod = 0.9 /obj/structure/window/shuttle/narsie_act() add_atom_colour("#3C3434", FIXED_COLOUR_PRIORITY) diff --git a/code/game/say.dm b/code/game/say.dm index 65647e69d5d6..b2f431e36963 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -95,13 +95,13 @@ GLOBAL_LIST_INIT(freqtospan, list( var/spanned = attach_spans(input, spans) return "[say_mod(input, message_mode)], \"[spanned]\"" -/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, message_mode) +/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, message_mode, no_quote = FALSE) if(has_language(language)) var/atom/movable/AM = speaker.GetSource() if(AM) //Basically means "if the speaker is virtual" - return AM.say_quote(raw_message, spans, message_mode) + return no_quote ? raw_message : AM.say_quote(raw_message, spans, message_mode) else - return speaker.say_quote(raw_message, spans, message_mode) + return no_quote ? raw_message : speaker.say_quote(raw_message, spans, message_mode) else if(language) var/atom/movable/AM = speaker.GetSource() var/datum/language/D = GLOB.language_datum_instances[language] @@ -109,9 +109,9 @@ GLOBAL_LIST_INIT(freqtospan, list( spans |= D.scramble_spans raw_message = D.scramble(raw_message) if(AM) - return AM.say_quote(raw_message, spans, message_mode) + return no_quote ? raw_message : AM.say_quote(raw_message, spans, message_mode) else - return speaker.say_quote(raw_message, spans, message_mode) + return no_quote ? raw_message : speaker.say_quote(raw_message, spans, message_mode) else return "makes a strange sound." diff --git a/code/game/turfs/closed/wall/material_walls.dm b/code/game/turfs/closed/wall/material_walls.dm index 674d0030100e..88043e82f639 100644 --- a/code/game/turfs/closed/wall/material_walls.dm +++ b/code/game/turfs/closed/wall/material_walls.dm @@ -1,6 +1,6 @@ /turf/closed/wall/material name = "wall" - desc = "A solid wall made out of a certain material" + desc = "A huge chunk of material used to separate rooms." icon = 'icons/turf/walls/materialwall.dmi' icon_state = "wall" canSmoothWith = list(/turf/closed/wall/material) @@ -17,3 +17,7 @@ for(var/i in custom_materials) var/datum/material/M = i new M.sheet_type(src, FLOOR(custom_materials[M] / MINERAL_MATERIAL_AMOUNT, 1)) + +/turf/closed/wall/material/mat_update_desc(mat) + desc = "A huge chunk of [mat] used to separate rooms." + diff --git a/code/game/turfs/closed/wall/mineral_walls.dm b/code/game/turfs/closed/wall/mineral_walls.dm index ec1fa3e49f28..f0b47e13f420 100644 --- a/code/game/turfs/closed/wall/mineral_walls.dm +++ b/code/game/turfs/closed/wall/mineral_walls.dm @@ -186,10 +186,11 @@ /turf/closed/wall/mineral/titanium //has to use this path due to how building walls works name = "wall" desc = "A light-weight titanium wall used in shuttles." - icon = 'icons/turf/walls/shuttle_wall.dmi' + icon = 'waspstation/icons/turf/walls/shuttle_wall.dmi' icon_state = "map-shuttle" explosion_block = 3 - flags_1 = CAN_BE_DIRTY_1 | CHECK_RICOCHET_1 + flags_1 = CAN_BE_DIRTY_1 + flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD sheet_type = /obj/item/stack/sheet/mineral/titanium smooth = SMOOTH_MORE|SMOOTH_DIAGONAL canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium) @@ -270,14 +271,8 @@ fixed_underlay = list("space"=1) /turf/closed/wall/mineral/plastitanium/explosive/ex_act(severity) - var/datum/explosion/acted_explosion = null - for(var/datum/explosion/E in GLOB.explosions) - if(E.explosion_id == explosion_id) - acted_explosion = E - break - if(acted_explosion && istype(acted_explosion.explosion_source, /obj/item/bombcore)) - var/obj/item/bombcore/large/bombcore = new(get_turf(src)) - bombcore.detonate() + var/obj/item/bombcore/large/bombcore = new(get_turf(src)) + bombcore.detonate() ..() //have to copypaste this code diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm index d471aa6ebef1..f8d9755034f6 100644 --- a/code/game/turfs/closed/walls.dm +++ b/code/game/turfs/closed/walls.dm @@ -12,7 +12,7 @@ baseturfs = /turf/open/floor/plating - //WaspStation End + flags_ricochet = RICOCHET_HARD ///lower numbers are harder. Used to determine the probability of a hulk smashing through. Also, (hardness - 40) is used as a modifier for objects trying to embed in this (hardness of 30 results in a -10% chance) var/hardness = 40 @@ -32,6 +32,16 @@ var/list/dent_decals +/turf/closed/wall/Initialize(mapload) + . = ..() + if(is_station_level(z)) + GLOB.station_turfs += src + +/turf/closed/wall/Destroy() + if(is_station_level(z)) + GLOB.station_turfs -= src + ..() + /turf/closed/wall/examine(mob/user) . += ..() . += deconstruction_hints(user) @@ -42,17 +52,6 @@ /turf/closed/wall/attack_tk() return -/turf/closed/wall/handle_ricochet(obj/projectile/P) //A huge pile of shitcode! - 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) - return TRUE - /turf/closed/wall/proc/dismantle_wall(devastated=0, explode=0) if(devastated) devastate_wall() diff --git a/code/game/turfs/open/floor.dm b/code/game/turfs/open/floor.dm index c6bbe6ec5ee2..17adf042f15a 100644 --- a/code/game/turfs/open/floor.dm +++ b/code/game/turfs/open/floor.dm @@ -25,7 +25,6 @@ tiled_dirt = TRUE /turf/open/floor/Initialize(mapload) - if (!broken_states) broken_states = typelist("broken_states", list("damaged1", "damaged2", "damaged3", "damaged4", "damaged5")) else @@ -56,6 +55,14 @@ icon_regular_floor = icon_state if(mapload && prob(33)) MakeDirty() + if(is_station_level(z)) + GLOB.station_turfs += src + + +/turf/open/floor/Destroy() + if(is_station_level(z)) + GLOB.station_turfs -= src + ..() /turf/open/floor/ex_act(severity, target) var/shielded = is_shielded() @@ -134,6 +141,10 @@ /turf/open/floor/proc/make_plating() return ScrapeAway(flags = CHANGETURF_INHERIT_AIR) +///For when the floor is placed under heavy load. Calls break_tile(), but exists to be overridden by floor types that should resist crushing force. +/turf/open/floor/proc/crush() + break_tile() + /turf/open/floor/ChangeTurf(path, new_baseturf, flags) if(!isfloorturf(src)) return ..() //fucking turfs switch the fucking src of the fucking running procs @@ -249,17 +260,17 @@ return FALSE to_chat(user, "You build an airlock.") var/obj/machinery/door/airlock/A = new the_rcd.airlock_type(src) - - A.electronics = new/obj/item/electronics/airlock(A) - - if(the_rcd.conf_access) - A.electronics.accesses = the_rcd.conf_access.Copy() - A.electronics.one_access = the_rcd.use_one_access - + A.electronics = new /obj/item/electronics/airlock(A) + if(the_rcd.airlock_electronics) + A.electronics.accesses = the_rcd.airlock_electronics.accesses.Copy() + A.electronics.one_access = the_rcd.airlock_electronics.one_access + A.electronics.unres_sides = the_rcd.airlock_electronics.unres_sides if(A.electronics.one_access) A.req_one_access = A.electronics.accesses else A.req_access = A.electronics.accesses + if(A.electronics.unres_sides) + A.unres_sides = A.electronics.unres_sides A.autoclose = TRUE if(A.has_hatch) A.setup_hatch() @@ -296,8 +307,7 @@ return FALSE /turf/open/floor/material - name = "plating" - desc = "A flooring made out of a certain material" + name = "floor" icon_state = "materialfloor" material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS diff --git a/code/game/turfs/open/floor/mineral_floor.dm b/code/game/turfs/open/floor/mineral_floor.dm index bbe3eaa29ff7..d7d45c7a1b8e 100644 --- a/code/game/turfs/open/floor/mineral_floor.dm +++ b/code/game/turfs/open/floor/mineral_floor.dm @@ -79,6 +79,7 @@ /turf/open/floor/mineral/titanium name = "shuttle floor" + icon = 'waspstation/icons/turf/floors/shuttle_floors.dmi' icon_state = "titanium" floor_tile = /obj/item/stack/tile/mineral/titanium broken_states = list("titanium_dam1","titanium_dam2","titanium_dam3","titanium_dam4","titanium_dam5") @@ -113,6 +114,7 @@ //PLASTITANIUM (syndieshuttle) /turf/open/floor/mineral/plastitanium name = "shuttle floor" + icon = 'waspstation/icons/turf/floors/shuttle_floors.dmi' icon_state = "plastitanium" floor_tile = /obj/item/stack/tile/mineral/plastitanium broken_states = list("plastitanium_dam1","plastitanium_dam2","plastitanium_dam3","plastitanium_dam4","plastitanium_dam5") diff --git a/code/game/turfs/open/floor/plating/asteroid.dm b/code/game/turfs/open/floor/plating/asteroid.dm index 76e96771211a..af748b2a655e 100644 --- a/code/game/turfs/open/floor/plating/asteroid.dm +++ b/code/game/turfs/open/floor/plating/asteroid.dm @@ -53,6 +53,9 @@ /turf/open/floor/plating/asteroid/MakeDry() return +/turf/open/floor/plating/asteroid/crush() + return + /turf/open/floor/plating/asteroid/attackby(obj/item/W, mob/user, params) . = ..() if(!.) diff --git a/code/game/turfs/open/space/space.dm b/code/game/turfs/open/space/space.dm index e6179595613f..0386fc1c376e 100644 --- a/code/game/turfs/open/space/space.dm +++ b/code/game/turfs/open/space/space.dm @@ -166,14 +166,21 @@ ty-- DT = locate(tx, ty, destination_z) - var/atom/movable/AM = A.pulling + var/atom/movable/pulling = A.pulling + var/atom/movable/puller = A A.forceMove(DT) - if(AM) - var/turf/T = get_step(A.loc,turn(A.dir, 180)) - AM.can_be_z_moved = FALSE - AM.forceMove(T) - A.start_pulling(AM) - AM.can_be_z_moved = TRUE + + while (pulling != null) + var/next_pulling = pulling.pulling + + var/turf/T = get_step(puller.loc, turn(puller.dir, 180)) + pulling.can_be_z_moved = FALSE + pulling.forceMove(T) + puller.start_pulling(pulling) + pulling.can_be_z_moved = TRUE + + puller = pulling + pulling = next_pulling //now we're on the new z_level, proceed the space drifting stoplag()//Let a diagonal move finish, if necessary diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 323f05b064dc..2d631459f912 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -1,3 +1,4 @@ +GLOBAL_LIST_EMPTY(station_turfs) /turf icon = 'icons/turf/floors.dmi' @@ -22,6 +23,7 @@ var/explosion_level = 0 //for preventing explosion dodging var/explosion_id = 0 + var/list/explosion_throw_details var/requires_activation //add to air processing after initialize? var/changing_turf = FALSE @@ -277,8 +279,6 @@ /turf/Entered(atom/movable/AM) ..() - if(explosion_level && AM.ex_check(explosion_id)) - AM.ex_act(explosion_level) // If an opaque movable atom moves around we need to potentially update visibility. if (AM.opacity) @@ -375,7 +375,7 @@ var/datum/progressbar/progress = new(user, things.len, src) while (do_after(usr, 10, TRUE, src, FALSE, CALLBACK(src_object, /datum/component/storage.proc/mass_remove_from_storage, src, things, progress))) stoplag(1) - qdel(progress) + progress.end_progress() return TRUE @@ -435,8 +435,13 @@ var/atom/movable/AM = A if(!AM.ex_check(explosion_id)) continue - A.ex_act(severity, target) - CHECK_TICK + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += A + if(EXPLODE_HEAVY) + SSexplosions.medobj += A + if(EXPLODE_LIGHT) + SSexplosions.lowobj += A /turf/narsie_act(force, ignore_mobs, probability = 20) . = (prob(probability) || force) @@ -491,9 +496,7 @@ /turf/proc/acid_melt() return -/turf/handle_fall(mob/faller, forced) - if(!forced) - return +/turf/handle_fall(mob/faller) if(has_gravity(src)) playsound(src, "bodyfall", 50, TRUE) faller.drop_all_held_items() @@ -554,3 +557,12 @@ . = ..() if(. != BULLET_ACT_FORCE_PIERCE) . = BULLET_ACT_TURF + +/// Handles exposing a turf to reagents. +/turf/expose_reagents(list/reagents, datum/reagents/source, method=TOUCH, volume_modifier=1, show_message=TRUE) + if((. = ..()) & COMPONENT_NO_EXPOSE_REAGENTS) + return + + for(var/reagent in reagents) + var/datum/reagent/R = reagent + . |= R.expose_turf(src, reagents[R]) diff --git a/code/game/world.dm b/code/game/world.dm index b7d3a9e646e9..648eac7f1044 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -19,6 +19,8 @@ GLOBAL_VAR(restart_counter) * */ /world/New() + if(fexists("byond-extools.dll")) + call("byond-extools.dll", "maptick_initialize")() enable_debugger() //Early profile for auto-profiler - will be stopped on profiler init if necessary. @@ -45,6 +47,7 @@ GLOBAL_VAR(restart_counter) SSdbcore.CheckSchemaVersion() SSdbcore.SetRoundID() SetupLogs() + load_poll_data() populate_gear_list() @@ -142,6 +145,8 @@ GLOBAL_VAR(restart_counter) GLOB.world_shuttle_log = "[GLOB.log_directory]/shuttle.log" GLOB.discord_api_log = "[GLOB.log_directory]/discord_api_log.log" + GLOB.demo_log = "[GLOB.log_directory]/demo.log" + #ifdef UNIT_TESTS GLOB.test_log = file("[GLOB.log_directory]/tests.log") start_log(GLOB.test_log) diff --git a/code/modules/NTNet/relays.dm b/code/modules/NTNet/relays.dm index 43c6334d65b6..6289158901e0 100644 --- a/code/modules/NTNet/relays.dm +++ b/code/modules/NTNet/relays.dm @@ -69,7 +69,7 @@ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "ntnet_relay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "NtnetRelay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state) ui.open() diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index 90d792a2864c..ada9cc654a78 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -212,7 +212,7 @@ return null if (C) //user is already connected!. - to_chat(C, "You are about to get disconnected for matching a sticky ban after you connected. If this turns out to be the ban evasion detection system going haywire, we will automatically detect this and revert the matches. if you feel that this is the case, please wait EXACTLY 6 seconds then reconnect using file -> reconnect to see if the match was automatically reversed.") + to_chat(C, "You are about to get disconnected for matching a sticky ban after you connected. If this turns out to be the ban evasion detection system going haywire, we will automatically detect this and revert the matches. if you feel that this is the case, please wait EXACTLY 6 seconds then reconnect using file -> reconnect to see if the match was automatically reversed.", confidential = TRUE) var/desc = "\nReason:(StickyBan) You, or another user of this computer or connection ([bannedckey]) is banned from playing here. The ban reason is:\n[ban["message"]]\nThis ban was applied by [ban["admin"]]\nThis is a BanEvasion Detection System ban, if you think this ban is a mistake, please wait EXACTLY 6 seconds, then try again before filing an appeal.\n" . = list("reason" = "Stickyban", "desc" = desc) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 3b72344ae847..160bc43d87e6 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -2,11 +2,11 @@ //////////////////////////////// /proc/message_admins(msg) msg = "ADMIN LOG: [msg]" - to_chat(GLOB.admins, msg) + to_chat(GLOB.admins, msg, confidential = TRUE) /proc/relay_msg_admins(msg) msg = "RELAY: [msg]" - to_chat(GLOB.admins, msg) + to_chat(GLOB.admins, msg, confidential = TRUE) ///////////////////////////////////////////////////////////////////////////////////////////////Panels @@ -22,7 +22,7 @@ log_admin("[key_name(usr)] checked the individual player panel for [key_name(M)][isobserver(usr)?"":" while in game"].") if(!M) - to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.") + to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.", confidential = TRUE) return var/body = "Options for [M.key]" @@ -225,7 +225,7 @@ if (!istype(src, /datum/admins)) src = usr.client.holder if (!istype(src, /datum/admins)) - to_chat(usr, "Error: you are not an admin!") + to_chat(usr, "Error: you are not an admin!", confidential = TRUE) return var/dat dat = text("Admin Newscaster

      Admin Newscaster Unit

      ") @@ -464,7 +464,10 @@ if(marked_datum && istype(marked_datum, /atom)) dat += "Duplicate Marked Datum
      " - usr << browse(dat, "window=admin2;size=240x280") + var/datum/browser/popup = new(usr, "admin2", null, 240, 280) + popup.set_content(dat) + popup.open() +// usr << browse(dat, "window=admin2;size=240x280") return /////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge @@ -541,7 +544,7 @@ if(message) if(!check_rights(R_SERVER,0)) message = adminscrub(message,500) - to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:\n \t [message]") + to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:\n \t [message]", confidential = TRUE) log_admin("Announce: [key_name(usr)] : [message]") SSredbot.send_discord_message("ooc", "**[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:**\n [message]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Announce") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -564,7 +567,7 @@ else message_admins("[key_name(usr)] set the admin notice.") log_admin("[key_name(usr)] set the admin notice:\n[new_admin_notice]") - to_chat(world, "Admin Notice:\n \t [new_admin_notice]") + to_chat(world, "Admin Notice:\n \t [new_admin_notice]", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Admin Notice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! GLOB.admin_notice = new_admin_notice return @@ -635,9 +638,9 @@ set name="Toggle Entering" GLOB.enter_allowed = !( GLOB.enter_allowed ) if (!( GLOB.enter_allowed )) - to_chat(world, "New players may no longer enter the game.") + to_chat(world, "New players may no longer enter the game.", confidential = TRUE) else - to_chat(world, "New players may now enter the game.") + to_chat(world, "New players may now enter the game.", confidential = TRUE) log_admin("[key_name(usr)] toggled new player game entering.") message_admins("[key_name_admin(usr)] toggled new player game entering.") world.update_status() @@ -650,9 +653,9 @@ var/alai = CONFIG_GET(flag/allow_ai) CONFIG_SET(flag/allow_ai, !alai) if (alai) - to_chat(world, "The AI job is no longer chooseable.") + to_chat(world, "The AI job is no longer chooseable.", confidential = TRUE) else - to_chat(world, "The AI job is chooseable now.") + to_chat(world, "The AI job is chooseable now.", confidential = TRUE) log_admin("[key_name(usr)] toggled AI allowed.") world.update_status() SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle AI", "[!alai ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -664,9 +667,9 @@ var/new_nores = !CONFIG_GET(flag/norespawn) CONFIG_SET(flag/norespawn, new_nores) if (!new_nores) - to_chat(world, "You may now respawn.") + to_chat(world, "You may now respawn.", confidential = TRUE) else - to_chat(world, "You may no longer respawn :(") + to_chat(world, "You may no longer respawn :(", confidential = TRUE) message_admins("[key_name_admin(usr)] toggled respawn to [!new_nores ? "On" : "Off"].") log_admin("[key_name(usr)] toggled respawn to [!new_nores ? "On" : "Off"].") world.update_status() @@ -685,10 +688,10 @@ SSticker.SetTimeLeft(newtime) SSticker.start_immediately = FALSE if(newtime < 0) - to_chat(world, "The game start has been delayed.") + to_chat(world, "The game start has been delayed.", confidential = TRUE) log_admin("[key_name(usr)] delayed the round start.") else - to_chat(world, "The game will start in [DisplayTimeText(newtime)].") + to_chat(world, "The game will start in [DisplayTimeText(newtime)].", confidential = TRUE) SEND_SOUND(world, sound('sound/ai/attention.ogg')) log_admin("[key_name(usr)] set the pre-game delay to [DisplayTimeText(newtime)].") SSblackbox.record_feedback("tally", "admin_verb", 1, "Delay Game Start") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -784,10 +787,10 @@ set name = "Show Traitor Panel" if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") + to_chat(usr, "This can only be used on instances of type /mob", confidential = TRUE) return if(!M.mind) - to_chat(usr, "This mob has no mind!") + to_chat(usr, "This mob has no mind!", confidential = TRUE) return M.mind.traitor_panel() @@ -800,9 +803,9 @@ set name="Toggle tinted welding helmes" GLOB.tinted_weldhelh = !( GLOB.tinted_weldhelh ) if (GLOB.tinted_weldhelh) - to_chat(world, "The tinted_weldhelh has been enabled!") + to_chat(world, "The tinted_weldhelh has been enabled!", confidential = TRUE) else - to_chat(world, "The tinted_weldhelh has been disabled!") + to_chat(world, "The tinted_weldhelh has been disabled!", confidential = TRUE) log_admin("[key_name(usr)] toggled tinted_weldhelh.") message_admins("[key_name_admin(usr)] toggled tinted_weldhelh.") SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Tinted Welding Helmets", "[GLOB.tinted_weldhelh ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -814,9 +817,9 @@ var/new_guest_ban = !CONFIG_GET(flag/guest_ban) CONFIG_SET(flag/guest_ban, new_guest_ban) if (new_guest_ban) - to_chat(world, "Guests may no longer enter the game.") + to_chat(world, "Guests may no longer enter the game.", confidential = TRUE) else - to_chat(world, "Guests may now enter the game.") + to_chat(world, "Guests may now enter the game.", confidential = TRUE) log_admin("[key_name(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.") message_admins("[key_name_admin(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.") SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Guests", "[!new_guest_ban ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -827,37 +830,37 @@ var/mob/living/silicon/S = i ai_number++ if(isAI(S)) - to_chat(usr, "AI [key_name(S, usr)]'s laws:") + to_chat(usr, "AI [key_name(S, usr)]'s laws:", confidential = TRUE) else if(iscyborg(S)) var/mob/living/silicon/robot/R = S - to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [key_name(R.connected_ai)])":"(Independent)"]: laws:") + to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [key_name(R.connected_ai)])":"(Independent)"]: laws:", confidential = TRUE) else if (ispAI(S)) - to_chat(usr, "pAI [key_name(S, usr)]'s laws:") + to_chat(usr, "pAI [key_name(S, usr)]'s laws:", confidential = TRUE) else - to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:") + to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:", confidential = TRUE) if (S.laws == null) - to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.") + to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.", confidential = TRUE) else S.laws.show_laws(usr) if(!ai_number) - to_chat(usr, "No AIs located" ) + to_chat(usr, "No AIs located" , confidential = TRUE) /datum/admins/proc/output_all_devil_info() var/devil_number = 0 for(var/datum/mind/D in SSticker.mode.devils) devil_number++ var/datum/antagonist/devil/devil = D.has_antag_datum(/datum/antagonist/devil) - to_chat(usr, "Devil #[devil_number]:

      " + devil.printdevilinfo()) + to_chat(usr, "Devil #[devil_number]:

      " + devil.printdevilinfo(), confidential = TRUE) if(!devil_number) - to_chat(usr, "No Devils located" ) + to_chat(usr, "No Devils located" , confidential = TRUE) /datum/admins/proc/output_devil_info(mob/living/M) if(is_devil(M)) var/datum/antagonist/devil/devil = M.mind.has_antag_datum(/datum/antagonist/devil) - to_chat(usr, devil.printdevilinfo()) + to_chat(usr, devil.printdevilinfo(), confidential = TRUE) else - to_chat(usr, "[M] is not a devil.") + to_chat(usr, "[M] is not a devil.", confidential = TRUE) /datum/admins/proc/manage_free_slots() if(!check_rights()) @@ -960,7 +963,7 @@ if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk continue if(message) - to_chat(C, message) + to_chat(C, message, confidential = TRUE) kicked_client_names.Add("[C.key]") qdel(C) return kicked_client_names diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index a35783fa0939..c89d3d0f0264 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -37,6 +37,6 @@ var/F = file("[GLOB.log_directory]/[selected].html") if(!fexists(F)) - to_chat(src, "No [selected] logfile was found.") + to_chat(src, "No [selected] logfile was found.", confidential = TRUE) return src << browse(F,"window=investigate[selected];size=800x300") diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index 0d060518de0a..a5d991f33f25 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -114,7 +114,7 @@ GLOBAL_PROTECT(protected_ranks) set waitfor = FALSE if(IsAdminAdvancedProcCall()) - to_chat(usr, "Admin rank DB Sync blocked: Advanced ProcCall detected.") + to_chat(usr, "Admin rank DB Sync blocked: Advanced ProcCall detected.", confidential = TRUE) return var/list/sql_ranks = list() @@ -129,7 +129,7 @@ GLOBAL_PROTECT(protected_ranks) //load our rank - > rights associations /proc/load_admin_ranks(dbfail, no_update) if(IsAdminAdvancedProcCall()) - to_chat(usr, "Admin Reload blocked: Advanced ProcCall detected.") + to_chat(usr, "Admin Reload blocked: Advanced ProcCall detected.", confidential = TRUE) return GLOB.admin_ranks.Cut() GLOB.protected_ranks.Cut() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 95689612fa34..9d93e208b248 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -178,7 +178,7 @@ GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release)) GLOBAL_PROTECT(admin_verbs_possess) GLOBAL_LIST_INIT(admin_verbs_permissions, list(/client/proc/edit_admin_permissions, /client/proc/edit_mentors)) GLOBAL_PROTECT(admin_verbs_permissions) -GLOBAL_LIST_INIT(admin_verbs_poll, list(/client/proc/create_poll)) +GLOBAL_LIST_INIT(admin_verbs_poll, list(/client/proc/poll_panel)) GLOBAL_PROTECT(admin_verbs_poll) //verbs which can be hidden - needs work @@ -313,7 +313,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) remove_admin_verbs() verbs += /client/proc/show_verbs - to_chat(src, "Almost all of your adminverbs have been hidden.") + to_chat(src, "Almost all of your adminverbs have been hidden.", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide All Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return @@ -324,7 +324,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) verbs -= /client/proc/show_verbs add_admin_verbs() - to_chat(src, "All of your adminverbs are now visible.") + to_chat(src, "All of your adminverbs are now visible.", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -348,7 +348,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) ghost.reenter_corpse() SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Reenter") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! else if(isnewplayer(mob)) - to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.") + to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.", confidential = TRUE) return FALSE else //ghostize @@ -367,10 +367,10 @@ GLOBAL_PROTECT(admin_verbs_hideable) if(holder && mob) if(mob.invisibility == INVISIBILITY_OBSERVER) mob.invisibility = initial(mob.invisibility) - to_chat(mob, "Invisimin off. Invisibility reset.") + to_chat(mob, "Invisimin off. Invisibility reset.", confidential = TRUE) else mob.invisibility = INVISIBILITY_OBSERVER - to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") + to_chat(mob, "Invisimin on. You are now as invisible as a ghost.", confidential = TRUE) /client/proc/check_antagonists() set name = "Check Antagonists" @@ -412,6 +412,13 @@ GLOBAL_PROTECT(admin_verbs_hideable) holder.Secrets() SSblackbox.record_feedback("tally", "admin_verb", 1, "Secrets Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/proc/poll_panel() + set name = "Server Poll Management" + set category = "Admin" + if(!check_rights(R_POLL)) + return + holder.poll_list_panel() + SSblackbox.record_feedback("tally", "admin_verb", 1, "Server Poll Management") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/findStealthKey(txt) if(txt) @@ -528,7 +535,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if (isnull(ex_power)) return var/range = round((2 * ex_power)**GLOB.DYN_EX_SCALE) - to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])") + to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])", confidential = TRUE) /client/proc/get_dynex_power() set category = "Debug" @@ -539,7 +546,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if (isnull(ex_range)) return var/power = (0.5 * ex_range)**(1/GLOB.DYN_EX_SCALE) - to_chat(usr, "Estimated Explosive Power: [power]") + to_chat(usr, "Estimated Explosive Power: [power]", confidential = TRUE) /client/proc/set_dynex_scale() set category = "Debug" @@ -595,7 +602,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) set name = "Give Disease" set desc = "Gives a Disease to a mob." if(!istype(T)) - to_chat(src, "You can only give a disease to a mob of type /mob/living.") + to_chat(src, "You can only give a disease to a mob of type /mob/living.", confidential = TRUE) return var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in sortList(SSdisease.diseases, /proc/cmp_typepaths_asc) if(!D) @@ -644,9 +651,10 @@ GLOBAL_PROTECT(admin_verbs_hideable) holder.deactivate() + to_chat(src, "You are now a normal player.") - log_admin("[src] deadmined themself.") - message_admins("[src] deadmined themself.") + log_admin("[src] deadminned themselves.") + message_admins("[src] deadminned themselves.") SSblackbox.record_feedback("tally", "admin_verb", 1, "Deadmin") /client/proc/readmin() @@ -669,7 +677,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if (!holder) return //This can happen if an admin attempts to vv themself into somebody elses's deadmin datum by getting ref via brute force - to_chat(src, "You are now an admin.") + to_chat(src, "You are now an admin.", confidential = TRUE) message_admins("[src] re-adminned themselves.") log_admin("[src] re-adminned themselves.") SSblackbox.record_feedback("tally", "admin_verb", 1, "Readmin") diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm index 5bd556ed453b..c3e466e2e717 100644 --- a/code/modules/admin/callproc/callproc.dm +++ b/code/modules/admin/callproc/callproc.dm @@ -20,7 +20,7 @@ return target = value["value"] if(!istype(target)) - to_chat(usr, "Invalid target.") + to_chat(usr, "Invalid target.", confidential = TRUE) return if("No") target = null @@ -40,12 +40,12 @@ if(targetselected) if(!hascall(target, procname)) - to_chat(usr, "Error: callproc(): type [target.type] has no [proctype] named [procpath].") + to_chat(usr, "Error: callproc(): type [target.type] has no [proctype] named [procpath].", confidential = TRUE) return else procpath = "/[proctype]/[procname]" if(!text2path(procpath)) - to_chat(usr, "Error: callproc(): [procpath] does not exist.") + to_chat(usr, "Error: callproc(): [procpath] does not exist.", confidential = TRUE) return var/list/lst = get_callproc_args() @@ -54,7 +54,7 @@ if(targetselected) if(!target) - to_chat(usr, "Error: callproc(): owner of proc no longer exists.") + to_chat(usr, "Error: callproc(): owner of proc no longer exists.", confidential = TRUE) return var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." log_admin(msg) @@ -71,7 +71,7 @@ get_retval += returnval . = get_callproc_returnval(returnval, procname) if(.) - to_chat(usr, .) + to_chat(usr, ., confidential = TRUE) GLOBAL_VAR(AdminProcCaller) GLOBAL_PROTECT(AdminProcCaller) @@ -89,11 +89,11 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) /// Wrapper for proccalls where the datum is flagged as vareditted /proc/WrapAdminProcCall(datum/target, procname, list/arguments) if(target && procname == "Del") - to_chat(usr, "Calling Del() is not allowed") + to_chat(usr, "Calling Del() is not allowed", confidential = TRUE) return if(target != GLOBAL_PROC && !target.CanProcCall(procname)) - to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!") + to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!", confidential = TRUE) return var/current_caller = GLOB.AdminProcCaller var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller @@ -101,10 +101,10 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]") if(current_caller && current_caller != ckey) if(!GLOB.AdminProcCallSpamPrevention[ckey]) - to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.") + to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.", confidential = TRUE) GLOB.AdminProcCallSpamPrevention[ckey] = TRUE UNTIL(!GLOB.AdminProcCaller) - to_chat(usr, "Running your proc") + to_chat(usr, "Running your proc", confidential = TRUE) GLOB.AdminProcCallSpamPrevention -= ckey else UNTIL(!GLOB.AdminProcCaller) @@ -145,14 +145,14 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) if(!procname) return if(!hascall(A,procname)) - to_chat(usr, "Error: callproc_datum(): type [A.type] has no proc named [procname].") + to_chat(usr, "Error: callproc_datum(): type [A.type] has no proc named [procname].", confidential = TRUE) return var/list/lst = get_callproc_args() if(!lst) return if(!A || !IsValidSrc(A)) - to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists.") + to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists.", confidential = TRUE) return log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." @@ -163,7 +163,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc . = get_callproc_returnval(returnval,procname) if(.) - to_chat(usr, .) + to_chat(usr, ., confidential = TRUE) /client/proc/get_callproc_args() var/argnum = input("Number of arguments","Number:",0) as num|null diff --git a/code/modules/admin/create_poll.dm b/code/modules/admin/create_poll.dm deleted file mode 100644 index 1a376ae11165..000000000000 --- a/code/modules/admin/create_poll.dm +++ /dev/null @@ -1,150 +0,0 @@ -/client/proc/create_poll() - set name = "Create Poll" - set category = "Admin" - if(!check_rights(R_POLL)) - return - if(!SSdbcore.Connect()) - to_chat(src, "Failed to establish database connection.") - return - var/polltype = input("Choose poll type.","Poll Type") as null|anything in list("Single Option","Text Reply","Rating","Multiple Choice", "Instant Runoff Voting") - var/choice_amount = 0 - switch(polltype) - if("Single Option") - polltype = POLLTYPE_OPTION - if("Text Reply") - polltype = POLLTYPE_TEXT - if("Rating") - polltype = POLLTYPE_RATING - if("Multiple Choice") - polltype = POLLTYPE_MULTI - choice_amount = input("How many choices should be allowed?","Select choice amount") as num|null - switch(choice_amount) - if(0) - to_chat(src, "Multiple choice poll must have at least one choice allowed.") - return - if(1) - polltype = POLLTYPE_OPTION - if(null) - return - if ("Instant Runoff Voting") - polltype = POLLTYPE_IRV - else - return 0 - var/starttime = SQLtime() - var/endtime = input("Set end time for poll as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than starting time for obvious reasons.", "Set end time", SQLtime()) as text|null - if(!endtime) - return - endtime = sanitizeSQL(endtime) - var/datum/DBQuery/query_validate_time = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[endtime]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[endtime]','%Y-%c-%d %T'), 0)") - if(!query_validate_time.warn_execute() || QDELETED(usr) || !src) - qdel(query_validate_time) - return - if(query_validate_time.NextRow()) - var/checktime = text2num(query_validate_time.item[1]) - if(!checktime) - to_chat(src, "Datetime entered is improperly formatted or not later than current server time.") - qdel(query_validate_time) - return - endtime = query_validate_time.item[1] - qdel(query_validate_time) - var/adminonly - switch(alert("Admin only poll?",,"Yes","No","Cancel")) - if("Yes") - adminonly = 1 - if("No") - adminonly = 0 - else - return - var/dontshow - switch(alert("Hide poll results from tracking until completed?",,"Yes","No","Cancel")) - if("Yes") - dontshow = 1 - if("No") - dontshow = 0 - else - return - var/sql_ckey = sanitizeSQL(ckey) - var/question = input("Write your question","Question") as message|null - if(!question) - return - question = sanitizeSQL(question) - var/list/sql_option_list = list() - if(polltype != POLLTYPE_TEXT) - var/add_option = 1 - while(add_option) - var/option = input("Write your option","Option") as message|null - if(!option) - return - option = sanitizeSQL(option) - var/default_percentage_calc = 0 - if(polltype != POLLTYPE_IRV) - switch(alert("Should this option be included by default when poll result percentages are generated?",,"Yes","No","Cancel")) - if("Yes") - default_percentage_calc = 1 - if("No") - default_percentage_calc = 0 - else - return - var/minval = 0 - var/maxval = 0 - var/descmin = "" - var/descmid = "" - var/descmax = "" - if(polltype == POLLTYPE_RATING) - minval = input("Set minimum rating value.","Minimum rating") as num|null - if(minval) - minval = sanitizeSQL(minval) - else if(minval == null) - return - maxval = input("Set maximum rating value.","Maximum rating") as num|null - if(maxval) - maxval = sanitizeSQL(maxval) - if(minval >= maxval) - to_chat(src, "Maximum rating value can't be less than or equal to minimum rating value") - continue - else if(maxval == null) - return - descmin = input("Optional: Set description for minimum rating","Minimum rating description") as message|null - if(descmin) - descmin = sanitizeSQL(descmin) - else if(descmin == null) - return - descmid = input("Optional: Set description for median rating","Median rating description") as message|null - if(descmid) - descmid = sanitizeSQL(descmid) - else if(descmid == null) - return - descmax = input("Optional: Set description for maximum rating","Maximum rating description") as message|null - if(descmax) - descmax = sanitizeSQL(descmax) - else if(descmax == null) - return - sql_option_list += list(list("text" = "'[option]'", "minval" = "'[minval]'", "maxval" = "'[maxval]'", "descmin" = "'[descmin]'", "descmid" = "'[descmid]'", "descmax" = "'[descmax]'", "default_percentage_calc" = "'[default_percentage_calc]'")) - switch(alert(" ",,"Add option","Finish", "Cancel")) - if("Add option") - add_option = 1 - if("Finish") - add_option = 0 - else - return 0 - var/m1 = "[key_name(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"] - Question: [question]" - var/m2 = "[key_name_admin(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"]
      Question: [question]" - var/datum/DBQuery/query_polladd_question = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_question")] (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip, dontshow) VALUES ('[polltype]', '[starttime]', '[endtime]', '[question]', '[adminonly]', '[choice_amount]', '[sql_ckey]', INET_ATON('[address]'), '[dontshow]')") - if(!query_polladd_question.warn_execute()) - qdel(query_polladd_question) - return - qdel(query_polladd_question) - if(polltype != POLLTYPE_TEXT) - var/pollid = 0 - var/datum/DBQuery/query_get_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()") - if(!query_get_id.warn_execute()) - qdel(query_get_id) - return - if(query_get_id.NextRow()) - pollid = query_get_id.item[1] - qdel(query_get_id) - for(var/list/i in sql_option_list) - i |= list("pollid" = "'[pollid]'") - SSdbcore.MassInsert(format_table_name("poll_option"), sql_option_list, warn = 1) - log_admin(m1) - message_admins(m2) diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index e654508a6d6c..0697a0dc8e22 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -58,7 +58,7 @@ var/mob/dead/observer/C = pick_n_take(candidates) var/mob/living/body = pick_n_take(bodies) - to_chat(body, "Your mob has been taken over by a ghost!") + to_chat(body, "Your mob has been taken over by a ghost!", confidential = TRUE) message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(body)])") body.ghostize(0) body.key = C.key @@ -83,7 +83,7 @@ var/turf/T = find_safe_turf() new /obj/effect/temp_visual/gravpush(get_turf(M)) M.forceMove(T) - to_chat(M, "Pop!") + to_chat(M, "Pop!", confidential = TRUE) /obj/effect/station_crash name = "station crash" @@ -108,7 +108,7 @@ /obj/effect/forcefield/arena_shuttle name = "portal" timeleft = 0 - var/list/warp_points + var/list/warp_points = list() /obj/effect/forcefield/arena_shuttle/Initialize() . = ..() @@ -121,23 +121,23 @@ var/mob/living/L = AM if(L.pulling && istype(L.pulling, /obj/item/bodypart/head)) - to_chat(L, "Your offering is accepted. You may pass.") + to_chat(L, "Your offering is accepted. You may pass.", confidential = TRUE) qdel(L.pulling) var/turf/LA = get_turf(pick(warp_points)) L.forceMove(LA) L.hallucination = 0 - to_chat(L, "The battle is won. Your bloodlust subsides.") + to_chat(L, "The battle is won. Your bloodlust subsides.", confidential = TRUE) for(var/obj/item/chainsaw/doomslayer/chainsaw in L) qdel(chainsaw) else - to_chat(L, "You are not yet worthy of passing. Drag a severed head to the barrier to be allowed entry to the hall of champions.") + to_chat(L, "You are not yet worthy of passing. Drag a severed head to the barrier to be allowed entry to the hall of champions.", confidential = TRUE) /obj/effect/landmark/shuttle_arena_safe name = "hall of champions" desc = "For the winners." /obj/effect/landmark/shuttle_arena_entrance - name = "the arena" + name = "\proper the arena" desc = "A lava filled battlefield." @@ -157,7 +157,7 @@ var/obj/effect/landmark/LA = pick(warp_points) var/mob/living/M = AM M.forceMove(get_turf(LA)) - to_chat(M, "You're trapped in a deadly arena! To escape, you'll need to drag a severed head to the escape portals.") + to_chat(M, "You're trapped in a deadly arena! To escape, you'll need to drag a severed head to the escape portals.", confidential = TRUE) INVOKE_ASYNC(src, .proc/do_bloodbath, M) /obj/effect/forcefield/arena_shuttle_entrance/proc/do_bloodbath(mob/living/L) diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 4ff7a536fe8f..25eb784f82fa 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -157,7 +157,7 @@ generally it would be used like so: /proc/admin_proc() if(!check_rights(R_ADMIN)) return - to_chat(world, "you have enough rights!") + to_chat(world, "you have enough rights!", confidential = TRUE) NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call you will have to do something like if(client.rights & R_ADMIN) yourself. @@ -168,7 +168,7 @@ you will have to do something like if(client.rights & R_ADMIN) yourself. return 1 else if(show_msg) - to_chat(usr, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].") + to_chat(usr, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].", confidential = TRUE) return 0 //probably a bit iffy - will hopefully figure out a better solution diff --git a/code/modules/admin/outfits.dm b/code/modules/admin/outfits.dm index 94fa17f4ecd5..d2c1d2fceb71 100644 --- a/code/modules/admin/outfits.dm +++ b/code/modules/admin/outfits.dm @@ -28,7 +28,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits /datum/admins/proc/delete_outfit(mob/admin,datum/outfit/O) GLOB.custom_outfits -= O qdel(O) - to_chat(admin,"Outfit deleted.") + to_chat(admin,"Outfit deleted.", confidential = TRUE) outfit_manager(admin) /datum/admins/proc/load_outfit(mob/admin) @@ -38,15 +38,15 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits var/filedata = file2text(outfit_file) var/json = json_decode(filedata) if(!json) - to_chat(admin,"JSON decode error.") + to_chat(admin,"JSON decode error.", confidential = TRUE) return var/otype = text2path(json["outfit_type"]) if(!ispath(otype,/datum/outfit)) - to_chat(admin,"Malformed/Outdated file.") + to_chat(admin,"Malformed/Outdated file.", confidential = TRUE) return var/datum/outfit/O = new otype if(!O.load_from(json)) - to_chat(admin,"Malformed/Outdated file.") + to_chat(admin,"Malformed/Outdated file.", confidential = TRUE) return GLOB.custom_outfits += O outfit_manager(admin) diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm index a8abd82f021d..9d5066d5f174 100644 --- a/code/modules/admin/permissionedit.dm +++ b/code/modules/admin/permissionedit.dm @@ -130,7 +130,7 @@ log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.") return if(IsAdminAdvancedProcCall()) - to_chat(usr, "Admin Edit blocked: Advanced ProcCall detected.") + to_chat(usr, "Admin Edit blocked: Advanced ProcCall detected.", confidential = TRUE) return var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/permissions) permissions_assets.send(src) @@ -145,19 +145,19 @@ skip = TRUE if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_admins) && task == "rank") if(admin_ckey in GLOB.protected_admins) - to_chat(usr, "Editing the rank of this admin is blocked by server configuration.") + to_chat(usr, "Editing the rank of this admin is blocked by server configuration.", confidential = TRUE) return if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions") if(D.rank in GLOB.protected_ranks) - to_chat(usr, "Editing the flags of this rank is blocked by server configuration.") + to_chat(usr, "Editing the flags of this rank is blocked by server configuration.", confidential = TRUE) return if(CONFIG_GET(flag/load_legacy_ranks_only) && (task == "add" || task == "rank" || task == "permissions")) - to_chat(usr, "Database rank loading is disabled, only temporary changes can be made to a rank's permissions and permanently creating a new rank is blocked.") + to_chat(usr, "Database rank loading is disabled, only temporary changes can be made to a rank's permissions and permanently creating a new rank is blocked.", confidential = TRUE) legacy_only = TRUE if(check_rights(R_DBRANKS, FALSE)) if(!skip) if(!SSdbcore.Connect()) - to_chat(usr, "Unable to connect to database, changes are temporary only.") + to_chat(usr, "Unable to connect to database, changes are temporary only.", confidential = TRUE) use_db = FALSE else use_db = alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel") @@ -209,7 +209,7 @@ if(!.) return FALSE if(!admin_ckey && (. in GLOB.admin_datums+GLOB.deadmins)) - to_chat(usr, "[admin_key] is already an admin.") + to_chat(usr, "[admin_key] is already an admin.", confidential = TRUE) return FALSE if(use_db) . = sanitizeSQL(.) @@ -220,7 +220,7 @@ return FALSE if(query_admin_in_db.NextRow()) qdel(query_admin_in_db) - to_chat(usr, "[admin_key] already listed in admin database. Check the Management tab if they don't appear in the list of admins.") + to_chat(usr, "[admin_key] already listed in admin database. Check the Management tab if they don't appear in the list of admins.", confidential = TRUE) return FALSE qdel(query_admin_in_db) var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin")] (ckey, `rank`) VALUES ('[.]', 'NEW ADMIN')") @@ -273,7 +273,7 @@ D.deactivate() //after logs so the deadmined admin can see the message. /datum/admins/proc/auto_deadmin() - to_chat(owner, "You are now a normal player.") + to_chat(owner, "You are now a normal player.", confidential = TRUE) var/old_owner = owner deactivate() message_admins("[old_owner] deadmined via auto-deadmin config.") @@ -430,13 +430,13 @@ return for(var/datum/admin_rank/R in GLOB.admin_ranks) if(R.name == admin_rank && (!(R.rights & usr.client.holder.rank.can_edit_rights) == R.rights)) - to_chat(usr, "You don't have edit rights to all the rights this rank has, rank deletion not permitted.") + to_chat(usr, "You don't have edit rights to all the rights this rank has, rank deletion not permitted.", confidential = TRUE) return if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && (admin_rank in GLOB.protected_ranks)) - to_chat(usr, "Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.") + to_chat(usr, "Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.", confidential = TRUE) return if(CONFIG_GET(flag/load_legacy_ranks_only)) - to_chat(usr, "Rank deletion not permitted while database rank loading is disabled.") + to_chat(usr, "Rank deletion not permitted while database rank loading is disabled.", confidential = TRUE) return admin_rank = sanitizeSQL(admin_rank) var/datum/DBQuery/query_admins_with_rank = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("admin")] WHERE `rank` = '[admin_rank]'") @@ -445,7 +445,7 @@ return if(query_admins_with_rank.NextRow()) qdel(query_admins_with_rank) - to_chat(usr, "Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen.") + to_chat(usr, "Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen.", confidential = TRUE) return qdel(query_admins_with_rank) if(alert("Are you sure you want to remove [admin_rank]?","Confirm Removal","Do it","Cancel") == "Do it") @@ -474,4 +474,4 @@ qdel(query_sync_lastadminrank) return qdel(query_sync_lastadminrank) - to_chat(usr, "Sync of [admin_key] successful.") + to_chat(usr, "Sync of [admin_key] successful.", confidential = TRUE) diff --git a/code/modules/admin/poll_management.dm b/code/modules/admin/poll_management.dm new file mode 100644 index 000000000000..f7062ad1722b --- /dev/null +++ b/code/modules/admin/poll_management.dm @@ -0,0 +1,735 @@ +/** + * Datum which holds details of a running poll loaded from the database and supplementary info. + * + * Used to minimize the need for querying this data every time it's needed. + * + */ +/datum/poll_question + ///Reference list of the options for this poll, not used by text response polls. + var/list/options = list() + ///Table id of this poll, will be null until poll has been created. + var/poll_id + ///The type of poll to be created, must be POLLTYPE_OPTION, POLLTYPE_TEXT, POLLTYPE_RATING, POLLTYPE_MULTI or POLLTYPE_IRV. + var/poll_type + ///Count of how many players have voted or responded to this poll. + var/poll_votes + ///Ckey of the poll's original author + var/created_by + ///Date and time the poll opens, timestamp format is YYYY-MM-DD HH:MM:SS. + var/start_datetime + ///Date and time the poll will run until, timestamp format is YYYY-MM-DD HH:MM:SS. + var/end_datetime + ///The title text of the poll, shows up on the list of polls. + var/question + ///Supplementary text displayed only when responding to a poll. + var/subtitle + ///Hides the poll from any client without a holder datum. + var/admin_only + ///The number of responses allowed in a multiple-choice poll, more can be selected but won't be recorded. + var/options_allowed + ///Hint for statbus, not used by the game; Stops the results of a poll from being displayed until the end_datetime is reached. + var/dont_show + ///Allows a player to change their vote to a poll they've already voted on, off by default. + var/allow_revoting + ///Indicates if a poll has been submitted or loaded from the DB so the management panel will open with edit functions. + var/edit_ready = FALSE + ///Holds duration data when creating or editing a poll and refreshing the poll creation window. + var/duration + ///Holds interval data when creating or editing a poll and refreshing the poll creation window. + var/interval + ///Indicates a poll is set to not start in the future, still visible for editing but not voting on. + var/future_poll + +/** + * Datum which holds details of a poll option loaded from the database. + * + * Used to minimize the need for querying this data every time it's needed. + * + */ +/datum/poll_option + ///Reference to the poll this option belongs to + var/datum/poll_question/parent_poll + ///Table id of this option, will be null until poll has been created. + var/option_id + ///Description/name of this option + var/text + ///For rating polls, the minimum selectable value allowed; Supported value range is -2147483648 to 2147483647 + var/min_val + ///For rating polls, the maximum selectable value allowed; Supported value range is -2147483648 to 2147483647 + var/max_val + ///Optional for rating polls, description shown next to the minimum value + var/desc_min = "" + ///Optional for rating polls, description shown next to the rounded whole middle value + var/desc_mid = "" + ///Optional for rating polls, description shown next to the maximum value + var/desc_max = "" + ///Hint for statbus, not used by the game; If this option should be included by default when calculating the resulting percentages of all options for this poll + var/default_percentage_calc + +/** + * Shows a list of all current and future polls and buttons to edit or delete them or create a new poll. + * + */ +/datum/admins/proc/poll_list_panel() + var/list/output = list("Current and future polls
      Note when editing polls or their options changes are not saved until you press Submit Poll.
      New PollReload Polls
      ") + for(var/p in GLOB.polls) + var/datum/poll_question/poll = p + output += {"[poll.question] + Edit + Delete + "} + if(poll.subtitle) + output += "
      [poll.subtitle]" + output += "
      [poll.future_poll ? "Starts" : "Started"] at [poll.start_datetime] | Ends at [poll.end_datetime]" + if(poll.admin_only) + output += " | Admin only" + if(poll.dont_show) + output += " | Hidden from tracking until complete" + output += " | [poll.poll_votes] players have [poll.poll_type == POLLTYPE_TEXT ? "responded" : "voted"]
      " + var/datum/browser/panel = new(usr, "plpanel", "Poll list Panel", 700, 400) + panel.set_content(jointext(output, "")) + panel.open() + +/** + * Show the options for creating a poll or editing its parameters along with its linked options. + * + */ +/datum/admins/proc/poll_management_panel(datum/poll_question/poll) + var/list/output = list("
      [HrefTokenFormField()]") + output += {"Poll type +
      + +
      + Question + + Multiple-choice options allowed + +
      + + + +
      +
      +
      + Duration +
      + + +
      + +
      +
      + + +
      +
      + Start +
      + +
      +
      + + +
+
+
+ Subtitle (Optional) +
+ +
+
+ "} + var/option_count = 0 + if(!poll) + output += {" + +
+ +
+ First enter the poll question details and press Initialize Question. +
+ Then add poll options and press Submit Poll to save and create the question and options. No options are required for Text Reply polls. +
+ Which poll type should I use? + "} + else + output += "" + if(poll.edit_ready) + output += {" +
+ "} + if(poll.poll_type == POLLTYPE_TEXT) + output += "Clear poll responses [poll.poll_votes] players have responded" + else + output += "Clear poll votes [poll.poll_votes] players have voted" + if(poll.poll_type == POLLTYPE_TEXT) + output += "
" + else + output += "

Add Option
" + if(length(poll.options)) + for(var/o in poll.options) + var/datum/poll_option/option = o + option_count++ + output += {"Option [option_count] + Edit + Delete +
[option.text] + "} + if(poll.poll_type == POLLTYPE_RATING) + output += {"
Minimum value: [option.min_val] | Maximum value: [option.max_val] +
Minimum description: [option.desc_min] +
Middle description: [option.desc_mid] +
Maximum description: [option.desc_max] + "} + output += "
" + var/datum/browser/panel = new(usr, "pmpanel", "Poll Management Panel", 780, 640) + panel.add_stylesheet("admin_panelscss", 'html/admin/admin_panels.css') + if(usr.client.prefs.tgui_fancy) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support + panel.add_stylesheet("admin_panelscss3", 'html/admin/admin_panels_css3.css') + panel.set_content(jointext(output, "")) + panel.open() + +/** + * Processes topic data from poll management panel. + * + * Reads through returned form data and assigns data to the poll datum, creating a new one if required, before passing it to be saved. + * Also does some simple error checking to ensure the poll will be valid before creation. + * + */ +/datum/admins/proc/poll_parse_href(list/href_list, datum/poll_question/poll) + if(!check_rights(R_POLL)) + return + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) + return + var/list/error_state = list() + var/new_poll = FALSE + var/clear_votes = FALSE + var/submit_ready = FALSE + if(!poll) + poll = new(creator = usr.client.ckey) + new_poll = TRUE + if(new_poll) + poll.poll_type = href_list["polltype"] + switch(href_list["radioduration"]) + if("runfor") + poll.duration = text2num(href_list["duration"]) + poll.interval = href_list["durationtype"] + if("rununtil") + if(href_list["enddatetimetext"] != "YYYY-MM-DD HH:MM:SS") + poll.duration = href_list["enddatetimetext"] + if(new_poll) + poll.end_datetime = poll.duration + if(!poll.duration) + error_state += "No duration was provided." + switch(href_list["radiostart"]) + if("startnow") + poll.start_datetime = null + if("startdatetime") + if(href_list["startdatetimetext"] && href_list["startdatetimetext"] != "YYYY-MM-DD HH:MM:SS") + poll.start_datetime = href_list["startdatetimetext"] + else + error_state += "Start datetime was selected but none was provided." + if(href_list["question"]) + poll.question = href_list["question"] + else + error_state += "No question was provided." + poll.subtitle = href_list["subtitle"] + if(href_list["adminonly"]) + poll.admin_only = TRUE + else + poll.admin_only = FALSE + if(href_list["dontshow"]) + poll.dont_show = TRUE + else + poll.dont_show = FALSE + if(href_list["allowrevoting"]) + poll.allow_revoting = TRUE + else + poll.allow_revoting = FALSE + if(href_list["clearvotesedit"]) + clear_votes = TRUE + if(href_list["submitpoll"]) + submit_ready = TRUE + if(poll.poll_type == POLLTYPE_MULTI) + if(text2num(href_list["optionsallowed"])) + poll.options_allowed = text2num(href_list["optionsallowed"]) + if(poll.options_allowed == 1) + error_state += "Multiple choice polls require more than one option allowed, use a standard option poll for singlular voting." + if(poll.options_allowed < 0) + error_state += "Multiple choice options allowed cannot be negative." + else + error_state += "Multiple choice poll was selected but no number of allowed options was provided." + if(submit_ready && poll.poll_type != POLLTYPE_TEXT && !length(poll.options)) + error_state += "This poll type requires at least one option." + if(error_state.len) + if(poll.edit_ready) + to_chat(usr, "Not all edits were applied because the following errors were present:\n[error_state.Join("\n")]", confidential = TRUE) + else + to_chat(usr, "Poll not [new_poll ? "initialized" : "submitted"] because the following errors were present:\n[error_state.Join("\n")]", confidential = TRUE) + if(new_poll) + qdel(poll) + return + if(submit_ready) + var/db = poll.edit_ready //if the poll is new it will need its options inserted for the first time + poll.save_poll_data(clear_votes) + if(!db) + poll.save_all_options() + poll_management_panel(poll) + +/datum/poll_question/New(id, polltype, starttime, endtime, question, subtitle, adminonly, multiplechoiceoptions, dontshow, allow_revoting, vote_count, creator, future, dbload = FALSE) + poll_id = text2num(id) + poll_type = polltype + start_datetime = starttime + end_datetime = endtime + src.question = question + src.subtitle = subtitle + admin_only = text2num(adminonly) + options_allowed = text2num(multiplechoiceoptions) + dont_show = text2num(dontshow) + src.allow_revoting = text2num(allow_revoting) + poll_votes = text2num(vote_count) || 0 + created_by = creator + future_poll = text2num(future) + edit_ready = dbload + GLOB.polls += src + +/datum/poll_question/Destroy() + GLOB.polls -= src + return ..() + +/** + * Sets a poll and its associated data as deleted in the database. + * + * Calls the procedure set_poll_deleted to set the deleted column to 1 for each row in the poll_ tables matching the poll id used. + * Then deletes each option datum and finally the poll itself. + * + */ +/datum/poll_question/proc/delete_poll() + if(!check_rights(R_POLL)) + return + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) + return + var/datum/DBQuery/query_delete_poll = SSdbcore.NewQuery("CALL set_poll_deleted('[sanitizeSQL(poll_id)]')") + if(!query_delete_poll.warn_execute()) + qdel(query_delete_poll) + return + qdel(query_delete_poll) + for(var/o in options) + var/datum/poll_option/option = o + qdel(option) + GLOB.polls -= src + qdel(src) + +/** + * Inserts or updates a poll question to the database. + * + * Uses INSERT ON DUPLICATE KEY UPDATE to handle both inserting and updating at once. + * The start and end datetimes and poll id for new polls is then retrieved for the poll datum. + * Arguments: + * * clear_votes - When true will call clear_poll_votes() to delete all votes matching this poll id. + * + */ +/datum/poll_question/proc/save_poll_data(clear_votes) + if(!check_rights(R_POLL)) + return + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) + return + var/poll_id_sql = "[sanitizeSQL(poll_id)]" + var/new_poll = FALSE + if(!poll_id_sql) + poll_id_sql = "NULL" + new_poll = TRUE + var/poll_type_sql = sanitizeSQL(poll_type) + var/question_sql = sanitizeSQL(question) + var/subtitle_sql = sanitizeSQL(subtitle) + var/admin_only_sql = sanitizeSQL(admin_only) + var/options_allowed_sql = "[sanitizeSQL(options_allowed)]" + if(poll_type != POLLTYPE_MULTI) + options_allowed_sql = "NULL" + var/dont_show_sql = sanitizeSQL(dont_show) + var/allow_revoting_sql = sanitizeSQL(allow_revoting) + var/admin_ckey = sanitizeSQL(created_by) + var/admin_ip = sanitizeSQL(usr.client.address) + var/end_datetime_sql + if(interval) + end_datetime_sql = "NOW() + INTERVAL [sanitizeSQL(duration)] [sanitizeSQL(interval)]" + else + end_datetime_sql = "'[sanitizeSQL(duration)]'" + var/start_datetime_sql + if(!start_datetime) + start_datetime_sql = "NOW()" + else + start_datetime_sql = "'[sanitizeSQL(start_datetime)]'" + var/kn = key_name(usr) + var/kna = key_name_admin(usr) + var/datum/DBQuery/query_save_poll = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_question")] (id, polltype, created_datetime, starttime, endtime, question, subtitle, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip, dontshow, allow_revoting) VALUES ([poll_id_sql], '[poll_type_sql]', NOW(), [start_datetime_sql], [end_datetime_sql], '[question_sql]', '[subtitle_sql]', '[admin_only_sql]', [options_allowed_sql], '[admin_ckey]', INET_ATON('[admin_ip]'), '[dont_show_sql]', '[allow_revoting_sql]') ON DUPLICATE KEY UPDATE starttime = [start_datetime_sql], endtime = [end_datetime_sql], question = '[question_sql]', subtitle = '[subtitle_sql]', adminonly = '[admin_only_sql]', multiplechoiceoptions = [options_allowed_sql], dontshow = '[dont_show_sql]', allow_revoting = '[allow_revoting_sql]'") + if(!query_save_poll.warn_execute()) + qdel(query_save_poll) + return + qdel(query_save_poll) + if(poll_id_sql == "NULL") + poll_id_sql = "LAST_INSERT_ID()" + var/datum/DBQuery/query_get_poll_id_start_endtime = SSdbcore.NewQuery("SELECT LAST_INSERT_ID(), starttime, endtime, IF(starttime > NOW(), 1, 0) FROM [format_table_name("poll_question")] WHERE id = [poll_id_sql]") + if(!query_get_poll_id_start_endtime.warn_execute()) + qdel(query_get_poll_id_start_endtime) + return + if(query_get_poll_id_start_endtime.NextRow()) + if(!poll_id) + poll_id = text2num(query_get_poll_id_start_endtime.item[1]) + start_datetime = query_get_poll_id_start_endtime.item[2] + end_datetime = query_get_poll_id_start_endtime.item[3] + future_poll = text2num(query_get_poll_id_start_endtime.item[4]) + qdel(query_get_poll_id_start_endtime) + if(clear_votes) + clear_poll_votes() + edit_ready = TRUE + var/msg = "has [new_poll ? "created a new" : "edited a"][admin_only ? " admin only" : ""] server poll. Question: [question]" + if(admin_only) + log_admin_private("[kn] [msg]") + else + log_admin("[kn] [msg]") + message_admins("[kna] [msg]") + +/** + * Saves all options of a poll to the database. + * + * Saves all the created options for a poll when it's submitted to the DB for the first time and associated an id with the options. + * Insertion and id querying for each option is done separately to ensure data integrity; this is less performant, but not significantly. + * Using MassInsert() would mean having to query a list of rows by poll_id or matching by fields afterwards, which doesn't guarantee accuracy. + * + */ +/datum/poll_question/proc/save_all_options() + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) + return + for(var/o in options) + var/datum/poll_option/option = o + option.save_option() + var/datum/DBQuery/query_get_option_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()") + if(!query_get_option_id.warn_execute()) + qdel(query_get_option_id) + return + if(query_get_option_id.NextRow()) + option.option_id = text2num(query_get_option_id.item[1]) + qdel(query_get_option_id) + +/** + * Deletes all votes or text replies for this poll, depending on its type. + * + */ +/datum/poll_question/proc/clear_poll_votes() + if(!check_rights(R_POLL)) + return + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) + return + var/table = "poll_vote" + if(poll_type == POLLTYPE_TEXT) + table = "poll_textreply" + var/datum/DBQuery/query_clear_poll_votes = SSdbcore.NewQuery("UPDATE [format_table_name("[table]")] SET deleted = 1 WHERE pollid = [sanitizeSQL(poll_id)]") + if(!query_clear_poll_votes.warn_execute()) + qdel(query_clear_poll_votes) + return + qdel(query_clear_poll_votes) + poll_votes = 0 + to_chat(usr, "Poll [poll_type == POLLTYPE_TEXT ? "responses" : "votes"] cleared.", confidential = TRUE) + +/** + * Show the options for creating a poll option or editing its parameters. + * + */ +/datum/admins/proc/poll_option_panel(datum/poll_question/poll, datum/poll_option/option) + var/list/output = list("
[HrefTokenFormField()]") + output += {" Option for poll [poll.question] +
+ +
+ "} + if(poll.poll_type == POLLTYPE_RATING) + output += {"Minimum value + + Maximum Value + +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ "} + output += {"
+ + + + "} + var/panel_height = 180 + if(poll.poll_type == POLLTYPE_RATING) + panel_height = 320 + var/datum/browser/panel = new(usr, "popanel", "Poll Option Panel", 370, panel_height) + panel.add_stylesheet("admin_panelscss", 'html/admin/admin_panels.css') + if(usr.client.prefs.tgui_fancy) //some browsers (IE8) have trouble with unsupported css3 elements that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support + panel.add_stylesheet("admin_panelscss3", 'html/admin/admin_panels_css3.css') + panel.set_content(jointext(output, "")) + panel.open() + +/** + * Processes topic data from poll option panel. + * + * Reads through returned form data and assigns data to the option datum, creating a new one if required, before passing it to be saved. + * Also does some simple error checking to ensure the option will be valid before creation. + * + */ +/datum/admins/proc/poll_option_parse_href(list/href_list, datum/poll_question/poll, datum/poll_option/option) + if(!check_rights(R_POLL)) + return + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) + return + var/list/error_state = list() + var/new_option = FALSE + if(!option) + option = new() + new_option = TRUE + if(href_list["optiontext"]) + option.text = href_list["optiontext"] + else + error_state += "No option text was provided." + if(href_list["defpercalc"]) + option.default_percentage_calc = TRUE + else + option.default_percentage_calc = FALSE + if(poll.poll_type == POLLTYPE_RATING) + var/value_in_range = text2num(href_list["minval"]) + if(href_list["minval"]) + if(ISINRANGE(value_in_range, -2147483647, 2147483647)) + option.min_val = value_in_range + else + error_state += "Minimum value out of range." + else + error_state += "No minimum value was provided." + value_in_range = text2num(href_list["maxval"]) + if(href_list["maxval"]) + if(ISINRANGE(value_in_range, -2147483647, 2147483647)) + if(value_in_range < option.min_val) + error_state += "Maximum value is less than minimum value." + else + option.max_val = value_in_range + else + error_state += "Maximum value out of range." + else + error_state += "No maximum value was provided." + if(href_list["descmincheck"]) + if(href_list["descmintext"]) + option.desc_min = href_list["descmintext"] + else + error_state += "Minimum value description was selected but not provided." + else + option.desc_min = null + if(href_list["descmidcheck"]) + if(href_list["descmidtext"]) + option.desc_mid = href_list["descmidtext"] + else + error_state += "Middle value description was selected but not provided." + else + option.desc_mid = null + if(href_list["descmaxcheck"]) + if(href_list["descmaxtext"]) + option.desc_max = href_list["descmaxtext"] + else + error_state += "Maximum value description was selected but not provided." + else + option.desc_max = null + if(error_state.len) + if(new_option) + to_chat(usr, "Option not added because the following errors were present:\n[error_state.Join("\n")]", confidential = TRUE) + qdel(option) + else + to_chat(usr, "Not all edits were applied because the following errors were present:\n[error_state.Join("\n")]", confidential = TRUE) + return + if(new_option) + poll.options += option + option.parent_poll = poll + if(poll.edit_ready) + option.save_option() + poll_management_panel(poll) + +/datum/poll_option/New(id, text, minval, maxval, descmin, descmid, descmax, default_percentage_calc) + option_id = text2num(id) + src.text = text + min_val = text2num(minval) + max_val = text2num(maxval) + desc_min = descmin + desc_mid = descmid + desc_max = descmax + src.default_percentage_calc = text2num(default_percentage_calc) + GLOB.poll_options += src + +/datum/poll_option/Destroy() + parent_poll.options -= src + parent_poll = null + GLOB.poll_options -= src + return ..() + +/** + * Inserts or updates a poll option to the database. + * + * Uses INSERT ON DUPLICATE KEY UPDATE to handle both inserting and updating at once. + * The list of columns and values is built dynamically to avoid excess data being sent when not a rating type poll. + * + */ +/datum/poll_option/proc/save_option() + if(!check_rights(R_POLL)) + return + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) + return + var/list/columns = list("text", "default_percentage_calc", "pollid", "id") + var/list/values = list("'[sanitizeSQL(text)]'", "[sanitizeSQL(default_percentage_calc)]", "[sanitizeSQL(parent_poll.poll_id)]") + if(option_id) + values += "[sanitizeSQL(option_id)]" + else + values += "NULL" + if(parent_poll.poll_type == POLLTYPE_RATING) + columns.Add("minval", "maxval", "descmin", "descmid", "descmax") + values.Add("[sanitizeSQL(min_val)]", "[sanitizeSQL(max_val)]") + if(desc_min) + values += "'[sanitizeSQL(desc_min)]'" + else + values += "NULL" + if(desc_mid) + values += "'[sanitizeSQL(desc_mid)]'" + else + values += "NULL" + if(desc_max) + values += "'[sanitizeSQL(desc_max)]'" + else + values += "NULL" + var/list/update_data = list() + var/count = 0 + for(var/i in columns) + count++ + if(i == "pollid" || i == "id") //we don't want to update the pollid or option id so skip including those + continue + update_data += "[i] = [values[count]]" + var/datum/DBQuery/query_update_poll_option = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_option")] ([jointext(columns, ",")]) VALUES ([jointext(values, ",")]) ON DUPLICATE KEY UPDATE [jointext(update_data, ", ")]") + if(!query_update_poll_option.warn_execute()) + qdel(query_update_poll_option) + return + qdel(query_update_poll_option) + +/** + * Sets a poll option and its votes as deleted in the database then deletes its datum. + * + */ +/datum/poll_option/proc/delete_option() + if(!check_rights(R_POLL)) + return + . = parent_poll + if(option_id) + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) + return + var/datum/DBQuery/query_delete_poll_option = SSdbcore.NewQuery("UPDATE [format_table_name("poll_option")] AS o INNER JOIN [format_table_name("poll_vote")] AS v ON o.id = v.optionid SET o.deleted = 1, v.deleted = 1 WHERE o.id = [sanitizeSQL(option_id)]") + if(!query_delete_poll_option.warn_execute()) + qdel(query_delete_poll_option) + return + qdel(query_delete_poll_option) + qdel(src) + +/** + * Loads all current and future server polls and their options to store both as datums. + * + */ +/proc/load_poll_data() + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) + return + var/datum/DBQuery/query_load_polls = SSdbcore.NewQuery("SELECT id, polltype, starttime, endtime, question, subtitle, adminonly, multiplechoiceoptions, dontshow, allow_revoting, IF(polltype='TEXT',(SELECT COUNT(ckey) FROM [format_table_name("poll_textreply")] AS t WHERE t.pollid = q.id AND deleted = 0), (SELECT COUNT(DISTINCT ckey) FROM [format_table_name("poll_vote")] AS v WHERE v.pollid = q.id AND deleted = 0)), IFNULL((SELECT byond_key FROM [format_table_name("player")] AS p WHERE p.ckey = q.createdby_ckey), createdby_ckey), IF(starttime > NOW(), 1, 0) FROM [format_table_name("poll_question")] AS q WHERE NOW() < endtime AND deleted = 0") + if(!query_load_polls.Execute()) + qdel(query_load_polls) + return + var/list/poll_ids = list() + while(query_load_polls.NextRow()) + new /datum/poll_question(query_load_polls.item[1], query_load_polls.item[2], query_load_polls.item[3], query_load_polls.item[4], query_load_polls.item[5], query_load_polls.item[6], query_load_polls.item[7], query_load_polls.item[8], query_load_polls.item[9], query_load_polls.item[10], query_load_polls.item[11], query_load_polls.item[12], query_load_polls.item[13], TRUE) + poll_ids += query_load_polls.item[1] + qdel(query_load_polls) + if(length(poll_ids)) + var/datum/DBQuery/query_load_poll_options = SSdbcore.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax, default_percentage_calc, pollid FROM [format_table_name("poll_option")] WHERE pollid IN ([jointext(poll_ids, ",")])") + if(!query_load_poll_options.Execute()) + qdel(query_load_poll_options) + return + while(query_load_poll_options.NextRow()) + var/datum/poll_option/option = new(query_load_poll_options.item[1], query_load_poll_options.item[2], query_load_poll_options.item[3], query_load_poll_options.item[4], query_load_poll_options.item[5], query_load_poll_options.item[6], query_load_poll_options.item[7], query_load_poll_options.item[8]) + var/option_poll_id = text2num(query_load_poll_options.item[9]) + for(var/q in GLOB.polls) + var/datum/poll_question/poll = q + if(poll.poll_id == option_poll_id) + poll.options += option + option.parent_poll = poll + qdel(query_load_poll_options) diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index d4e4f13fcb54..abb83a470eb1 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -52,6 +52,7 @@ Make all areas powered
Make all areas unpowered
Power all SMES
+ Anonymous Names (needs to be used in the lobby)
Triple AI mode (needs to be used in the lobby)
Everyone is the traitor
Summon Guns
@@ -59,7 +60,7 @@ Summon Events (Toggle)
There can only be one!
There can only be one! (40-second delay)
- Make all players retarded
+ Make all players brain damaged
Egalitarian Station Mode
Anarcho-Capitalist Station Mode
Break all lights
@@ -86,7 +87,11 @@
"} - usr << browse(dat.Join(), "window=secrets") + var/datum/browser/popup = new(usr, "secrets", null, 300, 430) + popup.set_content(dat.Join()) + popup.open() + +// usr << browse(dat.Join(), "window=secrets") return @@ -103,7 +108,10 @@ dat += "
  • [l]
  • " if(!GLOB.admin_log.len) dat += "No-one has done anything this round!" - usr << browse(dat, "window=admin_log") +// usr << browse(dat, "window=admin_log") + var/datum/browser/popup = new(usr, "admin_log", null, 300, 430) + popup.set_content(dat) + popup.open() if("mentor_log") var/dat = "Mentor Log
    " @@ -111,7 +119,10 @@ dat += "
  • [l]
  • " if(!GLOB.mentorlog.len) dat += "No mentors have done anything this round!" - usr << browse(dat, "window=mentor_log") +// usr << browse(dat, "window=mentor_log") + var/datum/browser/popup = new(usr, "mentor_log", null, 300, 430) + popup.set_content(dat) + popup.open() if("show_admins") var/dat = "Current admins:
    " @@ -119,7 +130,10 @@ for(var/ckey in GLOB.admin_datums) var/datum/admins/D = GLOB.admin_datums[ckey] dat += "[ckey] - [D.rank.name]
    " - usr << browse(dat, "window=showadmins;size=600x500") +// usr << browse(dat, "window=showadmins;size=600x500") + var/datum/browser/popup = new(usr, "showadmins", null, 600, 500) + popup.set_content(dat) + popup.open() if("tdomereset") if(!check_rights(R_ADMIN)) @@ -246,7 +260,7 @@ message_admins("[key_name_admin(usr)] [new_perma ? "stopped" : "started"] the arrivals shuttle") log_admin("[key_name(usr)] [new_perma ? "stopped" : "started"] the arrivals shuttle") else - to_chat(usr, "There is no arrivals shuttle.") + to_chat(usr, "There is no arrivals shuttle.", confidential = TRUE) if("showailaws") if(!check_rights(R_ADMIN)) return @@ -313,6 +327,12 @@ var/mob/living/carbon/human/H = i H.set_species(newtype) + if("anon_name") + if(!check_rights(R_FUN)) + return + usr.client.anon_names() + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Anonymous Names")) + if("tripleAI") if(!check_rights(R_FUN)) return @@ -429,7 +449,7 @@ if(droptype == "Yes") ADD_TRAIT(I, TRAIT_NODROP, ADMIN_TRAIT) else - to_chat(H, "You're not kawaii enough for this!") + to_chat(H, "You're not kawaii enough for this!", confidential = TRUE) if("whiteout") if(!check_rights(R_FUN)) @@ -459,14 +479,14 @@ DO.virus_type = virus E = DO - if("retardify") + if("massbraindamage") if(!check_rights(R_FUN)) return SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Braindamage")) for(var/mob/living/carbon/human/H in GLOB.player_list) - to_chat(H, "You suddenly feel stupid.") + to_chat(H, "You suddenly feel stupid.", confidential = TRUE) H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 60, 80) - message_admins("[key_name_admin(usr)] made everybody retarded") + message_admins("[key_name_admin(usr)] made everybody brain damaged") if("eagles")//SCRAW if(!check_rights(R_FUN)) @@ -651,7 +671,7 @@ var/list/prefs = settings["mainsettings"] if (prefs["amount"]["value"] < 1 || prefs["portalnum"]["value"] < 1) - to_chat(usr, "Number of portals and mobs to spawn must be at least 1.") + to_chat(usr, "Number of portals and mobs to spawn must be at least 1.", confidential = TRUE) return var/mob/pathToSpawn = prefs["typepath"]["value"] @@ -659,7 +679,7 @@ pathToSpawn = text2path(pathToSpawn) if (!ispath(pathToSpawn)) - to_chat(usr, "Invalid path [pathToSpawn].") + to_chat(usr, "Invalid path [pathToSpawn].", confidential = TRUE) return var/list/candidates = list() @@ -708,7 +728,7 @@ if (usr) log_admin("[key_name(usr)] used secret [item]") if (ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) + to_chat(world, text("A secret has been activated by []!", usr.key), confidential = TRUE) /proc/portalAnnounce(announcement, playlightning) set waitfor = 0 diff --git a/code/modules/admin/sound_emitter.dm b/code/modules/admin/sound_emitter.dm index b5fb625cbd48..4f6fe3552dba 100644 --- a/code/modules/admin/sound_emitter.dm +++ b/code/modules/admin/sound_emitter.dm @@ -54,7 +54,7 @@ /obj/effect/sound_emitter/AltClick(mob/user) if(check_rights_for(user.client, R_SOUND)) activate(user) - to_chat(user, "Sound emitter activated.") + to_chat(user, "Sound emitter activated.", confidential = TRUE) /obj/effect/sound_emitter/proc/edit_emitter(mob/user) var/dat = "" @@ -82,20 +82,20 @@ if(!new_label) return maptext = new_label - to_chat(user, "Label set to [maptext].") + to_chat(user, "Label set to [maptext].", confidential = TRUE) if(href_list["edit_sound_file"]) var/new_file = input(user, "Choose a sound file.", "Sound Emitter") as null|sound if(!new_file) return sound_file = new_file - to_chat(user, "New sound file set to [sound_file].") + to_chat(user, "New sound file set to [sound_file].", confidential = TRUE) if(href_list["edit_volume"]) var/new_volume = input(user, "Choose a volume.", "Sound Emitter", sound_volume) as null|num if(isnull(new_volume)) return new_volume = clamp(new_volume, 0, 100) sound_volume = new_volume - to_chat(user, "Volume set to [sound_volume]%.") + to_chat(user, "Volume set to [sound_volume]%.", confidential = TRUE) if(href_list["edit_mode"]) var/new_mode var/mode_list = list("Local (normal sound)" = SOUND_EMITTER_LOCAL, "Direct (not affected by environment/location)" = SOUND_EMITTER_DIRECT) @@ -103,7 +103,7 @@ if(!new_mode) return motus_operandi = mode_list[new_mode] - to_chat(user, "Mode set to [motus_operandi].") + to_chat(user, "Mode set to [motus_operandi].", confidential = TRUE) if(href_list["edit_range"]) var/new_range var/range_list = list("Radius (all mobs within a radius)" = SOUND_EMITTER_RADIUS, "Z-Level (all mobs on the same z)" = SOUND_EMITTER_ZLEVEL, "Global (all players)" = SOUND_EMITTER_GLOBAL) @@ -111,14 +111,14 @@ if(!new_range) return emitter_range = range_list[new_range] - to_chat(user, "Range set to [emitter_range].") + to_chat(user, "Range set to [emitter_range].", confidential = TRUE) if(href_list["edit_radius"]) var/new_radius = input(user, "Choose a radius.", "Sound Emitter", sound_volume) as null|num if(isnull(new_radius)) return new_radius = clamp(new_radius, 0, 127) play_radius = new_radius - to_chat(user, "Audible radius set to [play_radius].") + to_chat(user, "Audible radius set to [play_radius].", confidential = TRUE) if(href_list["play"]) activate(user) edit_emitter(user) //Refresh the UI to see our changes diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index bcb2ca1756b1..800a4cbcc9c5 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -86,9 +86,10 @@ if(edit_id) panel_height = 240 var/datum/browser/panel = new(usr, "banpanel", "Banning Panel", 910, panel_height) + panel.add_stylesheet("admin_panelscss", 'html/admin/admin_panels.css') panel.add_stylesheet("banpanelcss", 'html/admin/banpanel.css') if(usr.client.prefs.tgui_fancy) //some browsers (IE8) have trouble with unsupported css3 elements and DOM methods that break the panel's functionality, so we won't load those if a user is in no frills tgui mode since that's for similar compatability support - panel.add_stylesheet("banpanelcss3", 'html/admin/banpanel_css3.css') + panel.add_stylesheet("admin_panelscss3", 'html/admin/admin_panels_css3.css') panel.add_script("banpaneljs", 'html/admin/banpanel.js') var/list/output = list("[HrefTokenFormField()]") output += {" @@ -252,7 +253,7 @@ "} break_counter++ output += "
    " - var/list/long_job_lists = list("Civilian" = GLOB.civilian_positions, + var/list/long_job_lists = list("Service" = GLOB.service_positions, "Ghost and Other Roles" = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE), "Antagonist Positions" = list(ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_BLOB, ROLE_BROTHER, ROLE_CHANGELING, ROLE_CULTIST, @@ -282,7 +283,7 @@ if(!check_rights(R_BAN)) return if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return var/list/error_state = list() var/player_key @@ -377,7 +378,7 @@ if("server") roles_to_ban += "Server" if("role") - href_list.Remove("Command", "Security", "Engineering", "Medical", "Science", "Supply", "Silicon", "Abstract", "Civilian", "Ghost and Other Roles", "Antagonist Positions") //remove the role banner hidden input values + href_list.Remove("Command", "Security", "Engineering", "Medical", "Science", "Supply", "Silicon", "Abstract", "Service", "Ghost and Other Roles", "Antagonist Positions") //remove the role banner hidden input values if(href_list[href_list.len] == "roleban_delimiter") error_state += "Role ban was selected but no roles to ban were selected." else @@ -388,7 +389,7 @@ else error_state += "No ban type was selected." if(error_state.len) - to_chat(usr, "Ban not [edit_id ? "edited" : "created"] because the following errors were present:\n[error_state.Join("\n")]") + to_chat(usr, "Ban not [edit_id ? "edited" : "created"] because the following errors were present:\n[error_state.Join("\n")]", confidential = TRUE) return if(edit_id) edit_ban(edit_id, player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, reason, mirror_edit, old_key, old_ip, old_cid, old_applies, page, admin_key, changes) @@ -399,7 +400,7 @@ if(!check_rights(R_BAN)) return if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return var/player_ckey = sanitizeSQL(ckey(player_key)) player_ip = sanitizeSQL(player_ip) @@ -438,7 +439,7 @@ if(R_EVERYTHING && !(R_EVERYTHING & rank.can_edit_rights)) //edit rights are a more effective way to check hierarchical rank since many non-headmins have R_PERMISSIONS now max_adminbans = MAX_ADMINBANS_PER_HEADMIN if(adminban_count >= max_adminbans) - to_chat(usr, "You've already logged [max_adminbans] admin ban(s) or more. Do not abuse this function!") + to_chat(usr, "You've already logged [max_adminbans] admin ban(s) or more. Do not abuse this function!", confidential = TRUE) qdel(query_check_adminban_count) return qdel(query_check_adminban_count) @@ -500,7 +501,7 @@ var/is_admin = FALSE if(C) build_ban_cache(C) - to_chat(C, "You have been [applies_to_admins ? "admin " : ""]banned by [usr.client.key] from [roles_to_ban[1] == "Server" ? "the server" : " Roles: [roles_to_ban.Join(", ")]"].\nReason: [reason]
    This ban is [isnull(duration) ? "permanent." : "temporary, it will be removed in [time_message]."] The round ID is [GLOB.round_id].
    To appeal this ban go to [appeal_url]") + to_chat(C, "You have been [applies_to_admins ? "admin " : ""]banned by [usr.client.key] from [roles_to_ban[1] == "Server" ? "the server" : " Roles: [roles_to_ban.Join(", ")]"].\nReason: [reason]
    This ban is [isnull(duration) ? "permanent." : "temporary, it will be removed in [time_message]."] The round ID is [GLOB.round_id].
    To appeal this ban go to [appeal_url]", confidential = TRUE) if(GLOB.admin_datums[C.ckey] || GLOB.deadmins[C.ckey]) is_admin = TRUE if(roles_to_ban[1] == "Server" && (!is_admin || (is_admin && applies_to_admins))) @@ -510,7 +511,7 @@ for(var/client/i in GLOB.clients - C) if(i.address == player_ip || i.computer_id == player_cid) build_ban_cache(i) - to_chat(i, "You have been [applies_to_admins ? "admin " : ""]banned by [usr.client.key] from [roles_to_ban[1] == "Server" ? "the server" : " Roles: [roles_to_ban.Join(", ")]"].\nReason: [reason]
    This ban is [isnull(duration) ? "permanent." : "temporary, it will be removed in [time_message]."] The round ID is [GLOB.round_id].
    To appeal this ban go to [appeal_url]") + to_chat(i, "You have been [applies_to_admins ? "admin " : ""]banned by [usr.client.key] from [roles_to_ban[1] == "Server" ? "the server" : " Roles: [roles_to_ban.Join(", ")]"].\nReason: [reason]
    This ban is [isnull(duration) ? "permanent." : "temporary, it will be removed in [time_message]."] The round ID is [GLOB.round_id].
    To appeal this ban go to [appeal_url]", confidential = TRUE) if(GLOB.admin_datums[i.ckey] || GLOB.deadmins[i.ckey]) is_admin = TRUE if(roles_to_ban[1] == "Server" && (!is_admin || (is_admin && applies_to_admins))) @@ -520,7 +521,7 @@ if(!check_rights(R_BAN)) return if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return var/datum/browser/unban_panel = new(usr, "unbanpanel", "Unbanning Panel", 850, 600) unban_panel.add_stylesheet("unbanpanelcss", 'html/admin/unbanpanel.css') @@ -615,7 +616,7 @@ if(!check_rights(R_BAN)) return if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return var/target = ban_target_string(player_key, player_ip, player_cid) if(alert(usr, "Please confirm unban of [target] from [role].", "Unban confirmation", "Yes", "No") == "No") @@ -636,18 +637,18 @@ var/client/C = GLOB.directory[player_key] if(C) build_ban_cache(C) - to_chat(C, "[usr.client.key] has removed a ban from [role] for your key.") + to_chat(C, "[usr.client.key] has removed a ban from [role] for your key.", confidential = TRUE) for(var/client/i in GLOB.clients - C) if(i.address == player_ip || i.computer_id == player_cid) build_ban_cache(i) - to_chat(i, "[usr.client.key] has removed a ban from [role] for your IP or CID.") + to_chat(i, "[usr.client.key] has removed a ban from [role] for your IP or CID.", confidential = TRUE) unban_panel(player_key, admin_key, player_ip, player_cid, page) /datum/admins/proc/edit_ban(ban_id, player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, reason, mirror_edit, old_key, old_ip, old_cid, old_applies, admin_key, page, list/changes) if(!check_rights(R_BAN)) return if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return ban_id = sanitizeSQL(ban_id) var/player_ckey = sanitizeSQL(ckey(player_key)) @@ -689,7 +690,7 @@ if(R_EVERYTHING && !(R_EVERYTHING & rank.can_edit_rights)) //edit rights are a more effective way to check hierarchical rank since many non-headmins have R_PERMISSIONS now max_adminbans = MAX_ADMINBANS_PER_HEADMIN if(adminban_count >= max_adminbans) - to_chat(usr, "You've already logged [max_adminbans] admin ban(s) or more. Do not abuse this function!") + to_chat(usr, "You've already logged [max_adminbans] admin ban(s) or more. Do not abuse this function!", confidential = TRUE) qdel(query_check_adminban_count) return qdel(query_check_adminban_count) @@ -731,18 +732,18 @@ var/client/C = GLOB.directory[old_key] if(C) build_ban_cache(C) - to_chat(C, "[usr.client.key] has edited the [changes_keys_text] of a ban for your key.") + to_chat(C, "[usr.client.key] has edited the [changes_keys_text] of a ban for your key.", confidential = TRUE) for(var/client/i in GLOB.clients - C) if(i.address == old_ip || i.computer_id == old_cid) build_ban_cache(i) - to_chat(i, "[usr.client.key] has edited the [changes_keys_text] of a ban for your IP or CID.") + to_chat(i, "[usr.client.key] has edited the [changes_keys_text] of a ban for your IP or CID.", confidential = TRUE) unban_panel(player_key, null, null, null, page) /datum/admins/proc/ban_log(ban_id) if(!check_rights(R_BAN)) return if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return ban_id = sanitizeSQL(ban_id) var/datum/DBQuery/query_get_ban_edits = SSdbcore.NewQuery("SELECT edits FROM [format_table_name("ban")] WHERE id = '[ban_id]'") diff --git a/code/modules/admin/sql_message_system.dm b/code/modules/admin/sql_message_system.dm index 2b8d021ed867..f112759944a8 100644 --- a/code/modules/admin/sql_message_system.dm +++ b/code/modules/admin/sql_message_system.dm @@ -1,6 +1,6 @@ /proc/create_message(type, target_key, admin_ckey, text, timestamp, server, secret, logged = 1, browse, expiry, note_severity) if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return if(!type) return @@ -67,7 +67,7 @@ if(query_validate_expire_time.NextRow()) var/checktime = text2num(query_validate_expire_time.item[1]) if(!checktime) - to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.") + to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.", confidential = TRUE) qdel(query_validate_expire_time) return expiry = query_validate_expire_time.item[1] @@ -96,7 +96,7 @@ /proc/delete_message(message_id, logged = 1, browse) if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return message_id = text2num(message_id) if(!message_id) @@ -106,6 +106,7 @@ var/text var/user_key_name = key_name(usr) var/user_name_admin = key_name_admin(usr) + var/deleted_by_ckey = sanitizeSQL(usr.ckey) var/datum/DBQuery/query_find_del_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") if(!query_find_del_message.warn_execute()) qdel(query_find_del_message) @@ -115,7 +116,7 @@ target_key = query_find_del_message.item[2] text = query_find_del_message.item[3] qdel(query_find_del_message) - var/datum/DBQuery/query_del_message = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET deleted = 1 WHERE id = [message_id]") + var/datum/DBQuery/query_del_message = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET deleted = 1, deleted_ckey = '[deleted_by_ckey]' WHERE id = [message_id]") if(!query_del_message.warn_execute()) qdel(query_del_message) return @@ -132,7 +133,7 @@ /proc/edit_message(message_id, browse) if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return message_id = text2num(message_id) if(!message_id) @@ -171,7 +172,7 @@ /proc/edit_message_expiry(message_id, browse) if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return message_id = text2num(message_id) if(!message_id) @@ -206,7 +207,7 @@ if(query_validate_expire_time_edit.NextRow()) var/checktime = text2num(query_validate_expire_time_edit.item[1]) if(!checktime) - to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.") + to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.", confidential = TRUE) qdel(query_validate_expire_time_edit) qdel(query_find_edit_expiry_message) return @@ -229,7 +230,7 @@ /proc/edit_message_severity(message_id) if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return message_id = text2num(message_id) if(!message_id) @@ -268,7 +269,7 @@ /proc/toggle_message_secrecy(message_id) if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return message_id = text2num(message_id) if(!message_id) @@ -300,7 +301,7 @@ /proc/browse_messages(type, target_ckey, index, linkless = FALSE, filter, agegate = FALSE) if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return var/list/output = list() var/ruler = "
    " @@ -510,7 +511,7 @@ /proc/get_message_output(type, target_ckey) if(!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") + to_chat(usr, "Failed to establish database connection.", confidential = TRUE) return if(!type) return @@ -589,7 +590,7 @@ /*alternatively this proc can be run once to pass through every note and attempt to convert it before deleting the file, if done then AUTOCONVERT_NOTES should be turned off this proc can take several minutes to execute fully if converting and cause DD to hang if converting a lot of notes; it's not advised to do so while a server is live /proc/mass_convert_notes() - to_chat(world, "Beginning mass note conversion") + to_chat(world, "Beginning mass note conversion", confidential = TRUE) var/savefile/notesfile = new(NOTESFILE) if(!notesfile) log_game("Error: Cannot access [NOTESFILE]") @@ -597,7 +598,7 @@ this proc can take several minutes to execute fully if converting and cause DD t notesfile.cd = "/" for(var/ckey in notesfile.dir) convert_notes_sql(ckey) - to_chat(world, "Deleting NOTESFILE") + to_chat(world, "Deleting NOTESFILE", confidential = TRUE) fdel(NOTESFILE) - to_chat(world, "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES")*/ + to_chat(world, "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES", confidential = TRUE)*/ #undef NOTESFILE diff --git a/code/modules/admin/stickyban.dm b/code/modules/admin/stickyban.dm index b5ae1022a97f..ab185dc2dbb2 100644 --- a/code/modules/admin/stickyban.dm +++ b/code/modules/admin/stickyban.dm @@ -21,7 +21,7 @@ ban["ckey"] = ckey if (get_stickyban_from_ckey(ckey)) - to_chat(usr, "Error: Can not add a stickyban: User already has a current sticky ban") + to_chat(usr, "Error: Can not add a stickyban: User already has a current sticky ban", confidential = TRUE) return if (data["reason"]) @@ -56,12 +56,12 @@ var/ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: No sticky ban for [ckey] found!") + to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE) return if (alert("Are you sure you want to remove the sticky ban on [ckey]?","Are you sure","Yes","No") == "No") return if (!get_stickyban_from_ckey(ckey)) - to_chat(usr, "Error: The ban disappeared.") + to_chat(usr, "Error: The ban disappeared.", confidential = TRUE) return world.SetConfig("ban",ckey, null) SSstickyban.cache -= ckey @@ -87,12 +87,12 @@ var/alt = ckey(data["alt"]) var/ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: No sticky ban for [ckey] found!") + to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE) return var/key = LAZYACCESS(ban["keys"], alt) if (!key) - to_chat(usr, "Error: [alt] is not linked to [ckey]'s sticky ban!") + to_chat(usr, "Error: [alt] is not linked to [ckey]'s sticky ban!", confidential = TRUE) return if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them, Use \[E] to exempt them","Are you sure","Yes","No") == "No") @@ -101,13 +101,13 @@ //we have to do this again incase something changes ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: The ban disappeared.") + to_chat(usr, "Error: The ban disappeared.", confidential = TRUE) return key = LAZYACCESS(ban["keys"], alt) if (!key) - to_chat(usr, "Error: [alt] link to [ckey]'s sticky ban disappeared.") + to_chat(usr, "Error: [alt] link to [ckey]'s sticky ban disappeared.", confidential = TRUE) return LAZYREMOVE(ban["keys"], alt) @@ -129,7 +129,7 @@ var/ckey = data["ckey"] var/ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: No sticky ban for [ckey] found!") + to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE) return var/oldreason = ban["message"] var/reason = input(usr,"Reason","Reason","[ban["message"]]") as text|null @@ -138,7 +138,7 @@ //we have to do this again incase something changed while we waited for input ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: The ban disappeared.") + to_chat(usr, "Error: The ban disappeared.", confidential = TRUE) return ban["message"] = "[reason]" @@ -163,12 +163,12 @@ var/alt = ckey(data["alt"]) var/ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: No sticky ban for [ckey] found!") + to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE) return var/key = LAZYACCESS(ban["keys"], alt) if (!key) - to_chat(usr, "Error: [alt] is not linked to [ckey]'s sticky ban!") + to_chat(usr, "Error: [alt] is not linked to [ckey]'s sticky ban!", confidential = TRUE) return if (alert("Are you sure you want to exempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No") @@ -177,13 +177,13 @@ //we have to do this again incase something changes ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: The ban disappeared.") + to_chat(usr, "Error: The ban disappeared.", confidential = TRUE) return key = LAZYACCESS(ban["keys"], alt) if (!key) - to_chat(usr, "Error: [alt]'s link to [ckey]'s sticky ban disappeared.") + to_chat(usr, "Error: [alt]'s link to [ckey]'s sticky ban disappeared.", confidential = TRUE) return LAZYREMOVE(ban["keys"], alt) key["exempt"] = TRUE @@ -210,12 +210,12 @@ var/alt = ckey(data["alt"]) var/ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: No sticky ban for [ckey] found!") + to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE) return var/key = LAZYACCESS(ban["whitelist"], alt) if (!key) - to_chat(usr, "Error: [alt] is not exempt from [ckey]'s sticky ban!") + to_chat(usr, "Error: [alt] is not exempt from [ckey]'s sticky ban!", confidential = TRUE) return if (alert("Are you sure you want to unexempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No") @@ -224,12 +224,12 @@ //we have to do this again incase something changes ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: The ban disappeared.") + to_chat(usr, "Error: The ban disappeared.", confidential = TRUE) return key = LAZYACCESS(ban["whitelist"], alt) if (!key) - to_chat(usr, "Error: [alt]'s exemption from [ckey]'s sticky ban disappeared.") + to_chat(usr, "Error: [alt]'s exemption from [ckey]'s sticky ban disappeared.", confidential = TRUE) return LAZYREMOVE(ban["whitelist"], alt) @@ -252,7 +252,7 @@ if (!data["ckey"]) return if (!SSdbcore.Connect()) - to_chat(usr, "No database connection!") + to_chat(usr, "No database connection!", confidential = TRUE) return var/ckey = data["ckey"] @@ -261,7 +261,7 @@ return var/ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: No sticky ban for [ckey] found!") + to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE) return ban["timeout"] = TRUE @@ -279,7 +279,7 @@ if (!data["ckey"]) return if (!SSdbcore.Connect()) - to_chat(usr, "No database connection!") + to_chat(usr, "No database connection!", confidential = TRUE) return var/ckey = data["ckey"] @@ -292,7 +292,7 @@ cachedban["timeout"] = FALSE if (!ban) if (!cachedban) - to_chat(usr, "Error: No sticky ban for [ckey] found!") + to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE) return ban = cachedban @@ -312,11 +312,11 @@ return var/ban = get_stickyban_from_ckey(ckey) if (!ban) - to_chat(usr, "Error: No sticky ban for [ckey] found!") + to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE) return var/cached_ban = SSstickyban.cache[ckey] if (!cached_ban) - to_chat(usr, "Error: No cached sticky ban for [ckey] found!") + to_chat(usr, "Error: No cached sticky ban for [ckey] found!", confidential = TRUE) world.SetConfig("ban",ckey,null) log_admin_private("[key_name(usr)] has reverted [ckey]'s sticky ban to its state at round start.") diff --git a/code/modules/admin/team_panel.dm b/code/modules/admin/team_panel.dm index 6e2987486bf5..75abbb5391c2 100644 --- a/code/modules/admin/team_panel.dm +++ b/code/modules/admin/team_panel.dm @@ -55,7 +55,7 @@ if(!message) return for(var/datum/mind/M in members) - to_chat(M.current,message) + to_chat(M.current,message, confidential = TRUE) message_admins("[key_name_admin(usr)] messaged [name] team with : [message]") log_admin("Team Message: [key_name(usr)] -> [name] team : [message]") @@ -128,7 +128,7 @@ //After a bit of consideration i block team deletion if there's any members left until unified objective handling is in. /datum/team/proc/admin_delete(mob/user) if(members.len > 0) - to_chat(user,"Team has members left, remove them first and make sure you know what you're doing.") + to_chat(user,"Team has members left, remove them first and make sure you know what you're doing.", confidential = TRUE) return qdel(src) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index ccad5441b691..b6691e6888d9 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -31,7 +31,7 @@ if(AH) AH.Action(href_list["ahelp_action"]) else - to_chat(usr, "Ticket [ahelp_ref] has been deleted!") + to_chat(usr, "Ticket [ahelp_ref] has been deleted!", confidential = TRUE) else if(href_list["ahelp_tickets"]) GLOB.ahelp_tickets.BrowseTickets(text2num(href_list["ahelp_tickets"])) @@ -44,7 +44,7 @@ return var/mob/M = locate(href_list["getplaytimewindow"]) in GLOB.mob_list if(!M) - to_chat(usr, "ERROR: Mob not found.") + to_chat(usr, "ERROR: Mob not found.", confidential = TRUE) return cmd_show_exp_panel(M.client) @@ -53,7 +53,7 @@ return var/client/C = locate(href_list["toggleexempt"]) in GLOB.clients if(!C) - to_chat(usr, "ERROR: Client not found.") + to_chat(usr, "ERROR: Client not found.", confidential = TRUE) return toggle_exempt_status(C) @@ -61,7 +61,7 @@ if(!check_rights(R_ADMIN)) return if (!SSticker.mode) - to_chat(usr, "Not until the round starts!") + to_chat(usr, "Not until the round starts!", confidential = TRUE) return switch(href_list["makeAntag"]) if("traitors") @@ -346,7 +346,7 @@ var/mob/M = locate(href_list["mob"]) if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") + to_chat(usr, "This can only be used on instances of type /mob.", confidential = TRUE) return var/delmob = TRUE @@ -405,10 +405,10 @@ M.change_mob_type( /mob/living/simple_animal/parrot , null, null, delmob ) if("polyparrot") M.change_mob_type( /mob/living/simple_animal/parrot/Poly , null, null, delmob ) - if("constructarmored") - M.change_mob_type( /mob/living/simple_animal/hostile/construct/armored , null, null, delmob ) - if("constructbuilder") - M.change_mob_type( /mob/living/simple_animal/hostile/construct/builder , null, null, delmob ) + if("constructjuggernaut") + M.change_mob_type( /mob/living/simple_animal/hostile/construct/juggernaut , null, null, delmob ) + if("constructartificer") + M.change_mob_type( /mob/living/simple_animal/hostile/construct/artificer , null, null, delmob ) if("constructwraith") M.change_mob_type( /mob/living/simple_animal/hostile/construct/wraith , null, null, delmob ) if("shade") @@ -420,17 +420,17 @@ var/mob/M = locate(href_list["boot2"]) if(ismob(M)) if(!check_if_greater_rights_than(M.client)) - to_chat(usr, "Error: They have more rights than you do.") + to_chat(usr, "Error: They have more rights than you do.", confidential = TRUE) return if(alert(usr, "Kick [key_name(M)]?", "Confirm", "Yes", "No") != "Yes") return if(!M) - to_chat(usr, "Error: [M] no longer exists!") + to_chat(usr, "Error: [M] no longer exists!", confidential = TRUE) return if(!M.client) - to_chat(usr, "Error: [M] no longer has a client!") + to_chat(usr, "Error: [M] no longer has a client!", confidential = TRUE) return - to_chat(M, "You have been kicked from the server by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].") + to_chat(M, "You have been kicked from the server by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].", confidential = TRUE) log_admin("[key_name(usr)] kicked [key_name(M)].") message_admins("[key_name_admin(usr)] kicked [key_name_admin(M)].") qdel(M.client) @@ -896,7 +896,7 @@ 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].") - to_chat(world, "The mode is now: [GLOB.master_mode]") + to_chat(world, "The mode is now: [GLOB.master_mode]", confidential = TRUE) Game() // updates the main game menu if (askuser(usr, "Would you like to save this as the default mode for the server?", "Save mode", "Yes", "No", Timeout = null) == 1) SSticker.save_mode(GLOB.master_mode) @@ -922,7 +922,7 @@ var/mob/living/carbon/human/H = locate(href_list["monkeyone"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.", confidential = TRUE) return log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)].") @@ -935,7 +935,7 @@ var/mob/living/carbon/monkey/Mo = locate(href_list["humanone"]) if(!istype(Mo)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/monkey.") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/monkey.", confidential = TRUE) return log_admin("[key_name(usr)] attempting to humanize [key_name(Mo)].") @@ -948,7 +948,7 @@ var/mob/living/carbon/human/H = locate(href_list["corgione"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.", confidential = TRUE) return log_admin("[key_name(usr)] attempting to corgize [key_name(H)].") @@ -962,7 +962,7 @@ var/mob/M = locate(href_list["forcespeech"]) if(!ismob(M)) - to_chat(usr, "this can only be used on instances of type /mob.") + to_chat(usr, "this can only be used on instances of type /mob.", confidential = TRUE) 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) @@ -978,17 +978,17 @@ var/mob/M = locate(href_list["sendtoprison"]) if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") + to_chat(usr, "This can only be used on instances of type /mob.", confidential = TRUE) return if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.", confidential = TRUE) return if(alert(usr, "Send [key_name(M)] to Prison?", "Message", "Yes", "No") != "Yes") return M.forceMove(pick(GLOB.prisonwarp)) - to_chat(M, "You have been sent to Prison!") + to_chat(M, "You have been sent to Prison!", confidential = TRUE) log_admin("[key_name(usr)] has sent [key_name(M)] to Prison!") message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to Prison!") @@ -1000,11 +1000,11 @@ var/mob/M = locate(href_list["sendbacktolobby"]) if(!isobserver(M)) - to_chat(usr, "You can only send ghost players back to the Lobby.") + to_chat(usr, "You can only send ghost players back to the Lobby.", confidential = TRUE) return if(!M.client) - to_chat(usr, "[M] doesn't seem to have an active client.") + to_chat(usr, "[M] doesn't seem to have an active client.", confidential = TRUE) return if(alert(usr, "Send [key_name(M)] back to Lobby?", "Message", "Yes", "No") != "Yes") @@ -1026,10 +1026,10 @@ var/mob/M = locate(href_list["tdome1"]) if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living.") + to_chat(usr, "This can only be used on instances of type /mob/living.", confidential = TRUE) return if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.", confidential = TRUE) return var/mob/living/L = M @@ -1052,10 +1052,10 @@ var/mob/M = locate(href_list["tdome2"]) if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living.") + to_chat(usr, "This can only be used on instances of type /mob/living.", confidential = TRUE) return if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.", confidential = TRUE) return var/mob/living/L = M @@ -1078,10 +1078,10 @@ var/mob/M = locate(href_list["tdomeadmin"]) if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living.") + to_chat(usr, "This can only be used on instances of type /mob/living.", confidential = TRUE) return if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.", confidential = TRUE) return var/mob/living/L = M @@ -1101,10 +1101,10 @@ var/mob/M = locate(href_list["tdomeobserve"]) if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living.") + to_chat(usr, "This can only be used on instances of type /mob/living.", confidential = TRUE) return if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.", confidential = TRUE) return var/mob/living/L = M @@ -1128,7 +1128,7 @@ var/mob/living/L = locate(href_list["revive"]) if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /mob/living.") + to_chat(usr, "This can only be used on instances of type /mob/living.", confidential = TRUE) return L.revive(full_heal = TRUE, admin_revive = TRUE) @@ -1141,7 +1141,7 @@ var/mob/living/carbon/human/H = locate(href_list["makeai"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.", confidential = TRUE) return message_admins("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!") @@ -1154,7 +1154,7 @@ var/mob/living/carbon/human/H = locate(href_list["makealien"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.", confidential = TRUE) return usr.client.cmd_admin_alienize(H) @@ -1165,7 +1165,7 @@ var/mob/living/carbon/human/H = locate(href_list["makeslime"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.", confidential = TRUE) return usr.client.cmd_admin_slimeize(H) @@ -1176,7 +1176,7 @@ var/mob/living/carbon/human/H = locate(href_list["makeblob"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.", confidential = TRUE) return usr.client.cmd_admin_blobize(H) @@ -1188,7 +1188,7 @@ var/mob/living/carbon/human/H = locate(href_list["makerobot"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.", confidential = TRUE) return usr.client.cmd_admin_robotize(H) @@ -1199,7 +1199,7 @@ var/mob/M = locate(href_list["makeanimal"]) if(isnewplayer(M)) - to_chat(usr, "This cannot be used on instances of type /mob/dead/new_player.") + to_chat(usr, "This cannot be used on instances of type /mob/dead/new_player.", confidential = TRUE) return usr.client.cmd_admin_animalize(M) @@ -1261,7 +1261,7 @@ else if(href_list["adminmoreinfo"]) var/mob/M = locate(href_list["adminmoreinfo"]) in GLOB.mob_list if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") + to_chat(usr, "This can only be used on instances of type /mob.", confidential = TRUE) return var/location_description = "" @@ -1308,12 +1308,12 @@ else gender_description = "[M.gender]" - to_chat(src.owner, "Info about [M.name]: ") - to_chat(src.owner, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]") - to_chat(src.owner, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];") - to_chat(src.owner, "Location = [location_description];") - to_chat(src.owner, "[special_role_description]") - to_chat(src.owner, ADMIN_FULLMONTY_NONAME(M)) + to_chat(src.owner, "Info about [M.name]: ", confidential = TRUE) + to_chat(src.owner, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]", confidential = TRUE) + to_chat(src.owner, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];", confidential = TRUE) + to_chat(src.owner, "Location = [location_description];", confidential = TRUE) + to_chat(src.owner, "[special_role_description]", confidential = TRUE) + to_chat(src.owner, ADMIN_FULLMONTY_NONAME(M), confidential = TRUE) else if(href_list["addjobslot"]) if(!check_rights(R_ADMIN)) @@ -1340,7 +1340,7 @@ var/newtime = null newtime = input(usr, "How many jebs do you want?", "Add wanted posters", "[newtime]") as num|null if(!newtime) - to_chat(src.owner, "Setting to amount of positions filled for the job") + to_chat(src.owner, "Setting to amount of positions filled for the job", confidential = TRUE) job.total_positions = job.current_positions break job.total_positions = newtime @@ -1393,7 +1393,7 @@ var/mob/living/carbon/human/H = locate(href_list["adminspawncookie"]) if(!ishuman(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.", confidential = TRUE) return //let's keep it simple //milk to plasmemes and skeletons, meat to lizards, electricity bars to ethereals, cookies to everyone else @@ -1418,7 +1418,7 @@ log_admin("[key_name(H)] got their [new_item], spawned by [key_name(src.owner)].") message_admins("[key_name(H)] got their [new_item], spawned by [key_name(src.owner)].") SSblackbox.record_feedback("amount", "admin_cookies_spawned", 1) - to_chat(H, "Your prayers have been answered!! You received the best [new_item.name]!") + to_chat(H, "Your prayers have been answered!! You received the best [new_item.name]!", confidential = TRUE) SEND_SOUND(H, sound('sound/effects/pray_chaplain.ogg')) else if(href_list["adminsmite"]) @@ -1427,7 +1427,7 @@ var/mob/living/carbon/human/H = locate(href_list["adminsmite"]) in GLOB.mob_list if(!H || !istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human", confidential = TRUE) return usr.client.smite(H) @@ -1511,7 +1511,7 @@ var/mob/M = locate(href_list["individuallog"]) in GLOB.mob_list if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") + to_chat(usr, "This can only be used on instances of type /mob.", confidential = TRUE) return show_individual_logging_panel(M, href_list["log_src"], href_list["log_type"]) @@ -1521,7 +1521,7 @@ var/mob/M = locate(href_list["languagemenu"]) in GLOB.mob_list if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") + to_chat(usr, "This can only be used on instances of type /mob.", confidential = TRUE) return var/datum/language_holder/H = M.get_language_holder() H.open_language_menu(usr) @@ -1538,7 +1538,7 @@ if(!ismob(M)) var/datum/mind/D = M if(!istype(D)) - to_chat(usr, "This can only be used on instances of type /mob and /mind") + to_chat(usr, "This can only be used on instances of type /mob and /mind", confidential = TRUE) return else D.traitor_panel() @@ -1551,7 +1551,7 @@ var/mob/M = locate(href_list["borgpanel"]) if(!iscyborg(M)) - to_chat(usr, "This can only be used on cyborgs") + to_chat(usr, "This can only be used on cyborgs", confidential = TRUE) else open_borgopanel(M) @@ -1560,7 +1560,7 @@ return var/mob/M = locate(href_list["initmind"]) if(!ismob(M) || M.mind) - to_chat(usr, "This can only be used on instances on mindless mobs") + to_chat(usr, "This can only be used on instances on mindless mobs", confidential = TRUE) return M.mind_initialize() @@ -1638,7 +1638,7 @@ switch(where) if("inhand") if (!iscarbon(usr) && !iscyborg(usr)) - to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") + to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.", confidential = TRUE) where = "onfloor" target = usr @@ -1650,10 +1650,10 @@ 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.") + to_chat(usr, "You don't have any object marked. Abandoning spawn.", confidential = TRUE) 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.") + to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.", confidential = TRUE) return else target = marked_datum @@ -1970,7 +1970,7 @@ if(SSticker.IsRoundInProgress()) var/afkonly = text2num(href_list["afkonly"]) if(alert("Are you sure you want to kick all [afkonly ? "AFK" : ""] clients from the lobby??","Message","Yes","Cancel") != "Yes") - to_chat(usr, "Kick clients from lobby aborted") + to_chat(usr, "Kick clients from lobby aborted", confidential = TRUE) return var/list/listkicked = kick_clients_in_lobby("You were kicked from the lobby by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].", afkonly) @@ -1980,7 +1980,7 @@ 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.") + to_chat(usr, "You may only use this when the game is running.", confidential = TRUE) else if(href_list["create_outfit_finalize"]) if(!check_rights(R_ADMIN)) @@ -2037,7 +2037,7 @@ else if(href_list["viewruntime"]) var/datum/error_viewer/error_viewer = locate(href_list["viewruntime"]) if(!istype(error_viewer)) - to_chat(usr, "That runtime viewer no longer exists.") + to_chat(usr, "That runtime viewer no longer exists.", confidential = TRUE) return if(href_list["viewruntime_backto"]) @@ -2057,10 +2057,14 @@ thing_to_check = splittext(thing_to_check, ", ") - var/list/dat = list("Related accounts by [uppertext(href_list["showrelatedacc"])]:") + var/list/dat = list() dat += thing_to_check - usr << browse(dat.Join("
    "), "window=related_[C];size=420x300") +// usr << browse(dat.Join("
    "), "window=related_[C];size=420x300") + + var/datum/browser/popup = new(usr, "related_[C]", "Related accounts by [uppertext(href_list["showrelatedacc"])]:", 425, 300) + popup.set_content(dat.Join("
    ")) + popup.open() else if(href_list["modantagrep"]) if(!check_rights(R_ADMIN)) @@ -2202,6 +2206,55 @@ else if(href_list["beakerpanel"]) beaker_panel_act(href_list) + else if(href_list["reloadpolls"]) + GLOB.polls.Cut() + GLOB.poll_options.Cut() + load_poll_data() + poll_list_panel() + + else if(href_list["newpoll"]) + poll_management_panel() + + else if(href_list["editpoll"]) + var/datum/poll_question/poll = locate(href_list["editpoll"]) in GLOB.polls + poll_management_panel(poll) + + else if(href_list["deletepoll"]) + var/datum/poll_question/poll = locate(href_list["deletepoll"]) in GLOB.polls + poll.delete_poll() + poll_list_panel() + + else if(href_list["initializepoll"]) + poll_parse_href(href_list) + + else if(href_list["submitpoll"]) + var/datum/poll_question/poll = locate(href_list["submitpoll"]) in GLOB.polls + poll_parse_href(href_list, poll) + + else if(href_list["clearpollvotes"]) + var/datum/poll_question/poll = locate(href_list["clearpollvotes"]) in GLOB.polls + poll.clear_poll_votes() + poll_management_panel(poll) + + else if(href_list["addpolloption"]) + var/datum/poll_question/poll = locate(href_list["addpolloption"]) in GLOB.polls + poll_option_panel(poll) + + else if(href_list["editpolloption"]) + var/datum/poll_option/option = locate(href_list["editpolloption"]) in GLOB.poll_options + var/datum/poll_question/poll = locate(href_list["parentpoll"]) in GLOB.polls + poll_option_panel(poll, option) + + else if(href_list["deletepolloption"]) + var/datum/poll_option/option = locate(href_list["deletepolloption"]) in GLOB.poll_options + var/datum/poll_question/poll = option.delete_option() + poll_management_panel(poll) + + else if(href_list["submitoption"]) + var/datum/poll_option/option = locate(href_list["submitoption"]) in GLOB.poll_options + var/datum/poll_question/poll = locate(href_list["submitoptionpoll"]) in GLOB.polls + poll_option_parse_href(href_list, poll, option) + //Topics relating to Faxes else if(href_list["AdminFaxCreate"]) if(!check_rights(R_FUN)) @@ -2232,12 +2285,6 @@ var/input_text = 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 CentCom", "") as message|null if(!input_text) qdel(P) - return - - var/obj/item/pen/admin_writer = new /obj/item/pen(null) - - input_text = P.parsepencode(input_text, admin_writer, usr) // Encode everything from pencode to html - qdel(admin_writer) var/customname = input(src.owner, "Pick a title for the fax.", "Fax Title") as text|null if(!customname) @@ -2413,7 +2460,10 @@ dat += {"Secret
    "} dat += {"Random
    "} dat += {"Now: [GLOB.master_mode]"} - usr << browse(dat, "window=c_mode") + var/datum/browser/popup = new(usr, "c_mode", "Gamemode Panel", 500, 600) + popup.set_content(dat) + popup.open() +// usr << browse(dat, "window=c_mode") /datum/admins/proc/HandleFSecret() if(!check_rights(R_ADMIN)) diff --git a/code/modules/admin/verbs/BrokenInhands.dm b/code/modules/admin/verbs/BrokenInhands.dm index defb46446ec8..7b7c461ec06e 100644 --- a/code/modules/admin/verbs/BrokenInhands.dm +++ b/code/modules/admin/verbs/BrokenInhands.dm @@ -30,6 +30,6 @@ var/F = file("broken_icons.txt") fdel(F) WRITE_FILE(F, text) - to_chat(world, "Completely successfully and written to [F]") + to_chat(world, "Completely successfully and written to [F]", confidential = TRUE) diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index bb3c9df152d8..474c918a5ca5 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -209,7 +209,7 @@ var/list/results = world.SDQL2_query(query_text, key_name_admin(usr), "[key_name(usr)]") if(length(results) == 3) for(var/I in 1 to 3) - to_chat(usr, results[I]) + to_chat(usr, results[I], confidential = TRUE) SSblackbox.record_feedback("nested tally", "SDQL query", 1, list(ckey, query_text)) /world/proc/SDQL2_query(query_text, log_entry1, log_entry2) @@ -248,7 +248,7 @@ running += query var/msg = "Starting query #[query.id] - [query.get_query_text()]." if(usr) - to_chat(usr, "[msg]") + to_chat(usr, "[msg]", confidential = TRUE) log_admin(msg) query.ARun() else //Start all @@ -256,7 +256,7 @@ running += query var/msg = "Starting query #[query.id] - [query.get_query_text()]." if(usr) - to_chat(usr, "[msg]") + to_chat(usr, "[msg]", confidential = TRUE) log_admin(msg) query.ARun() @@ -277,7 +277,7 @@ finished = FALSE if(query.state == SDQL2_STATE_ERROR) if(usr) - to_chat(usr, "SDQL query [query.get_query_text()] errored. It will NOT be automatically garbage collected. Please remove manually.") + to_chat(usr, "SDQL query [query.get_query_text()] errored. It will NOT be automatically garbage collected. Please remove manually.", confidential = TRUE) running -= query else if(query.finished) @@ -294,12 +294,12 @@ running += next_query var/msg = "Starting query #[next_query.id] - [next_query.get_query_text()]." if(usr) - to_chat(usr, "[msg]") + to_chat(usr, "[msg]", confidential = TRUE) log_admin(msg) next_query.ARun() else if(usr) - to_chat(usr, "SDQL query [query.get_query_text()] was halted. It will NOT be automatically garbage collected. Please remove manually.") + to_chat(usr, "SDQL query [query.get_query_text()] was halted. It will NOT be automatically garbage collected. Please remove manually.", confidential = TRUE) running -= query while(!finished) @@ -531,7 +531,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null to_chat(showmob, "SDQL query results: [get_query_text()]
    \ SDQL query completed: [islist(obj_count_all)? length(obj_count_all) : obj_count_all] objects selected by path, and \ [where_switched? "[islist(obj_count_eligible)? length(obj_count_eligible) : obj_count_eligible] objects executed on after WHERE keyword selection." : ""]
    \ - SDQL query took [DisplayTimeText(end_time - start_time)] to complete.
    ") + SDQL query took [DisplayTimeText(end_time - start_time)] to complete.", confidential = TRUE) if(length(select_text)) var/text = islist(select_text)? select_text.Join() : select_text var/static/result_offset = 0 @@ -859,7 +859,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null if("or", "||") result = (result || val) else - to_chat(usr, "SDQL2: Unknown op [op]") + to_chat(usr, "SDQL2: Unknown op [op]", confidential = TRUE) result = null else result = val @@ -969,7 +969,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null querys[querys_pos] = parsed_tree querys_pos++ else //There was an error so don't run anything, and tell the user which query has errored. - to_chat(usr, "Parsing error on [querys_pos]\th query. Nothing was executed.") + to_chat(usr, "Parsing error on [querys_pos]\th query. Nothing was executed.", confidential = TRUE) return list() query_tree = list() do_parse = 0 @@ -988,22 +988,22 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null for(var/item in query_tree) if(istype(item, /list)) - to_chat(usr, "[spaces](") + to_chat(usr, "[spaces](", confidential = TRUE) SDQL_testout(item, indent + 1) - to_chat(usr, "[spaces])") + to_chat(usr, "[spaces])", confidential = TRUE) else - to_chat(usr, "[spaces][item]") + to_chat(usr, "[spaces][item]", confidential = TRUE) if(!isnum(item) && query_tree[item]) if(istype(query_tree[item], /list)) - to_chat(usr, "[spaces][whitespace](") + to_chat(usr, "[spaces][whitespace](", confidential = TRUE) SDQL_testout(query_tree[item], indent + 2) - to_chat(usr, "[spaces][whitespace])") + to_chat(usr, "[spaces][whitespace])", confidential = TRUE) else - to_chat(usr, "[spaces][whitespace][query_tree[item]]") + to_chat(usr, "[spaces][whitespace][query_tree[item]]", confidential = TRUE) //Staying as a world proc as this is called too often for changes to offset the potential IsAdminAdvancedProcCall checking overhead. /world/proc/SDQL_var(object, list/expression, start = 1, source, superuser, datum/SDQL2_query/query) @@ -1015,16 +1015,16 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null D = object if (object == world && (!long || expression[start + 1] == ".") && !(expression[start] in exclude) && copytext(expression[start], 1, 3) != "SS") //3 == length("SS") + 1 - to_chat(usr, "World variables are not allowed to be accessed. Use global.") + to_chat(usr, "World variables are not allowed to be accessed. Use global.", confidential = TRUE) return null else if(expression [start] == "{" && long) if(lowertext(copytext(expression[start + 1], 1, 3)) != "0x") //3 == length("0x") + 1 - to_chat(usr, "Invalid pointer syntax: [expression[start + 1]]") + to_chat(usr, "Invalid pointer syntax: [expression[start + 1]]", confidential = TRUE) return null v = locate("\[[expression[start + 1]]]") if(!v) - to_chat(usr, "Invalid pointer: [expression[start + 1]]") + to_chat(usr, "Invalid pointer: [expression[start + 1]]", confidential = TRUE) return null start++ long = start < expression.len @@ -1087,7 +1087,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null var/list/L = v var/index = query.SDQL_expression(source, expression[start + 2]) if(isnum(index) && (!ISINTEGER(index) || L.len < index)) - to_chat(usr, "Invalid list index: [index]") + to_chat(usr, "Invalid list index: [index]", confidential = TRUE) return null return L[index] return v @@ -1139,7 +1139,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null else if(char == "'") if(word != "") - to_chat(usr, "\red SDQL2: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.") + to_chat(usr, "\red SDQL2: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.", confidential = TRUE) return null word = "'" @@ -1159,7 +1159,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null word += char if(i > len) - to_chat(usr, "\red SDQL2: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again.") + to_chat(usr, "\red SDQL2: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again.", confidential = TRUE) return null query_list += "[word]'" @@ -1167,7 +1167,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null else if(char == "\"") if(word != "") - to_chat(usr, "\red SDQL2: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.") + to_chat(usr, "\red SDQL2: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.", confidential = TRUE) return null word = "\"" @@ -1187,7 +1187,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null word += char if(i > len) - to_chat(usr, "\red SDQL2: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again.") + to_chat(usr, "\red SDQL2: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again.", confidential = TRUE) return null query_list += "[word]\"" diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm index 1c26cd7932f6..b5be28c1e67f 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm @@ -63,7 +63,7 @@ /datum/SDQL_parser/proc/parse_error(error_message) error = 1 - to_chat(usr, "SQDL2 Parsing Error: [error_message]") + to_chat(usr, "SQDL2 Parsing Error: [error_message]", confidential = TRUE) return query.len + 1 /datum/SDQL_parser/proc/parse() diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index b08d83e42871..0027b617d7a4 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -203,7 +203,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) var/admin_number_present = send2tgs_adminless_only(initiator_ckey, "Ticket #[id]: [msg]") log_admin_private("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.") if(admin_number_present <= 0) - to_chat(C, "No active admins are online, your adminhelp was sent through TGS to admins who are available. This may use IRC or Discord.") + to_chat(C, "No active admins are online, your adminhelp was sent through TGS to admins who are available. This may use IRC or Discord.", confidential = TRUE) heard_by_no_admins = TRUE GLOB.ahelp_tickets.active_tickets += src @@ -268,20 +268,20 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) if(X.prefs.toggles & SOUND_ADMINHELP) SEND_SOUND(X, sound('sound/effects/adminhelp.ogg')) window_flash(X, ignorepref = TRUE) - to_chat(X, admin_msg) + to_chat(X, admin_msg, confidential = TRUE) //show it to the person adminhelping too - to_chat(initiator, "PM to-Admins: [msg]") + to_chat(initiator, "PM to-Admins: [msg]", confidential = TRUE) SSblackbox.LogAhelp(id, "Ticket Opened", msg, null, initiator.ckey) //Reopen a closed ticket /datum/admin_help/proc/Reopen() if(state == AHELP_ACTIVE) - to_chat(usr, "This ticket is already open.") + to_chat(usr, "This ticket is already open.", confidential = TRUE) return if(GLOB.ahelp_tickets.CKey2ActiveTicket(initiator_ckey)) - to_chat(usr, "This user already has an active ticket, cannot reopen this one.") + to_chat(usr, "This user already has an active ticket, cannot reopen this one.", confidential = TRUE) return statclick = new(null, src) @@ -342,7 +342,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) addtimer(CALLBACK(initiator, /client/proc/giveadminhelpverb), 50) AddInteraction("Resolved by [key_name].") - to_chat(initiator, "Your ticket has been resolved by an admin. The Adminhelp verb will be returned to you shortly.") + to_chat(initiator, "Your ticket has been resolved by an admin. The Adminhelp verb will be returned to you shortly.", confidential = TRUE) if(!silent) SSblackbox.record_feedback("tally", "ahelp_stats", 1, "resolved") var/msg = "Ticket [TicketHref("#[id]")] resolved by [key_name]" @@ -360,9 +360,9 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) SEND_SOUND(initiator, sound('sound/effects/adminhelp.ogg')) - to_chat(initiator, "- AdminHelp Rejected! -") - to_chat(initiator, "Your admin help was rejected. The adminhelp verb has been returned to you so that you may try again.") - to_chat(initiator, "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.") + to_chat(initiator, "- AdminHelp Rejected! -", confidential = TRUE) + to_chat(initiator, "Your admin help was rejected. The adminhelp verb has been returned to you so that you may try again.", confidential = TRUE) + to_chat(initiator, "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.", confidential = TRUE) SSblackbox.record_feedback("tally", "ahelp_stats", 1, "rejected") var/msg = "Ticket [TicketHref("#[id]")] rejected by [key_name]" @@ -381,7 +381,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) msg += "Your issue has been determined by an administrator to be an in character issue and does NOT require administrator intervention at this time. For further resolution you should pursue options that are in character." if(initiator) - to_chat(initiator, msg) + to_chat(initiator, msg, confidential = TRUE) SSblackbox.record_feedback("tally", "ahelp_stats", 1, "IC") msg = "Ticket [TicketHref("#[id]")] marked as IC by [key_name]" @@ -494,12 +494,12 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) set name = "Adminhelp" if(GLOB.say_disabled) //This is here to try to identify lag problems - to_chat(usr, "Speech is currently admin-disabled.") + to_chat(usr, "Speech is currently admin-disabled.", confidential = TRUE) return //handle muting and automuting if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).") + to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).", confidential = TRUE) return if(handle_spam_prevention(msg,MUTE_ADMINHELP)) return @@ -517,7 +517,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) current_ticket.TimeoutVerb() return else - to_chat(usr, "Ticket not found, creating new one...") + to_chat(usr, "Ticket not found, creating new one...", confidential = TRUE) else current_ticket.AddInteraction("[key_name_admin(usr)] opened a new ticket.") current_ticket.Close() diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index ad4b527b423c..2100dc21cdae 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -3,7 +3,7 @@ set desc = "Area to jump to" set category = "Admin - Game" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return if(!A) @@ -22,7 +22,7 @@ message_admins("[key_name_admin(usr)] jumped to [AREACOORD(A)]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Area") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! else - to_chat(src, "Nowhere to jump to!") + to_chat(src, "Nowhere to jump to!", confidential = TRUE) return @@ -30,7 +30,7 @@ set name = "Jump to Turf" set category = "Admin - Game" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return log_admin("[key_name(usr)] jumped to [AREACOORD(T)]") @@ -44,7 +44,7 @@ set name = "Jump to Mob" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return log_admin("[key_name(usr)] jumped to [key_name(M)]") @@ -56,14 +56,14 @@ SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! A.forceMove(M.loc) else - to_chat(A, "This mob is not located in the game world.") + to_chat(A, "This mob is not located in the game world.", confidential = TRUE) /client/proc/jumptocoord(tx as num, ty as num, tz as num) set category = "Admin - Game" set name = "Jump to Coordinate" if (!holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return if(src.mob) @@ -78,7 +78,7 @@ set name = "Jump to Key" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return var/list/keys = list() @@ -86,7 +86,7 @@ keys += M.client var/client/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) if(!selection) - to_chat(src, "No keys found.") + to_chat(src, "No keys found.", confidential = TRUE) return var/mob/M = selection.mob log_admin("[key_name(usr)] jumped to [key_name(M)]") @@ -101,7 +101,7 @@ set name = "Get Mob" set desc = "Mob to teleport" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return var/atom/loc = get_turf(usr) @@ -118,7 +118,7 @@ set desc = "Key to teleport" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return var/list/keys = list() @@ -144,7 +144,7 @@ set category = "Admin - Game" set name = "Send Mob" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return var/area/A = input(usr, "Pick an area.", "Pick an area") in GLOB.sortedAreas|null if(A && istype(A)) @@ -156,5 +156,5 @@ message_admins(msg) admin_ticket_log(M, msg) else - to_chat(src, "Failed to move mob to a valid location.") + to_chat(src, "Failed to move mob to a valid location.", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index ec7f2fb07ee8..453e4031a9d1 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -5,7 +5,7 @@ set category = null set name = "Admin PM Mob" if(!holder) - to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.") + to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.", confidential = TRUE) return if( !ismob(M) || !M.client ) return @@ -17,7 +17,7 @@ set category = "Admin" set name = "Admin PM" if(!holder) - to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.") + to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.", confidential = TRUE) return var/list/client/targets[0] for(var/client/T) @@ -36,7 +36,7 @@ /client/proc/cmd_ahelp_reply(whom) if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") + to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).", confidential = TRUE) return var/client/C if(istext(whom)) @@ -47,7 +47,7 @@ C = whom if(!C) if(holder) - to_chat(src, "Error: Admin-PM: Client not found.") + to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE) return var/datum/admin_help/AH = C.current_ticket @@ -64,12 +64,12 @@ //Fetching a message if needed. src is the sender and C is the target client /client/proc/cmd_admin_pm(whom, msg) if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") + to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).", confidential = TRUE) return if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo - to_chat(src, "You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.") - to_chat(src, "Message: [msg]") + to_chat(src, "You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.", confidential = TRUE) + to_chat(src, "Message: [msg]", confidential = TRUE) return var/client/recipient @@ -94,16 +94,16 @@ if(!msg) return if(holder) - to_chat(src, "Error: Use the admin IRC/Discord channel, nerd.") + to_chat(src, "Error: Use the admin IRC/Discord channel, nerd.", confidential = TRUE) return else if(!recipient) if(holder) - to_chat(src, "Error: Admin-PM: Client not found.") + to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE) if(msg) - to_chat(src, msg) + to_chat(src, msg, confidential = TRUE) return else if(msg) // you want to continue if there's no message instead of returning now current_ticket.MessageNoRecipient(msg) @@ -117,12 +117,12 @@ return if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") + to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).", confidential = TRUE) return if(!recipient) if(holder) - to_chat(src, "Error: Admin-PM: Client not found.") + to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE) else current_ticket.MessageNoRecipient(msg) return @@ -144,15 +144,16 @@ var/keywordparsedmsg = keywords_lookup(msg) if(external) - to_chat(src, "PM to-Admins: [rawmsg]") + to_chat(src, "PM to-Admins: [rawmsg]", confidential = TRUE) var/datum/admin_help/AH = admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to External: [keywordparsedmsg]") externalreplyamount-- + SSredbot.send_discord_message("admin", "[AH ? "#[AH.id] " : ""]Reply: [ckey]") send2tgs("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg) else if(recipient.holder) if(holder) //both are admins - to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]") - to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]") + to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]", confidential = TRUE) + to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]", confidential = TRUE) //omg this is dumb, just fill in both their tickets var/interaction_message = "PM from-[key_name(src, recipient, 1)] to-[key_name(recipient, src, 1)]: [keywordparsedmsg]" @@ -163,8 +164,8 @@ else //recipient is an admin but sender is not var/replymsg = "Reply PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]" admin_ticket_log(src, "[replymsg]") - to_chat(recipient, "[replymsg]") - to_chat(src, "PM to-Admins: [msg]") + to_chat(recipient, "[replymsg]", confidential = TRUE) + to_chat(src, "PM to-Admins: [msg]", confidential = TRUE) SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey) //play the receiving admin the adminhelp sound (if they have them enabled) @@ -179,10 +180,10 @@ already_logged = TRUE SSblackbox.LogAhelp(recipient.current_ticket.id, "Ticket Opened", msg, recipient.ckey, src.ckey) - to_chat(recipient, "-- Administrator private message --") - to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]") - to_chat(recipient, "Click on the administrator's name to reply.") - to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]") + to_chat(recipient, "-- Administrator private message --", confidential = TRUE) + to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]", confidential = TRUE) + to_chat(recipient, "Click on the administrator's name to reply.", confidential = TRUE) + to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]", confidential = TRUE) admin_ticket_log(recipient, "PM From [key_name_admin(src)]: [keywordparsedmsg]") @@ -198,20 +199,20 @@ INVOKE_ASYNC(src, .proc/popup_admin_pm, recipient, msg) else //neither are admins - to_chat(src, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.") + to_chat(src, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.", confidential = TRUE) return if(external) log_admin_private("PM: [key_name(src)]->External: [rawmsg]") for(var/client/X in GLOB.admins) - to_chat(X, "PM: [key_name(src, X, 0)]->External: [keywordparsedmsg]") + to_chat(X, "PM: [key_name(src, X, 0)]->External: [keywordparsedmsg]", confidential = TRUE) else window_flash(recipient, ignorepref = TRUE) log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]") //we don't use message_admins here because the sender/receiver might get it too for(var/client/X in GLOB.admins) if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient - to_chat(X, "PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]" ) + to_chat(X, "PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]" , confidential = TRUE) /client/proc/popup_admin_pm(client/recipient, msg) var/sender = src @@ -298,12 +299,13 @@ return "Error: No message" message_admins("External message from [sender] to [key_name_admin(C)] : [msg]") + SSredbot.send_discord_message("admin", "External message from [sender] to [key_name_admin(C)] : [msg]") log_admin_private("External PM: [sender] -> [key_name(C)] : [msg]") msg = emoji_parse(msg) - to_chat(C, "-- Administrator private message --") - to_chat(C, "Admin PM from-[adminname]: [msg]") - to_chat(C, "Click on the administrator's name to reply.") + to_chat(C, "-- Administrator private message --", confidential = TRUE) + to_chat(C, "Admin PM from-[adminname]: [msg]", confidential = TRUE) + to_chat(C, "Click on the administrator's name to reply.", confidential = TRUE) admin_ticket_log(C, "PM From [tgs_tagged]: [msg]") diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index e3d3a14879c7..92e948da54c8 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -15,7 +15,7 @@ msg = keywords_lookup(msg) var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && prefs.asaycolor) ? "" : "" msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [custom_asay_color][msg][custom_asay_color ? "":null]" - to_chat(GLOB.admins, msg) + to_chat(GLOB.admins, msg, confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/anonymousnames.dm b/code/modules/admin/verbs/anonymousnames.dm new file mode 100644 index 000000000000..9b36e887f01d --- /dev/null +++ b/code/modules/admin/verbs/anonymousnames.dm @@ -0,0 +1,65 @@ +/client/proc/anon_names() + set category = "Admin - Events" + set name = "Setup Anonymous Names" + + + if(SSticker.current_state > GAME_STATE_PREGAME) + to_chat(usr, "This option is currently only usable during pregame.") + return + + if(SSticker.anonymousnames) + SSticker.anonymousnames = ANON_DISABLED + to_chat(usr, "Disabled anonymous names.") + message_admins("[key_name_admin(usr)] has disabled anonymous names.") + return + var/list/names = list("Cancel", ANON_RANDOMNAMES, ANON_EMPLOYEENAMES) + var/result = input(usr, "Choose an anonymous theme","going dark") as null|anything in names + if(!usr || !result || result == "Cancel") + return + if(SSticker.current_state > GAME_STATE_PREGAME) + to_chat(usr, "You took too long! The game has started.") + return + + SSticker.anonymousnames = result + to_chat(usr, "Enabled anonymous names. THEME: [SSticker.anonymousnames].") + message_admins("[key_name_admin(usr)] has enabled anonymous names. THEME: [SSticker.anonymousnames].") + +/** + * anonymous_name: generates a corporate random name. used in admin event tool anonymous names + * + * first letter is always a letter + * Example name = "Employee Q5460Z" + * Arguments: + * * M - mob for preferences and gender + */ +/proc/anonymous_name(mob/M) + switch(SSticker.anonymousnames) + if(ANON_RANDOMNAMES) + return M.client.prefs.pref_species.random_name(M.gender,1) + if(ANON_EMPLOYEENAMES) + var/name = "Employee " + + for(var/i in 1 to 6) + if(prob(30) || i == 1) + name += ascii2text(rand(65, 90)) //A - Z + else + name += ascii2text(rand(48, 57)) //0 - 9 + return name + +/** + * anonymous_ai_name: generates a corporate random name (but for sillycones). used in admin event tool anonymous names + * + * first letter is always a letter + * Example name = "Employee Assistant Assuming Delta" + * Arguments: + * * is_ai - boolean to decide whether the name has "Core" (AI) or "Assistant" (Cyborg) + */ +/proc/anonymous_ai_name(is_ai = FALSE) + switch(SSticker.anonymousnames) + if(ANON_RANDOMNAMES) + return pick(GLOB.ai_names) + if(ANON_EMPLOYEENAMES) + var/verbs = capitalize(pick(GLOB.ing_verbs)) + var/phonetic = pick(GLOB.phonetic_alphabet) + + return "Employee [is_ai ? "Core" : "Assistant"] [verbs] [phonetic]" diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm index 42598521cfdc..42a9e07214e8 100644 --- a/code/modules/admin/verbs/atmosdebug.dm +++ b/code/modules/admin/verbs/atmosdebug.dm @@ -2,30 +2,30 @@ set category = "Mapping" set name = "Check Plumbing" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Plumbing") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! //all plumbing - yes, some things might get stated twice, doesn't matter. for(var/obj/machinery/atmospherics/components/pipe in GLOB.machines) if(pipe.z && (!pipe.nodes || !pipe.nodes.len || (null in pipe.nodes))) - to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]") + to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]", confidential = TRUE) //Manifolds for(var/obj/machinery/atmospherics/pipe/manifold/pipe in GLOB.machines) if(pipe.z && (!pipe.nodes || !pipe.nodes.len || (null in pipe.nodes))) - to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]") + to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]", confidential = TRUE) //Pipes for(var/obj/machinery/atmospherics/pipe/simple/pipe in GLOB.machines) if(pipe.z && (!pipe.nodes || !pipe.nodes.len || (null in pipe.nodes))) - to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]") + to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]", confidential = TRUE) /client/proc/powerdebug() set category = "Mapping" set name = "Check Power" if(!src.holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Power") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! var/list/results = list() @@ -53,4 +53,4 @@ var/obj/structure/cable/C = locate(/obj/structure/cable) in T.contents if(!C) results += "Unwired terminal at [ADMIN_VERBOSEJMP(term)]" - to_chat(usr, "[results.Join("\n")]") + to_chat(usr, "[results.Join("\n")]", confidential = TRUE) diff --git a/code/modules/admin/verbs/bluespacearty.dm b/code/modules/admin/verbs/bluespacearty.dm index 5b4b7db663e8..b257581e7961 100644 --- a/code/modules/admin/verbs/bluespacearty.dm +++ b/code/modules/admin/verbs/bluespacearty.dm @@ -5,7 +5,7 @@ var/mob/living/target = M if(!isliving(target)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living", confidential = TRUE) return explosion(target.loc, 0, 0, 0, 0) diff --git a/code/modules/admin/verbs/borgpanel.dm b/code/modules/admin/verbs/borgpanel.dm index 47767560936a..cbee1a066a5d 100644 --- a/code/modules/admin/verbs/borgpanel.dm +++ b/code/modules/admin/verbs/borgpanel.dm @@ -9,7 +9,7 @@ if (!istype(borgo, /mob/living/silicon/robot)) borgo = input("Select a borg", "Select a borg", null, null) as null|anything in sortNames(GLOB.silicon_mobs) if (!istype(borgo, /mob/living/silicon/robot)) - to_chat(usr, "Borg is required for borgpanel") + to_chat(usr, "Borg is required for borgpanel", confidential = TRUE) var/datum/borgpanel/borgpanel = new(usr, borgo) @@ -36,7 +36,7 @@ /datum/borgpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "borgopanel", "Borg Panel", 700, 700, master_ui, state) + ui = new(user, src, ui_key, "BorgPanel", "Borg Panel", 700, 700, master_ui, state) ui.open() /datum/borgpanel/ui_data(mob/user) diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index 52553b239493..a1af36e23a1f 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -3,12 +3,12 @@ set name = "Dsay" set hidden = 1 if(!holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return if(!mob) return if(prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot send DSAY messages (muted).") + to_chat(src, "You cannot send DSAY messages (muted).", confidential = TRUE) return if (handle_spam_prevention(msg,MUTE_DEADCHAT)) @@ -30,7 +30,7 @@ if(isnewplayer(M)) continue if (M.stat == DEAD || (M.client.holder && (M.client.prefs.chat_toggles & CHAT_DEAD))) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above - to_chat(M, rendered) + to_chat(M, rendered, confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Dsay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index bfc79c19bcf5..01070c6c8e93 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -280,7 +280,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that var/list/dat = list() if(SSticker.current_state == GAME_STATE_STARTUP) - to_chat(usr, "Game still loading, please hold!") + to_chat(usr, "Game still loading, please hold!", confidential = TRUE) return message_admins("[key_name_admin(usr)] used the Test Atmos Monitor debug command.") @@ -326,7 +326,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that var/list/station_areas_blacklist = typecacheof(list(/area/holodeck/rec_center, /area/shuttle, /area/engine/supermatter, /area/science/test_area, /area/space, /area/solar, /area/mine, /area/ruin, /area/asteroid)) if(SSticker.current_state == GAME_STATE_STARTUP) - to_chat(usr, "Game still loading, please hold!") + to_chat(usr, "Game still loading, please hold!", confidential = TRUE) return var/log_message @@ -644,19 +644,19 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that switch(input("Which list?") in list("Players","Admins","Mobs","Living Mobs","Dead Mobs","Clients","Joined Clients")) if("Players") - to_chat(usr, jointext(GLOB.player_list,",")) + to_chat(usr, jointext(GLOB.player_list,","), confidential = TRUE) if("Admins") - to_chat(usr, jointext(GLOB.admins,",")) + to_chat(usr, jointext(GLOB.admins,","), confidential = TRUE) if("Mobs") - to_chat(usr, jointext(GLOB.mob_list,",")) + to_chat(usr, jointext(GLOB.mob_list,","), confidential = TRUE) if("Living Mobs") - to_chat(usr, jointext(GLOB.alive_mob_list,",")) + to_chat(usr, jointext(GLOB.alive_mob_list,","), confidential = TRUE) if("Dead Mobs") - to_chat(usr, jointext(GLOB.dead_mob_list,",")) + to_chat(usr, jointext(GLOB.dead_mob_list,","), confidential = TRUE) if("Clients") - to_chat(usr, jointext(GLOB.clients,",")) + to_chat(usr, jointext(GLOB.clients,","), confidential = TRUE) if("Joined Clients") - to_chat(usr, jointext(GLOB.joined_player_list,",")) + to_chat(usr, jointext(GLOB.joined_player_list,","), confidential = TRUE) /client/proc/cmd_display_del_log() set category = "Debug" @@ -739,8 +739,8 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that if(istype(landmark)) var/datum/map_template/ruin/template = landmark.ruin_template usr.forceMove(get_turf(landmark)) - to_chat(usr, "[template.name]") - to_chat(usr, "[template.description]") + to_chat(usr, "[template.name]", confidential = TRUE) + to_chat(usr, "[template.description]", confidential = TRUE) /client/proc/place_ruin() set category = "Debug" @@ -781,10 +781,10 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that var/obj/effect/landmark/ruin/landmark = GLOB.ruin_landmarks[GLOB.ruin_landmarks.len] log_admin("[key_name(src)] randomly spawned ruin [ruinname] at [COORD(landmark)].") usr.forceMove(get_turf(landmark)) - to_chat(src, "[template.name]") - to_chat(src, "[template.description]") + to_chat(src, "[template.name]", confidential = TRUE) + to_chat(src, "[template.description]", confidential = TRUE) else - to_chat(src, "Failed to place [template.name].") + to_chat(src, "Failed to place [template.name].", confidential = TRUE) /client/proc/clear_dynamic_transit() set category = "Debug" diff --git a/code/modules/admin/verbs/fix_air.dm b/code/modules/admin/verbs/fix_air.dm index 47071282930e..869ef7d2633b 100644 --- a/code/modules/admin/verbs/fix_air.dm +++ b/code/modules/admin/verbs/fix_air.dm @@ -5,7 +5,7 @@ set desc = "Fixes air in specified radius." if(!holder) - to_chat(src, "Only administrators may use this command.") + to_chat(src, "Only administrators may use this command.", confidential = TRUE) return if(check_rights(R_ADMIN,1)) var/range=input("Enter range:","Num",2) as num diff --git a/code/modules/admin/verbs/fps.dm b/code/modules/admin/verbs/fps.dm index f120f540b9ff..4eb8dd8bc10c 100644 --- a/code/modules/admin/verbs/fps.dm +++ b/code/modules/admin/verbs/fps.dm @@ -11,7 +11,7 @@ var/new_fps = round(input("Sets game frames-per-second. Can potentially break the game (default: [cfg_fps])","FPS", world.fps) as num|null) if(new_fps <= 0) - to_chat(src, "Error: set_server_fps(): Invalid world.fps value. No changes made.") + to_chat(src, "Error: set_server_fps(): Invalid world.fps value. No changes made.", confidential = TRUE) return if(new_fps > cfg_fps * 1.5) if(alert(src, "You are setting fps to a high value:\n\t[new_fps] frames-per-second\n\tconfig.fps = [cfg_fps]","Warning!","Confirm","ABORT-ABORT-ABORT") != "Confirm") diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm index 0c3f197f894a..a07e1fb1e9e4 100644 --- a/code/modules/admin/verbs/getlogs.dm +++ b/code/modules/admin/verbs/getlogs.dm @@ -11,10 +11,10 @@ set desc = "View/retrieve logfiles for the current round." set category = "Admin" - browseserverlogs("[GLOB.log_directory]/") + browseserverlogs(current=TRUE) -/client/proc/browseserverlogs(path = "data/logs/") - path = browse_files(path) +/client/proc/browseserverlogs(current=FALSE) + var/path = browse_files(current ? BROWSE_ROOT_CURRENT_LOGS : BROWSE_ROOT_ALL_LOGS) if(!path) return @@ -31,5 +31,5 @@ src << ftp(file(path)) else return - to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.") + to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.", confidential = TRUE) return diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index 0e687c8b045e..0da71f41e1ed 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -23,7 +23,7 @@ if(template.load(T, centered = TRUE)) message_admins("[key_name_admin(src)] has placed a map template ([template.name]) at [ADMIN_COORDJMP(T)]") else - to_chat(src, "Failed to place map") + to_chat(src, "Failed to place map", confidential = TRUE) images -= preview /client/proc/map_template_upload() @@ -34,7 +34,7 @@ if(!map) return if(copytext("[map]", -4) != ".dmm")//4 == length(".dmm") - to_chat(src, "Filename must end in '.dmm': [map]") + to_chat(src, "Filename must end in '.dmm': [map]", confidential = TRUE) return var/datum/map_template/M switch(alert(src, "What kind of map is this?", "Map type", "Normal", "Shuttle", "Cancel")) @@ -45,7 +45,7 @@ else return if(!M.cached_map) - to_chat(src, "Map template '[map]' failed to parse properly.") + to_chat(src, "Map template '[map]' failed to parse properly.", confidential = TRUE) return var/datum/map_report/report = M.cached_map.check_for_errors() @@ -53,7 +53,7 @@ if(report) report.show_to(src) report_link = " - validation report" - to_chat(src, "Map template '[map]' failed validation.") + to_chat(src, "Map template '[map]' failed validation.", confidential = TRUE) if(report.loadable) var/response = alert(src, "The map failed validation, would you like to load it anyways?", "Map Errors", "Cancel", "Upload Anyways") if(response != "Upload Anyways") @@ -64,4 +64,4 @@ SSmapping.map_templates[M.name] = M message_admins("[key_name_admin(src)] has uploaded a map template '[map]' ([M.width]x[M.height])[report_link].") - to_chat(src, "Map template '[map]' ready to place ([M.width]x[M.height])") + to_chat(src, "Map template '[map]' ready to place ([M.width]x[M.height])", confidential = TRUE) diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 1efbc661a068..b86479583ef4 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -192,12 +192,12 @@ GLOBAL_LIST_EMPTY(dirty_vars) count++ if(count) - to_chat(usr, "[count] AT markers removed.") + to_chat(usr, "[count] AT markers removed.", confidential = TRUE) else for(var/t in GLOB.active_turfs_startlist) new /obj/effect/abstract/marker/at(t) count++ - to_chat(usr, "[count] AT markers placed.") + to_chat(usr, "[count] AT markers placed.", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turf Markers") @@ -253,7 +253,7 @@ GLOBAL_LIST_EMPTY(dirty_vars) count++ atom_list += A - to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level]") + to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level]", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Count Objects Zlevel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/count_objects_all() @@ -273,7 +273,7 @@ GLOBAL_LIST_EMPTY(dirty_vars) if(istype(A,type_path)) count++ - to_chat(world, "There are [count] objects of type [type_path] in the game world") + to_chat(world, "There are [count] objects of type [type_path] in the game world", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Count Objects All") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -375,4 +375,4 @@ GLOBAL_VAR_INIT(say_disabled, FALSE) messages += "[part.Join("")]" messages += "" - to_chat(src, messages.Join("")) + to_chat(src, messages.Join(""), confidential = TRUE) diff --git a/code/modules/admin/verbs/panicbunker.dm b/code/modules/admin/verbs/panicbunker.dm index feca326c1c1a..63183032466f 100644 --- a/code/modules/admin/verbs/panicbunker.dm +++ b/code/modules/admin/verbs/panicbunker.dm @@ -2,7 +2,7 @@ set category = "Server" set name = "Toggle Panic Bunker" if (!CONFIG_GET(flag/sql_enabled)) - to_chat(usr, "The Database is not enabled!") + to_chat(usr, "The Database is not enabled!", confidential = TRUE) return var/new_pb = !CONFIG_GET(flag/panic_bunker) diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 1df7fc62fde4..b731f85e21d7 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -23,7 +23,7 @@ var/res = alert(usr, "Show the title of this song to the players?",, "Yes","No", "Cancel") switch(res) if("Yes") - to_chat(world, "An admin played: [S]") + to_chat(world, "An admin played: [S]", confidential = TRUE) if("Cancel") return @@ -75,7 +75,7 @@ var/ytdl = CONFIG_GET(string/invoke_youtubedl) if(!ytdl) - to_chat(src, "Youtube-dl was not configured, action unavailable") //Check config.txt for the INVOKE_YOUTUBEDL value + to_chat(src, "Youtube-dl was not configured, action unavailable", confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value return var/web_sound_input = input("Enter content URL (supported sites only, leave blank to stop playing)", "Play Internet Sound via youtube-dl") as text|null @@ -87,8 +87,8 @@ web_sound_input = trim(web_sound_input) if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol)) - to_chat(src, "Non-http(s) URIs are not allowed.") - to_chat(src, "For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website.") + to_chat(src, "Non-http(s) URIs are not allowed.", confidential = TRUE) + to_chat(src, "For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website.", confidential = TRUE) return var/shell_scrubbed_input = shell_url_scrub(web_sound_input) var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height<=360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_scrubbed_input]\"") @@ -100,8 +100,8 @@ try data = json_decode(stdout) catch(var/exception/e) - to_chat(src, "Youtube-dl JSON parsing FAILED:") - to_chat(src, "[e]: [stdout]") + to_chat(src, "Youtube-dl JSON parsing FAILED:", confidential = TRUE) + to_chat(src, "[e]: [stdout]", confidential = TRUE) return if (data["url"]) @@ -116,7 +116,7 @@ var/res = alert(usr, "Show the title of and link to this song to the players?\n[title]",, "No", "Yes", "Cancel") switch(res) if("Yes") - to_chat(world, "An admin played: [webpage_url]") + to_chat(world, "An admin played: [webpage_url]", confidential = TRUE) if("Cancel") return @@ -124,8 +124,8 @@ log_admin("[key_name(src)] played web sound: [web_sound_input]") message_admins("[key_name(src)] played web sound: [web_sound_input]") else - to_chat(src, "Youtube-dl URL retrieval FAILED:") - to_chat(src, "[stderr]") + to_chat(src, "Youtube-dl URL retrieval FAILED:", confidential = TRUE) + to_chat(src, "[stderr]", confidential = TRUE) else //pressed ok with blank log_admin("[key_name(src)] stopped web sound") @@ -134,8 +134,8 @@ stop_web_sounds = TRUE if(web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol)) - to_chat(src, "BLOCKED: Content URL not using http(s) protocol") - to_chat(src, "The media provider returned a content URL that isn't using the HTTP or HTTPS protocol") + to_chat(src, "BLOCKED: Content URL not using http(s) protocol", confidential = TRUE) + to_chat(src, "The media provider returned a content URL that isn't using the HTTP or HTTPS protocol", confidential = TRUE) return if(web_sound_url || stop_web_sounds) for(var/m in GLOB.player_list) diff --git a/code/modules/admin/verbs/possess.dm b/code/modules/admin/verbs/possess.dm index 8f31dba1a250..9b226a97e427 100644 --- a/code/modules/admin/verbs/possess.dm +++ b/code/modules/admin/verbs/possess.dm @@ -3,7 +3,7 @@ set category = "Object" if((O.obj_flags & DANGEROUS_POSSESSION) && CONFIG_GET(flag/forbid_singulo_possession)) - to_chat(usr, "[O] is too powerful for you to possess.") + to_chat(usr, "[O] is too powerful for you to possess.", confidential = TRUE) return var/turf/T = get_turf(O) diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 3b6fd63a5e53..26545e251414 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -3,7 +3,7 @@ set name = "Pray" if(GLOB.say_disabled) //This is here to try to identify lag problems - to_chat(usr, "Speech is currently admin-disabled.") + to_chat(usr, "Speech is currently admin-disabled.", confidential = TRUE) return msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN) @@ -12,7 +12,7 @@ log_prayer("[src.key]/([src.name]): [msg]") if(usr.client) if(usr.client.prefs.muted & MUTE_PRAY) - to_chat(usr, "You cannot pray (muted).") + to_chat(usr, "You cannot pray (muted).", confidential = TRUE) return if(src.client.handle_spam_prevention(msg,MUTE_PRAY)) return @@ -44,11 +44,11 @@ for(var/client/C in GLOB.admins) if(C.prefs.chat_toggles & CHAT_PRAYER) - to_chat(C, msg) + to_chat(C, msg, confidential = TRUE) if(C.prefs.toggles & SOUND_PRAYERS) if(usr.job == "Chaplain") SEND_SOUND(C, sound('sound/effects/pray.ogg')) - to_chat(usr, "You pray to the gods: \"[msg_tmp]\"") + to_chat(usr, "You pray to the gods: \"[msg_tmp]\"", confidential = TRUE) SSredbot.send_discord_message("admin", "Prayer from [src.key]/([src.name]): [msg]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Prayer") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -57,20 +57,20 @@ /proc/CentCom_announce(text , mob/Sender) var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN) msg = "CENTCOM:[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]: [msg]" - to_chat(GLOB.admins, msg) + to_chat(GLOB.admins, msg, confidential = TRUE) for(var/obj/machinery/computer/communications/C in GLOB.machines) C.overrideCooldown() /proc/Syndicate_announce(text , mob/Sender) var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN) msg = "SYNDICATE:[ADMIN_FULLMONTY(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]: [msg]" - to_chat(GLOB.admins, msg) + to_chat(GLOB.admins, msg, confidential = TRUE) for(var/obj/machinery/computer/communications/C in GLOB.machines) C.overrideCooldown() /proc/Nuke_request(text , mob/Sender) var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN) msg = "NUKE CODE REQUEST:[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)] [ADMIN_SET_SD_CODE]: [msg]" - to_chat(GLOB.admins, msg) + to_chat(GLOB.admins, msg, confidential = TRUE) for(var/obj/machinery/computer/communications/C in GLOB.machines) C.overrideCooldown() diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index d0e42ebb45be..3f3a031f3c30 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -37,7 +37,7 @@ if(usr) if (usr.client) if(usr.client.holder) - to_chat(M, "You hear a voice in your head... [msg]") + to_chat(M, "You hear a voice in your head... [msg]", confidential = TRUE) log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" @@ -58,10 +58,10 @@ return if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human", confidential = TRUE) return if(!istype(H.ears, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset.") + to_chat(usr, "The person you are trying to contact is not wearing a headset.", confidential = TRUE) return if (!sender) @@ -77,7 +77,7 @@ log_directed_talk(mob, H, input, LOG_ADMIN, "reply") 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.\"") + 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.\"", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Headset Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -117,7 +117,7 @@ log_text = "Subtracted [num2text(msg)]" SSpersistence.antag_rep[C.ckey] = max(SSpersistence.antag_rep[C.ckey]-msg, 0) else - to_chat(src, "Invalid operation for antag rep modification: [operation] by user [key_name(usr)]") + to_chat(src, "Invalid operation for antag rep modification: [operation] by user [key_name(usr)]", confidential = TRUE) return if(SSpersistence.antag_rep[C.ckey] <= 0) @@ -138,7 +138,7 @@ if (!msg) return - to_chat(world, "[msg]") + to_chat(world, "[msg]", confidential = TRUE) log_admin("GlobalNarrate: [key_name(usr)] : [msg]") message_admins("[key_name_admin(usr)] Sent a global narrate") SSblackbox.record_feedback("tally", "admin_verb", 1, "Global Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -161,7 +161,7 @@ if( !msg ) return - to_chat(M, msg) + to_chat(M, msg, confidential = TRUE) log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]") msg = " DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]
    " message_admins(msg) @@ -183,7 +183,7 @@ if (!msg) return for(var/mob/M in view(range,A)) - to_chat(M, msg) + to_chat(M, msg, confidential = TRUE) log_admin("LocalNarrate: [key_name(usr)] at [AREACOORD(A)]: [msg]") message_admins(" LocalNarrate: [key_name_admin(usr)] at [ADMIN_VERBOSEJMP(A)]: [msg]
    ") @@ -196,7 +196,7 @@ return M.status_flags ^= GODMODE - to_chat(usr, "Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]") + to_chat(usr, "Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]", confidential = TRUE) log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") var/msg = "[key_name_admin(usr)] has toggled [ADMIN_LOOKUPFLW(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]" @@ -266,7 +266,7 @@ log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(whom)] from [mute_string]") message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(whom)] from [mute_string].") if(C) - to_chat(C, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") + to_chat(C, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.", confidential = TRUE) SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Auto Mute [feedback_string]", "1")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return @@ -280,7 +280,7 @@ log_admin("[key_name(usr)] has [muteunmute] [key_name(whom)] from [mute_string]") message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(whom)] from [mute_string].") if(C) - to_chat(C, "You have been [muteunmute] from [mute_string] by [key_name(usr, include_name = FALSE)].") + to_chat(C, "You have been [muteunmute] from [mute_string] by [key_name(usr, include_name = FALSE)].", confidential = TRUE) SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Mute [feedback_string]", "[P.muted & mute_type]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -301,7 +301,7 @@ if(candidates.len) ckey = input("Pick the player you want to respawn as a xeno.", "Suitable Candidates") as null|anything in sortKey(candidates) else - to_chat(usr, "Error: create_xeno(): no suitable candidates.") + to_chat(usr, "Error: create_xeno(): no suitable candidates.", confidential = TRUE) if(!istext(ckey)) return 0 @@ -355,7 +355,7 @@ Traitors and the like can also be revived with the previous role mostly intact. 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.") + to_chat(usr, "There is no active key like that in the game or the person is not currently a ghost.", confidential = TRUE) return if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something @@ -388,7 +388,7 @@ Traitors and the like can also be revived with the previous role mostly intact. //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.") + to_chat(new_xeno, "You have been fully respawned. Enjoy the game.", confidential = TRUE) var/msg = "[key_name_admin(usr)] has respawned [new_xeno.key] as a filthy xeno." message_admins(msg) admin_ticket_log(new_xeno, msg) @@ -401,7 +401,7 @@ Traitors and the like can also be revived with the previous role mostly intact. SSjob.SendToLateJoin(new_monkey) G_found.mind.transfer_to(new_monkey) //be careful when doing stuff like this! I've already checked the mind isn't in use new_monkey.key = G_found.key - to_chat(new_monkey, "You have been fully respawned. Enjoy the game.") + to_chat(new_monkey, "You have been fully respawned. Enjoy the game.", confidential = TRUE) var/msg = "[key_name_admin(usr)] has respawned [new_monkey.key] as a filthy xeno." message_admins(msg) admin_ticket_log(new_monkey, msg) @@ -424,7 +424,7 @@ Traitors and the like can also be revived with the previous role mostly intact. new_character.real_name = record_found.fields["name"] new_character.gender = record_found.fields["gender"] new_character.age = record_found.fields["age"] - new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], record_found.fields["name"], record_found.fields["blood_type"], new record_found.fields["species"], record_found.fields["features"]) + new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], null, record_found.fields["name"], record_found.fields["blood_type"], new record_found.fields["species"], record_found.fields["features"]) else var/datum/preferences/A = new() A.copy_to(new_character) @@ -500,7 +500,7 @@ Traitors and the like can also be revived with the previous role mostly intact. message_admins(msg) admin_ticket_log(new_character, msg) - to_chat(new_character, "You have been fully respawned. Enjoy the game.") + to_chat(new_character, "You have been fully respawned. Enjoy the game.", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Respawn Character") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return new_character @@ -555,7 +555,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!check_rights(R_ADMIN)) return - var/input = input(usr, "Enter a Command Report. Ensure it makes sense IC.", "What?", "") as message|null + var/input = input(usr, "Enter a Command Report. Ensure it makes sense IC. Command's name is currently set to [command_name()].", "What?", "") as message|null if(!input) return @@ -710,7 +710,7 @@ Traitors and the like can also be revived with the previous role mostly intact. var/list/L = M.get_contents() for(var/t in L) - to_chat(usr, "[t]") + to_chat(usr, "[t]", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Contents") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/toggle_view_range() @@ -773,14 +773,14 @@ Traitors and the like can also be revived with the previous role mostly intact. set desc = "Make everyone have a random appearance. You can only use this before rounds!" if(SSticker.HasRoundStarted()) - to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") + to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!", confidential = TRUE) return var/frn = CONFIG_GET(flag/force_random_names) if(frn) CONFIG_SET(flag/force_random_names, FALSE) message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.") - to_chat(usr, "Disabled.") + to_chat(usr, "Disabled.", confidential = TRUE) return @@ -792,9 +792,9 @@ Traitors and the like can also be revived with the previous role mostly intact. message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.") if(notifyplayers == "Yes") - to_chat(world, "Admin [usr.key] has forced the players to have completely random identities!") + to_chat(world, "Admin [usr.key] has forced the players to have completely random identities!", confidential = TRUE) - to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") + to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.", confidential = TRUE) CONFIG_SET(flag/force_random_names, TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Make Everyone Random") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -807,10 +807,10 @@ Traitors and the like can also be revived with the previous role mostly intact. var/new_are = !CONFIG_GET(flag/allow_random_events) CONFIG_SET(flag/allow_random_events, new_are) if(new_are) - to_chat(usr, "Random events enabled") + to_chat(usr, "Random events enabled", confidential = TRUE) message_admins("Admin [key_name_admin(usr)] has enabled random events.") else - to_chat(usr, "Random events disabled") + to_chat(usr, "Random events disabled", confidential = TRUE) message_admins("Admin [key_name_admin(usr)] has disabled random events.") SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Random Events", "[new_are ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -874,7 +874,7 @@ Traitors and the like can also be revived with the previous role mostly intact. mob.update_sight() - to_chat(usr, "You toggled your admin combo HUD [adding_hud ? "ON" : "OFF"].") + to_chat(usr, "You toggled your admin combo HUD [adding_hud ? "ON" : "OFF"].", confidential = TRUE) message_admins("[key_name_admin(usr)] toggled their admin combo HUD [adding_hud ? "ON" : "OFF"].") log_admin("[key_name(usr)] toggled their admin combo HUD [adding_hud ? "ON" : "OFF"].") SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Combo HUD", "[adding_hud ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -1076,7 +1076,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if(ishuman(target)) var/mob/living/carbon/human/H = target H.electrocution_animation(40) - to_chat(target, "The gods have punished you for your sins!") + to_chat(target, "The gods have punished you for your sins!", confidential = TRUE) if(ADMIN_PUNISHMENT_BRAINDAMAGE) target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 199, 199) if(ADMIN_PUNISHMENT_GIB) @@ -1123,7 +1123,7 @@ Traitors and the like can also be revived with the previous role mostly intact. return //We return here because punish_log() is handled by the centcom_podlauncher datum if(ADMIN_PUNISHMENT_MAZING) if(!puzzle_imprison(target)) - to_chat(usr,"Imprisonment failed!") + to_chat(usr,"Imprisonment failed!", confidential = TRUE) return if(ADMIN_PUNISHMENT_IMMERSE) immerse_player(target) @@ -1156,7 +1156,7 @@ Traitors and the like can also be revived with the previous role mostly intact. return if(!CONFIG_GET(flag/use_exp_tracking)) - to_chat(usr, "Tracking is disabled in the server configuration file.") + to_chat(usr, "Tracking is disabled in the server configuration file.", confidential = TRUE) return var/list/msg = list() @@ -1170,10 +1170,10 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!check_rights(R_ADMIN)) return if(!C) - to_chat(usr, "ERROR: Client not found.") + to_chat(usr, "ERROR: Client not found.", confidential = TRUE) return if(!CONFIG_GET(flag/use_exp_tracking)) - to_chat(usr, "Tracking is disabled in the server configuration file.") + to_chat(usr, "Tracking is disabled in the server configuration file.", confidential = TRUE) return var/list/body = list() @@ -1187,11 +1187,11 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!check_rights(R_ADMIN)) return if(!C) - to_chat(usr, "ERROR: Client not found.") + to_chat(usr, "ERROR: Client not found.", confidential = TRUE) return if(!C.set_db_player_flags()) - to_chat(usr, "ERROR: Unable read player flags from database. Please check logs.") + to_chat(usr, "ERROR: Unable read player flags from database. Please check logs.", confidential = TRUE) var/dbflags = C.prefs.db_flags var/newstate = FALSE if(dbflags & DB_FLAG_EXEMPT) @@ -1200,7 +1200,7 @@ Traitors and the like can also be revived with the previous role mostly intact. newstate = TRUE if(C.update_flag_db(DB_FLAG_EXEMPT, newstate)) - to_chat(usr, "ERROR: Unable to update player flags. Please check logs.") + to_chat(usr, "ERROR: Unable to update player flags. Please check logs.", confidential = TRUE) else message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]") log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]") diff --git a/code/modules/admin/verbs/reestablish_db_connection.dm b/code/modules/admin/verbs/reestablish_db_connection.dm index 438c1b9185a3..0e5af33fb728 100644 --- a/code/modules/admin/verbs/reestablish_db_connection.dm +++ b/code/modules/admin/verbs/reestablish_db_connection.dm @@ -2,7 +2,7 @@ set category = "Server" set name = "Reestablish DB Connection" if (!CONFIG_GET(flag/sql_enabled)) - to_chat(usr, "The Database is not enabled!") + to_chat(usr, "The Database is not enabled!", confidential = TRUE) return if (SSdbcore.IsConnected()) diff --git a/code/modules/admin/verbs/spawnobjasmob.dm b/code/modules/admin/verbs/spawnobjasmob.dm index 621e6c361890..825775808db2 100644 --- a/code/modules/admin/verbs/spawnobjasmob.dm +++ b/code/modules/admin/verbs/spawnobjasmob.dm @@ -38,7 +38,7 @@ basemob = text2path(mainsettings["mobtype"]["value"]) if (!ispath(basemob, /mob/living/simple_animal/hostile/mimic/copy) || !ispath(chosen_obj, /obj)) - to_chat(usr, "Mob or object path invalid") + to_chat(usr, "Mob or object path invalid", confidential = TRUE) basemob = new basemob(get_turf(usr), new chosen_obj(get_turf(usr)), usr, mainsettings["dropitem"]["value"] == "Yes" ? FALSE : TRUE, (mainsettings["googlyeyes"]["value"] == "Yes" ? FALSE : TRUE)) diff --git a/code/modules/admin/verbs/tripAI.dm b/code/modules/admin/verbs/tripAI.dm index 1bf4b6f0a63d..38221c386613 100644 --- a/code/modules/admin/verbs/tripAI.dm +++ b/code/modules/admin/verbs/tripAI.dm @@ -1,20 +1,15 @@ /client/proc/triple_ai() set category = "Admin - Events" - set name = "Create AI Triumvirate" + set name = "Toggle 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.") + to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.", confidential = TRUE) return var/datum/job/job = SSjob.GetJob("AI") if(!job) - to_chat(usr, "Unable to locate the AI job") + to_chat(usr, "Unable to locate the AI job", confidential = TRUE) 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.") - 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.") + SSticker.triai = !SSticker.triai + to_chat(usr, "There will [SSticker.triai ? "" : "not"] be an AI Triumvirate at round start.") + message_admins("[key_name_admin(usr)] has toggled [SSticker.triai ? "on" : "off"] triple AIs at round start.") diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm index 23f85cba1568..6a75ba301938 100644 --- a/code/modules/admin/view_variables/debug_variables.dm +++ b/code/modules/admin/view_variables/debug_variables.dm @@ -1,4 +1,5 @@ #define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) +/// Get displayed variable in VV variable list /proc/debug_variable(name, value, level, datum/D, sanitize = TRUE) //if D is a list, name will be index, and value will be assoc value. var/header if(D) @@ -35,6 +36,17 @@ else if (isfile(value)) item = "[VV_HTML_ENCODE(name)] = '[value]'" + else if(istype(value,/matrix)) // Needs to be before datum + var/matrix/M = value + item = {"[VV_HTML_ENCODE(name)] = +
      + + + + + + +
    [M.a][M.d]0
    [M.b][M.e]0
    [M.c][M.f]1
     
    "} //TODO link to modify_transform wrapper for all matrices else if (istype(value, /datum)) var/datum/DV = value if ("[DV]" != "[DV.type]") //if the thing as a name var, lets use it. diff --git a/code/modules/admin/view_variables/mass_edit_variables.dm b/code/modules/admin/view_variables/mass_edit_variables.dm index e29580b3b27d..636700cf50a4 100644 --- a/code/modules/admin/view_variables/mass_edit_variables.dm +++ b/code/modules/admin/view_variables/mass_edit_variables.dm @@ -38,7 +38,7 @@ 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.") + to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.", confidential = TRUE) return if(variable in GLOB.VVlocked) if(!check_rights(R_DEBUG)) @@ -56,11 +56,11 @@ default = vv_get_class(variable, var_value) if(isnull(default)) - to_chat(src, "Unable to determine variable type.") + to_chat(src, "Unable to determine variable type.", confidential = TRUE) else - to_chat(src, "Variable appears to be [uppertext(default)].") + to_chat(src, "Variable appears to be [uppertext(default)].", confidential = TRUE) - to_chat(src, "Variable contains: [var_value]") + to_chat(src, "Variable contains: [var_value]", confidential = TRUE) if(default == VV_NUM) var/dir_text = "" @@ -75,7 +75,7 @@ dir_text += "WEST" if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") + to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE) var/value = vv_get_value(default_class = default) var/new_value = value["value"] @@ -97,9 +97,9 @@ switch(class) if(VV_RESTORE_DEFAULT) - to_chat(src, "Finding items...") + to_chat(src, "Finding items...", confidential = TRUE) var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") + to_chat(src, "Changing [items.len] items...", confidential = TRUE) for(var/thing in items) if (!thing) continue @@ -123,9 +123,9 @@ for(var/V in varsvars) new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]") - to_chat(src, "Finding items...") + to_chat(src, "Finding items...", confidential = TRUE) var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") + to_chat(src, "Changing [items.len] items...", confidential = TRUE) for(var/thing in items) if (!thing) continue @@ -151,9 +151,9 @@ many = FALSE var/type = value["type"] - to_chat(src, "Finding items...") + to_chat(src, "Finding items...", confidential = TRUE) var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") + to_chat(src, "Changing [items.len] items...", confidential = TRUE) for(var/thing in items) if (!thing) continue @@ -169,9 +169,9 @@ CHECK_TICK else - to_chat(src, "Finding items...") + to_chat(src, "Finding items...", confidential = TRUE) var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") + to_chat(src, "Changing [items.len] items...", confidential = TRUE) for(var/thing in items) if (!thing) continue @@ -185,13 +185,13 @@ var/count = rejected+accepted if (!count) - to_chat(src, "No objects found") + to_chat(src, "No objects found", confidential = TRUE) return if (!accepted) - to_chat(src, "Every object rejected your edit") + to_chat(src, "Every object rejected your edit", confidential = TRUE) return if (rejected) - to_chat(src, "[rejected] out of [count] objects rejected your edit") + to_chat(src, "[rejected] out of [count] objects rejected your edit", confidential = TRUE) 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)") diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm index de7d86c5e757..acd926bbb920 100644 --- a/code/modules/admin/view_variables/modify_variables.dm +++ b/code/modules/admin/view_variables/modify_variables.dm @@ -102,7 +102,7 @@ GLOBAL_PROTECT(VVpixelmovement) L[var_value] = mod_list_add_ass(O) //hehe if (O) if (O.vv_edit_var(objectvar, L) == FALSE) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) 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]") @@ -112,7 +112,7 @@ GLOBAL_PROTECT(VVpixelmovement) if(!check_rights(R_VAREDIT)) return if(!istype(L, /list)) - to_chat(src, "Not a List.") + to_chat(src, "Not a List.", confidential = TRUE) return if(L.len > 1000) @@ -144,7 +144,7 @@ GLOBAL_PROTECT(VVpixelmovement) L = L.Copy() listclearnulls(L) if (!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS") log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS") @@ -154,7 +154,7 @@ GLOBAL_PROTECT(VVpixelmovement) if(variable == "(CLEAR DUPES)") L = uniqueList(L) if (!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES") log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES") @@ -164,7 +164,7 @@ GLOBAL_PROTECT(VVpixelmovement) if(variable == "(SHUFFLE)") L = shuffle(L) if (!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE") log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE") @@ -201,9 +201,9 @@ GLOBAL_PROTECT(VVpixelmovement) default = vv_get_class(objectvar, variable) - to_chat(src, "Variable appears to be [uppertext(default)].") + to_chat(src, "Variable appears to be [uppertext(default)].", confidential = TRUE) - to_chat(src, "Variable contains: [variable]") + to_chat(src, "Variable contains: [variable]", confidential = TRUE) if(default == VV_NUM) var/dir_text = "" @@ -219,7 +219,7 @@ GLOBAL_PROTECT(VVpixelmovement) dir_text += "WEST" if(dir_text) - to_chat(usr, "If a direction, direction is: [dir_text]") + to_chat(usr, "If a direction, direction is: [dir_text]", confidential = TRUE) var/original_var = variable @@ -247,7 +247,7 @@ GLOBAL_PROTECT(VVpixelmovement) L.Cut(index, index+1) if (O) if (O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) 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]") @@ -269,7 +269,7 @@ GLOBAL_PROTECT(VVpixelmovement) L[new_var] = old_assoc_value if (O) if (O.vv_edit_var(objectvar, L) == FALSE) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) 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]") @@ -297,7 +297,7 @@ GLOBAL_PROTECT(VVpixelmovement) 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])") + to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])", confidential = TRUE) return variable = param_var_name @@ -322,11 +322,11 @@ GLOBAL_PROTECT(VVpixelmovement) var/default = vv_get_class(variable, var_value) if(isnull(default)) - to_chat(src, "Unable to determine variable type.") + to_chat(src, "Unable to determine variable type.", confidential = TRUE) else - to_chat(src, "Variable appears to be [uppertext(default)].") + to_chat(src, "Variable appears to be [uppertext(default)].", confidential = TRUE) - to_chat(src, "Variable contains: [var_value]") + to_chat(src, "Variable contains: [var_value]", confidential = TRUE) if(default == VV_NUM) var/dir_text = "" @@ -341,7 +341,7 @@ GLOBAL_PROTECT(VVpixelmovement) dir_text += "WEST" if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") + to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE) if(autodetect_class && default != VV_NULL) if (default == VV_TEXT) @@ -378,7 +378,7 @@ GLOBAL_PROTECT(VVpixelmovement) if (O.vv_edit_var(variable, var_new) == FALSE) - to_chat(src, "Your edit was rejected by the object.") + to_chat(src, "Your edit was rejected by the object.", confidential = TRUE) return vv_update_display(O, "varedited", VV_MSG_EDITED) log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]") diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index b72256e41aae..d1745b87d3ca 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -25,7 +25,7 @@ var/mob/M = locate(href_list["rename"]) in GLOB.mob_list if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") + to_chat(usr, "This can only be used on instances of type /mob", confidential = TRUE) return var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN) @@ -43,7 +43,7 @@ var/atom/A = locate(href_list["rotatedatum"]) if(!istype(A)) - to_chat(usr, "This can only be done to instances of type /atom") + to_chat(usr, "This can only be done to instances of type /atom", confidential = TRUE) return switch(href_list["rotatedir"]) @@ -60,13 +60,13 @@ var/mob/living/carbon/monkey/Mo = locate(href_list["makehuman"]) in GLOB.mob_list if(!istype(Mo)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey") + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey", confidential = TRUE) return if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return if(!Mo) - to_chat(usr, "Mob doesn't exist anymore") + to_chat(usr, "Mob doesn't exist anymore", confidential = TRUE) return holder.Topic(href, list("humanone"=href_list["makehuman"])) @@ -86,7 +86,7 @@ return if(!L) - to_chat(usr, "Mob doesn't exist anymore") + to_chat(usr, "Mob doesn't exist anymore", confidential = TRUE) return var/newamt @@ -113,7 +113,7 @@ L.adjustStaminaLoss(amount) newamt = L.getStaminaLoss() else - to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") + to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]", confidential = TRUE) return if(amount != 0) diff --git a/code/modules/admin/view_variables/topic_basic.dm b/code/modules/admin/view_variables/topic_basic.dm index 79d7379b8bf4..0e27f46f8323 100644 --- a/code/modules/admin/view_variables/topic_basic.dm +++ b/code/modules/admin/view_variables/topic_basic.dm @@ -34,11 +34,11 @@ if (!C) return if(!target) - to_chat(usr, "The object you tried to expose to [C] no longer exists (nulled or hard-deled)") + to_chat(usr, "The object you tried to expose to [C] no longer exists (nulled or hard-deled)", confidential = TRUE) return message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a VV window") log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [target]") - to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window") + to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window", confidential = TRUE) C.debug_variables(target) if(check_rights(R_DEBUG)) if(href_list[VV_HK_DELETE]) @@ -60,7 +60,7 @@ if(!usr || !result || result == "---Components---" || result == "---Elements---") return if(QDELETED(src)) - to_chat(usr, "That thing doesn't exist anymore!") + to_chat(usr, "That thing doesn't exist anymore!", confidential = TRUE) return var/list/lst = get_callproc_args() if(!lst) diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index 06b584d15b6f..472ee5271d45 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -5,7 +5,7 @@ var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. if(!usr.client || !usr.client.holder) //This is usr because admins can call the proc on other clients, even if they're not admins, to show them VVs. - to_chat(usr, "You need to be an administrator to access this.") + to_chat(usr, "You need to be an administrator to access this.", confidential = TRUE) return if(!D) @@ -94,16 +94,7 @@ [title] - + "}, "window=asset_cache_browser&file=asset_cache_send_verify.htm") + + while(!completed_asset_jobs["[job]"] && t < timeout_time) // Reception is handled in Topic() + stoplag(1) // Lock up the caller until this is received. + t++ + if (t < timeout_time) + return TRUE diff --git a/code/modules/asset_cache/asset_cache_item.dm b/code/modules/asset_cache/asset_cache_item.dm new file mode 100644 index 000000000000..c705a1733334 --- /dev/null +++ b/code/modules/asset_cache/asset_cache_item.dm @@ -0,0 +1,21 @@ +/** + * # asset_cache_item + * + * An internal datum containing info on items in the asset cache. Mainly used to cache md5 info for speed. +**/ +/datum/asset_cache_item + var/name + var/md5 + var/resource + +/datum/asset_cache_item/New(name, file) + if (!isfile(file)) + file = fcopy_rsc(file) + md5 = md5(file) + if (!md5) + md5 = md5(fcopy_rsc(file)) + if (!md5) + CRASH("invalid asset sent to asset cache") + debug_world_log("asset cache unexpected success of second fcopy_rsc") + src.name = name + resource = file diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm new file mode 100644 index 000000000000..2e5881c67f30 --- /dev/null +++ b/code/modules/asset_cache/asset_list.dm @@ -0,0 +1,230 @@ + +//These datums are used to populate the asset cache, the proc "register()" does this. +//Place any asset datums you create in asset_list_items.dm + +//all of our asset datums, used for referring to these later +GLOBAL_LIST_EMPTY(asset_datums) + +//get an assetdatum or make a new one +/proc/get_asset_datum(type) + return GLOB.asset_datums[type] || new type() + +/datum/asset + var/_abstract = /datum/asset + +/datum/asset/New() + GLOB.asset_datums[type] = src + register() + +/datum/asset/proc/register() + return + +/datum/asset/proc/send(client) + return + + +//If you don't need anything complicated. +/datum/asset/simple + _abstract = /datum/asset/simple + var/assets = list() + +/datum/asset/simple/register() + for(var/asset_name in assets) + register_asset(asset_name, assets[asset_name]) + +/datum/asset/simple/send(client) + . = send_asset_list(client, assets) + + +// For registering or sending multiple others at once +/datum/asset/group + _abstract = /datum/asset/group + var/list/children + +/datum/asset/group/register() + for(var/type in children) + get_asset_datum(type) + +/datum/asset/group/send(client/C) + for(var/type in children) + var/datum/asset/A = get_asset_datum(type) + . = A.send(C) || . + + +// spritesheet implementation - coalesces various icons into a single .png file +// and uses CSS to select icons out of that file - saves on transferring some +// 1400-odd individual PNG files +#define SPR_SIZE 1 +#define SPR_IDX 2 +#define SPRSZ_COUNT 1 +#define SPRSZ_ICON 2 +#define SPRSZ_STRIPPED 3 + +/datum/asset/spritesheet + _abstract = /datum/asset/spritesheet + var/name + var/list/sizes = list() // "32x32" -> list(10, icon/normal, icon/stripped) + var/list/sprites = list() // "foo_bar" -> list("32x32", 5) + +/datum/asset/spritesheet/register() + if (!name) + CRASH("spritesheet [type] cannot register without a name") + ensure_stripped() + + var/res_name = "spritesheet_[name].css" + var/fname = "data/spritesheets/[res_name]" + fdel(fname) + text2file(generate_css(), fname) + register_asset(res_name, fcopy_rsc(fname)) + fdel(fname) + + for(var/size_id in sizes) + var/size = sizes[size_id] + register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED]) + +/datum/asset/spritesheet/send(client/C) + if (!name) + return + var/all = list("spritesheet_[name].css") + for(var/size_id in sizes) + all += "[name]_[size_id].png" + . = send_asset_list(C, all) + +/datum/asset/spritesheet/proc/ensure_stripped(sizes_to_strip = sizes) + for(var/size_id in sizes_to_strip) + var/size = sizes[size_id] + if (size[SPRSZ_STRIPPED]) + continue + + // save flattened version + var/fname = "data/spritesheets/[name]_[size_id].png" + fcopy(size[SPRSZ_ICON], fname) + var/error = rustg_dmi_strip_metadata(fname) + if(length(error)) + stack_trace("Failed to strip [name]_[size_id].png: [error]") + size[SPRSZ_STRIPPED] = icon(fname) + fdel(fname) + +/datum/asset/spritesheet/proc/generate_css() + var/list/out = list() + + for (var/size_id in sizes) + var/size = sizes[size_id] + var/icon/tiny = size[SPRSZ_ICON] + out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[name]_[size_id].png') no-repeat;}" + + for (var/sprite_id in sprites) + var/sprite = sprites[sprite_id] + var/size_id = sprite[SPR_SIZE] + var/idx = sprite[SPR_IDX] + var/size = sizes[size_id] + + var/icon/tiny = size[SPRSZ_ICON] + var/icon/big = size[SPRSZ_STRIPPED] + var/per_line = big.Width() / tiny.Width() + var/x = (idx % per_line) * tiny.Width() + var/y = round(idx / per_line) * tiny.Height() + + out += ".[name][size_id].[sprite_id]{background-position:-[x]px -[y]px;}" + + return out.Join("\n") + +/datum/asset/spritesheet/proc/Insert(sprite_name, icon/I, icon_state="", dir=SOUTH, frame=1, moving=FALSE) + I = icon(I, icon_state=icon_state, dir=dir, frame=frame, moving=moving) + if (!I || !length(icon_states(I))) // that direction or state doesn't exist + return + var/size_id = "[I.Width()]x[I.Height()]" + var/size = sizes[size_id] + + if (sprites[sprite_name]) + CRASH("duplicate sprite \"[sprite_name]\" in sheet [name] ([type])") + + if (size) + var/position = size[SPRSZ_COUNT]++ + var/icon/sheet = size[SPRSZ_ICON] + size[SPRSZ_STRIPPED] = null + sheet.Insert(I, icon_state=sprite_name) + sprites[sprite_name] = list(size_id, position) + else + sizes[size_id] = size = list(1, I, null) + sprites[sprite_name] = list(size_id, 0) + +/datum/asset/spritesheet/proc/InsertAll(prefix, icon/I, list/directions) + if (length(prefix)) + prefix = "[prefix]-" + + if (!directions) + directions = list(SOUTH) + + for (var/icon_state_name in icon_states(I)) + for (var/direction in directions) + var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]-" : "" + Insert("[prefix][prefix2][icon_state_name]", I, icon_state=icon_state_name, dir=direction) + +/datum/asset/spritesheet/proc/css_tag() + return {""} + +/datum/asset/spritesheet/proc/icon_tag(sprite_name) + var/sprite = sprites[sprite_name] + if (!sprite) + return null + var/size_id = sprite[SPR_SIZE] + return {""} + +/datum/asset/spritesheet/proc/icon_class_name(sprite_name) + var/sprite = sprites[sprite_name] + if (!sprite) + return null + var/size_id = sprite[SPR_SIZE] + return {"[name][size_id] [sprite_name]"} + +#undef SPR_SIZE +#undef SPR_IDX +#undef SPRSZ_COUNT +#undef SPRSZ_ICON +#undef SPRSZ_STRIPPED + + +/datum/asset/spritesheet/simple + _abstract = /datum/asset/spritesheet/simple + var/list/assets + +/datum/asset/spritesheet/simple/register() + for (var/key in assets) + Insert(key, assets[key]) + ..() + +//Generates assets based on iconstates of a single icon +/datum/asset/simple/icon_states + _abstract = /datum/asset/simple/icon_states + var/icon + var/list/directions = list(SOUTH) + var/frame = 1 + var/movement_states = FALSE + + var/prefix = "default" //asset_name = "[prefix].[icon_state_name].png" + var/generic_icon_names = FALSE //generate icon filenames using generate_asset_name() instead the above format + +/datum/asset/simple/icon_states/register(_icon = icon) + for(var/icon_state_name in icon_states(_icon)) + for(var/direction in directions) + var/asset = icon(_icon, icon_state_name, direction, frame, movement_states) + if (!asset) + continue + asset = fcopy_rsc(asset) //dedupe + var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]." : "" + var/asset_name = sanitize_filename("[prefix].[prefix2][icon_state_name].png") + if (generic_icon_names) + asset_name = "[generate_asset_name(asset)].png" + + register_asset(asset_name, asset) + +/datum/asset/simple/icon_states/multiple_icons + _abstract = /datum/asset/simple/icon_states/multiple_icons + var/list/icons + +/datum/asset/simple/icon_states/multiple_icons/register() + for(var/i in icons) + ..(i) + + diff --git a/code/modules/client/asset_cache.dm b/code/modules/asset_cache/asset_list_items.dm similarity index 52% rename from code/modules/client/asset_cache.dm rename to code/modules/asset_cache/asset_list_items.dm index 40bb4c024515..d50263b9a45a 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -1,767 +1,391 @@ -/* -Asset cache quick users guide: - -Make a datum at the bottom of this file with your assets for your thing. -The simple subsystem will most like be of use for most cases. -Then call get_asset_datum() with the type of the datum you created and store the return -Then call .send(client) on that stored return value. - -You can set verify to TRUE if you want send() to sleep until the client has the assets. -*/ - - -// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping. -// This is doubled for the first asset, then added per asset after -#define ASSET_CACHE_SEND_TIMEOUT 7 - -//When sending mutiple assets, how many before we give the client a quaint little sending resources message -#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8 - -//When passively preloading assets, how many to send at once? Too high creates noticable lag where as too low can flood the client's cache with "verify" files -#define ASSET_CACHE_PRELOAD_CONCURRENT 3 - -/client - var/list/cache = list() // List of all assets sent to this client by the asset cache. - var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement. - var/list/sending = list() - var/last_asset_job = 0 // Last job done. - -//This proc sends the asset to the client, but only if it needs it. -//This proc blocks(sleeps) unless verify is set to false -/proc/send_asset(client/client, asset_name, verify = TRUE) - if(!istype(client)) - if(ismob(client)) - var/mob/M = client - if(M.client) - client = M.client - - else - return 0 - - else - return 0 - - if(client.cache.Find(asset_name) || client.sending.Find(asset_name)) - return 0 - - log_asset("Sending asset [asset_name] to client [client]") - client << browse_rsc(SSassets.cache[asset_name], asset_name) - if(!verify) - client.cache += asset_name - return 1 - - client.sending |= asset_name - var/job = ++client.last_asset_job - - client << browse({" - - "}, "window=asset_cache_browser") - - var/t = 0 - var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT - while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() - stoplag(1) // Lock up the caller until this is received. - t++ - - if(client) - client.sending -= asset_name - client.cache |= asset_name - client.completed_asset_jobs -= job - - return 1 - -//This proc blocks(sleeps) unless verify is set to false -/proc/send_asset_list(client/client, list/asset_list, verify = TRUE) - if(!istype(client)) - if(ismob(client)) - var/mob/M = client - if(M.client) - client = M.client - - else - return 0 - - else - return 0 - - var/list/unreceived = asset_list - (client.cache + client.sending) - if(!unreceived || !unreceived.len) - return 0 - if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT) - to_chat(client, "Sending Resources...") - for(var/asset in unreceived) - if (asset in SSassets.cache) - log_asset("Sending asset [asset] to client [client]") - client << browse_rsc(SSassets.cache[asset], asset) - - if(!verify) // Can't access the asset cache browser, rip. - client.cache += unreceived - return 1 - - client.sending |= unreceived - var/job = ++client.last_asset_job - - client << browse({" - - "}, "window=asset_cache_browser") - - var/t = 0 - var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len - while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() - stoplag(1) // Lock up the caller until this is received. - t++ - - if(client) - client.sending -= unreceived - client.cache |= unreceived - client.completed_asset_jobs -= job - - return 1 - -//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start. -//The proc calls procs that sleep for long times. -/proc/getFilesSlow(client/client, list/files, register_asset = TRUE) - var/concurrent_tracker = 1 - for(var/file in files) - if (!client) - break - if (register_asset) - register_asset(file, files[file]) - if (concurrent_tracker >= ASSET_CACHE_PRELOAD_CONCURRENT) - concurrent_tracker = 1 - send_asset(client, file) - else - concurrent_tracker++ - send_asset(client, file, verify=FALSE) - - stoplag(0) //queuing calls like this too quickly can cause issues in some client versions - -//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. -//if it's an icon or something be careful, you'll have to copy it before further use. -/proc/register_asset(asset_name, asset) - SSassets.cache[asset_name] = asset - -//Generated names do not include file extention. -//Used mainly for code that deals with assets in a generic way -//The same asset will always lead to the same asset name -/proc/generate_asset_name(file) - return "asset.[md5(fcopy_rsc(file))]" - - -//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 -GLOBAL_LIST_EMPTY(asset_datums) - -//get an assetdatum or make a new one -/proc/get_asset_datum(type) - return GLOB.asset_datums[type] || new type() - -/datum/asset - var/_abstract = /datum/asset - -/datum/asset/New() - GLOB.asset_datums[type] = src - register() - -/datum/asset/proc/register() - return - -/datum/asset/proc/send(client) - return - - -//If you don't need anything complicated. -/datum/asset/simple - _abstract = /datum/asset/simple - var/assets = list() - var/verify = FALSE - -/datum/asset/simple/register() - for(var/asset_name in assets) - register_asset(asset_name, assets[asset_name]) - -/datum/asset/simple/send(client) - send_asset_list(client,assets,verify) - - -// For registering or sending multiple others at once -/datum/asset/group - _abstract = /datum/asset/group - var/list/children - -/datum/asset/group/register() - for(var/type in children) - get_asset_datum(type) - -/datum/asset/group/send(client/C) - for(var/type in children) - var/datum/asset/A = get_asset_datum(type) - A.send(C) - - -// spritesheet implementation - coalesces various icons into a single .png file -// and uses CSS to select icons out of that file - saves on transferring some -// 1400-odd individual PNG files -#define SPR_SIZE 1 -#define SPR_IDX 2 -#define SPRSZ_COUNT 1 -#define SPRSZ_ICON 2 -#define SPRSZ_STRIPPED 3 - -/datum/asset/spritesheet - _abstract = /datum/asset/spritesheet - var/name - var/list/sizes = list() // "32x32" -> list(10, icon/normal, icon/stripped) - var/list/sprites = list() // "foo_bar" -> list("32x32", 5) - var/verify = FALSE - -/datum/asset/spritesheet/register() - if (!name) - CRASH("spritesheet [type] cannot register without a name") - ensure_stripped() - - var/res_name = "spritesheet_[name].css" - var/fname = "data/spritesheets/[res_name]" - fdel(fname) - text2file(generate_css(), fname) - register_asset(res_name, fcopy_rsc(fname)) - fdel(fname) - - for(var/size_id in sizes) - var/size = sizes[size_id] - register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED]) - -/datum/asset/spritesheet/send(client/C) - if (!name) - return - var/all = list("spritesheet_[name].css") - for(var/size_id in sizes) - all += "[name]_[size_id].png" - send_asset_list(C, all, verify) - -/datum/asset/spritesheet/proc/ensure_stripped(sizes_to_strip = sizes) - for(var/size_id in sizes_to_strip) - var/size = sizes[size_id] - if (size[SPRSZ_STRIPPED]) - continue - - // save flattened version - var/fname = "data/spritesheets/[name]_[size_id].png" - fcopy(size[SPRSZ_ICON], fname) - var/error = rustg_dmi_strip_metadata(fname) - if(length(error)) - stack_trace("Failed to strip [name]_[size_id].png: [error]") - size[SPRSZ_STRIPPED] = icon(fname) - fdel(fname) - -/datum/asset/spritesheet/proc/generate_css() - var/list/out = list() - - for (var/size_id in sizes) - var/size = sizes[size_id] - var/icon/tiny = size[SPRSZ_ICON] - out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[name]_[size_id].png') no-repeat;}" - - for (var/sprite_id in sprites) - var/sprite = sprites[sprite_id] - var/size_id = sprite[SPR_SIZE] - var/idx = sprite[SPR_IDX] - var/size = sizes[size_id] - - var/icon/tiny = size[SPRSZ_ICON] - var/icon/big = size[SPRSZ_STRIPPED] - var/per_line = big.Width() / tiny.Width() - var/x = (idx % per_line) * tiny.Width() - var/y = round(idx / per_line) * tiny.Height() - - out += ".[name][size_id].[sprite_id]{background-position:-[x]px -[y]px;}" - - return out.Join("\n") - -/datum/asset/spritesheet/proc/Insert(sprite_name, icon/I, icon_state="", dir=SOUTH, frame=1, moving=FALSE) - I = icon(I, icon_state=icon_state, dir=dir, frame=frame, moving=moving) - if (!I || !length(icon_states(I))) // that direction or state doesn't exist - return - var/size_id = "[I.Width()]x[I.Height()]" - var/size = sizes[size_id] - - if (sprites[sprite_name]) - CRASH("duplicate sprite \"[sprite_name]\" in sheet [name] ([type])") - - if (size) - var/position = size[SPRSZ_COUNT]++ - var/icon/sheet = size[SPRSZ_ICON] - size[SPRSZ_STRIPPED] = null - sheet.Insert(I, icon_state=sprite_name) - sprites[sprite_name] = list(size_id, position) - else - sizes[size_id] = size = list(1, I, null) - sprites[sprite_name] = list(size_id, 0) - -/datum/asset/spritesheet/proc/InsertAll(prefix, icon/I, list/directions) - if (length(prefix)) - prefix = "[prefix]-" - - if (!directions) - directions = list(SOUTH) - - for (var/icon_state_name in icon_states(I)) - for (var/direction in directions) - var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]-" : "" - Insert("[prefix][prefix2][icon_state_name]", I, icon_state=icon_state_name, dir=direction) - -/datum/asset/spritesheet/proc/css_tag() - return {""} - -/datum/asset/spritesheet/proc/icon_tag(sprite_name) - var/sprite = sprites[sprite_name] - if (!sprite) - return null - var/size_id = sprite[SPR_SIZE] - return {""} - -/datum/asset/spritesheet/proc/icon_class_name(sprite_name) - var/sprite = sprites[sprite_name] - if (!sprite) - return null - var/size_id = sprite[SPR_SIZE] - return {"[name][size_id] [sprite_name]"} - -#undef SPR_SIZE -#undef SPR_IDX -#undef SPRSZ_COUNT -#undef SPRSZ_ICON -#undef SPRSZ_STRIPPED - - -/datum/asset/spritesheet/simple - _abstract = /datum/asset/spritesheet/simple - var/list/assets - -/datum/asset/spritesheet/simple/register() - for (var/key in assets) - Insert(key, assets[key]) - ..() - -//Generates assets based on iconstates of a single icon -/datum/asset/simple/icon_states - _abstract = /datum/asset/simple/icon_states - var/icon - var/list/directions = list(SOUTH) - var/frame = 1 - var/movement_states = FALSE - - var/prefix = "default" //asset_name = "[prefix].[icon_state_name].png" - var/generic_icon_names = FALSE //generate icon filenames using generate_asset_name() instead the above format - - verify = FALSE - -/datum/asset/simple/icon_states/register(_icon = icon) - for(var/icon_state_name in icon_states(_icon)) - for(var/direction in directions) - var/asset = icon(_icon, icon_state_name, direction, frame, movement_states) - if (!asset) - continue - asset = fcopy_rsc(asset) //dedupe - var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]." : "" - var/asset_name = sanitize_filename("[prefix].[prefix2][icon_state_name].png") - if (generic_icon_names) - asset_name = "[generate_asset_name(asset)].png" - - register_asset(asset_name, asset) - -/datum/asset/simple/icon_states/multiple_icons - _abstract = /datum/asset/simple/icon_states/multiple_icons - var/list/icons - -/datum/asset/simple/icon_states/multiple_icons/register() - for(var/i in icons) - ..(i) - - -//DEFINITIONS FOR ASSET DATUMS START HERE. - -/datum/asset/simple/tgui - assets = list( - "tgui.bundle.js" = 'tgui/packages/tgui/public/tgui.bundle.js', - "tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css', - "shim-html5shiv.js" = 'tgui/packages/tgui/public/shim-html5shiv.js', - "shim-ie8.js" = 'tgui/packages/tgui/public/shim-ie8.js', - "shim-dom4.js" = 'tgui/packages/tgui/public/shim-dom4.js', - "shim-css-om.js" = 'tgui/packages/tgui/public/shim-css-om.js', - ) - -/datum/asset/group/tgui - children = list( - /datum/asset/simple/tgui, - /datum/asset/simple/fontawesome - ) - -/datum/asset/simple/headers - assets = list( - "alarm_green.gif" = 'icons/program_icons/alarm_green.gif', - "alarm_red.gif" = 'icons/program_icons/alarm_red.gif', - "batt_5.gif" = 'icons/program_icons/batt_5.gif', - "batt_20.gif" = 'icons/program_icons/batt_20.gif', - "batt_40.gif" = 'icons/program_icons/batt_40.gif', - "batt_60.gif" = 'icons/program_icons/batt_60.gif', - "batt_80.gif" = 'icons/program_icons/batt_80.gif', - "batt_100.gif" = 'icons/program_icons/batt_100.gif', - "charging.gif" = 'icons/program_icons/charging.gif', - "downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif', - "downloader_running.gif" = 'icons/program_icons/downloader_running.gif', - "ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif', - "ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif', - "power_norm.gif" = 'icons/program_icons/power_norm.gif', - "power_warn.gif" = 'icons/program_icons/power_warn.gif', - "sig_high.gif" = 'icons/program_icons/sig_high.gif', - "sig_low.gif" = 'icons/program_icons/sig_low.gif', - "sig_lan.gif" = 'icons/program_icons/sig_lan.gif', - "sig_none.gif" = 'icons/program_icons/sig_none.gif', - "smmon_0.gif" = 'icons/program_icons/smmon_0.gif', - "smmon_1.gif" = 'icons/program_icons/smmon_1.gif', - "smmon_2.gif" = 'icons/program_icons/smmon_2.gif', - "smmon_3.gif" = 'icons/program_icons/smmon_3.gif', - "smmon_4.gif" = 'icons/program_icons/smmon_4.gif', - "smmon_5.gif" = 'icons/program_icons/smmon_5.gif', - "smmon_6.gif" = 'icons/program_icons/smmon_6.gif' - ) - -/datum/asset/spritesheet/simple/pda - name = "pda" - assets = list( - "atmos" = 'icons/pda_icons/pda_atmos.png', - "back" = 'icons/pda_icons/pda_back.png', - "bell" = 'icons/pda_icons/pda_bell.png', - "blank" = 'icons/pda_icons/pda_blank.png', - "boom" = 'icons/pda_icons/pda_boom.png', - "bucket" = 'icons/pda_icons/pda_bucket.png', - "medbot" = 'icons/pda_icons/pda_medbot.png', - "floorbot" = 'icons/pda_icons/pda_floorbot.png', - "cleanbot" = 'icons/pda_icons/pda_cleanbot.png', - "crate" = 'icons/pda_icons/pda_crate.png', - "cuffs" = 'icons/pda_icons/pda_cuffs.png', - "eject" = 'icons/pda_icons/pda_eject.png', - "flashlight" = 'icons/pda_icons/pda_flashlight.png', - "honk" = 'icons/pda_icons/pda_honk.png', - "mail" = 'icons/pda_icons/pda_mail.png', - "medical" = 'icons/pda_icons/pda_medical.png', - "menu" = 'icons/pda_icons/pda_menu.png', - "mule" = 'icons/pda_icons/pda_mule.png', - "notes" = 'icons/pda_icons/pda_notes.png', - "power" = 'icons/pda_icons/pda_power.png', - "rdoor" = 'icons/pda_icons/pda_rdoor.png', - "reagent" = 'icons/pda_icons/pda_reagent.png', - "refresh" = 'icons/pda_icons/pda_refresh.png', - "scanner" = 'icons/pda_icons/pda_scanner.png', - "signaler" = 'icons/pda_icons/pda_signaler.png', - "status" = 'icons/pda_icons/pda_status.png', - "dronephone" = 'icons/pda_icons/pda_dronephone.png', - "emoji" = 'icons/pda_icons/pda_emoji.png' - ) - -/datum/asset/spritesheet/simple/paper - name = "paper" - assets = list( - "stamp-clown" = 'icons/stamp_icons/large_stamp-clown.png', - "stamp-deny" = 'icons/stamp_icons/large_stamp-deny.png', - "stamp-ok" = 'icons/stamp_icons/large_stamp-ok.png', - "stamp-hop" = 'icons/stamp_icons/large_stamp-hop.png', - "stamp-cmo" = 'icons/stamp_icons/large_stamp-cmo.png', - "stamp-ce" = 'icons/stamp_icons/large_stamp-ce.png', - "stamp-hos" = 'icons/stamp_icons/large_stamp-hos.png', - "stamp-rd" = 'icons/stamp_icons/large_stamp-rd.png', - "stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png', - "stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png', - "stamp-law" = 'icons/stamp_icons/large_stamp-law.png' - ) - - -/datum/asset/simple/IRV - assets = list( - "jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js', - ) - -/datum/asset/group/IRV - children = list( - /datum/asset/simple/jquery, - /datum/asset/simple/IRV - ) - -/datum/asset/simple/changelog - assets = list( - "88x31.png" = 'html/88x31.png', - "bug-minus.png" = 'html/bug-minus.png', - "cross-circle.png" = 'html/cross-circle.png', - "hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png', - "image-minus.png" = 'html/image-minus.png', - "image-plus.png" = 'html/image-plus.png', - "music-minus.png" = 'html/music-minus.png', - "music-plus.png" = 'html/music-plus.png', - "tick-circle.png" = 'html/tick-circle.png', - "wrench-screwdriver.png" = 'html/wrench-screwdriver.png', - "spell-check.png" = 'html/spell-check.png', - "burn-exclamation.png" = 'html/burn-exclamation.png', - "chevron.png" = 'html/chevron.png', - "chevron-expand.png" = 'html/chevron-expand.png', - "scales.png" = 'html/scales.png', - "coding.png" = 'html/coding.png', - "ban.png" = 'html/ban.png', - "chrome-wrench.png" = 'html/chrome-wrench.png', - "changelog.css" = 'html/changelog.css' - ) - -/datum/asset/group/goonchat - children = list( - /datum/asset/simple/jquery, - /datum/asset/simple/goonchat, - /datum/asset/spritesheet/goonchat, - /datum/asset/simple/fontawesome - ) - -/datum/asset/simple/jquery - verify = FALSE - assets = list( - "jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js', - ) - -/datum/asset/simple/goonchat - verify = FALSE - assets = list( - "json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js', - "browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js', - "browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css', - "browserOutput_white.css" = 'code/modules/goonchat/browserassets/css/browserOutput_white.css', - ) - -/datum/asset/simple/fontawesome - verify = FALSE - assets = list( - "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot', - "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff', - "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot', - "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff', - "font-awesome.css" = 'html/font-awesome/css/all.min.css', - "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css' - ) - -/datum/asset/simple/fonts - verify = FALSE - assets = list( - "sga.ttf" = 'html/sga.ttf' - ) - -/datum/asset/spritesheet/goonchat - name = "chat" - -/datum/asset/spritesheet/goonchat/register() - InsertAll("emoji", 'icons/emoji.dmi') - - // pre-loading all lanugage icons also helps to avoid meta - InsertAll("language", 'icons/misc/language.dmi') - // catch languages which are pulling icons from another file - for(var/path in typesof(/datum/language)) - var/datum/language/L = path - var/icon = initial(L.icon) - if (icon != 'icons/misc/language.dmi') - var/icon_state = initial(L.icon_state) - Insert("language-[icon_state]", icon, icon_state=icon_state) - - ..() - -/datum/asset/simple/permissions - assets = list( - "padlock.png" = 'html/padlock.png' - ) - -/datum/asset/simple/notes - assets = list( - "high_button.png" = 'html/high_button.png', - "medium_button.png" = 'html/medium_button.png', - "minor_button.png" = 'html/minor_button.png', - "none_button.png" = 'html/none_button.png', - ) - -/datum/asset/simple/arcade - assets = list( - "boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif', - "boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif', - "boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif', - "boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif', - "boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif', - "boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif', - ) - -/datum/asset/spritesheet/simple/achievements - name ="achievements" - assets = list( - "default" = 'icons/UI_Icons/Achievements/default.png', - "basemisc" = 'icons/UI_Icons/Achievements/basemisc.png', - "baseboss" = 'icons/UI_Icons/Achievements/baseboss.png', - "baseskill" = 'icons/UI_Icons/Achievements/baseskill.png', - "bbgum" = 'icons/UI_Icons/Achievements/Boss/bbgum.png', - "colossus" = 'icons/UI_Icons/Achievements/Boss/colossus.png', - "hierophant" = 'icons/UI_Icons/Achievements/Boss/hierophant.png', - "legion" = 'icons/UI_Icons/Achievements/Boss/legion.png', - "miner" = 'icons/UI_Icons/Achievements/Boss/miner.png', - "swarmer" = 'icons/UI_Icons/Achievements/Boss/swarmer.png', - "tendril" = 'icons/UI_Icons/Achievements/Boss/tendril.png', - "featofstrength" = 'icons/UI_Icons/Achievements/Misc/featofstrength.png', - "helbital" = 'icons/UI_Icons/Achievements/Misc/helbital.png', - "jackpot" = 'icons/UI_Icons/Achievements/Misc/jackpot.png', - "meteors" = 'icons/UI_Icons/Achievements/Misc/meteors.png', - "timewaste" = 'icons/UI_Icons/Achievements/Misc/timewaste.png', - "upgrade" = 'icons/UI_Icons/Achievements/Misc/upgrade.png', - "clownking" = 'icons/UI_Icons/Achievements/Misc/clownking.png', - "clownthanks" = 'icons/UI_Icons/Achievements/Misc/clownthanks.png', - "rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png', - "snail" = 'icons/UI_Icons/Achievements/Misc/snail.png', - "mining" = 'icons/UI_Icons/Achievements/Skills/mining.png', - ) - -/datum/asset/spritesheet/simple/pills - name ="pills" - assets = list( - "pill1" = 'icons/UI_Icons/Pills/pill1.png', - "pill2" = 'icons/UI_Icons/Pills/pill2.png', - "pill3" = 'icons/UI_Icons/Pills/pill3.png', - "pill4" = 'icons/UI_Icons/Pills/pill4.png', - "pill5" = 'icons/UI_Icons/Pills/pill5.png', - "pill6" = 'icons/UI_Icons/Pills/pill6.png', - "pill7" = 'icons/UI_Icons/Pills/pill7.png', - "pill8" = 'icons/UI_Icons/Pills/pill8.png', - "pill9" = 'icons/UI_Icons/Pills/pill9.png', - "pill10" = 'icons/UI_Icons/Pills/pill10.png', - "pill11" = 'icons/UI_Icons/Pills/pill11.png', - "pill12" = 'icons/UI_Icons/Pills/pill12.png', - "pill13" = 'icons/UI_Icons/Pills/pill13.png', - "pill14" = 'icons/UI_Icons/Pills/pill14.png', - "pill15" = 'icons/UI_Icons/Pills/pill15.png', - "pill16" = 'icons/UI_Icons/Pills/pill16.png', - "pill17" = 'icons/UI_Icons/Pills/pill17.png', - "pill18" = 'icons/UI_Icons/Pills/pill18.png', - "pill19" = 'icons/UI_Icons/Pills/pill19.png', - "pill20" = 'icons/UI_Icons/Pills/pill20.png', - "pill21" = 'icons/UI_Icons/Pills/pill21.png', - "pill22" = 'icons/UI_Icons/Pills/pill22.png', - ) - -//this exists purely to avoid meta by pre-loading all language icons. -/datum/asset/language/register() - for(var/path in typesof(/datum/language)) - set waitfor = FALSE - var/datum/language/L = new path () - L.get_icon() - -/datum/asset/spritesheet/pipes - name = "pipes" - -/datum/asset/spritesheet/pipes/register() - for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi', 'icons/obj/plumbing/fluid_ducts.dmi')) - InsertAll("", each, GLOB.alldirs) - ..() - -// Representative icons for each research design -/datum/asset/spritesheet/research_designs - name = "design" - -/datum/asset/spritesheet/research_designs/register() - for (var/path in subtypesof(/datum/design)) - var/datum/design/D = path - - var/icon_file - var/icon_state - var/icon/I - - if(initial(D.research_icon) && initial(D.research_icon_state)) //If the design has an icon replacement skip the rest - icon_file = initial(D.research_icon) - icon_state = initial(D.research_icon_state) - if(!(icon_state in icon_states(icon_file))) - warning("design [D] with icon '[icon_file]' missing state '[icon_state]'") - continue - I = icon(icon_file, icon_state, SOUTH) - - else - // construct the icon and slap it into the resource cache - var/atom/item = initial(D.build_path) - if (!ispath(item, /atom)) - // biogenerator outputs to beakers by default - if (initial(D.build_type) & BIOGENERATOR) - item = /obj/item/reagent_containers/glass/beaker/large - else - continue // shouldn't happen, but just in case - - // circuit boards become their resulting machines or computers - if (ispath(item, /obj/item/circuitboard)) - var/obj/item/circuitboard/C = item - var/machine = initial(C.build_path) - if (machine) - item = machine - - icon_file = initial(item.icon) - icon_state = initial(item.icon_state) - - if(!(icon_state in icon_states(icon_file))) - warning("design [D] with icon '[icon_file]' missing state '[icon_state]'") - continue - I = icon(icon_file, icon_state, SOUTH) - - // computers (and snowflakes) get their screen and keyboard sprites - if (ispath(item, /obj/machinery/computer) || ispath(item, /obj/machinery/power/solar_control)) - var/obj/machinery/computer/C = item - var/screen = initial(C.icon_screen) - var/keyboard = initial(C.icon_keyboard) - var/all_states = icon_states(icon_file) - if (screen && (screen in all_states)) - I.Blend(icon(icon_file, screen, SOUTH), ICON_OVERLAY) - if (keyboard && (keyboard in all_states)) - I.Blend(icon(icon_file, keyboard, SOUTH), ICON_OVERLAY) - - Insert(initial(D.id), I) - return ..() - -/datum/asset/spritesheet/vending - name = "vending" - -/datum/asset/spritesheet/vending/register() - for (var/k in GLOB.vending_products) - var/atom/item = k - if (!ispath(item, /atom)) - continue - - var/icon_file = initial(item.icon) - var/icon_state = initial(item.icon_state) - var/icon/I - - var/icon_states_list = icon_states(icon_file) - if(icon_state in icon_states_list) - I = icon(icon_file, icon_state, SOUTH) - var/c = initial(item.color) - if (!isnull(c) && c != "#FFFFFF") - I.Blend(c, ICON_MULTIPLY) - else - var/icon_states_string - for (var/an_icon_state in icon_states_list) - if (!icon_states_string) - icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])" - else - icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])" - stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]") - I = icon('icons/turf/floors.dmi', "", SOUTH) - - var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-") - - Insert(imgid, I) - return ..() - -/datum/asset/simple/genetics - assets = list( - "dna_discovered.gif" = 'html/dna_discovered.gif', - "dna_undiscovered.gif" = 'html/dna_undiscovered.gif', - "dna_extra.gif" = 'html/dna_extra.gif' - ) +//DEFINITIONS FOR ASSET DATUMS START HERE. + +/datum/asset/simple/tgui + assets = list( + "tgui.bundle.js" = 'tgui/packages/tgui/public/tgui.bundle.js', + "tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css', + ) + +/datum/asset/group/tgui + children = list( + /datum/asset/simple/tgui, + /datum/asset/simple/fontawesome + ) + +/datum/asset/simple/headers + assets = list( + "alarm_green.gif" = 'icons/program_icons/alarm_green.gif', + "alarm_red.gif" = 'icons/program_icons/alarm_red.gif', + "batt_5.gif" = 'icons/program_icons/batt_5.gif', + "batt_20.gif" = 'icons/program_icons/batt_20.gif', + "batt_40.gif" = 'icons/program_icons/batt_40.gif', + "batt_60.gif" = 'icons/program_icons/batt_60.gif', + "batt_80.gif" = 'icons/program_icons/batt_80.gif', + "batt_100.gif" = 'icons/program_icons/batt_100.gif', + "charging.gif" = 'icons/program_icons/charging.gif', + "downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif', + "downloader_running.gif" = 'icons/program_icons/downloader_running.gif', + "ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif', + "ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif', + "power_norm.gif" = 'icons/program_icons/power_norm.gif', + "power_warn.gif" = 'icons/program_icons/power_warn.gif', + "sig_high.gif" = 'icons/program_icons/sig_high.gif', + "sig_low.gif" = 'icons/program_icons/sig_low.gif', + "sig_lan.gif" = 'icons/program_icons/sig_lan.gif', + "sig_none.gif" = 'icons/program_icons/sig_none.gif', + "smmon_0.gif" = 'icons/program_icons/smmon_0.gif', + "smmon_1.gif" = 'icons/program_icons/smmon_1.gif', + "smmon_2.gif" = 'icons/program_icons/smmon_2.gif', + "smmon_3.gif" = 'icons/program_icons/smmon_3.gif', + "smmon_4.gif" = 'icons/program_icons/smmon_4.gif', + "smmon_5.gif" = 'icons/program_icons/smmon_5.gif', + "smmon_6.gif" = 'icons/program_icons/smmon_6.gif' + ) + +/datum/asset/simple/radar_assets + assets = list( + "ntosradarbackground.png" = 'icons/UI_Icons/tgui/ntosradar_background.png', + "ntosradarpointer.png" = 'icons/UI_Icons/tgui/ntosradar_pointer.png', + "ntosradarpointerS.png" = 'icons/UI_Icons/tgui/ntosradar_pointer_S.png' + ) + +/datum/asset/spritesheet/simple/pda + name = "pda" + assets = list( + "atmos" = 'icons/pda_icons/pda_atmos.png', + "back" = 'icons/pda_icons/pda_back.png', + "bell" = 'icons/pda_icons/pda_bell.png', + "blank" = 'icons/pda_icons/pda_blank.png', + "boom" = 'icons/pda_icons/pda_boom.png', + "bucket" = 'icons/pda_icons/pda_bucket.png', + "medbot" = 'icons/pda_icons/pda_medbot.png', + "floorbot" = 'icons/pda_icons/pda_floorbot.png', + "cleanbot" = 'icons/pda_icons/pda_cleanbot.png', + "crate" = 'icons/pda_icons/pda_crate.png', + "cuffs" = 'icons/pda_icons/pda_cuffs.png', + "eject" = 'icons/pda_icons/pda_eject.png', + "flashlight" = 'icons/pda_icons/pda_flashlight.png', + "honk" = 'icons/pda_icons/pda_honk.png', + "mail" = 'icons/pda_icons/pda_mail.png', + "medical" = 'icons/pda_icons/pda_medical.png', + "menu" = 'icons/pda_icons/pda_menu.png', + "mule" = 'icons/pda_icons/pda_mule.png', + "notes" = 'icons/pda_icons/pda_notes.png', + "power" = 'icons/pda_icons/pda_power.png', + "rdoor" = 'icons/pda_icons/pda_rdoor.png', + "reagent" = 'icons/pda_icons/pda_reagent.png', + "refresh" = 'icons/pda_icons/pda_refresh.png', + "scanner" = 'icons/pda_icons/pda_scanner.png', + "signaler" = 'icons/pda_icons/pda_signaler.png', + "status" = 'icons/pda_icons/pda_status.png', + "dronephone" = 'icons/pda_icons/pda_dronephone.png', + "emoji" = 'icons/pda_icons/pda_emoji.png' + ) + +/datum/asset/spritesheet/simple/paper + name = "paper" + assets = list( + "stamp-clown" = 'icons/stamp_icons/large_stamp-clown.png', + "stamp-deny" = 'icons/stamp_icons/large_stamp-deny.png', + "stamp-ok" = 'icons/stamp_icons/large_stamp-ok.png', + "stamp-hop" = 'icons/stamp_icons/large_stamp-hop.png', + "stamp-cmo" = 'icons/stamp_icons/large_stamp-cmo.png', + "stamp-ce" = 'icons/stamp_icons/large_stamp-ce.png', + "stamp-hos" = 'icons/stamp_icons/large_stamp-hos.png', + "stamp-rd" = 'icons/stamp_icons/large_stamp-rd.png', + "stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png', + "stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png', + "stamp-law" = 'icons/stamp_icons/large_stamp-law.png' + ) + + +/datum/asset/simple/IRV + assets = list( + "jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js', + ) + +/datum/asset/group/IRV + children = list( + /datum/asset/simple/jquery, + /datum/asset/simple/IRV + ) + +/datum/asset/simple/changelog + assets = list( + "88x31.png" = 'html/88x31.png', + "bug-minus.png" = 'html/bug-minus.png', + "cross-circle.png" = 'html/cross-circle.png', + "hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png', + "image-minus.png" = 'html/image-minus.png', + "image-plus.png" = 'html/image-plus.png', + "music-minus.png" = 'html/music-minus.png', + "music-plus.png" = 'html/music-plus.png', + "tick-circle.png" = 'html/tick-circle.png', + "wrench-screwdriver.png" = 'html/wrench-screwdriver.png', + "spell-check.png" = 'html/spell-check.png', + "burn-exclamation.png" = 'html/burn-exclamation.png', + "chevron.png" = 'html/chevron.png', + "chevron-expand.png" = 'html/chevron-expand.png', + "scales.png" = 'html/scales.png', + "coding.png" = 'html/coding.png', + "ban.png" = 'html/ban.png', + "chrome-wrench.png" = 'html/chrome-wrench.png', + "changelog.css" = 'html/changelog.css' + ) + +/datum/asset/group/goonchat + children = list( + /datum/asset/simple/jquery, + /datum/asset/simple/goonchat, + /datum/asset/spritesheet/goonchat, + /datum/asset/simple/fontawesome + ) + +/datum/asset/simple/jquery + assets = list( + "jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js', + ) + +/datum/asset/simple/goonchat + assets = list( + "json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js', + "browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js', + "browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css', + "browserOutput_white.css" = 'code/modules/goonchat/browserassets/css/browserOutput_white.css', + ) + +/datum/asset/simple/fontawesome + assets = list( + "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot', + "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff', + "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot', + "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff', + "font-awesome.css" = 'html/font-awesome/css/all.min.css', + "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css' + ) + +/datum/asset/simple/fonts + assets = list( + "sga.ttf" = 'html/sga.ttf' + ) + +/datum/asset/spritesheet/goonchat + name = "chat" + +/datum/asset/spritesheet/goonchat/register() + InsertAll("emoji", 'icons/emoji.dmi') + + // pre-loading all lanugage icons also helps to avoid meta + InsertAll("language", 'icons/misc/language.dmi') + // catch languages which are pulling icons from another file + for(var/path in typesof(/datum/language)) + var/datum/language/L = path + var/icon = initial(L.icon) + if (icon != 'icons/misc/language.dmi') + var/icon_state = initial(L.icon_state) + Insert("language-[icon_state]", icon, icon_state=icon_state) + + ..() + +/datum/asset/simple/permissions + assets = list( + "padlock.png" = 'html/padlock.png' + ) + +/datum/asset/simple/notes + assets = list( + "high_button.png" = 'html/high_button.png', + "medium_button.png" = 'html/medium_button.png', + "minor_button.png" = 'html/minor_button.png', + "none_button.png" = 'html/none_button.png', + ) + +/datum/asset/simple/arcade + assets = list( + "boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif', + "boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif', + "boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif', + "boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif', + "boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif', + "boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif', + ) + +/datum/asset/spritesheet/simple/achievements + name ="achievements" + assets = list( + "default" = 'icons/UI_Icons/Achievements/default.png', + "basemisc" = 'icons/UI_Icons/Achievements/basemisc.png', + "baseboss" = 'icons/UI_Icons/Achievements/baseboss.png', + "baseskill" = 'icons/UI_Icons/Achievements/baseskill.png', + "bbgum" = 'icons/UI_Icons/Achievements/Boss/bbgum.png', + "colossus" = 'icons/UI_Icons/Achievements/Boss/colossus.png', + "hierophant" = 'icons/UI_Icons/Achievements/Boss/hierophant.png', + "legion" = 'icons/UI_Icons/Achievements/Boss/legion.png', + "miner" = 'icons/UI_Icons/Achievements/Boss/miner.png', + "swarmer" = 'icons/UI_Icons/Achievements/Boss/swarmer.png', + "tendril" = 'icons/UI_Icons/Achievements/Boss/tendril.png', + "featofstrength" = 'icons/UI_Icons/Achievements/Misc/featofstrength.png', + "helbital" = 'icons/UI_Icons/Achievements/Misc/helbital.png', + "jackpot" = 'icons/UI_Icons/Achievements/Misc/jackpot.png', + "meteors" = 'icons/UI_Icons/Achievements/Misc/meteors.png', + "timewaste" = 'icons/UI_Icons/Achievements/Misc/timewaste.png', + "upgrade" = 'icons/UI_Icons/Achievements/Misc/upgrade.png', + "clownking" = 'icons/UI_Icons/Achievements/Misc/clownking.png', + "clownthanks" = 'icons/UI_Icons/Achievements/Misc/clownthanks.png', + "rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png', + "snail" = 'icons/UI_Icons/Achievements/Misc/snail.png', + "mining" = 'icons/UI_Icons/Achievements/Skills/mining.png', + ) + +/datum/asset/spritesheet/simple/pills + name ="pills" + assets = list( + "pill1" = 'icons/UI_Icons/Pills/pill1.png', + "pill2" = 'icons/UI_Icons/Pills/pill2.png', + "pill3" = 'icons/UI_Icons/Pills/pill3.png', + "pill4" = 'icons/UI_Icons/Pills/pill4.png', + "pill5" = 'icons/UI_Icons/Pills/pill5.png', + "pill6" = 'icons/UI_Icons/Pills/pill6.png', + "pill7" = 'icons/UI_Icons/Pills/pill7.png', + "pill8" = 'icons/UI_Icons/Pills/pill8.png', + "pill9" = 'icons/UI_Icons/Pills/pill9.png', + "pill10" = 'icons/UI_Icons/Pills/pill10.png', + "pill11" = 'icons/UI_Icons/Pills/pill11.png', + "pill12" = 'icons/UI_Icons/Pills/pill12.png', + "pill13" = 'icons/UI_Icons/Pills/pill13.png', + "pill14" = 'icons/UI_Icons/Pills/pill14.png', + "pill15" = 'icons/UI_Icons/Pills/pill15.png', + "pill16" = 'icons/UI_Icons/Pills/pill16.png', + "pill17" = 'icons/UI_Icons/Pills/pill17.png', + "pill18" = 'icons/UI_Icons/Pills/pill18.png', + "pill19" = 'icons/UI_Icons/Pills/pill19.png', + "pill20" = 'icons/UI_Icons/Pills/pill20.png', + "pill21" = 'icons/UI_Icons/Pills/pill21.png', + "pill22" = 'icons/UI_Icons/Pills/pill22.png', + ) + +//this exists purely to avoid meta by pre-loading all language icons. +/datum/asset/language/register() + for(var/path in typesof(/datum/language)) + set waitfor = FALSE + var/datum/language/L = new path () + L.get_icon() + +/datum/asset/spritesheet/pipes + name = "pipes" + +/datum/asset/spritesheet/pipes/register() + for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi', 'icons/obj/plumbing/fluid_ducts.dmi')) + InsertAll("", each, GLOB.alldirs) + ..() + +// Representative icons for each research design +/datum/asset/spritesheet/research_designs + name = "design" + +/datum/asset/spritesheet/research_designs/register() + for (var/path in subtypesof(/datum/design)) + var/datum/design/D = path + + var/icon_file + var/icon_state + var/icon/I + + if(initial(D.research_icon) && initial(D.research_icon_state)) //If the design has an icon replacement skip the rest + icon_file = initial(D.research_icon) + icon_state = initial(D.research_icon_state) + if(!(icon_state in icon_states(icon_file))) + warning("design [D] with icon '[icon_file]' missing state '[icon_state]'") + continue + I = icon(icon_file, icon_state, SOUTH) + + else + // construct the icon and slap it into the resource cache + var/atom/item = initial(D.build_path) + if (!ispath(item, /atom)) + // biogenerator outputs to beakers by default + if (initial(D.build_type) & BIOGENERATOR) + item = /obj/item/reagent_containers/glass/beaker/large + else + continue // shouldn't happen, but just in case + + // circuit boards become their resulting machines or computers + if (ispath(item, /obj/item/circuitboard)) + var/obj/item/circuitboard/C = item + var/machine = initial(C.build_path) + if (machine) + item = machine + + icon_file = initial(item.icon) + icon_state = initial(item.icon_state) + + if(!(icon_state in icon_states(icon_file))) + warning("design [D] with icon '[icon_file]' missing state '[icon_state]'") + continue + I = icon(icon_file, icon_state, SOUTH) + + // computers (and snowflakes) get their screen and keyboard sprites + if (ispath(item, /obj/machinery/computer) || ispath(item, /obj/machinery/power/solar_control)) + var/obj/machinery/computer/C = item + var/screen = initial(C.icon_screen) + var/keyboard = initial(C.icon_keyboard) + var/all_states = icon_states(icon_file) + if (screen && (screen in all_states)) + I.Blend(icon(icon_file, screen, SOUTH), ICON_OVERLAY) + if (keyboard && (keyboard in all_states)) + I.Blend(icon(icon_file, keyboard, SOUTH), ICON_OVERLAY) + + Insert(initial(D.id), I) + return ..() + +/datum/asset/spritesheet/vending + name = "vending" + +/datum/asset/spritesheet/vending/register() + for (var/k in GLOB.vending_products) + var/atom/item = k + if (!ispath(item, /atom)) + continue + + var/icon_file = initial(item.icon) + var/icon_state = initial(item.icon_state) + var/icon/I + + var/icon_states_list = icon_states(icon_file) + if(icon_state in icon_states_list) + I = icon(icon_file, icon_state, SOUTH) + var/c = initial(item.color) + if (!isnull(c) && c != "#FFFFFF") + I.Blend(c, ICON_MULTIPLY) + else + var/icon_states_string + for (var/an_icon_state in icon_states_list) + if (!icon_states_string) + icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])" + else + icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])" + stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]") + I = icon('icons/turf/floors.dmi', "", SOUTH) + + var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-") + + Insert(imgid, I) + return ..() + +/datum/asset/simple/genetics + assets = list( + "dna_discovered.gif" = 'html/dna_discovered.gif', + "dna_undiscovered.gif" = 'html/dna_undiscovered.gif', + "dna_extra.gif" = 'html/dna_extra.gif' + ) + +/datum/asset/simple/orbit + assets = list( + "ghost.png" = 'html/ghost.png' + ) + +/datum/asset/simple/vv + assets = list( + "view_variables.css" = 'html/admin/view_variables.css' + ) + diff --git a/code/modules/asset_cache/validate_assets.html b/code/modules/asset_cache/validate_assets.html new file mode 100644 index 000000000000..9728bb5c285b --- /dev/null +++ b/code/modules/asset_cache/validate_assets.html @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm index 58353b861ec7..d1a9212f0b43 100644 --- a/code/modules/atmospherics/environmental/LINDA_fire.dm +++ b/code/modules/atmospherics/environmental/LINDA_fire.dm @@ -10,6 +10,7 @@ /turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh) + //If the air doesn't exist we just return false var/list/air_gases = air?.gases if(!air_gases) return @@ -169,6 +170,7 @@ if((temperature < FIRE_MINIMUM_TEMPERATURE_TO_EXIST) || (volume <= 1)) qdel(src) return + if(!location.air || (INSUFFICIENT(/datum/gas/plasma) && INSUFFICIENT(/datum/gas/tritium)) || INSUFFICIENT(/datum/gas/oxygen)) qdel(src) return diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index b0fb6f8cb688..8b70ac77d9ba 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -5,8 +5,14 @@ What are the archived variables for? */ #define MINIMUM_HEAT_CAPACITY 0.0003 #define MINIMUM_MOLE_COUNT 0.01 -#define QUANTIZE(variable) (round(variable,0.0000001))/*I feel the need to document what happens here. Basically this is used to catch most rounding errors, however it's previous value made it so that - once gases got hot enough, most procedures wouldnt occur due to the fact that the mole counts would get rounded away. Thus, we lowered it a few orders of magnititude */ +#define MOLAR_ACCURACY 1E-7 +#define QUANTIZE(variable) (round((variable), (MOLAR_ACCURACY)))/*I feel the need to document what happens here. Basically this is used + to catch most rounding errors, however its previous value made it so that + once gases got hot enough, most procedures wouldn't occur due to the fact that the mole + counts would get rounded away. Thus, we lowered it a few orders of magnitude + Edit: As far as I know this might have a bug caused by round(). When it has a second arg it will round up. + So for instance round(0.5, 1) == 1. Trouble is I haven't found any instances of it causing a bug, + and any attempts to fix it just killed atmos. I leave this to a greater man then I*/ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) @@ -40,49 +46,50 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) //listmos procs //use the macros in performance intensive areas. for their definitions, refer to code/__DEFINES/atmospherics.dm - //assert_gas(gas_id) - used to guarantee that the gas list for this id exists in gas_mixture.gases. + ///assert_gas(gas_id) - used to guarantee that the gas list for this id exists in gas_mixture.gases. //Must be used before adding to a gas. May be used before reading from a gas. /datum/gas_mixture/proc/assert_gas(gas_id) ASSERT_GAS(gas_id, src) - //assert_gases(args) - shorthand for calling ASSERT_GAS() once for each gas type. + ///assert_gases(args) - shorthand for calling ASSERT_GAS() once for each gas type. /datum/gas_mixture/proc/assert_gases(...) for(var/id in args) ASSERT_GAS(id, src) - //add_gas(gas_id) - similar to assert_gas(), but does not check for an existing - //gas list for this id. This can clobber existing gases. - //Used instead of assert_gas() when you know the gas does not exist. Faster than assert_gas(). + ///add_gas(gas_id) - similar to assert_gas(), but does not check for an existing gas list for this id. This can clobber existing gases. + ///Used instead of assert_gas() when you know the gas does not exist. Faster than assert_gas(). /datum/gas_mixture/proc/add_gas(gas_id) ADD_GAS(gas_id, gases) - //add_gases(args) - shorthand for calling add_gas() once for each gas_type. + ///add_gases(args) - shorthand for calling add_gas() once for each gas_type. /datum/gas_mixture/proc/add_gases(...) var/cached_gases = gases for(var/id in args) ADD_GAS(id, cached_gases) - //garbage_collect() - removes any gas list which is empty. - //If called with a list as an argument, only removes gas lists with IDs from that list. - //Must be used after subtracting from a gas. Must be used after assert_gas() - //if assert_gas() was called only to read from the gas. - //By removing empty gases, processing speed is increased. + ///garbage_collect() - removes any gas list which is empty. + ///If called with a list as an argument, only removes gas lists with IDs from that list. + ///Must be used after subtracting from a gas. Must be used after assert_gas() + ///if assert_gas() was called only to read from the gas. + ///By removing empty gases, processing speed is increased. /datum/gas_mixture/proc/garbage_collect(list/tocheck) var/list/cached_gases = gases for(var/id in (tocheck || cached_gases)) - if(QUANTIZE(cached_gases[id][MOLES]) <= 0 && QUANTIZE(cached_gases[id][ARCHIVE]) <= 0) + if(QUANTIZE(cached_gases[id][MOLES]) <= 0) cached_gases -= id //PV = nRT -/datum/gas_mixture/proc/heat_capacity(data = MOLES) //joules per kelvin + ///joules per kelvin +/datum/gas_mixture/proc/heat_capacity(data = MOLES) var/list/cached_gases = gases . = 0 for(var/id in cached_gases) var/gas_data = cached_gases[id] . += gas_data[data] * gas_data[GAS_META][META_GAS_SPECIFIC_HEAT] -/datum/gas_mixture/turf/heat_capacity(data = MOLES) // Same as above except vacuums return HEAT_CAPACITY_VACUUM + /// Same as above except vacuums return HEAT_CAPACITY_VACUUM +/datum/gas_mixture/turf/heat_capacity(data = MOLES) var/list/cached_gases = gases . = 0 for(var/id in cached_gases) @@ -91,11 +98,13 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) if(!.) . += HEAT_CAPACITY_VACUUM //we want vacuums in turfs to have the same heat capacity as space + /// Calculate moles /datum/gas_mixture/proc/total_moles() var/cached_gases = gases TOTAL_MOLES(cached_gases, .) -/datum/gas_mixture/proc/return_pressure() //kilopascals + /// Calculate pressure in kilopascals +/datum/gas_mixture/proc/return_pressure() if(volume > 0) // to prevent division by zero var/cached_gases = gases TOTAL_MOLES(cached_gases, .) @@ -103,64 +112,20 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) return return 0 -/datum/gas_mixture/proc/return_temperature() //kelvins + /// Calculate temperature in kelvins +/datum/gas_mixture/proc/return_temperature() return temperature -/datum/gas_mixture/proc/return_volume() //liters + /// Calculate volume in liters +/datum/gas_mixture/proc/return_volume() return max(0, volume) -/datum/gas_mixture/proc/thermal_energy() //joules + /// Calculate thermal energy in joules +/datum/gas_mixture/proc/thermal_energy() return THERMAL_ENERGY(src) //see code/__DEFINES/atmospherics.dm; use the define in performance critical areas + ///Update archived versions of variables. Returns: 1 in all cases /datum/gas_mixture/proc/archive() - //Update archived versions of variables - //Returns: 1 in all cases - -/datum/gas_mixture/proc/merge(datum/gas_mixture/giver) - //Merges all air from giver into self. Deletes giver. - //Returns: 1 if we are mutable, 0 otherwise - -/datum/gas_mixture/proc/remove(amount) - //Proportionally removes amount of gas from the gas_mixture - //Returns: gas_mixture with the gases removed - -/datum/gas_mixture/proc/remove_ratio(ratio) - //Proportionally removes amount of gas from the gas_mixture - //Returns: gas_mixture with the gases removed - -/datum/gas_mixture/proc/copy() - //Creates new, identical gas mixture - //Returns: duplicate gas mixture - -/datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample) - //Copies variables from sample - //Returns: 1 if we are mutable, 0 otherwise - -/datum/gas_mixture/proc/copy_from_turf(turf/model) - //Copies all gas info from the turf into the gas list along with temperature - //Returns: 1 if we are mutable, 0 otherwise - -/datum/gas_mixture/proc/parse_gas_string(gas_string) - //Copies variables from a particularly formatted string. - //Returns: 1 if we are mutable, 0 otherwise - -/datum/gas_mixture/proc/share(datum/gas_mixture/sharer) - //Performs air sharing calculations between two gas_mixtures assuming only 1 boundary length - //Returns: amount of gas exchanged (+ if sharer received) - -/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient) - //Performs temperature sharing calculations (via conduction) between two gas_mixtures assuming only 1 boundary length - //Returns: new temperature of the sharer - -/datum/gas_mixture/proc/compare(datum/gas_mixture/sample) - //Compares sample to self to see if within acceptable ranges that group processing may be enabled - //Returns: a string indicating what check failed, or "" if check passes - -/datum/gas_mixture/proc/react(turf/open/dump_location) - //Performs various reactions such as combustion or fusion (LOL) - //Returns: 1 if any reaction took place; 0 otherwise - -/datum/gas_mixture/archive() var/list/cached_gases = gases temperature_archived = temperature @@ -169,7 +134,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) return 1 -/datum/gas_mixture/merge(datum/gas_mixture/giver) + ///Merges all air from giver into self. Deletes giver. Returns: 1 if we are mutable, 0 otherwise +/datum/gas_mixture/proc/merge(datum/gas_mixture/giver) if(!giver) return 0 @@ -190,7 +156,9 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) return 1 -/datum/gas_mixture/remove(amount) + ///Proportionally removes amount of gas from the gas_mixture. + ///Returns: gas_mixture with the gases removed +/datum/gas_mixture/proc/remove(amount) var/sum var/list/cached_gases = gases TOTAL_MOLES(cached_gases, sum) @@ -209,7 +177,9 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) return removed -/datum/gas_mixture/remove_ratio(ratio) + ///Proportionally removes amount of gas from the gas_mixture. + ///Returns: gas_mixture with the gases removed +/datum/gas_mixture/proc/remove_ratio(ratio) if(ratio <= 0) return null ratio = min(ratio, 1) @@ -228,7 +198,52 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) return removed -/datum/gas_mixture/copy() + ///Removes a amount of a specific gas from the gas_mixture. + ///Returns: gas_mixture with the gas removed +/datum/gas_mixture/proc/remove_specific(gas_id, amount) + var/list/cached_gases = gases + amount = min(amount, cached_gases[gas_id][MOLES]) + if(amount <= 0) + return null + var/datum/gas_mixture/removed = new type + var/list/removed_gases = removed.gases + removed.temperature = temperature + ADD_GAS(gas_id, removed.gases) + removed_gases[gas_id][MOLES] = amount + cached_gases[gas_id][MOLES] -= amount + + garbage_collect(list(gas_id)) + return removed + + ///Distributes the contents of two mixes equally between themselves + //Returns: bool indicating whether gases moved between the two mixes +/datum/gas_mixture/proc/equalize(datum/gas_mixture/other) + . = FALSE + if(abs(return_temperature() - other.return_temperature()) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) + . = TRUE + var/self_heat_cap = heat_capacity() + var/other_heat_cap = other.heat_capacity() + var/new_temp = (temperature * self_heat_cap + other.temperature * other_heat_cap) / (self_heat_cap + other_heat_cap) + temperature = new_temp + other.temperature = new_temp + + var/min_p_delta = 0.1 + var/total_volume = volume + other.volume + var/list/gas_list = gases | other.gases + for(var/gas_id in gas_list) + assert_gas(gas_id) + other.assert_gas(gas_id) + //math is under the assumption temperatures are equal + if(abs(gases[gas_id][MOLES] / volume - other.gases[gas_id][MOLES] / other.volume) > min_p_delta / (R_IDEAL_GAS_EQUATION * temperature)) + . = TRUE + var/total_moles = gases[gas_id][MOLES] + other.gases[gas_id][MOLES] + gases[gas_id][MOLES] = total_moles * (volume/total_volume) + other.gases[gas_id][MOLES] = total_moles * (other.volume/total_volume) + + + ///Creates new, identical gas mixture + ///Returns: duplicate gas mixture +/datum/gas_mixture/proc/copy() var/list/cached_gases = gases var/datum/gas_mixture/copy = new type var/list/copy_gases = copy.gases @@ -240,22 +255,25 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) return copy - -/datum/gas_mixture/copy_from(datum/gas_mixture/sample) + ///Copies variables from sample, moles multiplicated by partial + ///Returns: 1 if we are mutable, 0 otherwise +/datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample, partial = 1) var/list/cached_gases = gases //accessing datum vars is slower than proc vars var/list/sample_gases = sample.gases + //remove all gases not in the sample + cached_gases &= sample_gases + temperature = sample.temperature for(var/id in sample_gases) ASSERT_GAS(id,src) - cached_gases[id][MOLES] = sample_gases[id][MOLES] - - //remove all gases not in the sample - cached_gases &= sample_gases + cached_gases[id][MOLES] = sample_gases[id][MOLES] * partial return 1 -/datum/gas_mixture/copy_from_turf(turf/model) + ///Copies all gas info from the turf into the gas list along with temperature + ///Returns: 1 if we are mutable, 0 otherwise +/datum/gas_mixture/proc/copy_from_turf(turf/model) parse_gas_string(model.initial_gas_mix) //acounts for changes in temperature @@ -265,7 +283,9 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) return 1 -/datum/gas_mixture/parse_gas_string(gas_string) + ///Copies variables from a particularly formatted string. + ///Returns: 1 if we are mutable, 0 otherwise +/datum/gas_mixture/proc/parse_gas_string(gas_string) gas_string = SSair.preprocess_gas_string(gas_string) var/list/gases = src.gases @@ -282,8 +302,9 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) gases[path][MOLES] = text2num(gas[id]) return 1 -/datum/gas_mixture/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4) - + ///Performs air sharing calculations between two gas_mixtures assuming only 1 boundary length + ///Returns: amount of gas exchanged (+ if sharer received) +/datum/gas_mixture/proc/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4) var/list/cached_gases = gases var/list/sharer_gases = sharer.gases @@ -344,11 +365,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) if(abs(new_sharer_heat_capacity/old_sharer_heat_capacity - 1) < 0.1) // <10% change in sharer heat capacity temperature_share(sharer, OPEN_HEAT_TRANSFER_COEFFICIENT) - if(length(cached_gases ^ sharer_gases)) //if all gases were present in both mixtures, we know that no gases are 0 - garbage_collect(cached_gases - sharer_gases) //any gases the sharer had, we are guaranteed to have. gases that it didn't have we are not. - sharer.garbage_collect(sharer_gases - cached_gases) //the reverse is equally true - if (initial(sharer.gc_share)) - sharer.garbage_collect() + garbage_collect() + sharer.garbage_collect() if(temperature_delta > MINIMUM_TEMPERATURE_TO_MOVE || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE) var/our_moles TOTAL_MOLES(cached_gases,our_moles) @@ -356,7 +374,9 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) TOTAL_MOLES(sharer_gases,their_moles) return (temperature_archived*(our_moles + moved_moles) - sharer.temperature_archived*(their_moles - moved_moles)) * R_IDEAL_GAS_EQUATION / volume -/datum/gas_mixture/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity) + ///Performs temperature sharing calculations (via conduction) between two gas_mixtures assuming only 1 boundary length + ///Returns: new temperature of the sharer +/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity) //transfer of thermal energy (via conduction) between self and sharer if(sharer) sharer_temperature = sharer.temperature_archived @@ -378,7 +398,9 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) return sharer_temperature //thermal energy of the system (self and sharer) is unchanged -/datum/gas_mixture/compare(datum/gas_mixture/sample) + ///Compares sample to self to see if within acceptable ranges that group processing may be enabled + ///Returns: a string indicating what check failed, or "" if check passes +/datum/gas_mixture/proc/compare(datum/gas_mixture/sample) var/list/sample_gases = sample.gases //accessing datum vars is slower than proc vars var/list/cached_gases = gases @@ -404,7 +426,9 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) return "" -/datum/gas_mixture/react(datum/holder) + ///Performs various reactions such as combustion or fusion (LOL) + ///Returns: 1 if any reaction took place; 0 otherwise +/datum/gas_mixture/proc/react(datum/holder) . = NO_REACTION var/list/cached_gases = gases if(!length(cached_gases)) @@ -442,23 +466,23 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) if(.) garbage_collect() -//Takes the amount of the gas you want to PP as an argument -//So I don't have to do some hacky switches/defines/magic strings -//eg: -//Tox_PP = get_partial_pressure(gas_mixture.toxins) -//O2_PP = get_partial_pressure(gas_mixture.oxygen) +///Takes the amount of the gas you want to PP as an argument +///So I don't have to do some hacky switches/defines/magic strings +///eg: +///Tox_PP = get_partial_pressure(gas_mixture.toxins) +///O2_PP = get_partial_pressure(gas_mixture.oxygen) /datum/gas_mixture/proc/get_breath_partial_pressure(gas_pressure) return (gas_pressure * R_IDEAL_GAS_EQUATION * temperature) / BREATH_VOLUME -//inverse +///inverse /datum/gas_mixture/proc/get_true_breath_pressure(partial_pressure) return (partial_pressure * BREATH_VOLUME) / (R_IDEAL_GAS_EQUATION * temperature) -//Mathematical proofs: -/* +///Mathematical proofs: +/** get_breath_partial_pressure(gas_pp) --> gas_pp/total_moles()*breath_pp = pp get_true_breath_pressure(pp) --> gas_pp = pp/breath_pp*total_moles() 10/20*5 = 2.5 10 = 2.5/5*20 -*/ +**/ diff --git a/code/modules/atmospherics/gasmixtures/gas_types.dm b/code/modules/atmospherics/gasmixtures/gas_types.dm index 1b67d2b95651..36e0565891b0 100644 --- a/code/modules/atmospherics/gasmixtures/gas_types.dm +++ b/code/modules/atmospherics/gasmixtures/gas_types.dm @@ -156,7 +156,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/g /datum/gas/freon id = "freon" - specific_heat = 300 + specific_heat = 600 name = "Freon" gas_overlay = "freon" moles_visible = MOLES_GAS_VISIBLE *30 diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm index 7626deff5ecc..723c0d8a80a4 100644 --- a/code/modules/atmospherics/gasmixtures/reactions.dm +++ b/code/modules/atmospherics/gasmixtures/reactions.dm @@ -267,7 +267,7 @@ datum/gas_reaction/freonfire/react(datum/gas_mixture/air, datum/holder) temperature_scale = 0 else temperature_scale = (FREON_MAXIMUM_BURN_TEMPERATURE - temperature)/(FREON_MAXIMUM_BURN_TEMPERATURE - FREON_LOWER_TEMPERATURE) //calculate the scale based on the temperature - if(temperature_scale > 0) + if(temperature_scale >= 0) oxygen_burn_rate = OXYGEN_BURN_RATE_BASE - temperature_scale if(cached_gases[/datum/gas/oxygen][MOLES] > cached_gases[/datum/gas/freon][MOLES]*FREON_OXYGEN_FULLBURN) freon_burn_rate = (cached_gases[/datum/gas/freon][MOLES]*temperature_scale)/FREON_BURN_RATE_DELTA @@ -281,7 +281,7 @@ datum/gas_reaction/freonfire/react(datum/gas_mixture/air, datum/holder) ASSERT_GAS(/datum/gas/carbon_dioxide,air) cached_gases[/datum/gas/carbon_dioxide][MOLES] += freon_burn_rate - if(temperature < 150 && temperature > 130 && prob(2)) + if(temperature < 160 && temperature > 120 && prob(2)) new /obj/item/stack/sheet/hot_ice(location) energy_released += FIRE_FREON_ENERGY_RELEASED * (freon_burn_rate) @@ -377,17 +377,51 @@ datum/gas_reaction/freonfire/react(datum/gas_mixture/air, datum/holder) air.temperature = clamp(((air.temperature*old_heat_capacity + reaction_energy)/new_heat_capacity),TCMB,INFINITY) return REACTING -/datum/gas_reaction/nitrylformation //The formation of nitryl. Endothermic. Requires N2O as a catalyst. +/datum/gas_reaction/nitrousformation //formationn of n2o, esothermic, requires bz as catalyst + priority = 3 + name = "Nitrous Oxide formation" + id = "nitrousformation" + +/datum/gas_reaction/nitrousformation/init_reqs() + min_requirements = list( + /datum/gas/oxygen = 10, + /datum/gas/nitrogen = 20, + /datum/gas/bz = 5, + "TEMP" = 200 + ) + +/datum/gas_reaction/nitrousformation/react(datum/gas_mixture/air) + var/list/cached_gases = air.gases + var/temperature = air.temperature + var/old_heat_capacity = air.heat_capacity() + var/heat_efficency = min(cached_gases[/datum/gas/oxygen][MOLES], cached_gases[/datum/gas/nitrogen][MOLES]) + var/energy_used = heat_efficency * NITROUS_FORMATION_ENERGY + ASSERT_GAS(/datum/gas/nitrous_oxide,air) + if ((cached_gases[/datum/gas/oxygen][MOLES] - heat_efficency < 0 ) || (cached_gases[/datum/gas/nitrogen][MOLES] - heat_efficency < 0)) //Shouldn't produce gas from nothing. + return NO_REACTION + if (temperature > 250) //maximum allowed temperature for the reaction + return NO_REACTION + cached_gases[/datum/gas/oxygen][MOLES] -= heat_efficency + cached_gases[/datum/gas/nitrogen][MOLES] -= heat_efficency * 2 + cached_gases[/datum/gas/nitrous_oxide][MOLES] += heat_efficency + + if(energy_used > 0) + var/new_heat_capacity = air.heat_capacity() + if(new_heat_capacity > MINIMUM_HEAT_CAPACITY) + air.temperature = max(((temperature * old_heat_capacity + energy_used) / new_heat_capacity),TCMB) //the air heats up when reacting + return REACTING + +/datum/gas_reaction/nitrylformation //The formation of nitryl. Endothermic. Requires bz. priority = 3 name = "Nitryl formation" id = "nitrylformation" /datum/gas_reaction/nitrylformation/init_reqs() min_requirements = list( - /datum/gas/oxygen = 20, + /datum/gas/oxygen = 10, /datum/gas/nitrogen = 20, - /datum/gas/pluoxium = 5, - "TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST*60 + /datum/gas/bz = 5, + "TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST * 60 ) /datum/gas_reaction/nitrylformation/react(datum/gas_mixture/air) @@ -395,19 +429,20 @@ datum/gas_reaction/freonfire/react(datum/gas_mixture/air, datum/holder) var/temperature = air.temperature var/old_heat_capacity = air.heat_capacity() - var/heat_efficency = min(temperature/(FIRE_MINIMUM_TEMPERATURE_TO_EXIST*100),cached_gases[/datum/gas/oxygen][MOLES],cached_gases[/datum/gas/nitrogen][MOLES]) - var/energy_used = heat_efficency*NITRYL_FORMATION_ENERGY + var/heat_efficency = min(temperature/(FIRE_MINIMUM_TEMPERATURE_TO_EXIST * 100),cached_gases[/datum/gas/oxygen][MOLES],cached_gases[/datum/gas/nitrogen][MOLES]) + var/energy_used = heat_efficency * NITRYL_FORMATION_ENERGY ASSERT_GAS(/datum/gas/nitryl,air) - if ((cached_gases[/datum/gas/oxygen][MOLES] - heat_efficency < 0 )|| (cached_gases[/datum/gas/nitrogen][MOLES] - heat_efficency < 0)) //Shouldn't produce gas from nothing. + if ((cached_gases[/datum/gas/oxygen][MOLES] - heat_efficency < 0 ) || (cached_gases[/datum/gas/nitrogen][MOLES] - heat_efficency < 0) || (cached_gases[/datum/gas/bz][MOLES] - heat_efficency < 0)) //Shouldn't produce gas from nothing. return NO_REACTION - cached_gases[/datum/gas/oxygen][MOLES] -= heat_efficency + cached_gases[/datum/gas/oxygen][MOLES] -= heat_efficency * 2 cached_gases[/datum/gas/nitrogen][MOLES] -= heat_efficency - cached_gases[/datum/gas/nitryl][MOLES] += heat_efficency*2 + cached_gases[/datum/gas/bz][MOLES] -= heat_efficency * 0.05 //bz gets consumed to balance the nitryl production and not make it too common and/or easy + cached_gases[/datum/gas/nitryl][MOLES] += heat_efficency if(energy_used > 0) var/new_heat_capacity = air.heat_capacity() if(new_heat_capacity > MINIMUM_HEAT_CAPACITY) - air.temperature = max(((temperature*old_heat_capacity - energy_used)/new_heat_capacity),TCMB) + air.temperature = max(((temperature * old_heat_capacity - energy_used) / new_heat_capacity),TCMB) //the air cools down when reacting return REACTING /datum/gas_reaction/bzformation //Formation of BZ by combining plasma and tritium at low pressures. Exothermic. @@ -458,7 +493,7 @@ datum/gas_reaction/freonfire/react(datum/gas_mixture/air, datum/holder) /datum/gas/plasma = 40, /datum/gas/carbon_dioxide = 20, /datum/gas/bz = 20, - "TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + "TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100 ) /datum/gas_reaction/freonformation/react(datum/gas_mixture/air) @@ -472,7 +507,7 @@ datum/gas_reaction/freonfire/react(datum/gas_mixture/air, datum/holder) return NO_REACTION cached_gases[/datum/gas/plasma][MOLES] -= heat_efficency * 5 cached_gases[/datum/gas/carbon_dioxide][MOLES] -= heat_efficency - cached_gases[/datum/gas/bz][MOLES] -= heat_efficency * 0.5 + cached_gases[/datum/gas/bz][MOLES] -= heat_efficency * 0.25 cached_gases[/datum/gas/freon][MOLES] += heat_efficency * 2 if(energy_used > 0) diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index 64f09c89d83e..3ad5860524e3 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -65,7 +65,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 4 active_power_usage = 8 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON req_access = list(ACCESS_ATMOSPHERICS) max_integrity = 250 integrity_failure = 0.33 @@ -73,7 +73,7 @@ resistance_flags = FIRE_PROOF ui_x = 440 ui_y = 650 - + FASTDMM_PROP(\ set_instance_vars(\ pixel_x = (dir & 3)? INSTANCE_VAR_DEFAULT : (dir == 4 ? -24 : 24),\ @@ -252,7 +252,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "airalarm", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AirAlarm", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/airalarm/ui_data(mob/user) @@ -389,7 +389,7 @@ if(usr.has_unlimited_silicon_privilege && !wires.is_cut(WIRE_IDSCAN)) locked = !locked . = TRUE - if("power", "toggle_filter", "widenet", "scrubbing") + if("power", "toggle_filter", "widenet", "scrubbing", "direction") send_signal(device_id, list("[action]" = params["val"]), usr) . = TRUE if("excheck") diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index 350f9152d41b..d775e10bfc42 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -15,7 +15,7 @@ move_resist = INFINITY //Moving a connected machine without actually doing the normal (dis)connection things will probably cause a LOT of issues. idle_power_usage = 0 active_power_usage = 0 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON layer = GAS_PIPE_HIDDEN_LAYER //under wires resistance_flags = FIRE_PROOF max_integrity = 200 @@ -66,6 +66,7 @@ nullifyNode(i) SSair.atmos_machinery -= src + SSair.pipenets_needing_rebuilt -= src if(SSair.currentpart == SSAIR_ATMOSMACHINERY) SSair.currentrun -= src diff --git a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm index 6bafba9abc39..b9c864a4b058 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm @@ -116,7 +116,7 @@ if(node2) node2.atmosinit() node2.addMember(src) - build_network() + SSair.add_to_rebuild_queue(src) return TRUE diff --git a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm index 2a6484648f6d..5cf5d12d0d47 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm @@ -29,6 +29,20 @@ Passive gate is similar to the regular pump except: ui_x = 335 ui_y = 115 +/obj/machinery/atmospherics/components/binary/passive_gate/CtrlClick(mob/user) + if(can_interact(user)) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS) + update_icon() + return ..() + +/obj/machinery/atmospherics/components/binary/passive_gate/AltClick(mob/user) + if(can_interact(user)) + target_pressure = MAX_OUTPUT_PRESSURE + investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) + update_icon() + return ..() + /obj/machinery/atmospherics/components/binary/passive_gate/Destroy() SSradio.remove_object(src,frequency) return ..() @@ -94,7 +108,7 @@ Passive gate is similar to the regular pump except: datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_pump", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/atmospherics/components/binary/passive_gate/ui_data() diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm index e95eb56be5ad..5cc297c6c563 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm @@ -31,14 +31,16 @@ ui_y = 115 /obj/machinery/atmospherics/components/binary/pump/CtrlClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(can_interact(user)) on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS) update_icon() return ..() /obj/machinery/atmospherics/components/binary/pump/AltClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(can_interact(user)) target_pressure = MAX_OUTPUT_PRESSURE + investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) update_icon() return ..() @@ -100,7 +102,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_pump", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/atmospherics/components/binary/pump/ui_data() diff --git a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm index 41007137b2cd..3fd93d37ba30 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm @@ -32,14 +32,16 @@ ui_y = 115 /obj/machinery/atmospherics/components/binary/volume_pump/CtrlClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(can_interact(user)) on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS) update_icon() return ..() /obj/machinery/atmospherics/components/binary/volume_pump/AltClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(can_interact(user)) transfer_rate = MAX_TRANSFER_RATE + investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS) update_icon() return ..() @@ -113,7 +115,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_pump", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/atmospherics/components/binary/volume_pump/ui_data() diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm index aab9f3f464c1..6cc35a93ca90 100644 --- a/code/modules/atmospherics/machinery/components/components_base.dm +++ b/code/modules/atmospherics/machinery/components/components_base.dm @@ -156,7 +156,7 @@ var/datum/pipeline/parent = parents[i] if(!parent) WARNING("Component is missing a pipenet! Rebuilding...") - build_network() + SSair.add_to_rebuild_queue(src) parent = parents[i] parent.update = 1 diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm index 74fc85c37ac7..c29422eddb48 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm @@ -18,14 +18,16 @@ ui_y = 187 /obj/machinery/atmospherics/components/trinary/filter/CtrlClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(can_interact(user)) on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS) update_icon() return ..() /obj/machinery/atmospherics/components/trinary/filter/AltClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(can_interact(user)) transfer_rate = MAX_TRANSFER_RATE + investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS) update_icon() return ..() @@ -123,7 +125,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_filter", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AtmosFilter", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/atmospherics/components/trinary/filter/ui_data() diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm index 440adce51d6c..68f82177e425 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm @@ -20,14 +20,16 @@ //node 3 is the outlet, nodes 1 & 2 are intakes /obj/machinery/atmospherics/components/trinary/mixer/CtrlClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(can_interact(user)) on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS) update_icon() return ..() /obj/machinery/atmospherics/components/trinary/mixer/AltClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(can_interact(user)) target_pressure = MAX_OUTPUT_PRESSURE + investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) update_icon() return ..() @@ -129,7 +131,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_mixer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AtmosMixer", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/atmospherics/components/trinary/mixer/ui_data() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index fb1c8524c8b2..bbd979e1b408 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -83,7 +83,13 @@ /obj/machinery/atmospherics/components/unary/cryo_cell/contents_explosion(severity, target) ..() if(beaker) - beaker.ex_act(severity, target) + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += beaker + if(EXPLODE_HEAVY) + SSexplosions.medobj += beaker + if(EXPLODE_LIGHT) + SSexplosions.lowobj += beaker /obj/machinery/atmospherics/components/unary/cryo_cell/handle_atom_del(atom/A) ..() @@ -204,7 +210,7 @@ if(beaker) if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic. beaker.reagents.trans_to(occupant, 1, efficiency * 0.25) // Transfer reagents. - beaker.reagents.reaction(occupant, VAPOR) + beaker.reagents.expose(occupant, VAPOR) air1.gases[/datum/gas/oxygen][MOLES] -= max(0,air1.gases[/datum/gas/oxygen][MOLES] - 2 / efficiency) //Let's use gas for this air1.garbage_collect() if(++reagent_transfer >= 10 * efficiency) // Throttle reagent transfer (higher efficiency will transfer the same amount but consume less from the beaker). @@ -333,7 +339,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "cryo", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Cryo", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/atmospherics/components/unary/cryo_cell/ui_data() @@ -415,13 +421,13 @@ . = TRUE /obj/machinery/atmospherics/components/unary/cryo_cell/CtrlClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && !state_open) + if(can_interact(user) && !state_open) on = !on update_icon() return ..() /obj/machinery/atmospherics/components/unary/cryo_cell/AltClick(mob/user) - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(can_interact(user)) if(state_open) close_machine() else @@ -461,6 +467,6 @@ if(node) node.atmosinit() node.addMember(src) - build_network() + SSair.add_to_rebuild_queue(src) #undef CRYOMOBS diff --git a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm index f495583884fb..d8c9957f350a 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm @@ -26,6 +26,20 @@ ui_x = 310 ui_y = 115 +/obj/machinery/atmospherics/components/unary/outlet_injector/CtrlClick(mob/user) + if(can_interact(user)) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS) + update_icon() + return ..() + +/obj/machinery/atmospherics/components/unary/outlet_injector/AltClick(mob/user) + if(can_interact(user)) + volume_rate = MAX_TRANSFER_RATE + investigate_log("was set to [volume_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS) + update_icon() + return ..() + /obj/machinery/atmospherics/components/unary/outlet_injector/Destroy() SSradio.remove_object(src,frequency) return ..() @@ -116,7 +130,7 @@ on = !on if("inject" in signal.data) - spawn inject() + INVOKE_ASYNC(src, .proc/inject) return if("set_volume_rate" in signal.data) @@ -134,7 +148,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_pump", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/atmospherics/components/unary/outlet_injector/ui_data() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm index 541550e1053b..4ddbd1ba6310 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm @@ -20,34 +20,10 @@ /obj/machinery/atmospherics/components/unary/passive_vent/process_atmos() ..() - var/active = FALSE - var/datum/gas_mixture/external = loc.return_air() var/datum/gas_mixture/internal = airs[1] - var/external_pressure = external.return_pressure() - var/internal_pressure = internal.return_pressure() - var/pressure_delta = abs(external_pressure - internal_pressure) - - if(pressure_delta > 0.5) - if(external_pressure < internal_pressure) - var/air_temperature = (external.temperature > 0) ? external.temperature : internal.temperature - var/transfer_moles = (pressure_delta * external.volume) / (air_temperature * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = internal.remove(transfer_moles) - external.merge(removed) - else - var/air_temperature = (internal.temperature > 0) ? internal.temperature : external.temperature - var/transfer_moles = (pressure_delta * internal.volume) / (air_temperature * R_IDEAL_GAS_EQUATION) - transfer_moles = min(transfer_moles, external.total_moles() * internal.volume / external.volume) - var/datum/gas_mixture/removed = external.remove(transfer_moles) - if(isnull(removed)) - return - internal.merge(removed) - - active = TRUE - - active = internal.temperature_share(external, OPEN_HEAT_TRANSFER_COEFFICIENT) ? TRUE : active - - if(active) + + if(internal.equalize(external)) air_update_turf() update_parents() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm index 8fd1b3b26d51..4e9407dde9a1 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm @@ -115,7 +115,7 @@ if(node) node.atmosinit() node.addMember(src) - build_network() + SSair.add_to_rebuild_queue(src) return TRUE /obj/machinery/atmospherics/components/unary/thermomachine/ui_status(mob/user) @@ -127,7 +127,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "thermomachine", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "ThermoMachine", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/atmospherics/components/unary/thermomachine/ui_data(mob/user) @@ -182,6 +182,7 @@ if(!istype(user) || !user.canUseTopic(src, BE_CLOSE)) return on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS) update_icon() investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", INVESTIGATE_ATMOS) message_admins("[src.name] was turned [on ? "on" : "off"] [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") @@ -226,8 +227,8 @@ if(!istype(user) || !user.canUseTopic(src, BE_CLOSE)) return target_temperature = min_temperature - investigate_log("was set to [target_temperature] K by [key_name(usr)]", INVESTIGATE_ATMOS) - message_admins("[src.name] was minimized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") + investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS) + message_admins("[src.name] was minimized by [ADMIN_LOOKUPFLW(user)] at [ADMIN_COORDJMP(T)], [A]") return TRUE /obj/machinery/atmospherics/components/unary/thermomachine/heater @@ -258,6 +259,6 @@ if(!istype(user) || !user.canUseTopic(src, BE_CLOSE)) return target_temperature = max_temperature - investigate_log("was set to [target_temperature] K by [key_name(usr)]", INVESTIGATE_ATMOS) - message_admins("[src.name] was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") + investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS) + message_admins("[src.name] was maximized by [ADMIN_LOOKUPFLW(user)] at [ADMIN_COORDJMP(T)], [A]") return TRUE diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm index 680dfd16a620..62a6da231d68 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm @@ -150,7 +150,7 @@ "device" = "VP", "timestamp" = world.time, "power" = on, - "direction" = pump_direction ? "release" : "siphon", + "direction" = pump_direction, "checks" = pressure_checks, "internal" = internal_pressure_bound, "external" = external_pressure_bound, @@ -295,7 +295,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/high_volume name = "large air vent" - power_channel = EQUIP + power_channel = AREA_USAGE_EQUIP /obj/machinery/atmospherics/components/unary/vent_pump/high_volume/New() ..() diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm index ac7219936264..c9b163c543fa 100644 --- a/code/modules/atmospherics/machinery/datum_pipeline.dm +++ b/code/modules/atmospherics/machinery/datum_pipeline.dm @@ -138,11 +138,7 @@ for(var/obj/machinery/atmospherics/pipe/member in members) member.air_temporary = new member.air_temporary.volume = member.volume - member.air_temporary.copy_from(air) - var/member_gases = member.air_temporary.gases - - for(var/id in member_gases) - member_gases[id][MOLES] *= member.volume/air.volume + member.air_temporary.copy_from(air, member.volume/air.volume) member.air_temporary.temperature = air.temperature @@ -265,7 +261,4 @@ //Update individual gas_mixtures by volume ratio for(var/i in GL) var/datum/gas_mixture/G = i - G.copy_from(total_gas_mixture) - var/list/G_gases = G.gases - for(var/id in G_gases) - G_gases[id][MOLES] *= G.volume/total_gas_mixture.volume + G.copy_from(total_gas_mixture, G.volume/total_gas_mixture.volume) diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index 239258a40584..ad0052784612 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/atmospherics/pipes/meter.dmi' icon_state = "meterX" layer = GAS_PUMP_LAYER - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 4 diff --git a/code/modules/atmospherics/machinery/pipes/layermanifold.dm b/code/modules/atmospherics/machinery/pipes/layermanifold.dm index cc29cbf8be33..5c598c0d9ecb 100644 --- a/code/modules/atmospherics/machinery/pipes/layermanifold.dm +++ b/code/modules/atmospherics/machinery/pipes/layermanifold.dm @@ -37,7 +37,7 @@ nodes = list() for(var/obj/machinery/atmospherics/A in needs_nullifying) A.disconnect(src) - A.build_network() + SSair.add_to_rebuild_queue(A) /obj/machinery/atmospherics/pipe/layer_manifold/proc/get_all_connected_nodes() return front_nodes + back_nodes + nodes diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm index 6b84bb04560c..ea87de083dfc 100644 --- a/code/modules/atmospherics/machinery/pipes/pipes.dm +++ b/code/modules/atmospherics/machinery/pipes/pipes.dm @@ -33,7 +33,7 @@ var/obj/machinery/atmospherics/oldN = nodes[i] ..() if(oldN) - oldN.build_network() + SSair.add_to_rebuild_queue(oldN) /obj/machinery/atmospherics/pipe/destroy_network() QDEL_NULL(parent) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index 3139ff42ec88..a2ccb9f91f6c 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -5,8 +5,8 @@ desc = "A canister for the storage of gas." icon_state = "yellow" density = TRUE - ui_x = 420 - ui_y = 405 + ui_x = 300 + ui_y = 232 var/valve_open = FALSE var/obj/machinery/atmospherics/components/binary/passive_gate/pump @@ -113,7 +113,7 @@ /obj/machinery/portable_atmospherics/canister/nob name = "hyper-noblium canister" desc = "Hyper-Noblium. More noble than all other gases." - icon_state = "freon" + icon_state = "nob" gas_type = /datum/gas/hypernoblium /obj/machinery/portable_atmospherics/canister/nitryl @@ -211,7 +211,7 @@ pump = new(src, FALSE) pump.on = TRUE pump.machine_stat = 0 - pump.build_network() + SSair.add_to_rebuild_queue(pump) /obj/machinery/portable_atmospherics/canister/Destroy() qdel(pump) @@ -254,6 +254,7 @@ . += "can-o0" WaspStation End */ + /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) @@ -343,7 +344,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "canister", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Canister", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/portable_atmospherics/canister/ui_data() diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm index d3768d3d216e..6c6075a2c905 100644 --- a/code/modules/atmospherics/machinery/portable/pump.dm +++ b/code/modules/atmospherics/machinery/portable/pump.dm @@ -22,7 +22,7 @@ pump = new(src, FALSE) pump.on = TRUE pump.machine_stat = 0 - pump.build_network() + SSair.add_to_rebuild_queue(pump) /obj/machinery/portable_atmospherics/pump/Destroy() var/turf/T = get_turf(src) @@ -87,7 +87,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "portable_pump", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "PortablePump", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/portable_atmospherics/pump/ui_data() diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm index 9729f6c49804..9b13b5020b57 100644 --- a/code/modules/atmospherics/machinery/portable/scrubber.dm +++ b/code/modules/atmospherics/machinery/portable/scrubber.dm @@ -3,7 +3,7 @@ icon_state = "pscrubber:0" density = TRUE ui_x = 320 - ui_y = 335 + ui_y = 350 var/on = FALSE var/volume_rate = 1000 @@ -78,7 +78,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "portable_scrubber", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "PortableScrubber", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/portable_atmospherics/scrubber/ui_data() diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 78227da1d772..22d2b7c619fe 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -3,9 +3,11 @@ //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" + name = "Mob Spawner" density = TRUE anchored = TRUE + icon = 'icons/effects/mapping_helpers.dmi' // These aren't *really* mapping helpers but it fits the most with it's common usage (to help place corpses in maps) + icon_state = "mobspawner" // So it shows up in the map editor var/mob_type = null var/mob_name = "" var/mob_gender = null @@ -221,6 +223,8 @@ var/obj/item/card/id/W = H.wear_id if(W) + if(H.age) + W.registered_age = H.age if(id_access) for(var/jobtype in typesof(/datum/job)) var/datum/job/J = new jobtype @@ -238,6 +242,7 @@ //Instant version - use when spawning corpses during runtime /obj/effect/mob_spawn/human/corpse + icon_state = "corpsehuman" roundstart = FALSE instant = TRUE @@ -253,6 +258,7 @@ /obj/effect/mob_spawn/human/corpse/delayed ghost_usable = FALSE //These are just not-yet-set corpses. instant = FALSE + invisibility = 101 // a fix for the icon not wanting to cooperate //Non-human spawners @@ -306,6 +312,7 @@ /obj/effect/mob_spawn/human/corpse/assistant name = "Assistant" outfit = /datum/outfit/job/assistant + icon_state = "corpsegreytider" /obj/effect/mob_spawn/human/corpse/assistant/beesease_infection disease = /datum/disease/beesease @@ -319,15 +326,18 @@ /obj/effect/mob_spawn/human/corpse/cargo_tech name = "Cargo Tech" outfit = /datum/outfit/job/cargo_tech + icon_state = "corpsecargotech" /obj/effect/mob_spawn/human/cook name = "Cook" outfit = /datum/outfit/job/cook + icon_state = "corpsecook" /obj/effect/mob_spawn/human/doctor name = "Doctor" outfit = /datum/outfit/job/doctor + icon_state = "corpsedoctor" /obj/effect/mob_spawn/human/doctor/alive @@ -351,6 +361,7 @@ /obj/effect/mob_spawn/human/engineer name = "Engineer" outfit = /datum/outfit/job/engineer/gloved + icon_state = "corpseengineer" /obj/effect/mob_spawn/human/engineer/rig outfit = /datum/outfit/job/engineer/gloved/rig @@ -358,14 +369,17 @@ /obj/effect/mob_spawn/human/clown name = "Clown" outfit = /datum/outfit/job/clown + icon_state = "corpseclown" /obj/effect/mob_spawn/human/scientist name = "Scientist" outfit = /datum/outfit/job/scientist + icon_state = "corpsescientist" /obj/effect/mob_spawn/human/miner name = "Shaft Miner" outfit = /datum/outfit/job/miner + icon_state = "corpseminer" /obj/effect/mob_spawn/human/miner/rig outfit = /datum/outfit/job/miner/equipped/hardsuit @@ -406,6 +420,14 @@ glasses = /obj/item/clothing/glasses/sunglasses/reagent id = /obj/item/card/id +/datum/outfit/spacebartender/post_equip(mob/living/carbon/human/H, visualsOnly) + . = ..() + + var/obj/item/card/id/W = H.wear_id + if(H.age < AGE_MINOR) + W.registered_age = AGE_MINOR + to_chat(H, "You're not technically old enough to access or serve alcohol, but your ID has been discreetly modified to display your age as [AGE_MINOR]. Try to keep that a secret!") + /obj/effect/mob_spawn/human/beach outfit = /datum/outfit/beachbum diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 65241e3513f9..2f7ae0f90651 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -279,7 +279,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations) . = ..() ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "gateway", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Gateway", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/gateway_control/ui_data(mob/user) @@ -297,6 +297,8 @@ GLOBAL_LIST_EMPTY(gateway_destinations) /obj/machinery/computer/gateway_control/ui_act(action, list/params) . = ..() + if(.) + return switch(action) if("linkup") try_to_linkup() diff --git a/code/modules/awaymissions/mission_code/moonoutpost19.dm b/code/modules/awaymissions/mission_code/moonoutpost19.dm index c0af9cd08cda..0feb096910a4 100644 --- a/code/modules/awaymissions/mission_code/moonoutpost19.dm +++ b/code/modules/awaymissions/mission_code/moonoutpost19.dm @@ -67,7 +67,7 @@ info = "Researcher: Dr. Mark Douglas
    Date: 17/06/2554

    Report:
    Earlier today we have observed a new phenomenon with our subjects. While feeding them our last monkey subject and throwing out the box, the aliens merely looked at us instead of infecting the monkey right away. They looked to be collectively distressed as they would no longer be given hosts, where instead we would move to the next phase of the experiment. When I glanced at the gas tanks and piping leading to their cell, I looked back to see all of them were up against the glass, even the queen! It was as if they all understood what was going to happen, even though we knew only the queen had the cognitive capability to do so.

    The only explanation for this is a form of communication between the aliens, but we have seen no such action take place anywhere in the cell until now. We also know that regular drone and hunter xenomorphs have no personality or instinct to survive by themselves. Perhaps the queen has a direct link to them? A form of a commander or overseer that controls their every move? A hivemind?" /obj/item/paper/fluff/awaymissions/moonoutpost19/research/xeno_behavior - name = "A Preliminary Study of Alien Behavior" + name = "\improper A Preliminary Study of Alien Behavior" info = "Researcher: Dr. Sakuma Sano
    Date: 08/06/2554

    Report:
    The xenomorphs we have come to study here are a remarkable species. They are almost universally aggressive across all castes, showing no remorse or guilt or pause before or after acts of violence. They appear to be a species entirely designed to kill. Oddly enough, even their method of reproduction is a brutal two-for-one method of birthing a new xenomorph and killing its host.

    The lone xenomorph we studied only five days ago showed little sign of intelligence. Only a simple drone that flung itself at the safety glass and shields repeatedly and thankfully without success. Once the drone molted into a queen, it became much more calm and calculating, merely looking at us and waiting while building its nest. As the hive grew in size and in numbers, so too did the intelligence of the common hunter and drone. We are still researching how they can communicate with one another and the relationship between the different castes and the queen. We will continue to update our research as we learn more about the species." /obj/item/paper/fluff/awaymissions/moonoutpost19/research/xeno_castes diff --git a/code/modules/awaymissions/pamphlet.dm b/code/modules/awaymissions/pamphlet.dm index b76c5dfe5853..8912a66aa937 100644 --- a/code/modules/awaymissions/pamphlet.dm +++ b/code/modules/awaymissions/pamphlet.dm @@ -1,6 +1,13 @@ /obj/item/paper/pamphlet name = "pamphlet" icon_state = "pamphlet" + show_written_words = FALSE + + +/obj/item/paper/pamphlet/violent_video_games + name = "pamphlet - \'Violent Video Games and You\'" + desc = "A pamphlet encouraging the reader to maintain a balanced lifestyle and take care of their mental health, while still enjoying video games in a healthy way. You probably don't need this..." + info = "They don't make you kill people. There, we said it. Now get back to work!" /obj/item/paper/pamphlet/gateway info = "Welcome to the Nanotrasen Gateway project...
    \ @@ -33,8 +40,3 @@ 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/ComponentInitialize() - . = ..() - AddElement(/datum/element/update_icon_blocker) diff --git a/code/modules/cargo/blackmarket/blackmarket_uplink.dm b/code/modules/cargo/blackmarket/blackmarket_uplink.dm index 35ba17e24185..3833b0505a5d 100644 --- a/code/modules/cargo/blackmarket/blackmarket_uplink.dm +++ b/code/modules/cargo/blackmarket/blackmarket_uplink.dm @@ -4,7 +4,7 @@ icon_state = "uplink" // UI variables. - var/ui_x = 720 + var/ui_x = 600 var/ui_y = 480 var/viewing_category var/viewing_market @@ -58,7 +58,7 @@ /obj/item/blackmarket_uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "blackmarket_uplink", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "BlackMarketUplink", name, ui_x, ui_y, master_ui, state) ui.open() /obj/item/blackmarket_uplink/ui_data(mob/user) diff --git a/code/modules/cargo/bounty_console.dm b/code/modules/cargo/bounty_console.dm index eed5ae2cf8e5..7424413c8e76 100644 --- a/code/modules/cargo/bounty_console.dm +++ b/code/modules/cargo/bounty_console.dm @@ -1,7 +1,5 @@ #define PRINTER_TIMEOUT 10 - - /obj/machinery/computer/bounty name = "\improper Nanotrasen bounty console" desc = "Used to check and claim bounties offered by Nanotrasen" @@ -9,10 +7,12 @@ circuit = /obj/item/circuitboard/computer/bounty light_color = "#E2853D"//orange var/printer_ready = 0 //cooldown var + var/static/datum/bank_account/cargocash /obj/machinery/computer/bounty/Initialize() . = ..() printer_ready = world.time + PRINTER_TIMEOUT + cargocash = SSeconomy.get_dep_account(ACCOUNT_CAR) /obj/machinery/computer/bounty/proc/print_paper() new /obj/item/paper/bounty_printout(loc) @@ -32,65 +32,34 @@
    • Reward: [B.reward_string()]
    • Completed: [B.completion_string()]
    "} -/obj/machinery/computer/bounty/ui_interact(mob/user) - . = ..() - +/obj/machinery/computer/bounty/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) if(!GLOB.bounties_list.len) setup_bounties() - - var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR) - var/list/dat = list({"Refresh - Print Paper -

    Credits: [D.account_balance]

    - - "}) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "CargoBountyConsole", name, 750, 600, master_ui, state) + ui.open() + +/obj/machinery/computer/bounty/ui_data(mob/user) + var/list/data = list() + var/list/bountyinfo = list() for(var/datum/bounty/B in GLOB.bounties_list) - if(B.claimed) - dat += "" - else if(B.can_claim()) - dat += "" - else - dat += "" + bountyinfo += list(list("name" = B.name, "description" = B.description, "reward_string" = B.reward_string(), "completion_string" = B.completion_string() , "claimed" = B.claimed, "can_claim" = B.can_claim(), "priority" = B.high_priority, "bounty_ref" = REF(B))) + data["stored_cash"] = cargocash.account_balance + data["bountydata"] = bountyinfo + return data - if(B.high_priority) - dat += {" - - "} - else - dat += {" - - "} - dat += "" - if(B.claimed) - dat += "" - else if(B.can_claim()) - dat += "" - else - dat += "" - dat += "" - dat += "
    NameDescriptionRewardCompletionStatus
    [B.name]High Priority: [B.description][B.reward_string()][B.name][B.description][B.reward_string()][B.completion_string()]ClaimedClaimUnclaimed
    " - dat = dat.Join() - var/datum/browser/popup = new(user, "bounties", "Nanotrasen Bounties", 700, 600) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - -/obj/machinery/computer/bounty/Topic(href, href_list) +/obj/machinery/computer/bounty/ui_act(action,params) if(..()) return - - switch(href_list["choice"]) + switch(action) + if("ClaimBounty") + var/datum/bounty/cashmoney = locate(params["bounty"]) in GLOB.bounties_list + if(cashmoney) + cashmoney.claim() + return TRUE if("Print") if(printer_ready < world.time) printer_ready = world.time + PRINTER_TIMEOUT print_paper() - - if("Claim") - var/datum/bounty/B = locate(href_list["d_rec"]) in GLOB.bounties_list - if(B) - B.claim() - - if(href_list["refresh"]) - playsound(src, "terminal_type", 25, FALSE) - - updateUsrDialog() + return diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index c557a207b99e..7c6aa16c5dd7 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -56,7 +56,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "centcom_podlauncher", "Config/Launch Supplypod", 700, 700, master_ui, state) + ui = new(user, src, ui_key, "CentcomPodLauncher", "Config/Launch Supplypod", 700, 700, master_ui, state) ui.open() /datum/centcom_podlauncher/ui_data(mob/user) //Sends info about the pod to the UI. @@ -445,8 +445,8 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm /datum/centcom_podlauncher/proc/updateCursor(var/launching) //Update the moues of the user if (holder) //Check to see if we have a client if (launching) //If the launching param is true, we give the user new mouse icons. - holder.mouse_up_icon = 'icons/effects/supplypod_target.dmi' //Icon for when mouse is released - holder.mouse_down_icon = 'icons/effects/supplypod_down_target.dmi' //Icon for when mouse is pressed + holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_target.dmi' //Icon for when mouse is released + holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_down_target.dmi' //Icon for when mouse is pressed holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released) holder.click_intercept = src //Create a click_intercept so we know where the user is clicking else diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm index e8bf847b4997..c07abe8f104e 100644 --- a/code/modules/cargo/console.dm +++ b/code/modules/cargo/console.dm @@ -68,7 +68,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "cargo", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Cargo", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/cargo/ui_data() diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm index e9bde3d46e47..0acf30c65b12 100644 --- a/code/modules/cargo/exports.dm +++ b/code/modules/cargo/exports.dm @@ -66,7 +66,7 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they /datum/export var/unit_name = "" // Unit name. Only used in "Received [total_amount] [name]s [message]." message var/message = "" - var/cost = 100 // Cost of item, in cargo credits. Must not alow for infinite price dupes, see above. + var/cost = 1 // Cost of item, in cargo credits. Must not alow for infinite price dupes, see above. var/k_elasticity = 1/30 //coefficient used in marginal price calculation that roughly corresponds to the inverse of price elasticity, or "quantity elasticity" var/list/export_types = list() // Type of the exported object. If none, the export datum is considered base type. var/include_subtypes = TRUE // Set to FALSE to make the datum apply only to a strict type. @@ -82,7 +82,7 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they ..() SSprocessing.processing += src init_cost = cost - export_types = typecacheof(export_types) + export_types = typecacheof(export_types, FALSE, !include_subtypes) exclude_types = typecacheof(exclude_types) /datum/export/Destroy() @@ -115,9 +115,9 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they /datum/export/proc/applies_to(obj/O, allowed_categories = NONE, apply_elastic = TRUE) if((allowed_categories & export_category) != export_category) return FALSE - if(!include_subtypes && !(O.type in export_types)) + if(!is_type_in_typecache(O, export_types)) return FALSE - if(include_subtypes && (!is_type_in_typecache(O, export_types) || is_type_in_typecache(O, exclude_types))) + if(include_subtypes && is_type_in_typecache(O, exclude_types)) return FALSE if(!get_cost(O, allowed_categories , apply_elastic)) return FALSE @@ -125,22 +125,31 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they return FALSE return TRUE -// Called only once, when the object is actually sold by the datum. -// Adds item's cost and amount to the current export cycle. -// get_cost, get_amount and applies_to do not neccesary mean a successful sale. +/** + * Calculates the exact export value of the object, while factoring in all the relivant variables. + * + * Called only once, when the object is actually sold by the datum. + * Adds item's cost and amount to the current export cycle. + * get_cost, get_amount and applies_to do not neccesary mean a successful sale. + * + */ /datum/export/proc/sell_object(obj/O, datum/export_report/report, dry_run = TRUE, allowed_categories = EXPORT_CARGO , apply_elastic = TRUE) + ///This is the value of the object, as derived from export datums. var/the_cost = get_cost(O, allowed_categories , apply_elastic) + ///Quantity of the object in question. var/amount = get_amount(O) + ///Utilized in the pricetag component. Splits the object's profit when it has a pricetag by the specified amount. var/profit_ratio = 0 + if(amount <=0 || the_cost <=0) return FALSE if(dry_run == FALSE) if(SEND_SIGNAL(O, COMSIG_ITEM_SOLD, item_value = get_cost(O, allowed_categories , apply_elastic)) & COMSIG_ITEM_SPLIT_VALUE) - profit_ratio = SEND_SIGNAL(O, COMSIG_ITEM_SPLIT_PROFIT) - the_cost = the_cost*((100-profit_ratio)/100) + profit_ratio = SEND_SIGNAL(O, COMSIG_ITEM_SPLIT_PROFIT_DRY) + the_cost = the_cost * ((100 - profit_ratio) * 0.01) else profit_ratio = SEND_SIGNAL(O, COMSIG_ITEM_SPLIT_PROFIT) - the_cost = the_cost*((100-profit_ratio)/100) + the_cost = the_cost * ((100 - profit_ratio) * 0.01) report.total_value[src] += the_cost if(istype(O, /datum/export/material)) diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm index 59f406b713b5..713fb5c3e4cc 100644 --- a/code/modules/cargo/exports/large_objects.dm +++ b/code/modules/cargo/exports/large_objects.dm @@ -121,3 +121,25 @@ cost = 25 unit_name = "security barrier" export_types = list(/obj/item/grenade/barrier, /obj/structure/barricade/security) +/* Waspstation Begin - Cargo Rebalance? + +/datum/export/large/gas_canister + cost = 10 //Base cost of canister. You get more for nice gases inside. + unit_name = "Gas Canister" + export_types = list(/obj/machinery/portable_atmospherics/canister) +/datum/export/large/gas_canister/get_cost(obj/O) + var/obj/machinery/portable_atmospherics/canister/C = O + var/worth = 10 + var/gases = C.air_contents.gases + C.air_contents.assert_gases(/datum/gas/bz,/datum/gas/stimulum,/datum/gas/hypernoblium,/datum/gas/miasma,/datum/gas/tritium,/datum/gas/pluoxium,/datum/gas/freon) + + worth += gases[/datum/gas/bz][MOLES]*4 + worth += gases[/datum/gas/stimulum][MOLES]*100 + worth += gases[/datum/gas/hypernoblium][MOLES]*1000 + worth += gases[/datum/gas/miasma][MOLES]*10 + worth += gases[/datum/gas/tritium][MOLES]*5 + worth += gases[/datum/gas/pluoxium][MOLES]*5 + worth += gases[/datum/gas/freon][MOLES]*15 + return worth + +Waspstation end */ diff --git a/code/modules/cargo/exports/parts.dm b/code/modules/cargo/exports/parts.dm index 2b0a1447b21d..0df089543989 100644 --- a/code/modules/cargo/exports/parts.dm +++ b/code/modules/cargo/exports/parts.dm @@ -50,7 +50,7 @@ include_subtypes = FALSE -/datum/export/modular_part/battery +/datum/export/modular_part/battery/upgraded cost = 100 unit_name = "upgraded computer power cell" export_types = list(/obj/item/stock_parts/cell/computer/micro) diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm index af01a8246844..440c0dcbcd47 100644 --- a/code/modules/cargo/expressconsole.dm +++ b/code/modules/cargo/expressconsole.dm @@ -91,7 +91,7 @@ /obj/machinery/computer/cargo/express/ui_interact(mob/living/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state. ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "cargo_express", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "CargoExpress", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/cargo/express/ui_data(mob/user) diff --git a/code/modules/cargo/gondolapod.dm b/code/modules/cargo/gondolapod.dm index 7ecbc6fd152c..1216c55fca66 100644 --- a/code/modules/cargo/gondolapod.dm +++ b/code/modules/cargo/gondolapod.dm @@ -42,7 +42,7 @@ set name = "Release Contents" set category = "Gondola" set desc = "Release any contents stored within your vast belly." - linked_pod.open(src, forced = TRUE) + linked_pod.open_pod(src, forced = TRUE) /mob/living/simple_animal/pet/gondola/gondolapod/examine(mob/user) . = ..() diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index 0540db14a22e..2d4a3ab8d562 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -237,7 +237,7 @@ /datum/supply_pack/security group = "Security" - access = ACCESS_SECURITY + access_any = ACCESS_FORENSICS_LOCKERS //| ACCESS_SECURITY crate_type = /obj/structure/closet/crate/secure/gear /datum/supply_pack/security/ammo @@ -276,6 +276,16 @@ crate_name = "disabler crate" dangerous = TRUE +/datum/supply_pack/security/dumdum + name = ".38 DumDum Speedloader" + desc = "Contains one speedloader of .38 DumDum ammunition, good for embedding in soft targets. Requires Security or Forensics access to open." + cost = 1200 + access = FALSE + small_item = TRUE + access_any = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) + contains = list(/obj/item/ammo_box/c38/dumdum) + crate_name = ".38 match crate" + /datum/supply_pack/security/forensics name = "Forensics Crate" desc = "Stay hot on the criminal's heels with Nanotrasen's Detective Essentials(tm). Contains a forensics scanner, six evidence bags, camera, tape recorder, white crayon, and of course, a fedora. Requires Security access to open." @@ -307,6 +317,16 @@ crate_name = "laser crate" dangerous = TRUE +/datum/supply_pack/security/match + name = ".38 Match Grade Speedloader" + desc = "Contains one speedloader of match grade .38 ammunition, perfect for showing off trickshots. Requires Security or Forensics access to open." + cost = 1200 + access = FALSE + small_item = TRUE + access_any = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) + contains = list(/obj/item/ammo_box/c38/match) + crate_name = ".38 match crate" + /datum/supply_pack/security/securitybarriers name = "Security Barrier Grenades" desc = "Stem the tide with four Security Barrier grenades. Requires Security access to open." @@ -338,6 +358,20 @@ /obj/item/clothing/head/beret/sec/navyhos/black) crate_name = "security clothing crate" +/datum/supply_pack/security/stingpack + name = "Stingbang Grenade Pack" + desc = "Contains five \"stingbang\" grenades, perfect for stopping riots and playing morally unthinkable pranks. Requires Security access to open." + cost = 2500 + contains = list(/obj/item/storage/box/stingbangs) + crate_name = "stingbang grenade pack crate" + +/datum/supply_pack/security/stingpack/single + name = "Stingbang Single-Pack" + desc = "Contains one \"stingbang\" grenade, perfect for playing meanhearted pranks. Requires Security access to open." + cost = 1400 + small_item = TRUE + contains = list(/obj/item/grenade/stingbang) + /datum/supply_pack/security/supplies name = "Security Supplies Crate" desc = "Contains seven flashbangs, seven teargas grenades, six flashes, and seven handcuffs. Requires Security access to open." @@ -2288,7 +2322,7 @@ /datum/supply_pack/costumes_toys/randomised/toys name = "Toy Crate" desc = "Who cares about pride and accomplishment? Skip the gaming and get straight to the sweet rewards with this product! Contains five random toys. Warranty void if used to prank research directors." - cost = 5000 // or play the arcade machines ya lazy bum + cost = 4000 // or play the arcade machines ya lazy bum num_contained = 5 contains = list() crate_name = "toy crate" @@ -2517,7 +2551,7 @@ /datum/supply_pack/misc/religious_supplies name = "Religious Supplies Crate" desc = "Keep your local chaplain happy and well-supplied, lest they call down judgement upon your cargo bay. Contains two bottles of holywater, bibles, chaplain robes, and burial garmets." - cost = 4000 // it costs so much because the Space Church is ran by Space Jews + cost = 4000 // it costs so much because the Space Church needs funding to build a cathedral contains = list(/obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/book/bible/booze, diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index 1603d54dd9e3..29ee4c2bd7b9 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -73,7 +73,7 @@ /obj/structure/closet/supplypod/extractionpod/specialisedPod(atom/movable/holder) holder.forceMove(pick(GLOB.holdingfacility)) // land in ninja jail - open(holder, forced = TRUE) + open_pod(holder, forced = TRUE) /obj/structure/closet/supplypod/Initialize() . = ..() @@ -112,7 +112,10 @@ /obj/structure/closet/supplypod/contents_explosion() //Supplypods also protect their contents from the harmful effects of fucking exploding. return -/obj/structure/closet/supplypod/toggle(mob/living/user) //Supplypods shouldn't be able to be manually opened under any circumstances, as the open() proc generates supply order datums +/obj/structure/closet/supplypod/toggle(mob/living/user) + return + +/obj/structure/closet/supplypod/open(mob/living/user, force = TRUE) //Supplypods shouldn't be able to be manually opened under any circumstances return /obj/structure/closet/supplypod/proc/handleReturningClose(atom/movable/holder, returntobay) @@ -130,7 +133,7 @@ QDEL_IN(risingPod, 10) reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() ) bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever - open(holder, forced = TRUE) + open_pod(holder, forced = TRUE) else reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() ) bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever @@ -189,11 +192,11 @@ moveToNullspace() addtimer(CALLBACK(src, .proc/open, benis), openingDelay) //After the openingDelay passes, we use the open proc from this supplyprod while referencing the contents of the "holder", in this case the gondolapod mob else if (style == STYLE_SEETHROUGH) - open(src) + open_pod(src) else - addtimer(CALLBACK(src, .proc/open, src), openingDelay) //After the openingDelay passes, we use the open proc from this supplypod, while referencing this supplypod's contents + addtimer(CALLBACK(src, .proc/open_pod, src), openingDelay) //After the openingDelay passes, we use the open proc from this supplypod, while referencing this supplypod's contents -/obj/structure/closet/supplypod/open(atom/movable/holder, broken = FALSE, forced = FALSE) //The holder var represents an atom whose contents we will be working with +/obj/structure/closet/supplypod/proc/open_pod(atom/movable/holder, broken = FALSE, forced = FALSE) //The holder var represents an atom whose contents we will be working with if (!holder) return if (opened) //This is to ensure we don't open something that has already been opened @@ -257,7 +260,7 @@ update_icon() /obj/structure/closet/supplypod/Destroy() - open(src, broken = TRUE) //Lets dump our contents by opening up + open_pod(holder = src, broken = TRUE) //Lets dump our contents by opening up . = ..() //------------------------------------FALLING SUPPLY POD-------------------------------------// diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 15a71b088445..c07368b81912 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -125,3 +125,42 @@ var/keysend_tripped = FALSE ///custom movement keys for this client var/list/movement_keys = list() + + ///Autoclick list of two elements, first being the clicked thing, second being the parameters. + var/list/atom/selected_target[2] + ///Autoclick variable referencing the associated item. + var/obj/item/active_mousedown_item = null + ///Used in MouseDrag to preserve the original mouse click parameters + var/mouseParams = "" + ///Used in MouseDrag to preserve the last mouse-entered location. + var/mouseLocation = null + ///Used in MouseDrag to preserve the last mouse-entered object. + var/mouseObject = null + //Middle-mouse-button click dragtime control for aimbot exploit detection. + var/middragtime = 0 + //Middle-mouse-button clicked object control for aimbot exploit detection. + var/atom/middragatom + + /// Messages currently seen by this client + var/list/seen_messages + + var/datum/viewData/view_size + + var/list/parallax_layers + var/list/parallax_layers_cached + var/atom/movable/movingmob + var/turf/previous_turf + ///world.time of when we can state animate()ing parallax again + var/dont_animate_parallax + ///world.time of last parallax update + var/last_parallax_shift + ///ds between parallax updates + var/parallax_throttle = 0 + var/parallax_movedir = 0 + var/parallax_layers_max = 4 + var/parallax_animate_timer + ///A lazy list of atoms we've examined in the last EXAMINE_MORE_TIME (default 1.5) seconds, so that we will call [atom/proc/examine_more()] instead of [atom/proc/examine()] on them when examining + var/list/recent_examines + + /// rate limiting for the crew manifest + var/crew_manifest_delay diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 6428adb7115b..9bd54ea9ffc4 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -39,11 +39,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( // asset_cache var/asset_cache_job if(href_list["asset_cache_confirm_arrival"]) - asset_cache_job = round(text2num(href_list["asset_cache_confirm_arrival"])) - //because we skip the limiter, we have to make sure this is a valid arrival and not somebody tricking us - // into letting append to a list without limit. - if (asset_cache_job > 0 && asset_cache_job <= last_asset_job && !(asset_cache_job in completed_asset_jobs)) - completed_asset_jobs += asset_cache_job + asset_cache_job = asset_cache_confirm_arrival(href_list["asset_cache_confirm_arrival"]) + if (!asset_cache_job) return var/mtl = CONFIG_GET(number/minute_topic_limit) @@ -102,6 +99,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if (asset_cache_job && (asset_cache_job in completed_asset_jobs)) to_chat(src, "An error has been detected in how your client is receiving resources. Attempting to correct.... (If you keep seeing these messages you might want to close byond and reconnect)") src << browse("...", "window=asset_cache_browser") + return + if (href_list["asset_cache_preload_data"]) + asset_cache_preload_data(href_list["asset_cache_preload_data"]) + return // Keypress passthrough if(href_list["__keydown"]) @@ -489,6 +490,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) GLOB.directory -= ckey log_access("Logout: [key_name(src)]") GLOB.ahelp_tickets.ClientLogout(src) + SSserver_maint.UpdateHubStatus() if(credits) QDEL_LIST(credits) if(holder) @@ -516,6 +518,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) if(movingmob != null) movingmob.client_mobs_in_contents -= mob UNSETEMPTY(movingmob.client_mobs_in_contents) + seen_messages = null Master.UpdateTickRate() . = ..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening return QDEL_HINT_HARDDEL_NOW @@ -619,6 +622,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) var/datum/DBQuery/query_log_connection = SSdbcore.NewQuery("INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`server_ip`,`server_port`,`round_id`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')),'[world.port]','[GLOB.round_id]','[sql_ckey]',INET_ATON('[sql_ip]'),'[sql_computerid]')") query_log_connection.Execute() qdel(query_log_connection) + + SSserver_maint.UpdateHubStatus() + if(new_player) player_age = -1 . = player_age @@ -873,8 +879,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) 'html/browser/playeroptions.css', ) spawn (10) //removing this spawn causes all clients to not get verbs. + + //load info on what assets the client has + src << browse('code/modules/asset_cache/validate_assets.html', "window=asset_cache_browser") + //Precache the client with all other assets slowly, so as to not block other browse() calls - getFilesSlow(src, SSassets.preload, register_asset = FALSE) + addtimer(CALLBACK(GLOBAL_PROC, /proc/getFilesSlow, src, SSassets.preload, FALSE), 5 SECONDS) + #if (PRELOAD_RSC == 0) for (var/name in GLOB.vox_sounds) var/file = GLOB.vox_sounds[name] diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 771999db6488..5a3177714370 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -28,6 +28,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/UI_style = null var/buttons_locked = FALSE var/hotkeys = TRUE + var/chat_on_map = FALSE + var/max_chat_length = CHAT_MESSAGE_MAX_LENGTH + var/see_chat_non_mob = FALSE // Custom Keybindings var/list/key_bindings = list() @@ -727,6 +730,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "UI Style: [UI_style]
    " dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
    " dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
    " + dat += "Show Runechat Chat Bubbles: [chat_on_map ? "Enabled" : "Disabled"]
    " + dat += "Runechat message char limit: [max_chat_length]
    " + dat += "See Runechat for non-mobs: [see_chat_non_mob ? "Enabled" : "Disabled"]
    " dat += "
    " dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"]
    " dat += "Hotkey mode: [(hotkeys) ? "Hotkeys" : "Default"]
    " @@ -872,35 +878,40 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Hide Dead Chat: [(chat_toggles & CHAT_DEAD)?"Shown":"Hidden"]
    " dat += "Hide Radio Messages: [(chat_toggles & CHAT_RADIO)?"Shown":"Hidden"]
    " dat += "Hide Prayers: [(chat_toggles & CHAT_PRAYER)?"Shown":"Hidden"]
    " + dat += "Ignore Being Summoned as Cult Ghost: [(toggles & ADMIN_IGNORE_CULT_GHOST)?"Don't Allow Being Summoned":"Allow Being Summoned"]
    " if(CONFIG_GET(flag/allow_admin_asaycolor)) dat += "
    " dat += "ASAY Color:     Change
    " //deadmin dat += "

    Deadmin While Playing

    " - if(CONFIG_GET(flag/auto_deadmin_players)) + var/timegate = CONFIG_GET(number/auto_deadmin_timegate) + if(timegate) + dat += "Noted roles will automatically deadmin during the first [FLOOR(timegate / 600, 1)] minutes of the round, and will defer to individual preferences after.
    " + + if(CONFIG_GET(flag/auto_deadmin_players) && !timegate) dat += "Always Deadmin: FORCED
    " else - dat += "Always Deadmin: [(toggles & DEADMIN_ALWAYS)?"Enabled":"Disabled"]
    " + dat += "Always Deadmin: [timegate ? "(Time Locked) " : ""][(toggles & DEADMIN_ALWAYS)?"Enabled":"Disabled"]
    " if(!(toggles & DEADMIN_ALWAYS)) dat += "
    " - if(!CONFIG_GET(flag/auto_deadmin_antagonists)) - dat += "As Antag: [(toggles & DEADMIN_ANTAGONIST)?"Deadmin":"Keep Admin"]
    " + if(!CONFIG_GET(flag/auto_deadmin_antagonists) || (CONFIG_GET(flag/auto_deadmin_antagonists) && !timegate)) + dat += "As Antag: [timegate ? "(Time Locked) " : ""][(toggles & DEADMIN_ANTAGONIST)?"Deadmin":"Keep Admin"]
    " else dat += "As Antag: FORCED
    " - if(!CONFIG_GET(flag/auto_deadmin_heads)) - dat += "As Command: [(toggles & DEADMIN_POSITION_HEAD)?"Deadmin":"Keep Admin"]
    " + if(!CONFIG_GET(flag/auto_deadmin_heads) || (CONFIG_GET(flag/auto_deadmin_heads) && !timegate)) + dat += "As Command: [timegate ? "(Time Locked) " : ""][(toggles & DEADMIN_POSITION_HEAD)?"Deadmin":"Keep Admin"]
    " else dat += "As Command: FORCED
    " - if(!CONFIG_GET(flag/auto_deadmin_security)) - dat += "As Security: [(toggles & DEADMIN_POSITION_SECURITY)?"Deadmin":"Keep Admin"]
    " + if(!CONFIG_GET(flag/auto_deadmin_security) || (CONFIG_GET(flag/auto_deadmin_security) && !timegate)) + dat += "As Security: [timegate ? "(Time Locked) " : ""][(toggles & DEADMIN_POSITION_SECURITY)?"Deadmin":"Keep Admin"]
    " else dat += "As Security: FORCED
    " - if(!CONFIG_GET(flag/auto_deadmin_silicons)) - dat += "As Silicon: [(toggles & DEADMIN_POSITION_SILICON)?"Deadmin":"Keep Admin"]
    " + if(!CONFIG_GET(flag/auto_deadmin_silicons) || (CONFIG_GET(flag/auto_deadmin_silicons) && !timegate)) + dat += "As Silicon: [timegate ? "(Time Locked) " : ""][(toggles & DEADMIN_POSITION_SILICON)?"Deadmin":"Keep Admin"]
    " else dat += "As Silicon: FORCED
    " @@ -1869,6 +1880,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(phobiaType) phobia = phobiaType + if ("max_chat_length") + var/desiredlength = input(user, "Choose the max character length of shown Runechat messages. Valid range is 1 to [CHAT_MESSAGE_MAX_LENGTH] (default: [initial(max_chat_length)]))", "Character Preference", max_chat_length) as null|num + if (!isnull(desiredlength)) + max_chat_length = clamp(desiredlength, 1, CHAT_MESSAGE_MAX_LENGTH) + else switch(href_list["preference"]) if("showgear") @@ -1958,6 +1974,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) key_bindings = (hotkeys) ? deepCopyList(GLOB.hotkey_keybinding_list_by_key) : deepCopyList(GLOB.classic_keybinding_list_by_key) user.client.update_movement_keys() + if("chat_on_map") + chat_on_map = !chat_on_map + if("see_chat_non_mob") + see_chat_non_mob = !see_chat_non_mob + if("action_buttons") buttons_locked = !buttons_locked if("tgui_fancy") @@ -1994,7 +2015,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) toggles ^= DEADMIN_POSITION_SECURITY if("toggle_deadmin_silicon") toggles ^= DEADMIN_POSITION_SILICON - + if("toggle_ignore_cult_ghost") + toggles ^= ADMIN_IGNORE_CULT_GHOST if("be_special") var/be_special_type = href_list["be_special_type"] diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index dc7b04443173..ac7f0cd6e9a7 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -5,7 +5,7 @@ // You do not need to raise this if you are adding new values that have sane defaults. // Only raise this value when changing the meaning/format/name/layout of an existing value // where you would want the updater procs below to run -#define SAVEFILE_VERSION_MAX 33 +#define SAVEFILE_VERSION_MAX 34 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn @@ -56,6 +56,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car if(current_version < 33) toggles |= SOUND_ENDOFROUND + if(current_version < 34) + auto_fit_viewport = TRUE + /datum/preferences/proc/update_character(current_version, savefile/S) if(current_version < 19) pda_style = "mono" @@ -165,6 +168,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["lastchangelog"] >> lastchangelog S["UI_style"] >> UI_style S["hotkeys"] >> hotkeys + S["chat_on_map"] >> chat_on_map + S["max_chat_length"] >> max_chat_length + S["see_chat_non_mob"] >> see_chat_non_mob S["tgui_fancy"] >> tgui_fancy S["tgui_lock"] >> tgui_lock S["buttons_locked"] >> buttons_locked @@ -213,6 +219,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1]) hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys)) + chat_on_map = sanitize_integer(chat_on_map, 0, 1, initial(chat_on_map)) + max_chat_length = sanitize_integer(max_chat_length, 1, CHAT_MESSAGE_MAX_LENGTH, initial(max_chat_length)) + see_chat_non_mob = sanitize_integer(see_chat_non_mob, 0, 1, initial(see_chat_non_mob)) tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy)) tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock)) buttons_locked = sanitize_integer(buttons_locked, 0, 1, initial(buttons_locked)) @@ -233,7 +242,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car crew_objectives = sanitize_integer(crew_objectives, 0, 1, initial(crew_objectives)) pda_style = sanitize_inlist(pda_style, GLOB.pda_styles, initial(pda_style)) pda_color = sanitize_hexcolor(pda_color, 6, 1, initial(pda_color)) - key_bindings = sanitize_islist(key_bindings, list()) + key_bindings = sanitize_keybindings(key_bindings) if(!purchased_gear) @@ -259,6 +268,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["lastchangelog"], lastchangelog) WRITE_FILE(S["UI_style"], UI_style) WRITE_FILE(S["hotkeys"], hotkeys) + WRITE_FILE(S["chat_on_map"], chat_on_map) + WRITE_FILE(S["max_chat_length"], max_chat_length) + WRITE_FILE(S["see_chat_non_mob"], see_chat_non_mob) WRITE_FILE(S["tgui_fancy"], tgui_fancy) WRITE_FILE(S["tgui_lock"], tgui_lock) WRITE_FILE(S["buttons_locked"], buttons_locked) @@ -560,6 +572,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car return TRUE +/proc/sanitize_keybindings(value) + var/list/base_bindings = sanitize_islist(value,list()) + for(var/key in base_bindings) + base_bindings[key] = base_bindings[key] & GLOB.keybindings_by_name + if(!length(base_bindings[key])) + base_bindings -= key + return base_bindings + #undef SAVEFILE_VERSION_MAX #undef SAVEFILE_VERSION_MIN diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 7a3745b50789..b8e4759ba939 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -65,6 +65,8 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") //The linkify span classes and linkify=TRUE below make ooc text get clickable chat href links if you pass in something resembling a url for(var/client/C in GLOB.clients) if(C.prefs.chat_toggles & CHAT_OOC) + if(holder?.fakekey in C.prefs.ignoring) + continue if(holder) if(!holder.fakekey || C.holder) if(check_rights_for(src, R_ADMIN)) @@ -286,43 +288,125 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") body += "" usr << browse(body.Join(), "window=playerplaytime[ckey];size=550x615") -/client/proc/ignore_key(client, displayed_key) - var/client/C = client - if(C.key in prefs.ignoring) - prefs.ignoring -= C.key - else - prefs.ignoring |= C.key - to_chat(src, "You are [(C.key in prefs.ignoring) ? "now" : "no longer"] ignoring [displayed_key] on the OOC channel.") - prefs.save_preferences() - +// Ignore verb /client/verb/select_ignore() set name = "Ignore" set category = "OOC" set desc ="Ignore a player's messages on the OOC channel" + // Make a list to choose players from + var/list/players = list() - var/see_ghost_names = isobserver(mob) - var/list/choices = list() - var/displayed_choicename = "" + // Use keys and fakekeys for the same purpose + var/displayed_key = "" + + // Try to add every player who's online to the list for(var/client/C in GLOB.clients) + // Don't add ourself + if(C == src) + continue + + // Don't add players we've already ignored if they're not using a fakekey + if((C.key in prefs.ignoring) && !C.holder?.fakekey) + continue + + // Don't add players using a fakekey we've already ignored + if(C.holder?.fakekey in prefs.ignoring) + continue + + // Use the player's fakekey if they're using one if(C.holder?.fakekey) - displayed_choicename = C.holder.fakekey + displayed_key = C.holder.fakekey + + // Use the player's key if they're not using a fakekey else - displayed_choicename = C.key - if(isobserver(C.mob) && see_ghost_names) - choices["[C.mob]([displayed_choicename])"] = C + displayed_key = C.key + + // Check if both we and the player are ghosts and they're not using a fakekey + if(isobserver(mob) && isobserver(C.mob) && !C.holder?.fakekey) + // Show us the player's mob name in the list in front of their displayed key + // Add the player's displayed key to the list + players["[C.mob]([displayed_key])"] = displayed_key + + // Add the player's displayed key to the list if we or the player aren't a ghost or they're using a fakekey else - choices[displayed_choicename] = C - choices = sortList(choices) - var/selection = input("Please, select a player!", "Ignore", null, null) as null|anything in choices - if(!selection || !(selection in choices)) + players[displayed_key] = displayed_key + + // Check if the list is empty + if(!players.len) + // Express that there are no players we can ignore in chat + to_chat(src, "There are no other players you can ignore!") + + // Stop running + return + + // Sort the list + players = sortList(players) + + // Request the player to ignore + var/selection = input("Please, select a player!", "Ignore", null, null) as null|anything in players + + // Stop running if we didn't receieve a valid selection + if(!selection || !(selection in players)) + return + + // Store the selected player + selection = players[selection] + + // Check if the selected player is on our ignore list + if(selection in prefs.ignoring) + // Express that the selected player is already on our ignore list in chat + to_chat(src, "You are already ignoring [selection]!") + + // Stop running return - displayed_choicename = selection // ckey string - selection = choices[selection] // client - if(selection == src) - to_chat(src, "You can't ignore yourself.") + + // Add the selected player to our ignore list + prefs.ignoring.Add(selection) + + // Save our preferences + prefs.save_preferences() + + // Express that we've ignored the selected player in chat + to_chat(src, "You are now ignoring [selection] on the OOC channel.") + +// Unignore verb +/client/verb/select_unignore() + set name = "Unignore" + set category = "OOC" + set desc = "Stop ignoring a player's messages on the OOC channel" + + // Check if we've ignored any players + if(!prefs.ignoring.len) + // Express that we haven't ignored any players in chat + to_chat(src, "You haven't ignored any players!") + + // Stop running return - ignore_key(selection, displayed_choicename) + + // Request the player to unignore + var/selection = input("Please, select a player!", "Unignore", null, null) as null|anything in prefs.ignoring + + // Stop running if we didn't receive a selection + if(!selection) + return + + // Check if the selected player is not on our ignore list + if(!(selection in prefs.ignoring)) + // Express that the selected player is not on our ignore list in chat + to_chat(src, "You are not ignoring [selection]!") + + // Stop running + return + + // Remove the selected player from our ignore list + prefs.ignoring.Remove(selection) + + // Save our preferences + prefs.save_preferences() + + // Express that we've unignored the selected player in chat + to_chat(src, "You are no longer ignoring [selection] on the OOC channel.") /client/proc/show_previous_roundend_report() set name = "Your Last Round" diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm index a5cd6911b28c..2c19a8346f50 100644 --- a/code/modules/clothing/glasses/engine_goggles.dm +++ b/code/modules/clothing/glasses/engine_goggles.dm @@ -12,6 +12,7 @@ icon_state = "trayson-meson" item_state = "trayson-meson" actions_types = list(/datum/action/item_action/toggle_mode) + glass_colour_type = /datum/client_colour/glass_colour/gray vision_flags = NONE darkness_view = 2 @@ -43,11 +44,22 @@ vision_flags = SEE_TURFS darkness_view = 1 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + change_glass_color(user, /datum/client_colour/glass_colour/yellow) if(MODE_TRAY) //undoes the last mode, meson vision_flags = NONE darkness_view = 2 lighting_alpha = null + change_glass_color(user, /datum/client_colour/glass_colour/lightblue) + + if(MODE_RAD) + change_glass_color(user, /datum/client_colour/glass_colour/lightgreen) + + if(MODE_SHUTTLE) + change_glass_color(user, /datum/client_colour/glass_colour/red) + + if(MODE_NONE) + change_glass_color(user, initial(glass_colour_type)) if(ishuman(user)) var/mob/living/carbon/human/H = user @@ -89,17 +101,17 @@ for(var/i in rad_places) var/turf/place = i - if(get_dist(user, place) >= range*2) //Rads are easier to see than wires under the floor + if(get_dist(user, place) >= range*5) //Rads are easier to see than wires under the floor continue var/strength = round(rad_places[i] / 1000, 0.1) - var/image/pic = new(loc = place) + var/image/pic = image(loc = place) var/mutable_appearance/MA = new() - MA.alpha = 180 - MA.maptext = "[strength]k" - MA.color = "#64C864" - MA.layer = FLY_LAYER + MA.maptext = "[strength]k" + MA.color = "#04e604" + MA.layer = RAD_TEXT_LAYER + MA.plane = GAME_PLANE pic.appearance = MA - flick_overlay(pic, list(user.client), 8) + flick_overlay(pic, list(user.client), 10) /obj/item/clothing/glasses/meson/engine/proc/show_shuttle() var/mob/living/carbon/human/user = loc diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index ad5b23ec36ba..f2893724900c 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -148,13 +148,14 @@ desc = "An extremely robust, space-worthy helmet in a nefarious red and black stripe pattern." icon_state = "swatsyndie" item_state = "swatsyndie" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 40, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 50, "acid" = 50) + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 40, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 100, "acid" = 100) cold_protection = HEAD min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT heat_protection = HEAD max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT clothing_flags = STOPSPRESSUREDAMAGE strip_delay = 80 + resistance_flags = FIRE_PROOF | ACID_PROOF dog_fashion = null /obj/item/clothing/head/helmet/police diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index a36236cdbf30..62d370027498 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -426,7 +426,7 @@ icon_state = "kippah" /obj/item/clothing/head/medievaljewhat - name = "medieval Jew hat" + name = "medieval Jewish hat" desc = "A silly looking hat, intended to be placed on the heads of the station's oppressed religious minorities." icon_state = "medievaljewhat" diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index 635bc3f63321..1902fa504709 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -83,6 +83,17 @@ resistance_flags = FLAMMABLE actions_types = list(/datum/action/item_action/adjust) dog_fashion = /datum/dog_fashion/head/clown + var/list/clownmask_designs = list() + +/obj/item/clothing/mask/gas/clown_hat/Initialize(mapload) + .=..() + clownmask_designs = list( + "True Form" = image(icon = src.icon, icon_state = "clown"), + "The Feminist" = image(icon = src.icon, icon_state = "sexyclown"), + "The Jester" = image(icon = src.icon, icon_state = "chaos"), + "The Madman" = image(icon = src.icon, icon_state = "joker"), + "The Rainbow Color" = image(icon = src.icon, icon_state = "rainbow") + ) /obj/item/clothing/mask/gas/clown_hat/ui_action_click(mob/user) if(!istype(user) || user.incapacitated()) @@ -95,7 +106,9 @@ options["The Rainbow Color"] ="rainbow" options["The Jester"] ="chaos" //Nepeta33Leijon is holding me captive and forced me to help with this please send help - var/choice = input(user,"To what form do you wish to Morph this mask?","Morph Mask") in sortList(options) + var/choice = show_radial_menu(user,src, clownmask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) + if(!choice) + return FALSE if(src && choice && !user.incapacitated() && in_range(user,src)) icon_state = options[choice] @@ -104,7 +117,7 @@ var/datum/action/A = X A.UpdateButtonIcon() to_chat(user, "Your Clown Mask has now morphed into [choice], all praise the Honkmother!") - return 1 + return TRUE /obj/item/clothing/mask/gas/sexyclown name = "sexy-clown wig and mask" @@ -125,7 +138,16 @@ flags_cover = MASKCOVERSEYES resistance_flags = FLAMMABLE actions_types = list(/datum/action/item_action/adjust) + var/list/mimemask_designs = list() +/obj/item/clothing/mask/gas/mime/Initialize(mapload) + .=..() + mimemask_designs = list( + "Blanc" = image(icon = src.icon, icon_state = "mime"), + "Excité" = image(icon = src.icon, icon_state = "sexymime"), + "Triste" = image(icon = src.icon, icon_state = "sadmime"), + "Effrayé" = image(icon = src.icon, icon_state = "scaredmime") + ) /obj/item/clothing/mask/gas/mime/ui_action_click(mob/user) if(!istype(user) || user.incapacitated()) @@ -137,7 +159,9 @@ options["Effrayé"] = "scaredmime" options["Excité"] ="sexymime" - var/choice = input(user,"To what form do you wish to Morph this mask?","Morph Mask") in sortList(options) + var/choice = show_radial_menu(user,src, mimemask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) + if(!choice) + return FALSE if(src && choice && !user.incapacitated() && in_range(user,src)) icon_state = options[choice] @@ -146,7 +170,7 @@ var/datum/action/A = X A.UpdateButtonIcon() to_chat(user, "Your Mime Mask has now morphed into [choice]!") - return 1 + return TRUE /obj/item/clothing/mask/gas/monkeymask name = "monkey mask" @@ -195,7 +219,16 @@ max_integrity = 100 actions_types = list(/datum/action/item_action/adjust) dog_fashion = null + var/list/tikimask_designs = list() +/obj/item/clothing/mask/gas/tiki_mask/Initialize(mapload) + .=..() + tikimask_designs = list( + "Original Tiki" = image(icon = src.icon, icon_state = "tiki_eyebrow"), + "Happy Tiki" = image(icon = src.icon, icon_state = "tiki_happy"), + "Confused Tiki" = image(icon = src.icon, icon_state = "tiki_confused"), + "Angry Tiki" = image(icon = src.icon, icon_state = "tiki_angry") + ) /obj/item/clothing/mask/gas/tiki_mask/ui_action_click(mob/user) var/mob/M = usr @@ -205,7 +238,9 @@ options["Confused Tiki"] = "tiki_confused" options["Angry Tiki"] ="tiki_angry" - var/choice = input(M,"To what form do you wish to change this mask?","Morph Mask") in sortList(options) + var/choice = show_radial_menu(user,src, tikimask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) + if(!choice) + return FALSE if(src && choice && !M.stat && in_range(M,src)) icon_state = options[choice] diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm index 3fbdb5f8cea2..023d454d11e1 100644 --- a/code/modules/clothing/outfits/ert.dm +++ b/code/modules/clothing/outfits/ert.dm @@ -171,6 +171,7 @@ r_pocket = /obj/item/pda/heads l_hand = /obj/item/clipboard id = /obj/item/card/id/centcom + backpack_contents = list(/obj/item/stamp/centcom=1) /datum/outfit/centcom/centcom_official/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) if(visualsOnly) diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index 590769797eba..dcbca200c098 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -149,16 +149,23 @@ * * * * user: who is the person interacting with the shoes? */ -/obj/item/clothing/shoes/proc/handle_tying(mob/living/carbon/human/user) +/obj/item/clothing/shoes/proc/handle_tying(mob/user) ///our_guy here is the wearer, if one exists (and he must exist, or we don't care) var/mob/living/carbon/human/our_guy = loc if(!istype(our_guy)) return + if(!in_range(user, our_guy)) + to_chat(user, "You aren't close enough to interact with [src]'s laces!") + return + if(user == loc && tied != SHOES_TIED) // if they're our own shoes, go tie-wards + if(INTERACTING_WITH(user, our_guy)) + to_chat(user, "You're already interacting with [src]!") + return user.visible_message("[user] begins [tied ? "unknotting" : "tying"] the laces of [user.p_their()] [src.name].", "You begin [tied ? "unknotting" : "tying"] the laces of your [src.name]...") - if(do_after(user, lace_time, needhand=TRUE, target=src)) + if(do_after(user, lace_time, needhand=TRUE, target=our_guy, extra_checks=CALLBACK(src, .proc/still_shoed, our_guy))) to_chat(user, "You [tied ? "unknot" : "tie"] the laces of your [src.name].") if(tied == SHOES_UNTIED) adjust_laces(SHOES_TIED, user) @@ -166,36 +173,43 @@ adjust_laces(SHOES_UNTIED, user) else // if they're someone else's shoes, go knot-wards - if(user.mobility_flags & MOBILITY_STAND) + var/mob/living/L = user + if(istype(L) && (L.mobility_flags & MOBILITY_STAND)) to_chat(user, "You must be on the floor to interact with [src]!") return if(tied == SHOES_KNOTTED) to_chat(user, "The laces on [loc]'s [src.name] are already a hopelessly tangled mess!") return + if(INTERACTING_WITH(user, our_guy)) + to_chat(user, "You're already interacting with [src]!") + return var/mod_time = lace_time to_chat(user, "You quietly set to work [tied ? "untying" : "knotting"] [loc]'s [src.name]...") if(HAS_TRAIT(user, TRAIT_CLUMSY)) // based clowns trained their whole lives for this mod_time *= 0.75 - if(do_after(user, mod_time, needhand=TRUE, target=src)) + if(do_after(user, mod_time, needhand=TRUE, target=our_guy, extra_checks=CALLBACK(src, .proc/still_shoed, our_guy))) to_chat(user, "You [tied ? "untie" : "knot"] the laces on [loc]'s [src.name].") if(tied == SHOES_UNTIED) adjust_laces(SHOES_KNOTTED, user) else adjust_laces(SHOES_UNTIED, user) else // if one of us moved - user.visible_message("[our_guy] stamps on [user]'s hand, mid-shoelace [tied ? "knotting" : "untying"]!", "Ow! [our_guy] stamps on your hand!", user) - to_chat(our_guy, "You stamp on [user]'s hand! What the- they were [tied ? "knotting" : "untying"] your shoelaces!") + user.visible_message("[our_guy] stamps on [user]'s hand, mid-shoelace [tied ? "knotting" : "untying"]!", "Ow! [our_guy] stamps on your hand!", list(our_guy)) + to_chat(our_guy, "You stamp on [user]'s hand! What the- [user.p_they()] [user.p_were()] [tied ? "knotting" : "untying"] your shoelaces!") user.emote("scream") - var/obj/item/bodypart/ouchie = user.get_bodypart(pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) - if(ouchie) - ouchie.receive_damage(15) - user.Paralyze(5) + if(istype(L)) + var/obj/item/bodypart/ouchie = L.get_bodypart(pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) + if(ouchie) + ouchie.receive_damage(brute = 10, stamina = 40) + L.Paralyze(10) -/** - * check_trip runs on each step to see if we fall over as a result of our lace status. Knotted laces are a guaranteed trip, while untied shoes are just a chance to stumble - */ +///checking to make sure we're still on the person we're supposed to be, for lacing do_after's +/obj/item/clothing/shoes/proc/still_shoed(mob/living/carbon/our_guy) + return (loc == our_guy) + +///check_trip runs on each step to see if we fall over as a result of our lace status. Knotted laces are a guaranteed trip, while untied shoes are just a chance to stumble /obj/item/clothing/shoes/proc/check_trip() var/mob/living/carbon/human/our_guy = loc if(!istype(our_guy)) // are they REALLY /our guy/? @@ -213,10 +227,13 @@ if(1) // .1% chance to trip and fall over (note these are per step while our laces are undone) our_guy.Paralyze(5) our_guy.Knockdown(10) + SEND_SIGNAL(our_guy, COMSIG_ADD_MOOD_EVENT, "trip", /datum/mood_event/tripped) // well we realized they're knotted now! our_guy.visible_message("[our_guy] trips on [our_guy.p_their()] untied shoelaces and falls! What a klutz!", "You trip on your untied shoelaces and fall over!") + if(2 to 5) // .4% chance to stumble and lurch forward our_guy.throw_at(get_step(our_guy, our_guy.dir), 3, 2) to_chat(our_guy, "You stumble on your untied shoelaces and lurch forward!") + if(6 to 13) // .7% chance to stumble and fling what we're holding var/have_anything = FALSE for(var/obj/item/I in our_guy.held_items) @@ -228,9 +245,9 @@ if(!our_guy.has_movespeed_modifier(/datum/movespeed_modifier/shove)) our_guy.add_movespeed_modifier(/datum/movespeed_modifier/shove) addtimer(CALLBACK(our_guy, /mob/living/carbon/human/proc/clear_shove_slowdown), SHOVE_SLOWDOWN_LENGTH) + if(26 to 1000) wiser = FALSE - if(wiser) SEND_SIGNAL(our_guy, COMSIG_ADD_MOOD_EVENT, "untied", /datum/mood_event/untied) // well we realized they're untied now! our_alert = our_guy.throw_alert("shoealert", /obj/screen/alert/shoes/untied) @@ -239,7 +256,7 @@ /obj/item/clothing/shoes/attack_hand(mob/living/carbon/human/user) if(!istype(user)) return ..() - if(loc == user && tied != SHOES_TIED) + if(loc == user && tied != SHOES_TIED && (user.mobility_flags & MOBILITY_USE)) handle_tying(user) return ..() @@ -247,10 +264,12 @@ /obj/item/clothing/shoes/attack_self(mob/user) . = ..() + if(INTERACTING_WITH(user, src)) + to_chat(user, "You're already interacting with [src]!") + return + to_chat(user, "You begin [tied ? "untying" : "tying"] the laces on [src]...") - if(do_after(user, lace_time, needhand=TRUE, target=src)) + + if(do_after(user, lace_time, needhand=TRUE, target=src,extra_checks=CALLBACK(src, .proc/still_shoed, user))) to_chat(user, "You [tied ? "untie" : "tie"] the laces on [src].") - if(tied == SHOES_UNTIED) - adjust_laces(SHOES_TIED, user) - else - adjust_laces(SHOES_UNTIED, user) + adjust_laces(tied ? SHOES_TIED : SHOES_UNTIED, user) diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index d8f2faaaf8b7..9100485372c8 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -9,7 +9,7 @@ equip_delay_other = 40 max_integrity = 250 resistance_flags = NONE - armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) /obj/item/clothing/suit/armor/Initialize() . = ..() @@ -177,7 +177,7 @@ desc = "This vest appears to be made of of highly flexible materials that absorb impacts with ease." icon_state = "infiltrator" item_state = "infiltrator" - armor = list("melee" = 30, "bullet" = 40, "laser" = 20, "energy" = 30, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + armor = list("melee" = 40, "bullet" = 40, "laser" = 30, "energy" = 40, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) resistance_flags = FIRE_PROOF | ACID_PROOF strip_delay = 80 diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index 1d1376544bd9..ee1ae267941f 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -11,15 +11,6 @@ blood_overlay_type = "armor" body_parts_covered = CHEST|GROIN 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) - pocket_storage_component_path = /datum/component/storage/concrete/pockets/exo/large // WaspStation Edit - Exowear Pockets - -/obj/item/clothing/suit/apron/waders - name = "horticultural waders" - desc = "A pair of heavy duty leather waders, perfect for insulating your soft flesh from spills, soil and thorns." - icon_state = "hort_waders" - item_state = "hort_waders" - body_parts_covered = CHEST|GROIN|LEGS - permeability_coefficient = 0.5 /obj/item/clothing/suit/apron/waders name = "horticultural waders" diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index ba7c237f8d69..1822caf09086 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -76,6 +76,7 @@ icon_state = "justice" item_state = "justice" flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) /obj/item/clothing/suit/judgerobe @@ -554,6 +555,7 @@ cold_protection = HEAD min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT flags_inv = HIDEHAIR|HIDEEARS + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) /obj/item/clothing/suit/hooded/wintercoat/captain name = "captain's winter coat" @@ -568,6 +570,7 @@ /obj/item/clothing/head/hooded/winterhood/captain icon_state = "winterhood_captain" + armor = list("melee" = 25, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) /obj/item/clothing/suit/hooded/wintercoat/security name = "security winter coat" @@ -582,6 +585,7 @@ /obj/item/clothing/head/hooded/winterhood/security icon_state = "winterhood_security" + armor = list("melee" = 25, "bullet" = 15, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45) /obj/item/clothing/suit/hooded/wintercoat/medical name = "medical winter coat" @@ -593,6 +597,7 @@ /obj/item/clothing/head/hooded/winterhood/medical icon_state = "winterhood_medical" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45) /obj/item/clothing/suit/hooded/wintercoat/science name = "science winter coat" @@ -604,6 +609,7 @@ /obj/item/clothing/head/hooded/winterhood/science icon_state = "winterhood_science" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) /obj/item/clothing/suit/hooded/wintercoat/engineering name = "engineering winter coat" @@ -615,6 +621,7 @@ /obj/item/clothing/head/hooded/winterhood/engineering icon_state = "winterhood_engineer" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 20, "fire" = 30, "acid" = 45) /obj/item/clothing/suit/hooded/wintercoat/engineering/atmos name = "atmospherics winter coat" @@ -652,6 +659,10 @@ armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) hoodtype = /obj/item/clothing/head/hooded/winterhood/miner +/obj/item/clothing/head/hooded/winterhood/miner + icon_state = "winterhood_miner" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + /obj/item/clothing/head/hooded/ablative name = "ablative hood" desc = "Hood hopefully belonging to an ablative trenchcoat. Includes a visor for cool-o-vision." @@ -702,9 +713,6 @@ if (prob(hit_reflect_chance)) return TRUE -/obj/item/clothing/head/hooded/winterhood/miner - icon_state = "winterhood_miner" - /obj/item/clothing/suit/spookyghost name = "spooky ghost" desc = "This is obviously just a bedsheet, but maybe try it on?" diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index edf33bdabb40..d81f56b32f39 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -86,6 +86,7 @@ if(attached_accessory && slot != ITEM_SLOT_HANDS && ishuman(user)) var/mob/living/carbon/human/H = user attached_accessory.on_uniform_equip(src, user) + H.fan_hud_set_fandom() if(attached_accessory.above_suit) H.update_inv_wear_suit() @@ -94,6 +95,7 @@ attached_accessory.on_uniform_dropped(src, user) if(ishuman(user)) var/mob/living/carbon/human/H = user + H.fan_hud_set_fandom() if(attached_accessory.above_suit) H.update_inv_wear_suit() @@ -128,6 +130,7 @@ var/mob/living/carbon/human/H = loc H.update_inv_w_uniform() H.update_inv_wear_suit() + H.fan_hud_set_fandom() return TRUE @@ -149,6 +152,7 @@ var/mob/living/carbon/human/H = loc H.update_inv_w_uniform() H.update_inv_wear_suit() + H.fan_hud_set_fandom() /obj/item/clothing/under/examine(mob/user) diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm index bb3b03a7699a..e22e083b7ea8 100755 --- a/code/modules/clothing/under/accessories.dm +++ b/code/modules/clothing/under/accessories.dm @@ -187,7 +187,7 @@ desc = "An award for distinguished combat and sacrifice in defence of Nanotrasen's commercial interests. Often awarded to security staff." /obj/item/clothing/accessory/medal/silver/excellence - name = "the head of personnel award for outstanding achievement in the field of excellence" + name = "\proper the head of personnel award for outstanding achievement in the field of excellence" desc = "Nanotrasen's dictionary defines excellence as \"the quality or condition of being excellent\". This is awarded to those rare crewmembers who fit that definition." /obj/item/clothing/accessory/medal/gold @@ -322,7 +322,7 @@ name = "Clown Pin" desc = "A pin to show off your appreciation for clowns and clowning" icon_state = "fan_clown_pin" - above_suit = TRUE + above_suit = FALSE minimize_when_attached = TRUE attachment_slot = CHEST @@ -340,7 +340,7 @@ name = "Mime Pin" desc = "A pin to show off your appreciation for mimes and miming" icon_state = "fan_mime_pin" - above_suit = TRUE + above_suit = FALSE minimize_when_attached = TRUE attachment_slot = CHEST diff --git a/code/modules/clothing/under/jobs/centcom.dm b/code/modules/clothing/under/jobs/centcom.dm index 5d694164eea5..7b56081c9558 100644 --- a/code/modules/clothing/under/jobs/centcom.dm +++ b/code/modules/clothing/under/jobs/centcom.dm @@ -1,4 +1,4 @@ -/obj/item/clothing/under/rank +/obj/item/clothing/under/rank/centcom icon = 'icons/obj/clothing/under/centcom.dmi' mob_overlay_icon = 'icons/mob/clothing/under/centcom.dmi' diff --git a/code/modules/detectivework/footprints_and_rag.dm b/code/modules/detectivework/footprints_and_rag.dm index e56c7494e0ba..ba6563d0657f 100644 --- a/code/modules/detectivework/footprints_and_rag.dm +++ b/code/modules/detectivework/footprints_and_rag.dm @@ -33,7 +33,7 @@ C.visible_message("[user] smothers \the [C] with \the [src]!", "[user] smothers you with \the [src]!", "You hear some struggling and muffled cries of surprise.") log_combat(user, C, "smothered", src, log_object) else - reagents.reaction(C, TOUCH) + reagents.expose(C, TOUCH) reagents.clear_reagents() C.visible_message("[user] touches \the [C] with \the [src].") log_combat(user, C, "touched", src, log_object) diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm index 14047cbd9cc2..6913235e56fa 100644 --- a/code/modules/detectivework/scanner.dm +++ b/code/modules/detectivework/scanner.dm @@ -51,8 +51,6 @@ P.info = text("
    Forensic Record - (FR-[])


    ", frNum) P.info += jointext(log, "
    ") P.info += "
    Notes:
    " - P.info_links = P.info - P.updateinfolinks() P.update_icon() if(ismob(loc)) diff --git a/code/modules/discord/accountlink.dm b/code/modules/discord/accountlink.dm index 38b7453e631e..117e21ae9d4b 100644 --- a/code/modules/discord/accountlink.dm +++ b/code/modules/discord/accountlink.dm @@ -21,7 +21,7 @@ if(!stored_id) // Account is not linked var/know_how = alert("Do you know how to get a Discord user ID? This ID is NOT your Discord username and numbers! (Pressing NO will open a guide.)","Question","Yes","No","Cancel Linking") if(know_how == "No") // Opens discord support on how to collect IDs - src << link("https://support.discordapp.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID") + src << link("https://tgstation13.org/wiki/How_to_find_your_Discord_User_ID") if(know_how == "Cancel Linking") return var/entered_id = input("Please enter your Discord ID (18-ish digits)", "Enter Discord ID", null, null) as text|null @@ -32,8 +32,8 @@ var/choice = alert("You already have the Discord Account [stored_id] linked to [usr.ckey]. Would you like to link a different account?","Already Linked","Yes","No") if(choice == "Yes") var/know_how = alert("Do you know how to get a Discord user ID? This ID is NOT your Discord username and numbers! (Pressing NO will open a guide.)","Question","Yes","No", "Cancel Linking") - if(know_how == "No") // Opens discord support on how to collect IDs - src << link("https://support.discordapp.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID") + if(know_how == "No") + src << link("https://tgstation13.org/wiki/How_to_find_your_Discord_User_ID") if(know_how == "Cancel Linking") return @@ -86,4 +86,5 @@ return // honey its time for your role flattening + to_chat(usr, "Discord verified") SSdiscord.grant_role(stored_id) diff --git a/code/modules/discord/tgs_commands.dm b/code/modules/discord/tgs_commands.dm index 1b8a82f2c2f3..e2a0e8263f40 100644 --- a/code/modules/discord/tgs_commands.dm +++ b/code/modules/discord/tgs_commands.dm @@ -30,3 +30,14 @@ return "That ckey is not associated to this discord account. If someone has used your ID, please inform an administrator" else return "Account not setup for linkage" + + +/// Gets the discord user's Discord UserID +/datum/tgs_chat_command/myuserid + name = "myuserid" + help_text = "Returns your userid" + +/datum/tgs_chat_command/myuserid/Run(datum/tgs_chat_user/sender, params) + var/discordid = SSdiscord.id_clean(sender.mention) + return "<@[discordid]> Your Discord UserID is [discordid]" + diff --git a/code/modules/economy/account.dm b/code/modules/economy/account.dm index 51053c4ce775..7d007c633bf9 100644 --- a/code/modules/economy/account.dm +++ b/code/modules/economy/account.dm @@ -43,6 +43,8 @@ /datum/bank_account/proc/transfer_money(datum/bank_account/from, amount) if(from.has_money(amount)) adjust_money(amount) + SSblackbox.record_feedback("amount", "credits_transferred", amount) + log_econ("[amount] credits were transferred from [from.account_holder]'s account to [src.account_holder]") from.adjust_money(-amount) return TRUE return FALSE @@ -51,6 +53,8 @@ var/money_to_transfer = account_job.paycheck * amt_of_paychecks if(free) adjust_money(money_to_transfer) + SSblackbox.record_feedback("amount", "free_income", money_to_transfer) + log_econ("[money_to_transfer] credits were given to [src.account_holder]'s account from income.") else var/datum/bank_account/D = SSeconomy.get_dep_account(account_job.paycheck_department) if(D) @@ -74,25 +78,29 @@ icon_source = id_card.get_cached_flat_icon() var/mob/card_holder = recursive_loc_check(A, /mob) if(ismob(card_holder)) //If on a mob - if(card_holder.client && !(card_holder.client.prefs.chat_toggles & CHAT_BANKCARD) && !force) + if(!card_holder.client || (!(card_holder.client.prefs.chat_toggles & CHAT_BANKCARD) && !force)) return - card_holder.playsound_local(get_turf(card_holder), 'sound/machines/twobeep_high.ogg', 50, TRUE) if(card_holder.can_hear()) + card_holder.playsound_local(get_turf(card_holder), 'sound/machines/twobeep_high.ogg', 50, TRUE) to_chat(card_holder, "[icon2html(icon_source, card_holder)] [message]") else if(isturf(A.loc)) //If on the ground - for(var/mob/M in hearers(1,get_turf(A))) - if(M.client && !(M.client.prefs.chat_toggles & CHAT_BANKCARD) && !force) - return - playsound(A, 'sound/machines/twobeep_high.ogg', 50, TRUE) - A.audible_message("[icon2html(icon_source, hearers(A))] [message]", null, 1) - break + var/turf/T = A.loc + for(var/mob/M in hearers(1,T)) + if(!M.client || (!(M.client.prefs.chat_toggles & CHAT_BANKCARD) && !force)) + continue + if(M.can_hear()) + M.playsound_local(T, 'sound/machines/twobeep_high.ogg', 50, TRUE) + to_chat(M, "[icon2html(icon_source, M)] [message]") else + var/atom/sound_atom for(var/mob/M in A.loc) //If inside a container with other mobs (e.g. locker) - if(M.client && !(M.client.prefs.chat_toggles & CHAT_BANKCARD) && !force) - return - M.playsound_local(get_turf(M), 'sound/machines/twobeep_high.ogg', 50, TRUE) + if(!M.client || (!(M.client.prefs.chat_toggles & CHAT_BANKCARD) && !force)) + continue + if(!sound_atom) + sound_atom = A.drop_location() //in case we're inside a bodybag in a crate or something. doing this here to only process it if there's a valid mob who can hear the sound. if(M.can_hear()) + M.playsound_local(get_turf(sound_atom), 'sound/machines/twobeep_high.ogg', 50, TRUE) to_chat(M, "[icon2html(icon_source, M)] [message]") /datum/bank_account/department diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm index c0350b63b1a0..6f8355564a99 100644 --- a/code/modules/events/anomaly.dm +++ b/code/modules/events/anomaly.dm @@ -23,7 +23,8 @@ /area/solar, /area/holodeck, /area/shuttle, - /area/maintenance) + /area/maintenance, + /area/science/test_area) ) //Subtypes from the above that actually should explode. diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index f52adb3274d0..b5a008503e6e 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -121,7 +121,10 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 if(isturf(clong) || isobj(clong)) if(clong.density) - clong.ex_act(EXPLODE_HEAVY) + if(isturf(clong)) + SSexplosions.medturf += clong + if(isobj(clong)) + SSexplosions.medobj += clong else if(isliving(clong)) penetrate(clong) diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index a856efc601a2..0db4cde1d4ae 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -291,7 +291,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "cargo_hold_terminal", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "CargoHoldTerminal", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/piratepad_control/ui_data(mob/user) diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm index 67470e0e0b68..716fbd13e268 100644 --- a/code/modules/events/portal_storm.dm +++ b/code/modules/events/portal_storm.dm @@ -17,8 +17,8 @@ max_occurrences = 0 /datum/round_event/portal_storm/portal_storm_narsie - boss_types = list(/mob/living/simple_animal/hostile/construct/builder = 6) - hostile_types = list(/mob/living/simple_animal/hostile/construct/armored/hostile = 8,\ + boss_types = list(/mob/living/simple_animal/hostile/construct/artificer/hostile = 6) + hostile_types = list(/mob/living/simple_animal/hostile/construct/juggernaut/hostile = 8,\ /mob/living/simple_animal/hostile/construct/wraith/hostile = 6) /datum/round_event/portal_storm diff --git a/code/modules/events/processor_overload.dm b/code/modules/events/processor_overload.dm index f2dc3841ebc2..8513d549c5a9 100644 --- a/code/modules/events/processor_overload.dm +++ b/code/modules/events/processor_overload.dm @@ -34,6 +34,6 @@ explosion(get_turf(P), 0, 0, 2) // Only a level 1 explosion actually damages the machine // at all - P.ex_act(EXPLODE_DEVASTATE) + SSexplosions.highobj += P else P.emp_act(EMP_HEAVY) diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 40e8739deda7..207439f689b7 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -150,8 +150,13 @@ quality = NEGATIVE /datum/spacevine_mutation/aggressive_spread/on_spread(obj/structure/spacevine/holder, turf/target) - target.ex_act(severity, null, src) // vine immunity handled at /mob/ex_act - + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highturf += target + if(EXPLODE_HEAVY) + SSexplosions.medturf += target + if(EXPLODE_LIGHT) + SSexplosions.lowturf += target /datum/spacevine_mutation/aggressive_spread/on_buckle(obj/structure/spacevine/holder, mob/living/buckled) buckled.ex_act(severity, null, src) diff --git a/code/modules/events/wizard/embeddies.dm b/code/modules/events/wizard/embeddies.dm index 087bf68627ab..fe08b9c7432e 100644 --- a/code/modules/events/wizard/embeddies.dm +++ b/code/modules/events/wizard/embeddies.dm @@ -14,7 +14,7 @@ if(!I.embedding || I.embedding == EMBED_HARMLESS) I.embedding = EMBED_POINTY - I.AddElement(/datum/element/embed, I.embedding) + I.updateEmbedding() I.name = "pointy [I.name]" GLOB.embedpocalypse = TRUE @@ -40,7 +40,7 @@ if(!I.embedding) I.embedding = EMBED_HARMLESS - I.AddElement(/datum/element/embed, I.embedding) + I.updateEmbedding() I.name = "sticky [I.name]" GLOB.stickpocalypse = TRUE diff --git a/code/modules/events/wizard/shuffle.dm b/code/modules/events/wizard/shuffle.dm index 7e3742922321..55dc909abf7e 100644 --- a/code/modules/events/wizard/shuffle.dm +++ b/code/modules/events/wizard/shuffle.dm @@ -91,11 +91,11 @@ shuffle_inplace(mobs) - var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new /obj/effect/proc_holder/spell/targeted/mind_transfer + var/obj/effect/proc_holder/spell/pointed/mind_transfer/swapper = new while(mobs.len > 1) var/mob/living/carbon/human/H = pick(mobs) mobs -= H - swapper.cast(list(H), mobs[mobs.len], 1) + swapper.cast(list(H), mobs[mobs.len], TRUE) mobs -= mobs[mobs.len] for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index f17f457ce117..d341a008b998 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -698,31 +698,29 @@ GLOBAL_LIST_INIT(hallucination_list, list( else if(get_dist(target,H)[broh]
    's eyes water as [broh.p_they()] chug the can of [src]!") + if(3 to 6) + if(prob(20)) + user.visible_message("[broh] makes \an [pick(list("uncomfortable", "gross", "troubling"))] gurgling noise as [broh.p_they()] chug the can of [src]!") + if(9 to INFINITY) + broh.vomit(2, stun=FALSE) + /obj/item/reagent_containers/food/drinks/soda_cans/bullet_act(obj/projectile/P) . = ..() @@ -638,6 +665,24 @@ open_soda(user) return ..() +/obj/item/reagent_containers/food/drinks/soda_cans/attacked_by(obj/item/I, mob/living/user) + if(I.sharpness && !pierced && user && user.a_intent != INTENT_HARM) + user.visible_message("[user] pierces [src] with [I].", "You pierce \the [src] with [I].") + playsound(src, "can_open", 50, TRUE) + pierced = TRUE + return + else if(I.force) + user.visible_message("[user] crushes [src] with [I]! Party foul!", "You crush \the [src] with [I]! Party foul!") + playsound(src, "can_open", 50, TRUE) + var/obj/item/trash/can/crushed_can = new /obj/item/trash/can(src.loc) + crushed_can.icon_state = icon_state + var/atom/throw_target = get_edge_target_turf(crushed_can, pick(GLOB.alldirs)) + crushed_can.throw_at(throw_target, rand(1,3), 7) + qdel(src) + return + + . = ..() + /obj/item/reagent_containers/food/drinks/soda_cans/cola name = "Space Cola" desc = "Cola. in space." diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm index cb06bc68c2c0..eba92b17f405 100644 --- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm +++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm @@ -20,6 +20,7 @@ var/const/duration = 13 //Directly relates to the 'knockdown' duration. Lowered by armor (i.e. helmets) isGlass = TRUE foodtype = ALCOHOL + age_restricted = TRUE // wrryy can't set an init value to see if foodtype contains ALCOHOL so here we go /obj/item/reagent_containers/food/drinks/bottle/update_overlays() . = ..() @@ -197,6 +198,7 @@ icon_state = "bottleofnothing" list_reagents = list(/datum/reagent/consumable/nothing = 100) foodtype = NONE + age_restricted = FALSE /obj/item/reagent_containers/food/drinks/bottle/patron name = "Wrapp Artiste Patron" @@ -362,6 +364,7 @@ isGlass = FALSE list_reagents = list(/datum/reagent/consumable/orangejuice = 100) foodtype = FRUIT | BREAKFAST + age_restricted = FALSE /obj/item/reagent_containers/food/drinks/bottle/cream name = "milk cream" @@ -374,6 +377,7 @@ isGlass = FALSE list_reagents = list(/datum/reagent/consumable/cream = 100) foodtype = DAIRY + age_restricted = FALSE /obj/item/reagent_containers/food/drinks/bottle/tomatojuice name = "tomato juice" @@ -386,6 +390,7 @@ isGlass = FALSE list_reagents = list(/datum/reagent/consumable/tomatojuice = 100) foodtype = VEGETABLES + age_restricted = FALSE /obj/item/reagent_containers/food/drinks/bottle/limejuice name = "lime juice" @@ -398,6 +403,7 @@ isGlass = FALSE list_reagents = list(/datum/reagent/consumable/limejuice = 100) foodtype = FRUIT + age_restricted = FALSE /obj/item/reagent_containers/food/drinks/bottle/pineapplejuice name = "pineapple juice" @@ -410,6 +416,7 @@ isGlass = FALSE list_reagents = list(/datum/reagent/consumable/pineapplejuice = 100) foodtype = FRUIT | PINEAPPLE + age_restricted = FALSE /obj/item/reagent_containers/food/drinks/bottle/menthol name = "menthol" @@ -430,6 +437,7 @@ isGlass = TRUE list_reagents = list(/datum/reagent/consumable/grenadine = 100) foodtype = FRUIT + age_restricted = FALSE /obj/item/reagent_containers/food/drinks/bottle/applejack name = "Buckin' Bronco's Applejack" @@ -513,7 +521,7 @@ log_bomber(user, "has primed a", src, "for detonation") to_chat(user, "You light [src] on fire.") - add_overlay(GLOB.fire_overlay) + add_overlay(custom_fire_overlay ? custom_fire_overlay : GLOB.fire_overlay) if(!isGlass) addtimer(CALLBACK(src, .proc/explode), 5 SECONDS) @@ -535,7 +543,7 @@ to_chat(user, "The flame's spread too far on it!") return to_chat(user, "You snuff out the flame on [src].") - cut_overlay(GLOB.fire_overlay) + cut_overlay(custom_fire_overlay ? custom_fire_overlay : GLOB.fire_overlay) active = 0 /obj/item/reagent_containers/food/drinks/bottle/pruno diff --git a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm index 0307348d2027..046c7249a178 100644 --- a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm +++ b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm @@ -3,7 +3,6 @@ /obj/item/reagent_containers/food/drinks/drinkingglass name = "drinking glass" desc = "Your standard drinking glass." - custom_price = 50 icon_state = "glass_empty" amount_per_transfer_from_this = 10 volume = 50 @@ -44,7 +43,6 @@ /obj/item/reagent_containers/food/drinks/drinkingglass/shotglass name = "shot glass" desc = "A shot glass - the universal symbol for bad decisions." - custom_price = 50 icon_state = "shotglass" gulp_size = 15 amount_per_transfer_from_this = 15 @@ -113,7 +111,7 @@ target.visible_message("[user] splashes the contents of [src] onto [target]!", \ "[user] splashes the contents of [src] onto you!") log_combat(user, target, "splashed", src) - reagents.reaction(target, TOUCH) + reagents.expose(target, TOUCH) reagents.clear_reagents() return ..() @@ -126,6 +124,6 @@ else if(reagents.total_volume && user.a_intent == INTENT_HARM) user.visible_message("[user] splashes the contents of [src] onto [target]!", \ "You splash the contents of [src] onto [target].") - reagents.reaction(target, TOUCH) + reagents.expose(target, TOUCH) reagents.clear_reagents() return diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm index 09227e2c3d20..5ff732dc487d 100644 --- a/code/modules/food_and_drinks/food/condiment.dm +++ b/code/modules/food_and_drinks/food/condiment.dm @@ -92,7 +92,7 @@ log_combat(user, M, "fed", reagents.log_list()) var/fraction = min(10/reagents.total_volume, 1) - reagents.reaction(M, INGEST, fraction) + reagents.expose(M, INGEST, fraction) reagents.trans_to(M, 10, transfered_by = user) playsound(M.loc,'sound/items/drink.ogg', rand(10,50), TRUE) return 1 diff --git a/code/modules/food_and_drinks/food/snacks_bread.dm b/code/modules/food_and_drinks/food/snacks_bread.dm index 841fb1216868..2fc40a9f9d2f 100644 --- a/code/modules/food_and_drinks/food/snacks_bread.dm +++ b/code/modules/food_and_drinks/food/snacks_bread.dm @@ -269,7 +269,7 @@ desc = "[desc] Deep-fried to perfection." if(60 to INFINITY) add_atom_colour(rgb(33,19,9), FIXED_COLOUR_PRIORITY) - name = "the physical manifestation of the very concept of fried foods" + name = "\proper the physical manifestation of the very concept of fried foods" desc = "A heavily-fried...something. Who can tell anymore?" filling_color = color foodtype |= FRIED diff --git a/code/modules/food_and_drinks/food/snacks_burgers.dm b/code/modules/food_and_drinks/food/snacks_burgers.dm index 70dc4083a5ec..5bff4f8edb27 100644 --- a/code/modules/food_and_drinks/food/snacks_burgers.dm +++ b/code/modules/food_and_drinks/food/snacks_burgers.dm @@ -364,7 +364,7 @@ /obj/item/reagent_containers/food/snacks/burger/chicken name = "chicken sandwich" //Apparently the proud people of Americlapstan object to this thing being called a burger. Apparently McDonald's just calls it a burger in Europe as to not scare and confuse us. - desc = "A delicious chicken sandwich, it is said the proceeds from this treat helps criminalize homosexuality on the space frontier." + desc = "A delicious chicken sandwich, it is said the proceeds from this treat helps criminalize disarming people on the space frontier." icon_state = "chickenburger" tastes = list("bun" = 2, "chicken" = 4, "God's covenant" = 1) bonus_reagents = list(/datum/reagent/consumable/mayonnaise = 3, /datum/reagent/consumable/cooking_oil = 2, /datum/reagent/consumable/nutriment = 2) diff --git a/code/modules/food_and_drinks/food/snacks_egg.dm b/code/modules/food_and_drinks/food/snacks_egg.dm index 777417d312e0..1f87aff7f6ba 100644 --- a/code/modules/food_and_drinks/food/snacks_egg.dm +++ b/code/modules/food_and_drinks/food/snacks_egg.dm @@ -40,7 +40,7 @@ if(chick_count < MAX_CHICKENS) //Chicken code uses this MAX_CHICKENS variable, so I figured that I'd use it again here. Even this check and the check in chicken code both use the MAX_CHICKENS variable, they use independent counter variables and thus are independent of each other. new /mob/living/simple_animal/chick(T) chick_count++ - reagents.reaction(hit_atom, TOUCH) + reagents.expose(hit_atom, TOUCH) qdel(src) /obj/item/reagent_containers/food/snacks/egg/attackby(obj/item/W, mob/user, params) diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm index f2ebbc43db0a..692270b1b24d 100644 --- a/code/modules/food_and_drinks/food/snacks_meat.dm +++ b/code/modules/food_and_drinks/food/snacks_meat.dm @@ -101,10 +101,10 @@ tastes = list("meat" = 1, "salmon" = 1) foodtype = MEAT | ALCOHOL -/obj/item/reagent_containers/food/snacks/faggot - name = "faggot" +/obj/item/reagent_containers/food/snacks/meatball + name = "meatball" desc = "A great meal all round. Not a cord of wood." - icon_state = "faggot" + icon_state = "meatball" list_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 1) filling_color = "#800000" tastes = list("meat" = 1) @@ -179,6 +179,33 @@ visible_message("[src] fails to expand!") qdel(src) +/obj/item/reagent_containers/food/snacks/monkeycube/suicide_act(mob/living/M) + M.visible_message("[M] is putting [src] in [M.p_their()] mouth! It looks like [M.p_theyre()] trying to commit suicide!") + var/eating_success = do_after(M, 10, TRUE, src, TRUE) + if(QDELETED(M)) //qdeletion: the nuclear option of self-harm + return SHAME + if(!eating_success || QDELETED(src)) //checks if src is gone or if they failed to wait for a second + M.visible_message("[M] chickens out!") + return SHAME + if(HAS_TRAIT(M, TRAIT_NOHUNGER)) //plasmamen don't have saliva/stomach acid + M.visible_message("[M] realizes [M.p_their()] body won't activate [src]!" + ,"Your body won't activate [src]...") + return SHAME + playsound(M, 'sound/items/eatfood.ogg', rand(10,50), TRUE) + M.temporarilyRemoveItemFromInventory(src) //removes from hands, keeps in M + addtimer(CALLBACK(src, .proc/finish_suicide, M), 15) //you've eaten it, you can run now + return MANUAL_SUICIDE + +/obj/item/reagent_containers/food/snacks/monkeycube/proc/finish_suicide(mob/living/M) ///internal proc called by a monkeycube's suicide_act using a timer and callback. takes as argument the mob/living who activated the suicide + if(QDELETED(M) || QDELETED(src)) + return + if((src.loc != M)) //how the hell did you manage this + to_chat(M, "Something happened to [src]...") + return + Expand() + M.visible_message("[M]'s torso bursts open as a primate emerges!") + M.gib(null, TRUE, null, TRUE) + /obj/item/reagent_containers/food/snacks/monkeycube/syndicate faction = list("neutral", ROLE_SYNDICATE) @@ -283,6 +310,19 @@ tastes = list("meat" = 3, "smokey sauce" = 1) foodtype = MEAT +/obj/item/reagent_containers/food/snacks/meatclown + name = "meat clown" + desc = "A delicious, round piece of meat clown. How horrifying." + icon_state = "meatclown" + bonus_reagents = list(/datum/reagent/consumable/nutriment = 3, /datum/reagent/consumable/nutriment/vitamin = 1, /datum/reagent/consumable/banana = 2) + list_reagents = list(/datum/reagent/consumable/nutriment = 2) + tastes = list("meat" = 5, "clowns" = 3, "sixteen teslas" = 1) + foodtype = MEAT + +/obj/item/reagent_containers/food/snacks/meatclown/ComponentInitialize() + . = ..() + AddComponent(/datum/component/slippery, 30) + //////////////////////////////////////////// KEBABS AND OTHER SKEWERS //////////////////////////////////////////// /obj/item/reagent_containers/food/snacks/kebab diff --git a/code/modules/food_and_drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm index 08a82af86638..23dbe07bd0cb 100644 --- a/code/modules/food_and_drinks/food/snacks_other.dm +++ b/code/modules/food_and_drinks/food/snacks_other.dm @@ -12,6 +12,15 @@ tastes = list("cheese" = 1) foodtype = DAIRY +/obj/item/reagent_containers/food/snacks/royalcheese + name = "royal cheese" + desc = "Ascend the throne. Consume the wheel. Feel the POWER." + icon_state = "royalcheese" + list_reagents = list(/datum/reagent/consumable/nutriment = 15, /datum/reagent/consumable/nutriment/vitamin = 5, /datum/reagent/gold = 20, /datum/reagent/toxin/mutagen = 5) + w_class = WEIGHT_CLASS_BULKY + tastes = list("cheese" = 4, "royalty" = 1) + foodtype = DAIRY + /obj/item/reagent_containers/food/snacks/cheesewedge name = "cheese wedge" desc = "A wedge of delicious Cheddar. The cheese wheel it was cut from can't have gone far." @@ -436,7 +445,7 @@ var/mob/living/carbon/C = loc if (src == C.wear_mask) // if it's in the human/monkey mouth, transfer reagents to the mob var/fraction = min(REAGENTS_METABOLISM/reagents.total_volume, 1) - reagents.reaction(C, INGEST, fraction) + reagents.expose(C, INGEST, fraction) if(!reagents.trans_to(C, REAGENTS_METABOLISM)) reagents.remove_any(REAGENTS_METABOLISM) return @@ -531,6 +540,34 @@ tastes = list("paint thinner" = 1) color = "#EE35FF" +/obj/item/reagent_containers/food/snacks/chewable/bubblegum/bubblegum + name = "bubblegum gum" + desc = "A rubbery strip of gum. You don't feel like eating it is a good idea." + color = "#913D3D" + list_reagents = list(/datum/reagent/blood = 15) + tastes = list("hell" = 1) + +/obj/item/reagent_containers/food/snacks/chewable/bubblegum/bubblegum/process() + . = ..() + if(iscarbon(loc)) + hallucinate(loc) + + +/obj/item/reagent_containers/food/snacks/chewable/bubblegum/bubblegum/On_Consume(mob/living/eater) + . = ..() + if(iscarbon(eater)) + hallucinate(eater) + +///This proc has a 5% chance to have a bubblegum line appear, with an 85% chance for just text and 15% for a bubblegum hallucination and scarier text. +/obj/item/reagent_containers/food/snacks/chewable/bubblegum/bubblegum/proc/hallucinate(mob/living/carbon/victim) + if(!prob(5)) //cursed by bubblegum + return + if(prob(15)) + new /datum/hallucination/oh_yeah(victim) + to_chat(victim, "[pick("I AM IMMORTAL.","I SHALL TAKE YOUR WORLD.","I SEE YOU.","YOU CANNOT ESCAPE ME FOREVER.","NOTHING CAN HOLD ME.")]") + else + to_chat(victim, "[pick("You hear faint whispers.","You smell ash.","You feel hot.","You hear a roar in the distance.")]") + /obj/item/reagent_containers/food/snacks/gumball name = "gumball" desc = "A colorful, sugary gumball." diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm index 72793c9735e6..96872939ff1a 100644 --- a/code/modules/food_and_drinks/food/snacks_pastry.dm +++ b/code/modules/food_and_drinks/food/snacks_pastry.dm @@ -37,6 +37,10 @@ filling_color = "#FF69B4" return TRUE +/// Returns the sprite of the donut while in a donut box +/obj/item/reagent_containers/food/snacks/donut/proc/in_box_sprite() + return "[icon_state]_inbox" + /obj/item/reagent_containers/food/snacks/donut/checkLiked(fraction, mob/M) //Sec officers always love donuts if(last_check_time + 50 < world.time) if(ishuman(M)) @@ -158,6 +162,10 @@ tastes = list("jelly" = 1, "donut" = 3) foodtype = JUNKFOOD | GRAIN | FRIED | FRUIT | SUGAR | BREAKFAST +// Jelly donuts don't have holes, but look the same on the outside +/obj/item/reagent_containers/food/snacks/donut/jelly/in_box_sprite() + return "[replacetext(icon_state, "jelly", "donut")]_inbox" + /obj/item/reagent_containers/food/snacks/donut/jelly/Initialize() . = ..() if(extra_reagent) @@ -560,6 +568,9 @@ . = ..() AddElement(/datum/element/dunkable, 10) +/obj/item/reagent_containers/food/snacks/cookie/sleepy + list_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/toxin/chloralhydrate = 10) + /obj/item/reagent_containers/food/snacks/fortunecookie name = "fortune cookie" desc = "A true prophecy in each cookie!" diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm index 0e7ea9ff110f..1ac3b49c0112 100644 --- a/code/modules/food_and_drinks/food/snacks_pie.dm +++ b/code/modules/food_and_drinks/food/snacks_pie.dm @@ -40,7 +40,7 @@ var/turf/T = get_turf(hit_atom) new/obj/effect/decal/cleanable/food/pie_smudge(T) if(reagents && reagents.total_volume) - reagents.reaction(hit_atom, TOUCH) + reagents.expose(hit_atom, TOUCH) if(isliving(hit_atom)) var/mob/living/L = hit_atom if(stunning) diff --git a/code/modules/food_and_drinks/food/snacks_pizza.dm b/code/modules/food_and_drinks/food/snacks_pizza.dm index fbe695fe7200..9b8e949b06e6 100644 --- a/code/modules/food_and_drinks/food/snacks_pizza.dm +++ b/code/modules/food_and_drinks/food/snacks_pizza.dm @@ -216,3 +216,13 @@ filling_color = "#FFFFFF" foodtype = GRAIN | VEGETABLES +/obj/item/reagent_containers/food/snacks/pizzaslice/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/kitchen/rollingpin)) + if(!isturf(loc)) + to_chat(user, "You need to put [src] on a surface to roll it out!") + return + new /obj/item/stack/sheet/pizza(loc) + to_chat(user, "You smoosh [src] into a cheesy sheet.") + qdel(src) + return + return ..() diff --git a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm index adba7822d06a..a41ecd673556 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm @@ -147,7 +147,7 @@ God bless America. return var/mob/living/carbon/C = user.pulling user.visible_message("[user] dunks [C]'s face in [src]!") - reagents.reaction(C, TOUCH) + reagents.expose(C, TOUCH) var/permeability = 1 - C.get_permeability_protection(list(HEAD)) C.apply_damage(min(30 * permeability, reagents.total_volume), BURN, BODY_ZONE_HEAD) reagents.remove_any((reagents.total_volume/2)) diff --git a/code/modules/food_and_drinks/kitchen_machinery/grill.dm b/code/modules/food_and_drinks/kitchen_machinery/grill.dm index 170650be99e4..ded8aba5be5f 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/grill.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/grill.dm @@ -42,13 +42,18 @@ return ..() if(istype(I, /obj/item/reagent_containers)) if(istype(I, /obj/item/reagent_containers/food) && !istype(I, /obj/item/reagent_containers/food/drinks)) - if(HAS_TRAIT(I, TRAIT_NODROP) || (I.item_flags & (ABSTRACT | DROPDEL))) + var/obj/item/reagent_containers/food/food_item = I + if(HAS_TRAIT(food_item, TRAIT_NODROP) || (food_item.item_flags & (ABSTRACT | DROPDEL))) return ..() + else if(food_item.foodtype & GRILLED) + to_chat(user, "[food_item] has already been grilled!") + return else if(!grill_fuel) to_chat(user, "There is not enough fuel!") return - else if(!grilled_item && user.transferItemToLoc(I, src)) - grilled_item = I + else if(!grilled_item && user.transferItemToLoc(food_item, src)) + grilled_item = food_item + grilled_item.foodtype |= GRILLED to_chat(user, "You put the [grilled_item] on [src].") update_icon() grill_loop.start() diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 4c2234ed68b4..f7355e933215 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -165,7 +165,7 @@ /obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "smartvend", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "SmartVend", name, ui_x, ui_y, master_ui, state) ui.set_autoupdate(FALSE) ui.open() diff --git a/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm index 3e886d061b40..69e212c58856 100644 --- a/code/modules/food_and_drinks/recipes/drinks_recipes.dm +++ b/code/modules/food_and_drinks/recipes/drinks_recipes.dm @@ -432,7 +432,7 @@ /datum/chemical_reaction/stinger results = list(/datum/reagent/consumable/ethanol/stinger = 15) - required_reagents = list(/datum/reagent/consumable/ethanol/whiskey = 10, /datum/reagent/consumable/ethanol/creme_de_menthe = 5 ) + required_reagents = list(/datum/reagent/consumable/ethanol/cognac = 10, /datum/reagent/consumable/ethanol/creme_de_menthe = 5 ) /datum/chemical_reaction/quintuplesec results = list(/datum/reagent/consumable/ethanol/quintuple_sec = 15) diff --git a/code/modules/food_and_drinks/recipes/processor_recipes.dm b/code/modules/food_and_drinks/recipes/processor_recipes.dm index 4ed027dcdd2b..55db7cf06b84 100644 --- a/code/modules/food_and_drinks/recipes/processor_recipes.dm +++ b/code/modules/food_and_drinks/recipes/processor_recipes.dm @@ -7,7 +7,7 @@ /datum/food_processor_process/meat input = /obj/item/reagent_containers/food/snacks/meat/slab - output = /obj/item/reagent_containers/food/snacks/faggot + output = /obj/item/reagent_containers/food/snacks/meatball /datum/food_processor_process/bacon input = /obj/item/reagent_containers/food/snacks/meat/rawcutlet diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm index 71f0eda4cbc1..34de52f0e58f 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm @@ -148,7 +148,7 @@ /datum/crafting_recipe/food/sausage name = "Sausage" reqs = list( - /obj/item/reagent_containers/food/snacks/faggot = 1, + /obj/item/reagent_containers/food/snacks/meatball = 1, /obj/item/reagent_containers/food/snacks/meat/cutlet = 2 ) result = /obj/item/reagent_containers/food/snacks/sausage @@ -167,7 +167,7 @@ reqs = list( /obj/item/reagent_containers/food/snacks/doughslice = 1, /obj/item/reagent_containers/food/snacks/grown/garlic = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1 + /obj/item/reagent_containers/food/snacks/meatball = 1 ) result = /obj/item/reagent_containers/food/snacks/rawkhinkali subcategory = CAT_MEAT @@ -201,7 +201,7 @@ subcategory = CAT_MEAT /datum/crafting_recipe/food/ricepork - name = "Rice and pork" + name = "Rice and Pork" reqs = list( /obj/item/reagent_containers/food/snacks/salad/boiledrice = 1, /obj/item/reagent_containers/food/snacks/meat/cutlet = 2 @@ -218,3 +218,12 @@ ) result = /obj/item/reagent_containers/food/snacks/bbqribs subcategory = CAT_MEAT + +/datum/crafting_recipe/food/meatclown + name = "Meat Clown" + reqs = list( + /obj/item/reagent_containers/food/snacks/meat/steak/plain = 1, + /obj/item/reagent_containers/food/snacks/grown/banana = 1 + ) + result = /obj/item/reagent_containers/food/snacks/meatclown + subcategory = CAT_MEAT diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm index 2177080329c4..6a43df3328e2 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm @@ -343,3 +343,14 @@ ) result = /obj/item/reagent_containers/food/snacks/customizable/poutine subcategory = CAT_MISCFOOD + +/datum/crafting_recipe/food/royalcheese + name = "Royal Cheese" + reqs = list( + /obj/item/reagent_containers/food/snacks/store/cheesewheel = 1, + /obj/item/clothing/head/crown = 1, + /datum/reagent/medicine/strange_reagent = 5, + /datum/reagent/toxin/mutagen = 5 + ) + result = /obj/item/reagent_containers/food/snacks/royalcheese + subcategory = CAT_MISCFOOD diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm index 364658dbc507..8dfcb5b9097e 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm @@ -322,7 +322,7 @@ datum/crafting_recipe/food/donut/meat name = "Donk-pocket" reqs = list( /obj/item/reagent_containers/food/snacks/pastrybase = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1 + /obj/item/reagent_containers/food/snacks/meatball = 1 ) result = /obj/item/reagent_containers/food/snacks/donkpocket subcategory = CAT_PASTRY @@ -342,7 +342,7 @@ datum/crafting_recipe/food/donut/meat name = "Spicy-pocket" reqs = list( /obj/item/reagent_containers/food/snacks/pastrybase = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1, + /obj/item/reagent_containers/food/snacks/meatball = 1, /obj/item/reagent_containers/food/snacks/grown/chili ) result = /obj/item/reagent_containers/food/snacks/donkpocket/spicy @@ -353,7 +353,7 @@ datum/crafting_recipe/food/donut/meat name = "Teriyaki-pocket" reqs = list( /obj/item/reagent_containers/food/snacks/pastrybase = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1, + /obj/item/reagent_containers/food/snacks/meatball = 1, /datum/reagent/consumable/soysauce = 3 ) result = /obj/item/reagent_containers/food/snacks/donkpocket/teriyaki @@ -364,7 +364,7 @@ datum/crafting_recipe/food/donut/meat name = "Pizza-pocket" reqs = list( /obj/item/reagent_containers/food/snacks/pastrybase = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1, + /obj/item/reagent_containers/food/snacks/meatball = 1, /obj/item/reagent_containers/food/snacks/grown/tomato = 1 ) result = /obj/item/reagent_containers/food/snacks/donkpocket/pizza @@ -396,7 +396,7 @@ datum/crafting_recipe/food/donut/meat name = "Gondola-pocket" reqs = list( /obj/item/reagent_containers/food/snacks/pastrybase = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1, + /obj/item/reagent_containers/food/snacks/meatball = 1, /datum/reagent/tranquility = 5 ) result = /obj/item/reagent_containers/food/snacks/donkpocket/gondola @@ -463,7 +463,7 @@ datum/crafting_recipe/food/donut/meat reqs = list( /datum/reagent/consumable/soysauce = 5, /obj/item/reagent_containers/food/snacks/bun = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1, + /obj/item/reagent_containers/food/snacks/meatball = 1, /obj/item/reagent_containers/food/snacks/grown/cabbage = 1 ) result = /obj/item/reagent_containers/food/snacks/meatbun @@ -588,7 +588,7 @@ datum/crafting_recipe/food/donut/meat result = /obj/item/reagent_containers/food/snacks/honeybun subcategory = CAT_PASTRY - /datum/crafting_recipe/food/cannoli +/datum/crafting_recipe/food/cannoli name = "Cannoli" reqs = list( /obj/item/reagent_containers/food/snacks/pastrybase = 1, diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm index 70f4b5072737..2cb668df4fd0 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm @@ -83,7 +83,7 @@ name = "Sassysage pizza" reqs = list( /obj/item/reagent_containers/food/snacks/pizzabread = 1, - /obj/item/reagent_containers/food/snacks/faggot = 3, + /obj/item/reagent_containers/food/snacks/meatball = 3, /obj/item/reagent_containers/food/snacks/cheesewedge = 1, /obj/item/reagent_containers/food/snacks/grown/tomato = 1 ) diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm index 39eeb7d9369f..920691bcb755 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm @@ -29,7 +29,7 @@ /obj/item/reagent_containers/glass/bowl = 1, /obj/item/reagent_containers/food/snacks/grown/ambrosia/vulgaris = 3, /obj/item/reagent_containers/food/snacks/grown/potato = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1 + /obj/item/reagent_containers/food/snacks/meatball = 1 ) result = /obj/item/reagent_containers/food/snacks/salad/validsalad subcategory = CAT_SALAD diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm index 338cbe191e8e..7f5b6c4ed34a 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm @@ -8,7 +8,7 @@ reqs = list( /datum/reagent/water = 10, /obj/item/reagent_containers/glass/bowl = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1, + /obj/item/reagent_containers/food/snacks/meatball = 1, /obj/item/reagent_containers/food/snacks/grown/carrot = 1, /obj/item/reagent_containers/food/snacks/grown/potato = 1 ) diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm index a3cc40fbda60..796c360c3448 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm @@ -24,7 +24,7 @@ name = "Spaghetti meatball" reqs = list( /obj/item/reagent_containers/food/snacks/spaghetti/boiledspaghetti = 1, - /obj/item/reagent_containers/food/snacks/faggot = 2 + /obj/item/reagent_containers/food/snacks/meatball = 2 ) result = /obj/item/reagent_containers/food/snacks/spaghetti/meatballspaghetti subcategory = CAT_SPAGHETTI @@ -33,7 +33,7 @@ name = "Spesslaw" reqs = list( /obj/item/reagent_containers/food/snacks/spaghetti/boiledspaghetti = 1, - /obj/item/reagent_containers/food/snacks/faggot = 4 + /obj/item/reagent_containers/food/snacks/meatball = 4 ) result = /obj/item/reagent_containers/food/snacks/spaghetti/spesslaw subcategory = CAT_SPAGHETTI diff --git a/code/modules/games/kotahi.dm b/code/modules/games/kotahi.dm new file mode 100644 index 000000000000..3d9a72154f45 --- /dev/null +++ b/code/modules/games/kotahi.dm @@ -0,0 +1,20 @@ +/obj/item/toy/cards/deck/kotahi + name = "\improper KOTAHI deck" + desc = "A deck of kotahi cards. House rules to argue over not included." + icon = 'icons/obj/toy.dmi' + icon_state = "deck_kotahi_full" + deckstyle = "kotahi" + +//Populate the deck. +/obj/item/toy/cards/deck/kotahi/populate_deck() + for(var/colour in list("Red","Yellow","Green","Blue")) + cards += "[colour] 0" //kotahi decks have only one colour of each 0, weird huh? + for(var/k in 0 to 1) //two of each colour of number + cards += "[colour] skip" + cards += "[colour] reverse" + cards += "[colour] draw 2" + for(var/i in 1 to 9) + cards += "[colour] [i]" + for(var/k in 0 to 3) //4 wilds and draw 4s + cards += "Wildcard" + cards += "Draw 4" diff --git a/code/modules/games/tarot.dm b/code/modules/games/tarot.dm new file mode 100644 index 000000000000..089f204cf17c --- /dev/null +++ b/code/modules/games/tarot.dm @@ -0,0 +1,25 @@ +//These cards certainly won't tell the future, but you can play some nice games with them. +/obj/item/toy/cards/deck/tarot + name = "tarot game deck" + desc = "A full 78 card game deck of tarot cards. Complete with 4 suites of 14 cards, and a full suite of trump cards." + icon = 'icons/obj/toy.dmi' + icon_state = "deck_tarot_full" + deckstyle = "tarot" + +/obj/item/toy/cards/deck/tarot/populate_deck() + for(var/suit in list("Hearts", "Pikes", "Clovers", "Tiles")) + for(var/i in 1 to 10) + cards += "[i] of [suit]" + for(var/person in list("Valet", "Chevalier", "Dame", "Roi")) + cards += "[person] of [suit]" + for(var/trump in list("The Magician", "The High Priestess", "The Empress", "The Emperor", "The Hierophant", "The Lover", "The Chariot", "Justice", "The Hermit", "The Wheel of Fortune", "Strength", "The Hanged Man", "Death", "Temperance", "The Devil", "The Tower", "The Star", "The Moon", "The Sun", "Judgement", "The World", "The Fool")) + cards += "[trump]" + +/obj/item/toy/cards/deck/tarot/draw_card(mob/user) + . = ..() + var/obj/item/toy/cards/singlecard/C = . + var/matrix/M = matrix() + M.Turn(180) + if(prob(50)) + C.transform = M + return diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm index 95811d30f5ab..de3456cbdea4 100644 --- a/code/modules/goonchat/browserOutput.dm +++ b/code/modules/goonchat/browserOutput.dm @@ -233,7 +233,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico log_world("\[[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]\] Client: [(src.owner.key ? src.owner.key : src.owner)] triggered JS error: [error]") //Global chat procs -/proc/to_chat_immediate(target, message, handle_whitespace = TRUE, trailing_newline = TRUE) +/proc/to_chat_immediate(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE) if(!target || !message) return @@ -288,11 +288,11 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico // url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript. C << output(url_encode(url_encode(message)), "browseroutput:output") -/proc/to_chat(target, message, handle_whitespace = TRUE, trailing_newline = TRUE) +/proc/to_chat(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE) if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized) - to_chat_immediate(target, message, handle_whitespace, trailing_newline) + to_chat_immediate(target, message, handle_whitespace, trailing_newline, confidential) return - SSchat.queue(target, message, handle_whitespace, trailing_newline) + SSchat.queue(target, message, handle_whitespace, trailing_newline, confidential) /datum/chatOutput/proc/swaptolightmode() //Dark mode light mode stuff. Yell at KMC if this breaks! (See darkmode.dm for documentation) owner.force_white_theme() diff --git a/code/modules/holiday/easter.dm b/code/modules/holiday/easter.dm index 2f0e032c9467..edbecd743e4c 100644 --- a/code/modules/holiday/easter.dm +++ b/code/modules/holiday/easter.dm @@ -218,7 +218,7 @@ /datum/reagent/consumable/sodiumchloride = 1, /datum/reagent/consumable/blackpepper = 1, /obj/item/reagent_containers/food/snacks/boiledegg = 1, - /obj/item/reagent_containers/food/snacks/faggot = 1 + /obj/item/reagent_containers/food/snacks/meatball = 1 ) result = /obj/item/reagent_containers/food/snacks/scotchegg subcategory = CAT_MISCFOOD diff --git a/code/modules/holiday/hebrew_calendar.dm b/code/modules/holiday/foreign_calendar.dm similarity index 60% rename from code/modules/holiday/hebrew_calendar.dm rename to code/modules/holiday/foreign_calendar.dm index 183d73c86db4..d05502061e42 100644 --- a/code/modules/holiday/hebrew_calendar.dm +++ b/code/modules/holiday/foreign_calendar.dm @@ -1,21 +1,48 @@ #define BYOND_EPOCH 2451544.5 #define HEBREW_EPOCH 347995.5 +#define ISLAMIC_EPOCH 1948439.5 /* Source for the method of calcuation https://www.fourmilab.ch/documents/calendar/ by John Walker 2015, released under public domain */ -/datum/hebrew_calendar - var/dd - var/mm +/datum/foreign_calendar + var/static/jd var/yy + var/mm + var/dd + +/datum/foreign_calendar/New() + if (!jd) + jd = realtime_to_jd() + set_date(jd) + +/datum/foreign_calendar/proc/set_date() + return -/datum/hebrew_calendar/New() - var/julian = realtime_to_jd() - set_date(julian) +/datum/foreign_calendar/proc/realtime_to_jd() + return round(world.realtime / 864000) + BYOND_EPOCH + +////////////////////////////// +// Islamic Calendar // +////////////////////////////// +/datum/foreign_calendar/islamic/proc/leap_islamic(yr) + return ((yr * 11 + 14) % 30) < 11 + +/datum/foreign_calendar/islamic/set_date() + var/jd_adj = round(jd) + 0.5 // adjust julian date so it ends in .5 + yy = round(((30 * (jd_adj - ISLAMIC_EPOCH)) + 10646) / 10631) + mm = min(12, CEILING(((jd - (29 + islamic_to_jd(yy, 1, 1))) / 29.5) + 1, 1)) + dd = jd - islamic_to_jd(yy, mm, 1) + 1 -/datum/hebrew_calendar/proc/hebrew_leap(year) +/datum/foreign_calendar/islamic/proc/islamic_to_jd(year, month, day) + return day + CEILING(29.5 * (month - 1), 1) + (year - 1) * 354 + round((3 + (11 * year)) / 30) + ISLAMIC_EPOCH - 1 + +////////////////////////////// +// Hebrew Calendar // +////////////////////////////// +/datum/foreign_calendar/hebrew/proc/hebrew_leap(year) switch (year % 19) if (0, 3, 6, 8, 11, 14, 17) return TRUE @@ -23,7 +50,7 @@ by John Walker 2015, released under public domain return FALSE // Hebrew to Julian -/datum/hebrew_calendar/proc/hebrew_to_jd(year, month, day) +/datum/foreign_calendar/hebrew/proc/hebrew_to_jd(year, month, day) var/months = hebrew_year_months(year) var/jd = HEBREW_EPOCH + hebrew_delay_1(year) + hebrew_delay_2(year) + day + 1 if (month < 7) @@ -38,7 +65,9 @@ by John Walker 2015, released under public domain // Julian to Hebrew -/datum/hebrew_calendar/proc/set_date(jd) +/datum/foreign_calendar/hebrew/set_date(jd) + if (yy && mm && dd) + return jd = round(jd) + 0.5 var/count = round(((jd - HEBREW_EPOCH) * 98496) / 35975351) var/year = count - 1 @@ -52,14 +81,14 @@ by John Walker 2015, released under public domain mm = month dd = day -/datum/hebrew_calendar/proc/hebrew_year_months(year) +/datum/foreign_calendar/hebrew/proc/hebrew_year_months(year) if (hebrew_leap(year)) return 13 else return 12 // Delay based on starting day of the year -/datum/hebrew_calendar/proc/hebrew_delay_1(year) +/datum/foreign_calendar/hebrew/proc/hebrew_delay_1(year) var/months = round(((235 * year) - 234) / 19) var/parts = 12084 + (13753 * months) var/day = (months * 29) + round(parts / 25920) @@ -68,7 +97,7 @@ by John Walker 2015, released under public domain return day // Delay based on length of adjacent years -/datum/hebrew_calendar/proc/hebrew_delay_2(year) +/datum/foreign_calendar/hebrew/proc/hebrew_delay_2(year) var/last = hebrew_delay_1(year - 1) var/present = hebrew_delay_1(year) var/next = hebrew_delay_1(year + 1) @@ -79,10 +108,10 @@ by John Walker 2015, released under public domain else return 0 -/datum/hebrew_calendar/proc/hebrew_year_days(year) +/datum/foreign_calendar/hebrew/proc/hebrew_year_days(year) return hebrew_to_jd(year + 1, 7, 1) - hebrew_to_jd(year, 7, 1) -/datum/hebrew_calendar/proc/hebrew_month_days(year, month) +/datum/foreign_calendar/hebrew/proc/hebrew_month_days(year, month) switch (month) // First of all, dispose of fixed-length 29 day months if (2, 4, 6, 10, 13) @@ -102,8 +131,6 @@ by John Walker 2015, released under public domain // Nope, it's a 30 day month return 30 -/datum/hebrew_calendar/proc/realtime_to_jd() - return round(world.realtime / 864000) + BYOND_EPOCH - +#undef ISLAMIC_EPOCH #undef BYOND_EPOCH #undef HEBREW_EPOCH diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index f64c5311fd56..3c2e51465fd6 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -1,5 +1,5 @@ /datum/holiday - var/name = "Bugsgiving" + var/name = "If you see this the holiday calendar code is broken" var/begin_day = 1 var/begin_month = 0 @@ -462,36 +462,27 @@ /datum/holiday/moth/getStationPrefix() return pick("Mothball","Lepidopteran","Lightbulb","Moth","Giant Atlas","Twin-spotted Sphynx","Madagascan Sunset","Luna","Death's Head","Emperor Gum","Polyphenus","Oleander Hawk","Io","Rosy Maple","Cecropia","Noctuidae","Giant Leopard","Dysphania Militaris","Garden Tiger") -/datum/holiday/ramadan - name = "Start of Ramadan" - -/* +/datum/holiday/islamic + name = "Islamic calendar code broken" -For anyone who stumbles on this some time in the future: this was calibrated to 2017 -Calculated based on the start and end of Ramadan in 2000 (First year of the Gregorian Calendar supported by BYOND) -This is going to be accurate for at least a decade, likely a lot longer -Since the date fluctuates, it may be inaccurate one year and then accurate for several after -Inaccuracies will never be by more than one day for at least a hundred years -Finds the number of days since the day in 2000 and gets the modulo of that and the average length of a Muslim year since the first one (622 AD, Gregorian) -Since Ramadan is an entire month that lasts 29.5 days on average, the start and end are holidays and are calculated from the two dates in 2000 - -*/ +/datum/holiday/islamic/shouldCelebrate(dd, mm, yy, ww, ddd) + var/datum/foreign_calendar/islamic/cal = new + return ..(cal.dd, cal.mm, cal.yy, ww, ddd) -/datum/holiday/ramadan/shouldCelebrate(dd, mm, yy, ww, ddd) - if (round(((world.realtime - 285984000) / 864000) % 354.373435326843) == 0) - return TRUE - return FALSE +/datum/holiday/islamic/ramadan + name = "Start of Ramadan" + begin_month = 9 + begin_day = 1 + end_day = 3 -/datum/holiday/ramadan/getStationPrefix() - return pick("Harm","Halaal","Jihad","Muslim") +/datum/holiday/islamic/ramadan/getStationPrefix() + return pick("Haram","Halaal","Jihad","Muslim", "Al", "Mohammad", "Rashidun", "Umayyad", "Abbasid", "Abdul", "Fatimid", "Ayyubid", "Almohad", "Abu") -/datum/holiday/ramadan/end +/datum/holiday/islamic/ramadan/end name = "End of Ramadan" - -/datum/holiday/ramadan/end/shouldCelebrate(dd, mm, yy, ww, ddd) - if (round(((world.realtime - 312768000) / 864000) % 354.373435326843) == 0) - return TRUE - return FALSE + end_month = 10 + begin_day = 28 + end_day = 1 /datum/holiday/lifeday name = "Life Day" @@ -622,10 +613,10 @@ Since Ramadan is an entire month that lasts 29.5 days on average, the start and return "Happy National Hot Dog Day!" /datum/holiday/hebrew - name = "Jewish Bugsgiving" + name = "If you see this the Hebrew holiday calendar code is broken" /datum/holiday/hebrew/shouldCelebrate(dd, mm, yy, ww, ddd) - var/datum/hebrew_calendar/cal = new /datum/hebrew_calendar() + var/datum/foreign_calendar/hebrew/cal = new return ..(cal.dd, cal.mm, cal.yy, ww, ddd) /datum/holiday/hebrew/hanukkah diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm index cbb7abf67dfb..4b3343a84ff5 100644 --- a/code/modules/holodeck/computer.dm +++ b/code/modules/holodeck/computer.dm @@ -67,6 +67,11 @@ return else linked.linked = src + var/area/my_area = get_area(src) + if(my_area) + linked.power_usage = my_area.power_usage + else + linked.power_usage = new /list(AREA_USAGE_LEN) generate_program_list() load_program(offline_program, FALSE, FALSE) @@ -75,6 +80,7 @@ emergency_shutdown() if(linked) linked.linked = null + linked.power_usage = new /list(AREA_USAGE_LEN) return ..() /obj/machinery/computer/holodeck/power_change() @@ -84,7 +90,7 @@ /obj/machinery/computer/holodeck/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "holodeck", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Holodeck", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/computer/holodeck/ui_data(mob/user) @@ -149,7 +155,7 @@ for(var/turf/T in linked) if(prob(30)) do_sparks(2, 1, T) - T.ex_act(EXPLODE_LIGHT) + SSexplosions.lowturf += T T.hotspot_expose(1000,500,1) if(!(obj_flags & EMAGGED)) diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm index ac8ceb57ca8b..6d681c3dc323 100644 --- a/code/modules/holodeck/items.dm +++ b/code/modules/holodeck/items.dm @@ -154,7 +154,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 6 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON /obj/machinery/readybutton/attack_ai(mob/user as mob) to_chat(user, "The station AI is not to interact with these devices!") diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm index f71a4fefe103..356d8560eb3a 100644 --- a/code/modules/hydroponics/biogenerator.dm +++ b/code/modules/hydroponics/biogenerator.dm @@ -30,7 +30,13 @@ /obj/machinery/biogenerator/contents_explosion(severity, target) ..() if(beaker) - beaker.ex_act(severity, target) + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.highobj += beaker + if(EXPLODE_HEAVY) + SSexplosions.medobj += beaker + if(EXPLODE_LIGHT) + SSexplosions.lowobj += beaker /obj/machinery/biogenerator/handle_atom_del(atom/A) ..() @@ -293,7 +299,10 @@ /obj/machinery/biogenerator/proc/detach(mob/living/user) if(beaker) - user.put_in_hands(beaker) + if(can_interact(user)) + user.put_in_hands(beaker) + else + beaker.drop_location(get_turf(src)) beaker = null update_icon() diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm index d46f37d1580a..8d996e6b17d9 100644 --- a/code/modules/hydroponics/fermenting_barrel.dm +++ b/code/modules/hydroponics/fermenting_barrel.dm @@ -50,6 +50,9 @@ to_chat(user, "You place [I] into [src] to start the fermentation process.") addtimer(CALLBACK(src, .proc/makeWine, fruit), rand(80, 120) * speed_multiplier) return TRUE + if(I) + if(I.is_refillable()) + return FALSE //so we can refill them via their afterattack. else return ..() diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index b17e17bc7893..8ad7000675be 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -66,7 +66,6 @@ /obj/item/reagent_containers/food/snacks/grown/attackby(obj/item/O, mob/user, params) ..() if (istype(O, /obj/item/plant_analyzer)) - flick("[icon_state]-scan", O) //makes it so that it plays the scan animation upon scanning, waspstation edit var/msg = "*---------*\n This is \a [src].\n" if(seed) msg += seed.get_analyzer_text() @@ -120,9 +119,9 @@ for(var/datum/plant_gene/trait/trait in seed.genes) trait.on_squash(src, target) - reagents.reaction(T) + reagents.expose(T) for(var/A in T) - reagents.reaction(A) + reagents.expose(A) qdel(src) diff --git a/code/modules/hydroponics/grown/kudzu.dm b/code/modules/hydroponics/grown/kudzu.dm index 5f1fdcdaa920..0c55aeca70ff 100644 --- a/code/modules/hydroponics/grown/kudzu.dm +++ b/code/modules/hydroponics/grown/kudzu.dm @@ -14,7 +14,7 @@ growthstages = 4 rarity = 30 var/list/mutations = list() - reagents_add = list(/datum/reagent/medicine/charcoal = 0.04, /datum/reagent/consumable/nutriment = 0.02) + reagents_add = list(/datum/reagent/medicine/C2/multiver = 0.04, /datum/reagent/consumable/nutriment = 0.02) /obj/item/seeds/kudzu/Copy() var/obj/item/seeds/kudzu/S = ..() diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm index 875cbf3d37b3..783a7cb01e28 100644 --- a/code/modules/hydroponics/grown/mushrooms.dm +++ b/code/modules/hydroponics/grown/mushrooms.dm @@ -21,7 +21,7 @@ growthstages = 4 genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism) growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' - reagents_add = list(/datum/reagent/medicine/morphine = 0.35, /datum/reagent/medicine/charcoal = 0.35, /datum/reagent/consumable/nutriment = 0) + reagents_add = list(/datum/reagent/medicine/morphine = 0.35, /datum/reagent/medicine/C2/multiver = 0.35, /datum/reagent/consumable/nutriment = 0) /obj/item/reagent_containers/food/snacks/grown/mushroom/reishi seed = /obj/item/seeds/reishi diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm index b1581c9dcee2..020d554cf534 100644 --- a/code/modules/hydroponics/grown/replicapod.dm +++ b/code/modules/hydroponics/grown/replicapod.dm @@ -118,7 +118,7 @@ features["mcolor"] = "#59CE00" for(var/V in quirks) new V(podman) - podman.hardset_dna(null,null,podman.real_name,blood_type, new /datum/species/pod,features)//Discard SE's and UI's, podman cloning is inaccurate, and always make them a podman + podman.hardset_dna(null,null,null,podman.real_name,blood_type, new /datum/species/pod,features)//Discard SE's and UI's, podman cloning is inaccurate, and always make them a podman podman.set_cloned_appearance() log_cloning("[key_name(mind)] cloned as a podman via [src] in [parent] at [AREACOORD(parent)].") diff --git a/code/modules/hydroponics/grown/tea_coffee.dm b/code/modules/hydroponics/grown/tea_coffee.dm index 3d62ecdf7fcf..c89c65d36068 100644 --- a/code/modules/hydroponics/grown/tea_coffee.dm +++ b/code/modules/hydroponics/grown/tea_coffee.dm @@ -14,6 +14,7 @@ icon_dead = "tea-dead" genes = list(/datum/plant_gene/trait/repeated_harvest) mutatelist = list(/obj/item/seeds/tea/astra) + reagents_add = list(/datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/toxin/teapowder = 0.1) /obj/item/reagent_containers/food/snacks/grown/tea seed = /obj/item/seeds/tea diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index d01ce95e1c2d..2df185c4b849 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -532,8 +532,8 @@ to_chat(user, "[src] warms as it might on a spring day under a genuine Sun.") // Antitoxin binds shit pretty well. So the tox goes significantly down - if(S.has_reagent(/datum/reagent/medicine/charcoal, 1)) - adjustToxic(-round(S.get_reagent_amount(/datum/reagent/medicine/charcoal) * 2)) + if(S.has_reagent(/datum/reagent/medicine/C2/multiver, 1)) + adjustToxic(-round(S.get_reagent_amount(/datum/reagent/medicine/C2/multiver) * 2)) // NIGGA, YOU JUST WENT ON FULL RETARD. if(S.has_reagent(/datum/reagent/toxin, 1)) diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index 0fe66eec4e4b..a2acc29cc6ea 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -427,7 +427,7 @@ if(L.reagents && L.can_inject(null, 0)) var/injecting_amount = max(1, G.seed.potency*0.2) // Minimum of 1, max of 20 var/fraction = min(injecting_amount/G.reagents.total_volume, 1) - G.reagents.reaction(L, INJECT, fraction) + G.reagents.expose(L, INJECT, fraction) G.reagents.trans_to(L, injecting_amount) to_chat(target, "You are pricked by [G]!") log_combat(G, L, "pricked and attempted to inject reagents from [G] to [L]. Last touched by: [G.fingerprintslast].") diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index c4b52acae1ae..444b849aa2c7 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -438,3 +438,19 @@ genes += P else qdel(P) + +/obj/item/seeds/proc/remove_random_reagents(lower = 0, upper = 2) + var/amount_random_reagents = rand(lower, upper) + for(var/i in 1 to amount_random_reagents) + var/datum/reagent/chemical = pick(reagents_add) + qdel(chemical) + +/obj/item/seeds/proc/remove_random_traits(lower = 0, upper = 2) + var/list/genepool = list() + var/amount_random_traits = rand(lower, upper) + for(var/datum/plant_gene/trait in genes) + genepool += trait + + for(var/i in 1 to amount_random_traits) + var/datum/plant_gene/planted_gene = pick(genepool) + qdel(planted_gene) diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index b467f8e975a4..8de819294a11 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -361,6 +361,8 @@ else C.assignment = J.title //Wasp end + if(H.age) + C.registered_age = H.age C.update_label() for(var/A in SSeconomy.bank_accounts) var/datum/bank_account/B = A diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm index 79a751d29819..ac65c35e9366 100644 --- a/code/modules/jobs/job_types/ai.dm +++ b/code/modules/jobs/job_types/ai.dm @@ -37,7 +37,10 @@ H.forceMove(lateJoinCore.loc) qdel(lateJoinCore) var/mob/living/silicon/ai/AI = H - AI.apply_pref_name("ai", M.client) //If this runtimes oh well jobcode is fucked. + if(SSticker.anonymousnames) + AI.fully_replace_character_name(AI.real_name, anonymous_ai_name(is_ai = TRUE)) + else + AI.apply_pref_name("ai", M.client) //If this runtimes oh well jobcode is fucked. //what is this no energy attitude man AI.set_core_display_icon(null, M.client) //we may have been created after our borg diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm index cac090a135a5..4c82b5b61ba3 100644 --- a/code/modules/jobs/job_types/bartender.dm +++ b/code/modules/jobs/job_types/bartender.dm @@ -8,7 +8,6 @@ spawn_positions = 1 supervisors = "the head of personnel" selection_color = "#bbe291" - exp_type_department = EXP_TYPE_SERVICE // This is so the jobs menu can work properly wiki_page = "Drinks" //WaspStation Edit - Wikilinks/Warning outfit = /datum/outfit/job/bartender @@ -28,8 +27,15 @@ ears = /obj/item/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/civilian/bartender alt_uniform = /obj/item/clothing/under/rank/civilian/bartender/purple //Wasp Edit - Alt Uniforms - alt_suit = /obj/item/clothing/suit/apron/purple_bartender + alt_suit = /obj/item/clothing/suit/apron/purple_bartender suit = /obj/item/clothing/suit/armor/vest backpack_contents = list(/obj/item/storage/box/beanbag=1) shoes = /obj/item/clothing/shoes/laceup +/datum/outfit/job/bartender/post_equip(mob/living/carbon/human/H, visualsOnly) + . = ..() + + var/obj/item/card/id/W = H.wear_id + if(H.age < AGE_MINOR) + W.registered_age = AGE_MINOR + to_chat(H, "You're not technically old enough to access or serve alcohol, but your ID has been discreetly modified to display your age as [AGE_MINOR]. Try to keep that a secret!") diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm index e27b37d35f0d..33454e1f3fb6 100644 --- a/code/modules/jobs/job_types/cargo_technician.dm +++ b/code/modules/jobs/job_types/cargo_technician.dm @@ -30,4 +30,4 @@ alt_suit = /obj/item/clothing/suit/hazardvest dcoat = /obj/item/clothing/suit/hooded/wintercoat/cargo //Wasp Edit - Alt Uniforms l_hand = /obj/item/export_scanner - + backpack_contents = list(/obj/item/modular_computer/tablet/preset/cargo=1) diff --git a/code/modules/jobs/job_types/chaplain.dm b/code/modules/jobs/job_types/chaplain.dm index 7068f6f4e828..d03584d9af12 100644 --- a/code/modules/jobs/job_types/chaplain.dm +++ b/code/modules/jobs/job_types/chaplain.dm @@ -7,15 +7,15 @@ total_positions = 1 spawn_positions = 1 supervisors = "the head of personnel" - selection_color = "#dddddd" wiki_page = "Chaplain" //WaspStation Edit - Wikilinks/Warning + selection_color = "#bbe291" outfit = /datum/outfit/job/chaplain access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) minimal_access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE) paycheck = PAYCHECK_EASY - paycheck_department = ACCOUNT_CIV + paycheck_department = ACCOUNT_SRV display_order = JOB_DISPLAY_ORDER_CHAPLAIN @@ -53,7 +53,6 @@ B.deity_name = new_deity - switch(lowertext(new_religion)) if("christianity") // DEFAULT_RELIGION B.name = pick("The Holy Bible","The Dead Sea Scrolls") @@ -79,7 +78,7 @@ B.name = "Fluorescent Incandescence" if("lol", "wtf", "gay", "penis", "ass", "poo", "badmin", "shitmin", "deadmin", "cock", "cocks", "meme", "memes") B.name = pick("Woodys Got Wood: The Aftermath", "War of the Cocks", "Sweet Bro and Hella Jef: Expanded Edition","F.A.T.A.L. Rulebook") - H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100) // starts off retarded as fuck + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100) // starts off brain damaged as fuck if("monkeyism","apism","gorillism","primatism") B.name = pick("Going Bananas", "Bananas Out For Harambe") if("mormonism") @@ -90,6 +89,8 @@ B.name = "The Holy Piby" if("satanism") B.name = "The Unholy Bible" + if("sikhism") + B.name = "Guru Granth Sahib" if("science") B.name = pick("Principle of Relativity", "Quantum Enigma: Physics Encounters Consciousness", "Programming the Universe", "Quantum Physics and Theology", "String Theory for Dummies", "How To: Build Your Own Warp Drive", "The Mysteries of Bluespace", "Playing God: Collector's Edition") if("scientology") @@ -124,6 +125,12 @@ ears = /obj/item/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/civilian/chaplain alt_uniform = /obj/item/clothing/under/pants/youngfolksjeans //Wasp Edit - Alt Uniforms - backpack_contents = list(/obj/item/camera/spooky = 1) + backpack_contents = list( + /obj/item/stamp/chap = 1, + /obj/item/camera/spooky = 1 + ) + backpack = /obj/item/storage/backpack/cultpack satchel = /obj/item/storage/backpack/cultpack + + chameleon_extras = /obj/item/stamp/chap diff --git a/code/modules/jobs/job_types/chemist.dm b/code/modules/jobs/job_types/chemist.dm index 8ca356c6c54d..56178dc3f179 100644 --- a/code/modules/jobs/job_types/chemist.dm +++ b/code/modules/jobs/job_types/chemist.dm @@ -37,6 +37,7 @@ satchel = /obj/item/storage/backpack/satchel/chem duffelbag = /obj/item/storage/backpack/duffelbag/med courierbag = /obj/item/storage/backpack/messenger/chem + box = /obj/item/storage/box/survival/medical chameleon_extras = /obj/item/gun/syringe diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm index ce3fda69ba22..277a1e8a25c6 100644 --- a/code/modules/jobs/job_types/chief_medical_officer.dm +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -52,13 +52,14 @@ satchel = /obj/item/storage/backpack/satchel/med duffelbag = /obj/item/storage/backpack/duffelbag/med courierbag = /obj/item/storage/backpack/messenger/med + box = /obj/item/storage/box/survival/medical chameleon_extras = list(/obj/item/gun/syringe, /obj/item/stamp/cmo) /datum/outfit/job/cmo/hardsuit name = "Chief Medical Officer (Hardsuit)" - mask = /obj/item/clothing/mask/breath + mask = /obj/item/clothing/mask/breath/medical suit = /obj/item/clothing/suit/space/hardsuit/medical/cmo suit_store = /obj/item/tank/internals/oxygen r_pocket = /obj/item/flashlight/pen diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm index 67d00e7e0bb4..e4e2e2207a28 100644 --- a/code/modules/jobs/job_types/clown.dm +++ b/code/modules/jobs/job_types/clown.dm @@ -7,9 +7,9 @@ total_positions = 1 spawn_positions = 1 supervisors = "the head of personnel" - selection_color = "#dddddd" wiki_page = "Clown" //WaspStation Edit - Wikilinks/Warning special_notice = "There is a difference between funny pranks and griefing. Know it." //WaspStation Edit - Wikilinks/Warning + selection_color = "#bbe291" outfit = /datum/outfit/job/clown @@ -63,3 +63,5 @@ H.dna.add_mutation(CLOWNMUT) for(var/datum/mutation/human/clumsy/M in H.dna.mutations) M.mutadone_proof = TRUE + var/datum/atom_hud/fan = GLOB.huds[DATA_HUD_FAN] + fan.add_hud_to(H) diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm index dd916adafb8b..f927afc64597 100644 --- a/code/modules/jobs/job_types/curator.dm +++ b/code/modules/jobs/job_types/curator.dm @@ -7,15 +7,15 @@ total_positions = 1 spawn_positions = 1 supervisors = "the head of personnel" - selection_color = "#dddddd" wiki_page = "Curator" //WaspStation Edit - Wikilinks/Warning + selection_color = "#bbe291" outfit = /datum/outfit/job/curator access = list(ACCESS_LIBRARY, ACCESS_CONSTRUCTION, ACCESS_MINING_STATION) minimal_access = list(ACCESS_LIBRARY, ACCESS_CONSTRUCTION, ACCESS_MINING_STATION) paycheck = PAYCHECK_EASY - paycheck_department = ACCOUNT_CIV + paycheck_department = ACCOUNT_SRV display_order = JOB_DISPLAY_ORDER_CURATOR diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm index f55950752afc..ad97f52a36d1 100644 --- a/code/modules/jobs/job_types/head_of_personnel.dm +++ b/code/modules/jobs/job_types/head_of_personnel.dm @@ -15,8 +15,7 @@ exp_requirements = 180 wiki_page = "Head_of_Personnel" //WaspStation Edit - Wikilinks/Warning exp_type = EXP_TYPE_CREW - exp_type_department = EXP_TYPE_SUPPLY - special_notice = "You are NOT security, and should NEVER UNDER ANY CIRCUMSTANCES act like them." //WaspStation Edit - Wikilinks/Warning + exp_type_department = EXP_TYPE_SERVICE outfit = /datum/outfit/job/head_of_personnel diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm index 41a893d8deaf..66ae30a14436 100644 --- a/code/modules/jobs/job_types/lawyer.dm +++ b/code/modules/jobs/job_types/lawyer.dm @@ -7,9 +7,9 @@ total_positions = 2 spawn_positions = 2 supervisors = "the head of personnel" - selection_color = "#dddddd" special_notice = "You are not a security officer. However, you represent the law and can defend those who are mishandled by security in court." //WaspStation Edit - Wikilinks/Warning wiki_page = "Lawyer" //WaspStation Edit - Wikilinks/Warning + selection_color = "#bbe291" var/lawyers = 0 //Counts lawyer amount outfit = /datum/outfit/job/lawyer @@ -17,7 +17,7 @@ access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) minimal_access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS) paycheck = PAYCHECK_EASY - paycheck_department = ACCOUNT_CIV + paycheck_department = ACCOUNT_SRV mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) display_order = JOB_DISPLAY_ORDER_LAWYER diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm index 3379800ad418..5fd172540f33 100644 --- a/code/modules/jobs/job_types/medical_doctor.dm +++ b/code/modules/jobs/job_types/medical_doctor.dm @@ -38,6 +38,7 @@ satchel = /obj/item/storage/backpack/satchel/med duffelbag = /obj/item/storage/backpack/duffelbag/med courierbag = /obj/item/storage/backpack/messenger/med + box = /obj/item/storage/box/survival/medical chameleon_extras = /obj/item/gun/syringe diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm index 11a3db7f18ae..fa01a5777218 100644 --- a/code/modules/jobs/job_types/mime.dm +++ b/code/modules/jobs/job_types/mime.dm @@ -7,8 +7,8 @@ total_positions = 1 spawn_positions = 1 supervisors = "the head of personnel" - selection_color = "#dddddd" wiki_page = "Mime" //WaspStation Edit - Wikilinks/Warning + selection_color = "#bbe291" outfit = /datum/outfit/job/mime @@ -35,11 +35,16 @@ gloves = /obj/item/clothing/gloves/color/white head = /obj/item/clothing/head/frenchberet suit = /obj/item/clothing/suit/toggle/suspenders - backpack_contents = list(/obj/item/book/mimery=1, /obj/item/reagent_containers/food/drinks/bottle/bottleofnothing=1) + backpack_contents = list( + /obj/item/stamp/mime = 1, + /obj/item/book/mimery = 1, + /obj/item/reagent_containers/food/drinks/bottle/bottleofnothing = 1 + ) backpack = /obj/item/storage/backpack/mime satchel = /obj/item/storage/backpack/mime + chameleon_extras = /obj/item/stamp/mime /datum/outfit/job/mime/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) ..() @@ -51,6 +56,9 @@ H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mime/speak(null)) H.mind.miming = TRUE + var/datum/atom_hud/fan = GLOB.huds[DATA_HUD_FAN] + fan.add_hud_to(H) + /obj/item/book/mimery name = "Guide to Dank Mimery" desc = "A primer on basic pantomime." diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm index f2ff341c7479..4011190af4d3 100644 --- a/code/modules/jobs/job_types/paramedic.dm +++ b/code/modules/jobs/job_types/paramedic.dm @@ -43,5 +43,6 @@ satchel = /obj/item/storage/backpack/satchel/med duffelbag = /obj/item/storage/backpack/duffelbag/med courierbag = /obj/item/storage/backpack/messenger/para //WaspStation Edit - Paramedic Bling + box = /obj/item/storage/box/survival/medical chameleon_extras = /obj/item/gun/syringe diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm index 2621c7e06f6c..122f87e48bbc 100644 --- a/code/modules/jobs/job_types/prisoner.dm +++ b/code/modules/jobs/job_types/prisoner.dm @@ -7,6 +7,7 @@ total_positions = 0 spawn_positions = 2 supervisors = "the security team" + selection_color = "#ffe1c3" outfit = /datum/outfit/job/prisoner diff --git a/code/modules/jobs/job_types/psychologist.dm b/code/modules/jobs/job_types/psychologist.dm index 0acdb5ea2a20..92348918a816 100644 --- a/code/modules/jobs/job_types/psychologist.dm +++ b/code/modules/jobs/job_types/psychologist.dm @@ -8,7 +8,7 @@ total_positions = 1 spawn_positions = 1 supervisors = "the head of personnel and the chief medical officer" - selection_color = "#dddddd" + selection_color = "#bbe291" outfit = /datum/outfit/job/psychologist @@ -31,6 +31,8 @@ pda_slot = ITEM_SLOT_BELT l_hand = /obj/item/clipboard + backpack_contents = list(/obj/item/storage/pill_bottle/mannitol, /obj/item/storage/pill_bottle/psicodine, /obj/item/storage/pill_bottle/paxpsych, /obj/item/storage/pill_bottle/happinesspsych, /obj/item/storage/pill_bottle/lsdpsych) + backpack = /obj/item/storage/backpack/medic satchel = /obj/item/storage/backpack/satchel/med duffelbag = /obj/item/storage/backpack/duffelbag/med diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm index d2cbc8a6dc10..68c08be7b1eb 100644 --- a/code/modules/jobs/job_types/quartermaster.dm +++ b/code/modules/jobs/job_types/quartermaster.dm @@ -10,6 +10,7 @@ selection_color = "#d7b088" wiki_page = "Quartermaster" //WaspStation Edit - Wikilinks/Warning special_notice = "You are not a head of staff. You answer to the Head of Personnel." //WaspStation Edit - Wikilinks/Warning + exp_type_department = EXP_TYPE_SUPPLY // This is so the jobs menu can work properly outfit = /datum/outfit/job/quartermaster @@ -33,6 +34,7 @@ shoes = /obj/item/clothing/shoes/sneakers/brown glasses = /obj/item/clothing/glasses/sunglasses l_hand = /obj/item/clipboard + backpack_contents = list(/obj/item/modular_computer/tablet/preset/cargo=1) chameleon_extras = /obj/item/stamp/qm diff --git a/code/modules/jobs/job_types/virologist.dm b/code/modules/jobs/job_types/virologist.dm index 577fcf39920a..338e476e0576 100644 --- a/code/modules/jobs/job_types/virologist.dm +++ b/code/modules/jobs/job_types/virologist.dm @@ -41,3 +41,4 @@ satchel = /obj/item/storage/backpack/satchel/vir duffelbag = /obj/item/storage/backpack/duffelbag/med courierbag = /obj/item/storage/backpack/messenger/viro + box = /obj/item/storage/box/survival/medical diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm index 4d7a5f639dce..c96d689d28df 100644 --- a/code/modules/jobs/jobs.dm +++ b/code/modules/jobs/jobs.dm @@ -30,13 +30,14 @@ GLOBAL_LIST_INIT(science_positions, list( GLOBAL_LIST_INIT(supply_positions, list( - "Head of Personnel", "Quartermaster", "Cargo Technician", "Shaft Miner")) -GLOBAL_LIST_INIT(civilian_positions, list( +GLOBAL_LIST_INIT(service_positions, list( + "Head of Personnel", + "Lieutenant", "Bartender", "Botanist", "Cook", @@ -70,14 +71,14 @@ GLOBAL_LIST_INIT(position_categories, list( EXP_TYPE_ENGINEERING = list("jobs" = engineering_positions, "color" = "#ffeeaa"), EXP_TYPE_SUPPLY = list("jobs" = supply_positions, "color" = "#ddddff"), EXP_TYPE_SILICON = list("jobs" = nonhuman_positions - "pAI", "color" = "#ccffcc"), - EXP_TYPE_SERVICE = list("jobs" = civilian_positions, "color" = "#bbe291"), + EXP_TYPE_SERVICE = list("jobs" = service_positions, "color" = "#bbe291"), EXP_TYPE_MEDICAL = list("jobs" = medical_positions, "color" = "#ffddf0"), EXP_TYPE_SCIENCE = list("jobs" = science_positions, "color" = "#ffddff"), EXP_TYPE_SECURITY = list("jobs" = security_positions, "color" = "#ffdddd") )) GLOBAL_LIST_INIT(exp_jobsmap, list( - EXP_TYPE_CREW = list("titles" = command_positions | engineering_positions | medical_positions | science_positions | supply_positions | security_positions | civilian_positions | list("AI","Cyborg")), // crew positions + EXP_TYPE_CREW = list("titles" = command_positions | engineering_positions | medical_positions | science_positions | supply_positions | security_positions | service_positions | list("AI","Cyborg")), // crew positions EXP_TYPE_COMMAND = list("titles" = command_positions), EXP_TYPE_ENGINEERING = list("titles" = engineering_positions), EXP_TYPE_MEDICAL = list("titles" = medical_positions), @@ -85,7 +86,7 @@ GLOBAL_LIST_INIT(exp_jobsmap, list( 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" = civilian_positions) + EXP_TYPE_SERVICE = list("titles" = service_positions) )) GLOBAL_LIST_INIT(exp_specialmap, list( diff --git a/code/modules/language/language_menu.dm b/code/modules/language/language_menu.dm index a7ce211a1875..0df7c01fca8c 100644 --- a/code/modules/language/language_menu.dm +++ b/code/modules/language/language_menu.dm @@ -11,7 +11,7 @@ /datum/language_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.language_menu_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "language_menu", "Language Menu", 700, 600, master_ui, state) + ui = new(user, src, ui_key, "LanguageMenu", "Language Menu", 700, 600, master_ui, state) ui.open() /datum/language_menu/ui_data(mob/user) diff --git a/code/modules/library/lib_codex_gigas.dm b/code/modules/library/lib_codex_gigas.dm index 598d375888cc..e7bb86ff7992 100644 --- a/code/modules/library/lib_codex_gigas.dm +++ b/code/modules/library/lib_codex_gigas.dm @@ -95,7 +95,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "codex_gigas", name, 450, 450, master_ui, state) + ui = new(user, src, ui_key, "CodexGigas", name, 450, 450, master_ui, state) ui.open() /obj/item/book/codex_gigas/ui_data(mob/user) diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index c658c906fd88..c216acc6ba14 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -25,11 +25,11 @@ var/title var/category = "Any" var/author - var/SQLquery + var/search_page = 0 /obj/machinery/computer/libraryconsole/ui_interact(mob/user) . = ..() - var/dat = "" // + var/list/dat = list() // switch(screenstate) if(0) dat += "

    Search Settings


    " @@ -42,13 +42,38 @@ dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
    " else if(QDELETED(user)) return - else if(!SQLquery) - dat += "ERROR: Malformed search request. Please contact your system administrator for assistance.
    " else dat += "" dat += "" - - var/datum/DBQuery/query_library_list_books = SSdbcore.NewQuery(SQLquery) + author = sanitizeSQL(author) + title = sanitizeSQL(title) + category = sanitizeSQL(category) + var/SQLsearch = "isnull(deleted) AND " + if(category == "Any") + SQLsearch += "author LIKE '%[author]%' AND title LIKE '%[title]%'" + else + SQLsearch += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'" + var/bookcount = 0 + var/booksperpage = 20 + var/datum/DBQuery/query_library_count_books = SSdbcore.NewQuery("SELECT COUNT(id) FROM [format_table_name("library")] WHERE [SQLsearch]") + if(!query_library_count_books.warn_execute()) + qdel(query_library_count_books) + return + if(query_library_count_books.NextRow()) + bookcount = text2num(query_library_count_books.item[1]) + qdel(query_library_count_books) + if(bookcount > booksperpage) + dat += "Page: " + var/pagecount = 1 + var/list/pagelist = list() + while(bookcount > 0) + pagelist += "[pagecount == search_page + 1 ? "\[[pagecount]\]" : "\[[pagecount]\]"]" + bookcount -= booksperpage + pagecount++ + dat += pagelist.Join(" | ") + search_page = text2num(sanitizeSQL(search_page)) + var/limit = " LIMIT [booksperpage * search_page], [booksperpage]" + var/datum/DBQuery/query_library_list_books = SSdbcore.NewQuery("SELECT author, title, category, id FROM [format_table_name("library")] WHERE [SQLsearch][limit]") if(!query_library_list_books.Execute()) dat += "ERROR: Unable to retrieve book listings. Please contact your system administrator for assistance.
    " else @@ -64,7 +89,7 @@ dat += "
    AUTHORTITLECATEGORYSS13BN

    " dat += "\[Go Back\]
    " var/datum/browser/popup = new(user, "publiclibrary", name, 600, 400) - popup.set_content(dat) + popup.set_content(jointext(dat, "")) popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) popup.open() @@ -81,29 +106,24 @@ title = sanitize(newtitle) else title = null - title = sanitizeSQL(title) if(href_list["setcategory"]) var/newcategory = input("Choose a category to search for:") in list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion") if(newcategory) category = sanitize(newcategory) else category = "Any" - category = sanitizeSQL(category) if(href_list["setauthor"]) var/newauthor = input("Enter an author to search for:") as text|null if(newauthor) author = sanitize(newauthor) else author = null - author = sanitizeSQL(author) if(href_list["search"]) - SQLquery = "SELECT author, title, category, id FROM [format_table_name("library")] WHERE isnull(deleted) AND " - if(category == "Any") - SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%'" - else - SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'" screenstate = 1 + if(href_list["bookpagecount"]) + search_page = text2num(href_list["bookpagecount"]) + if(href_list["back"]) screenstate = 0 diff --git a/code/modules/library/soapstone.dm b/code/modules/library/soapstone.dm index 8c37a8645b71..3f1ea429e547 100644 --- a/code/modules/library/soapstone.dm +++ b/code/modules/library/soapstone.dm @@ -207,7 +207,7 @@ /obj/structure/chisel_message/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "engraved_message", name, 600, 300, master_ui, state) + ui = new(user, src, ui_key, "EngravedMessage", name, 600, 300, master_ui, state) ui.open() /obj/structure/chisel_message/ui_data(mob/user) diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index cf800e23e25e..b45a86f689ab 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -83,11 +83,11 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, T = thing if (T.lighting_object) active = TRUE + return // God that was a mess, now to do the rest of the corner code! Hooray! /datum/lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b) - - if ((abs(delta_r)+abs(delta_g)+abs(delta_b)) == 0) + if (!(delta_r || delta_g || delta_b)) // 0 is falsey ok return lum_r += delta_r @@ -99,7 +99,7 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, SSlighting.corners_queue += src /datum/lighting_corner/proc/update_objects() - // Cache these values a head of time so 4 individual lighting objects don't all calculate them individually. + // Cache these values ahead of time so 4 individual lighting objects don't all calculate them individually. var/lum_r = src.lum_r var/lum_g = src.lum_g var/lum_b = src.lum_b @@ -124,20 +124,18 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, for (var/TT in masters) var/turf/T = TT - if (T.lighting_object) - if (!T.lighting_object.needs_update) - T.lighting_object.needs_update = TRUE - SSlighting.objects_queue += T.lighting_object + if (T.lighting_object && !T.lighting_object.needs_update) + T.lighting_object.needs_update = TRUE + SSlighting.objects_queue += T.lighting_object /datum/lighting_corner/dummy/New() return - -/datum/lighting_corner/Destroy(var/force) +/datum/lighting_corner/Destroy(force) if (!force) return QDEL_HINT_LETMELIVE - stack_trace("Ok, Look, /tg/, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% retarded and needs his head checked. Thanks. Send them my regards by the way.") + stack_trace("Ok, Look, /tg/, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is brain damaged and needs his head checked. Thanks. Send them my regards by the way.") return ..() diff --git a/code/modules/mapping/space_management/space_transition.dm b/code/modules/mapping/space_management/space_transition.dm index 5a9d52033c1d..73970a792891 100644 --- a/code/modules/mapping/space_management/space_transition.dm +++ b/code/modules/mapping/space_management/space_transition.dm @@ -125,7 +125,7 @@ S.destination_x = x_pos_transition[side] == 1 ? S.x : x_pos_transition[side] S.destination_y = y_pos_transition[side] == 1 ? S.y : y_pos_transition[side] S.destination_z = zdestination - + // Mirage border code var/mirage_dir if(S.x == 1 + TRANSITIONEDGE) diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm index d1c3b3421f1c..9e7e8cda1ebe 100644 --- a/code/modules/mining/equipment/mining_tools.dm +++ b/code/modules/mining/equipment/mining_tools.dm @@ -24,6 +24,13 @@ user.visible_message("[user] couldn't do it!") return SHAME +/obj/item/pickaxe/rusted + name = "rusty pickaxe" + desc = "A pickaxe that's been left to rust." + attack_verb = list("ineffectively hit") + force = 1 + throwforce = 1 + /obj/item/pickaxe/mini name = "compact pickaxe" desc = "A smaller, compact version of the standard pickaxe." @@ -85,11 +92,22 @@ icon_state = "jackhammer" item_state = "jackhammer" toolspeed = 0.1 //the epitome of powertools. extremely fast mining - w_class = WEIGHT_CLASS_HUGE //the epitome of power(gamer)tools is CHUNCKY usesound = 'sound/weapons/sonic_jackhammer.ogg' hitsound = 'sound/weapons/sonic_jackhammer.ogg' desc = "Cracks rocks with sonic blasts." +/obj/item/pickaxe/improvised + name = "improvised pickaxe" + desc = "A pickaxe made with a knife and crowbar taped together, how does it not break?" + icon_state = "ipickaxe" + item_state = "ipickaxe" + force = 10 + throwforce = 7 + toolspeed = 3 //3 times slower than a normal pickaxe + slot_flags = ITEM_SLOT_BELT + w_class = WEIGHT_CLASS_NORMAL + custom_materials = list(/datum/material/iron=12050) //metal needed for a crowbar and for a knife, why the FUCK does a knife cost 6 metal sheets while a crowbar costs 0.025 sheets? shit makes no sense fuck this + /obj/item/shovel name = "shovel" desc = "A large tool for digging and moving dirt." diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index d91d1c3af22c..60a343858419 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -39,7 +39,7 @@ GLOBAL_LIST(labor_sheet_values) datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "labor_claim_console", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "LaborClaimConsole", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/mineral/labor_claim_console/ui_data(mob/user) diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index fec0d14ec74e..29a98cdb667e 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -611,16 +611,19 @@ reagent_state = LIQUID color = "#FFEBEB" -/datum/reagent/flightpotion/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message = 1) +/datum/reagent/flightpotion/expose_mob(mob/living/M, method=TOUCH, reac_volume, show_message = 1) if(iscarbon(M) && M.stat != DEAD) var/mob/living/carbon/C = M var/holycheck = ishumanbasic(C) - if(!(holycheck || islizard(C)) || reac_volume < 5) // implying xenohumans are holy //as with all things, + if(reac_volume < 5 || !(holycheck || islizard(C) || (ismoth(C) && C.dna.features["moth_wings"] != "Burnt Off"))) // implying xenohumans are holy //as with all things, if(method == INGEST && show_message) to_chat(C, "You feel nothing but a terrible aftertaste.") return ..() - - to_chat(C, "A terrible pain travels down your back as wings burst out!") + if(C.dna.species.has_innate_wings) + to_chat(C, "A terrible pain travels down your back as your wings change shape!") + C.dna.features["moth_wings"] = "None" + else + to_chat(C, "A terrible pain travels down your back as wings burst out!") C.dna.species.GiveSpeciesFlight(C) if(holycheck) to_chat(C, "You feel blessed!") diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index 78bc44263bc8..97d441b2d476 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -3,9 +3,47 @@ /**********************Mineral processing unit console**************************/ /obj/machinery/mineral + processing_flags = START_PROCESSING_MANUALLY + subsystem_type = /datum/controller/subsystem/processing/fastprocess + /// The current direction of `input_turf`, in relation to the machine. var/input_dir = NORTH + /// The current direction, in relation to the machine, that items will be output to. var/output_dir = SOUTH + /// The turf the machines listens to for items to pick up. Calls the `pickup_item()` proc. + var/turf/input_turf = null + /// Determines if this machine needs to pick up items. Used to avoid registering signals to `/mineral` machines that don't pickup items. + var/needs_item_input = FALSE +/obj/machinery/mineral/Initialize(mapload) + . = ..() + if(needs_item_input) + register_input_turf() + +/// Gets the turf in the `input_dir` direction adjacent to the machine, and registers signals for ATOM_ENTERED and ATOM_CREATED. Calls the `pickup_item()` proc when it recieves these signals. +/obj/machinery/mineral/proc/register_input_turf() + input_turf = get_step(src, input_dir) + if(input_turf) // make sure there is actually a turf + RegisterSignal(input_turf, list(COMSIG_ATOM_CREATED, COMSIG_ATOM_ENTERED), .proc/pickup_item) + +/// Unregisters signals that are registered the machine's input turf, if it has one. +/obj/machinery/mineral/proc/unregister_input_turf() + if(input_turf) + UnregisterSignal(input_turf, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_CREATED)) + +/** + Base proc for all `/mineral` subtype machines to use. Place your item pickup behavior in this proc when you override it for your specific machine. + + Called when the COMSIG_ATOM_ENTERED and COMSIG_ATOM_CREATED signals are sent. + + Arguments: + * source - the turf that is listening for the signals. + * target - the atom that just moved onto the `source` turf. + * oldLoc - the old location that `target` was at before moving onto `source`. +*/ +/obj/machinery/mineral/proc/pickup_item(datum/source, atom/movable/target, atom/oldLoc) + return + +/// Generic unloading proc. Takes an atom as an argument and forceMove's it to the turf adjacent to this machine in the `output_dir` direction. /obj/machinery/mineral/proc/unload_mineral(atom/movable/S) S.forceMove(drop_location()) var/turf/T = get_step(src,output_dir) @@ -19,7 +57,6 @@ density = TRUE var/obj/machinery/mineral/processing_unit/machine = null var/machinedir = EAST - speed_process = TRUE /obj/machinery/mineral/processing_unit_console/Initialize() . = ..() @@ -58,6 +95,7 @@ if(href_list["set_on"]) machine.on = (href_list["set_on"] == "on") + machine.begin_processing() updateUsrDialog() return @@ -75,6 +113,7 @@ icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "furnace" density = TRUE + needs_item_input = TRUE var/obj/machinery/mineral/CONSOLE = null var/on = FALSE var/datum/material/selected_material = null @@ -93,10 +132,6 @@ QDEL_NULL(stored_research) return ..() -/obj/machinery/mineral/processing_unit/HasProximity(atom/movable/AM) - if(istype(AM, /obj/item/stack/ore) && AM.loc == get_step(src, input_dir)) - process_ore(AM) - /obj/machinery/mineral/processing_unit/proc/process_ore(obj/item/stack/ore/O) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/material_amount = materials.get_item_material_amount(O) @@ -142,8 +177,12 @@ return dat +/obj/machinery/mineral/processing_unit/pickup_item(datum/source, atom/movable/target, atom/oldLoc) + if(istype(target, /obj/item/stack/ore)) + process_ore(target) + /obj/machinery/mineral/processing_unit/process() - if (on) + if(on) if(selected_material) smelt_ore() @@ -153,6 +192,8 @@ if(CONSOLE) CONSOLE.updateUsrDialog() + else + end_processing() /obj/machinery/mineral/processing_unit/proc/smelt_ore() var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index cafc8b133794..2af7e26ea7f5 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -10,19 +10,19 @@ input_dir = NORTH output_dir = SOUTH req_access = list(ACCESS_MINERAL_STOREROOM) - speed_process = TRUE layer = BELOW_OBJ_LAYER circuit = /obj/item/circuitboard/machine/ore_redemption ui_x = 440 ui_y = 550 + needs_item_input = TRUE + processing_flags = START_PROCESSING_MANUALLY var/points = 0 - var/ore_pickup_rate = 15 var/ore_multiplier = 1 var/point_upgrade = 1 var/list/ore_values = list(/datum/material/iron = 1, /datum/material/glass = 1, /datum/material/plasma = 15, /datum/material/silver = 16, /datum/material/gold = 18, /datum/material/titanium = 30, /datum/material/uranium = 30, /datum/material/diamond = 50, /datum/material/bluespace = 50, /datum/material/bananium = 60) - var/message_sent = FALSE - var/list/ore_buffer = list() + /// Variable that holds a timer which is used for callbacks to `send_console_message()`. Used for preventing multiple calls to this proc while the ORM is eating a stack of ores. + var/console_notify_timer var/datum/techweb/stored_research var/obj/item/disk/design_disk/inserted_disk var/datum/component/remote_materials/materials @@ -38,23 +38,19 @@ return ..() /obj/machinery/mineral/ore_redemption/RefreshParts() - var/ore_pickup_rate_temp = 15 var/point_upgrade_temp = 1 var/ore_multiplier_temp = 1 for(var/obj/item/stock_parts/matter_bin/B in component_parts) ore_multiplier_temp = 0.65 + (0.35 * B.rating) - for(var/obj/item/stock_parts/manipulator/M in component_parts) - ore_pickup_rate_temp = 15 * M.rating for(var/obj/item/stock_parts/micro_laser/L in component_parts) point_upgrade_temp = 0.65 + (0.35 * L.rating) - ore_pickup_rate = ore_pickup_rate_temp point_upgrade = point_upgrade_temp ore_multiplier = round(ore_multiplier_temp, 0.01) /obj/machinery/mineral/ore_redemption/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Smelting [ore_multiplier] sheet(s) per piece of ore.
    Reward point generation at [point_upgrade*100]%.
    Ore pickup speed at [ore_pickup_rate].
    " + . += "The status display reads: Smelting [ore_multiplier] sheet(s) per piece of ore.
    Reward point generation at [point_upgrade*100]%.
    " if(panel_open) . += "Alt-click to rotate the input and output direction." @@ -66,8 +62,6 @@ if(O.refined_type == null) return - ore_buffer -= O - if(O && O.refined_type) points += O.points * point_upgrade * O.amount @@ -113,17 +107,15 @@ return build_amount /obj/machinery/mineral/ore_redemption/proc/process_ores(list/ores_to_process) - var/current_amount = 0 for(var/ore in ores_to_process) - if(current_amount >= ore_pickup_rate) - break smelt_ore(ore) /obj/machinery/mineral/ore_redemption/proc/send_console_message() var/datum/component/material_container/mat_container = materials.mat_container if(!mat_container || !is_station_level(z)) return - message_sent = TRUE + + console_notify_timer = null var/area/A = get_area(src) var/msg = "Now available in [A]:
    " @@ -149,26 +141,29 @@ )) signal.send_to_receivers() -/obj/machinery/mineral/ore_redemption/process() +/obj/machinery/mineral/ore_redemption/pickup_item(datum/source, atom/movable/target, atom/oldLoc) if(!materials.mat_container || panel_open || !powered()) return - var/atom/input = get_step(src, input_dir) - var/obj/structure/ore_box/OB = locate() in input - if(OB) - input = OB - - for(var/obj/item/stack/ore/O in input) - if(QDELETED(O)) - continue - ore_buffer |= O - O.forceMove(src) - CHECK_TICK - - if(LAZYLEN(ore_buffer)) - message_sent = FALSE - process_ores(ore_buffer) - else if(!message_sent) - send_console_message() + + if(istype(target, /obj/structure/ore_box)) + var/obj/structure/ore_box/box = target + process_ores(box.contents) + else if(istype(target, /obj/item/stack/ore)) + var/obj/item/stack/ore/O = target + smelt_ore(O) + else + return + + if(!console_notify_timer) + // gives 5 seconds for a load of ores to be sucked up by the ORM before it sends out request console notifications. This should be enough time for most deposits that people make + console_notify_timer = addtimer(CALLBACK(src, .proc/send_console_message), 5 SECONDS) + +/obj/machinery/mineral/ore_redemption/default_unfasten_wrench(mob/user, obj/item/I) + . = ..() + if(anchored) + register_input_turf() // someone just wrenched us down, re-register the turf + else + unregister_input_turf() // someone just un-wrenched us, unregister the turf /obj/machinery/mineral/ore_redemption/attackby(obj/item/W, mob/user, params) if(default_unfasten_wrench(user, W)) @@ -199,16 +194,18 @@ . = ..() if(!user.canUseTopic(src, BE_CLOSE)) return - if (panel_open) + if(panel_open) input_dir = turn(input_dir, -90) output_dir = turn(output_dir, -90) to_chat(user, "You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)].") + unregister_input_turf() // someone just rotated the input and output directions, unregister the old turf + register_input_turf() // register the new one return TRUE /obj/machinery/mineral/ore_redemption/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "ore_redemption_machine", "Ore Redemption Machine", ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "OreRedemptionMachine", "Ore Redemption Machine", ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/mineral/ore_redemption/ui_data(mob/user) diff --git a/code/modules/mining/machine_unloading.dm b/code/modules/mining/machine_unloading.dm index 900e49a47525..92a81871ce83 100644 --- a/code/modules/mining/machine_unloading.dm +++ b/code/modules/mining/machine_unloading.dm @@ -8,24 +8,14 @@ density = TRUE input_dir = WEST output_dir = EAST - speed_process = TRUE + needs_item_input = TRUE + processing_flags = START_PROCESSING_MANUALLY -/obj/machinery/mineral/unloading_machine/process() - var/turf/T = get_step(src,input_dir) - if(T) - var/limit - for(var/obj/structure/ore_box/B in T) - for (var/obj/item/stack/ore/O in B) - B.contents -= O - unload_mineral(O) - limit++ - if (limit>=10) - return - CHECK_TICK - CHECK_TICK - for(var/obj/item/I in T) - unload_mineral(I) - limit++ - if (limit>=10) - return - CHECK_TICK +/obj/machinery/mineral/unloading_machine/pickup_item(datum/source, atom/movable/target, atom/oldLoc) + if(istype(target, /obj/structure/ore_box)) + var/obj/structure/ore_box/box = target + for(var/obj/item/stack/ore/O in box) + unload_mineral(O) + else if(istype(target, /obj/item/stack/ore)) + var/obj/item/stack/ore/O = target + unload_mineral(O) diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm index 4416a5fd34b5..579614928138 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -17,6 +17,7 @@ new /datum/data/mining_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 300), new /datum/data/mining_equipment("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100), new /datum/data/mining_equipment("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100), + new /datum/data/mining_equipment("Bubblegum Gum Packet", /obj/item/storage/box/gum/bubblegum, 100), new /datum/data/mining_equipment("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150), new /datum/data/mining_equipment("Soap", /obj/item/soap/nanotrasen, 200), new /datum/data/mining_equipment("Laser Pointer", /obj/item/laser_pointer, 300), @@ -98,7 +99,7 @@ if(!ui) var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/vending) assets.send(user) - ui = new(user, src, ui_key, "mining_vendor", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "MiningVendor", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/mineral/equipment_vendor/ui_static_data(mob/user) diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm index c01d5f2d7c67..40cbd8449a41 100644 --- a/code/modules/mining/mint.dm +++ b/code/modules/mining/mint.dm @@ -9,6 +9,7 @@ input_dir = EAST ui_x = 300 ui_y = 250 + needs_item_input = TRUE var/produced_coins = 0 // how many coins the machine has made in it's last cycle var/processing = FALSE @@ -34,16 +35,19 @@ chosen = SSmaterials.GetMaterialRef(chosen) -/obj/machinery/mineral/mint/process() - var/turf/T = get_step(src, input_dir) +/obj/machinery/mineral/mint/pickup_item(datum/source, atom/movable/target, atom/oldLoc) + if(!istype(target, /obj/item/stack)) + return + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + var/obj/item/stack/S = target - for(var/obj/item/stack/O in T) - var/inserted = materials.insert_item(O) - if(inserted) - qdel(O) + if(materials.insert_item(S)) + qdel(S) +/obj/machinery/mineral/mint/process() if(processing) + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/datum/material/M = chosen if(!M) @@ -71,30 +75,30 @@ if(!found_new) processing = FALSE else + end_processing() icon_state = "coinpress0" /obj/machinery/mineral/mint/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "mint", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "Mint", name, ui_x, ui_y, master_ui, state) ui.open() /obj/machinery/mineral/mint/ui_data() var/list/data = list() - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + data["inserted_materials"] = list() + data["chosen_material"] = null + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) for(var/datum/material/inserted_material in materials.materials) var/amount = materials.get_material_amount(inserted_material) - if(!amount) continue - data["inserted_materials"] += list(list( "material" = inserted_material.name, - "amount" = amount + "amount" = amount, )) - if(chosen == inserted_material) data["chosen_material"] = inserted_material.name @@ -104,23 +108,27 @@ return data; /obj/machinery/mineral/mint/ui_act(action, params, datum/tgui/ui) - . = ..() if(.) return - - switch(action) - if ("startpress") - if (!processing) - produced_coins = 0 - processing = TRUE - if ("stoppress") - processing = FALSE - if ("changematerial") - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - for(var/datum/material/mat in materials.materials) - if (params["material_name"] == mat.name) - chosen = mat + if(action == "startpress") + if (!processing) + if(produced_coins > 0) + log_econ("[produced_coins] coins were created by [src] in the last cycle.") + produced_coins = 0 + processing = TRUE + begin_processing() + return TRUE + if (action == "stoppress") + processing = FALSE + end_processing() + return TRUE + if (action == "changematerial") + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + for(var/datum/material/mat in materials.materials) + if (params["material_name"] == mat.name) + chosen = mat + return TRUE /obj/machinery/mineral/mint/proc/create_coins() var/turf/T = get_step(src,output_dir) @@ -134,3 +142,4 @@ B = new /obj/item/storage/bag/money(src) unload_mineral(B) O.forceMove(B) + SSblackbox.record_feedback("amount", "coins_minted", 1) diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm index 7463f8943da7..10961cfa6097 100644 --- a/code/modules/mining/satchel_ore_boxdm.dm +++ b/code/modules/mining/satchel_ore_boxdm.dm @@ -65,7 +65,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "ore_box", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, ui_key, "OreBox", name, ui_x, ui_y, master_ui, state) ui.open() /obj/structure/ore_box/ui_data() diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm index b3e74523099f..8caef04e3d4b 100644 --- a/code/modules/mob/dead/dead.dm +++ b/code/modules/mob/dead/dead.dm @@ -119,6 +119,8 @@ INITIALIZE_IMMEDIATE(/mob/dead) /mob/dead/Login() . = ..() + if(!. || !client) + return FALSE var/turf/T = get_turf(src) if (isturf(T)) update_z(T.z) diff --git a/code/modules/mob/dead/new_player/login.dm b/code/modules/mob/dead/new_player/login.dm index c93d9e05aa43..a040bf2ba0ec 100644 --- a/code/modules/mob/dead/new_player/login.dm +++ b/code/modules/mob/dead/new_player/login.dm @@ -1,4 +1,6 @@ /mob/dead/new_player/Login() + if(!client) + return if(CONFIG_GET(flag/use_exp_tracking)) client.set_exp_from_db() client.set_db_player_flags() @@ -7,7 +9,9 @@ mind.active = 1 mind.current = src - ..() + . = ..() + if(!. || !client) + return FALSE var/motd = global.config.motd if(motd) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 343b6bb54ad1..e8662e7a3bcd 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -58,20 +58,19 @@ if(!IsGuestKey(src.key)) if (SSdbcore.Connect()) - var/isadmin = 0 - if(src.client && src.client.holder) - isadmin = 1 - var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[sanitizeSQL(ckey)]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[sanitizeSQL(ckey)]\")") + var/isadmin = FALSE + if(client?.holder) + isadmin = TRUE + var/sql_ckey = sanitizeSQL(ckey) + var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = 0 AND")] Now() BETWEEN starttime AND endtime AND deleted = 0 AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = '[sql_ckey]' AND deleted = 0) AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = '[sql_ckey]' AND deleted = 0)") var/rs = REF(src) - if(query_get_new_polls.Execute()) - var/newpoll = 0 - if(query_get_new_polls.NextRow()) - newpoll = 1 - - if(newpoll) - output += "

    Show Player Polls (NEW!)

    " - else - output += "

    Show Player Polls

    " + if(!query_get_new_polls.Execute()) + qdel(query_get_new_polls) + return + if(query_get_new_polls.NextRow()) + output += "

    Show Player Polls (NEW!)

    " + else + output += "

    Show Player Polls

    " qdel(query_get_new_polls) if(QDELETED(src)) return @@ -164,9 +163,6 @@ AttemptLateSpawn(href_list["SelectedJob"]) return - if(!ready && href_list["preference"]) - if(client) - client.prefs.process_link(src, href_list) else if(!href_list["late_join"]) new_player_panel() @@ -174,85 +170,13 @@ handle_player_polling() return - if(href_list["pollid"]) - var/pollid = href_list["pollid"] - if(istext(pollid)) - pollid = text2num(pollid) - if(isnum(pollid) && ISINTEGER(pollid)) - src.poll_player(pollid) - return + if(href_list["viewpoll"]) + var/datum/poll_question/poll = locate(href_list["viewpoll"]) in GLOB.polls + poll_player(poll) - if(href_list["votepollid"] && href_list["votetype"]) - var/pollid = text2num(href_list["votepollid"]) - var/votetype = href_list["votetype"] - //lets take data from the user to decide what kind of poll this is, without validating it - //what could go wrong - switch(votetype) - if(POLLTYPE_OPTION) - var/optionid = text2num(href_list["voteoptionid"]) - if(vote_on_poll(pollid, optionid)) - to_chat(usr, "Vote successful.") - else - to_chat(usr, "Vote failed, please try again or contact an administrator.") - if(POLLTYPE_TEXT) - var/replytext = href_list["replytext"] - if(log_text_poll_reply(pollid, replytext)) - to_chat(usr, "Feedback logging successful.") - else - to_chat(usr, "Feedback logging failed, please try again or contact an administrator.") - if(POLLTYPE_RATING) - var/id_min = text2num(href_list["minid"]) - var/id_max = text2num(href_list["maxid"]) - - if( (id_max - id_min) > 100 ) //Basic exploit prevention - //(protip, this stops no exploits) - to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") - return - - for(var/optionid = id_min; optionid <= id_max; optionid++) - if(!isnull(href_list["o[optionid]"])) //Test if this optionid was replied to - var/rating - if(href_list["o[optionid]"] == "abstain") - rating = null - else - rating = text2num(href_list["o[optionid]"]) - if(!isnum(rating) || !ISINTEGER(rating)) - return - - if(!vote_on_numval_poll(pollid, optionid, rating)) - to_chat(usr, "Vote failed, please try again or contact an administrator.") - return - to_chat(usr, "Vote successful.") - if(POLLTYPE_MULTI) - var/id_min = text2num(href_list["minoptionid"]) - var/id_max = text2num(href_list["maxoptionid"]) - - if( (id_max - id_min) > 100 ) //Basic exploit prevention - to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") - return - - for(var/optionid = id_min; optionid <= id_max; optionid++) - if(!isnull(href_list["option_[optionid]"])) //Test if this optionid was selected - var/i = vote_on_multi_poll(pollid, optionid) - switch(i) - if(0) - continue - if(1) - to_chat(usr, "Vote failed, please try again or contact an administrator.") - return - if(2) - to_chat(usr, "Maximum replies reached.") - break - to_chat(usr, "Vote successful.") - if(POLLTYPE_IRV) - if (!href_list["IRVdata"]) - to_chat(src, "No ordering data found. Please try again or contact an administrator.") - return - var/list/votelist = splittext(href_list["IRVdata"], ",") - if (!vote_on_irv_poll(pollid, votelist)) - to_chat(src, "Vote failed, please try again or contact an administrator.") - return - to_chat(src, "Vote successful.") + if(href_list["votepollref"]) + var/datum/poll_question/poll = locate(href_list["votepollref"]) in GLOB.polls + vote_on_poll_handler(poll, href_list) //When you cop out of the round (NB: this HAS A SLEEP FOR PLAYER INPUT IN IT) /mob/dead/new_player/proc/make_me_an_observer() @@ -509,7 +433,7 @@ "Supply" = list(jobs = list(), titles = GLOB.supply_positions, color = "#ead4ae"), "Miscellaneous" = list(jobs = list(), titles = list(), color = "#ffffff", colBreak = TRUE), "Synthetic" = list(jobs = list(), titles = GLOB.nonhuman_positions, color = "#ccffcc"), - "Service" = list(jobs = list(), titles = GLOB.civilian_positions, color = "#cccccc"), + "Service" = list(jobs = list(), titles = GLOB.service_positions, color = "#cccccc"), "Medical" = list(jobs = list(), titles = GLOB.medical_positions, color = "#99ffe6", colBreak = TRUE), "Science" = list(jobs = list(), titles = GLOB.science_positions, color = "#e6b3e6"), "Security" = list(jobs = list(), titles = GLOB.security_positions, color = "#ff9999"), @@ -580,6 +504,7 @@ var/mob/living/carbon/human/H = new(loc) var/frn = CONFIG_GET(flag/force_random_names) + var/admin_anon_names = SSticker.anonymousnames if(!frn) frn = is_banned_from(ckey, "Appearance") if(QDELETED(src)) @@ -588,6 +513,10 @@ client.prefs.random_character() client.prefs.real_name = client.prefs.pref_species.random_name(gender,1) + if(admin_anon_names)//overrides random name because it achieves the same effect and is an admin enabled event tool + client.prefs.random_character() + client.prefs.real_name = anonymous_name(src) + var/is_antag if(mind in GLOB.pre_setup_antags) is_antag = TRUE @@ -616,6 +545,12 @@ qdel(src) /mob/dead/new_player/proc/ViewManifest() + if(!client) + return + if(world.time < client.crew_manifest_delay) + return + client.crew_manifest_delay = world.time + (1 SECONDS) + var/dat = "" dat += "

    Crew Manifest

    " dat += GLOB.data_core.get_manifest_html() diff --git a/code/modules/mob/dead/new_player/poll.dm b/code/modules/mob/dead/new_player/poll.dm index c729fb7fc1e6..869ce4cad8fd 100644 --- a/code/modules/mob/dead/new_player/poll.dm +++ b/code/modules/mob/dead/new_player/poll.dm @@ -1,620 +1,510 @@ -/datum/polloption - var/optionid - var/optiontext - +/** + * Shows a list of currently running polls a player can vote/has voted on + * + */ /mob/dead/new_player/proc/handle_player_polling() - if(!SSdbcore.IsConnected()) - to_chat(usr, "Failed to establish database connection.") - return - var/datum/DBQuery/query_poll_get = SSdbcore.NewQuery("SELECT id, question FROM [format_table_name("poll_question")] WHERE Now() BETWEEN starttime AND endtime [(client.holder ? "" : "AND adminonly = false")]") - if(!query_poll_get.warn_execute()) - qdel(query_poll_get) - return - var/output = "
    Player polls
    " - var/i = 0 + var/list/output = list("
    Player polls
    ") var/rs = REF(src) - while(query_poll_get.NextRow()) - var/pollid = query_poll_get.item[1] - var/pollquestion = query_poll_get.item[2] - output += "" - i++ - qdel(query_poll_get) + for(var/p in GLOB.polls) + var/datum/poll_question/poll = p + if((poll.admin_only && !client.holder) || poll.future_poll) + continue + output += "" output += "
    [pollquestion]
    [poll.question]
    " - if(!QDELETED(src)) - src << browse(output,"window=playerpolllist;size=500x300") + src << browse(jointext(output, ""),"window=playerpolllist;size=500x300") -/mob/dead/new_player/proc/poll_player(pollid) - if(!pollid) - return - if (!SSdbcore.Connect()) - to_chat(usr, "Failed to establish database connection.") +/** + * Redirects a player to the correct poll window based on poll type. + * + */ +/mob/dead/new_player/proc/poll_player(datum/poll_question/poll) + if(!poll) return - var/datum/DBQuery/query_poll_get_details = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid]") - if(!query_poll_get_details.warn_execute()) - qdel(query_poll_get_details) + if(!SSdbcore.Connect()) + to_chat(src, "Failed to establish database connection.") return - var/pollstarttime = "" - var/pollendtime = "" - var/pollquestion = "" - var/polltype = "" - var/multiplechoiceoptions = 0 - if(query_poll_get_details.NextRow()) - pollstarttime = query_poll_get_details.item[1] - pollendtime = query_poll_get_details.item[2] - pollquestion = query_poll_get_details.item[3] - polltype = query_poll_get_details.item[4] - multiplechoiceoptions = text2num(query_poll_get_details.item[5]) - qdel(query_poll_get_details) - switch(polltype) + switch(poll.poll_type) if(POLLTYPE_OPTION) - var/datum/DBQuery/query_option_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - if(!query_option_get_votes.warn_execute()) - qdel(query_option_get_votes) - return - var/votedoptionid = 0 - if(query_option_get_votes.NextRow()) - votedoptionid = text2num(query_option_get_votes.item[1]) - qdel(query_option_get_votes) - var/list/datum/polloption/options = list() - var/datum/DBQuery/query_option_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - if(!query_option_options.warn_execute()) - qdel(query_option_options) - return - while(query_option_options.NextRow()) - var/datum/polloption/PO = new() - PO.optionid = text2num(query_option_options.item[1]) - PO.optiontext = query_option_options.item[2] - options += PO - qdel(query_option_options) - var/output = "
    Player poll
    " - output += "Question: [pollquestion]
    " - output += "Poll runs from [pollstarttime] until [pollendtime]

    " - if(!votedoptionid) - output += "" - output += "" - output += "" - output += "" - output += "
    " - for(var/datum/polloption/O in options) - if(O.optionid && O.optiontext) - if(votedoptionid) - if(votedoptionid == O.optionid) - output += "[O.optiontext]
    " - else - output += "[O.optiontext]
    " - else - output += "[O.optiontext]
    " - output += "
    " - if(!votedoptionid) - output += "

    " - output += "" - output += "

    " - src << browse(null ,"window=playerpolllist") - src << browse(output,"window=playerpoll;size=500x250") + poll_player_option(poll) if(POLLTYPE_TEXT) - var/datum/DBQuery/query_text_get_votes = SSdbcore.NewQuery("SELECT replytext FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - if(!query_text_get_votes.warn_execute()) - qdel(query_text_get_votes) - return - var/vote_text = "" - if(query_text_get_votes.NextRow()) - vote_text = query_text_get_votes.item[1] - qdel(query_text_get_votes) - var/output = "
    Player poll
    " - output += "Question: [pollquestion]
    " - output += "Feedback gathering runs from [pollstarttime] until [pollendtime]

    " - output += "

    " - output += "" - output += "" - output += "" - output += "Please provide feedback below. You can use any letters of the English alphabet, numbers and the symbols: . , ! ? : ; -
    " - output += "" - output += "

    " - output += "
    " - output += "" - output += "" - output += "" - output += "" - output += "
    " - - src << browse(null ,"window=playerpolllist") - src << browse(output,"window=playerpoll;size=500x500") + poll_player_text(poll) if(POLLTYPE_RATING) - var/datum/DBQuery/query_rating_get_votes = SSdbcore.NewQuery("SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, [format_table_name("poll_vote")] v WHERE o.pollid = [pollid] AND v.ckey = '[ckey]' AND o.id = v.optionid") - if(!query_rating_get_votes.warn_execute()) - qdel(query_rating_get_votes) - return - var/output = "
    Player poll
    " - output += "Question: [pollquestion]
    " - output += "Poll runs from [pollstarttime] until [pollendtime]

    " - var/rating - while(query_rating_get_votes.NextRow()) - var/optiontext = query_rating_get_votes.item[1] - rating = query_rating_get_votes.item[2] - output += "
    [optiontext] - [rating]" - qdel(query_rating_get_votes) - if(!rating) - output += "

    " - output += "" - output += "" - output += "" - var/minid = 999999 - var/maxid = 0 - var/datum/DBQuery/query_rating_options = SSdbcore.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - if(!query_rating_options.warn_execute()) - qdel(query_rating_options) - return - while(query_rating_options.NextRow()) - var/optionid = text2num(query_rating_options.item[1]) - var/optiontext = query_rating_options.item[2] - var/minvalue = text2num(query_rating_options.item[3]) - var/maxvalue = text2num(query_rating_options.item[4]) - var/descmin = query_rating_options.item[5] - var/descmid = query_rating_options.item[6] - var/descmax = query_rating_options.item[7] - if(optionid < minid) - minid = optionid - if(optionid > maxid) - maxid = optionid - var/midvalue = round( (maxvalue + minvalue) / 2) - output += "
    [optiontext]: " - qdel(query_rating_options) - output += "" - output += "" - output += "

    " - if(!QDELETED(src)) - src << browse(null ,"window=playerpolllist") - src << browse(output,"window=playerpoll;size=500x500") + poll_player_rating(poll) if(POLLTYPE_MULTI) - var/datum/DBQuery/query_multi_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - if(!query_multi_get_votes.warn_execute()) - qdel(query_multi_get_votes) - return - var/list/votedfor = list() - while(query_multi_get_votes.NextRow()) - votedfor.Add(text2num(query_multi_get_votes.item[1])) - qdel(query_multi_get_votes) - var/list/datum/polloption/options = list() - var/maxoptionid = 0 - var/minoptionid = 0 - var/datum/DBQuery/query_multi_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - if(!query_multi_options.warn_execute()) - qdel(query_multi_options) - return - while(query_multi_options.NextRow()) - var/datum/polloption/PO = new() - PO.optionid = text2num(query_multi_options.item[1]) - PO.optiontext = query_multi_options.item[2] - if(PO.optionid > maxoptionid) - maxoptionid = PO.optionid - if(PO.optionid < minoptionid || !minoptionid) - minoptionid = PO.optionid - options += PO - qdel(query_multi_options) - var/output = "
    Player poll
    " - output += "Question: [pollquestion]
    You can select up to [multiplechoiceoptions] options. If you select more, the first [multiplechoiceoptions] will be saved.
    " - output += "Poll runs from [pollstarttime] until [pollendtime]

    " - if(!votedfor.len) - output += "

    " - output += "" - output += "" - output += "" - output += "" - output += "" - output += "
    " - for(var/datum/polloption/O in options) - if(O.optionid && O.optiontext) - if(votedfor.len) - if(O.optionid in votedfor) - output += "[O.optiontext]
    " - else - output += "[O.optiontext]
    " - else - output += "[O.optiontext]
    " - output += "
    " - if(!votedfor.len) - output += "

    " - output += "
    " - src << browse(null ,"window=playerpolllist") - src << browse(output,"window=playerpoll;size=500x250") + poll_player_multi(poll) if(POLLTYPE_IRV) - var/datum/asset/irv_assets = get_asset_datum(/datum/asset/group/IRV) - irv_assets.send(src) - - var/datum/DBQuery/query_irv_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - if(!query_irv_get_votes.warn_execute()) - qdel(query_irv_get_votes) - return - - var/list/votedfor = list() - while(query_irv_get_votes.NextRow()) - votedfor.Add(text2num(query_irv_get_votes.item[1])) - qdel(query_irv_get_votes) + poll_player_irv(poll) - var/list/datum/polloption/options = list() +/** + * Shows voting window for an option type poll, listing its options and relevant details. + * + * If already voted on, the option a player voted for is pre-selected. + * + */ +/mob/dead/new_player/proc/poll_player_option(datum/poll_question/poll) + var/datum/DBQuery/query_option_get_voted = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [sanitizeSQL(poll.poll_id)] AND ckey = '[sanitizeSQL(ckey)]' AND deleted = 0") + if(!query_option_get_voted.warn_execute()) + qdel(query_option_get_voted) + return + var/voted_option_id = 0 + if(query_option_get_voted.NextRow()) + voted_option_id = text2num(query_option_get_voted.item[1]) + qdel(query_option_get_voted) + var/list/output = list("
    Player poll
    Question: [poll.question]
    ") + if(poll.subtitle) + output += "[poll.subtitle]
    " + output += "Poll runs from [poll.start_datetime] until [poll.end_datetime]
    " + if(poll.allow_revoting) + output += "Revoting is enabled." + if(!voted_option_id || poll.allow_revoting) + output += {"
    + + + "} + output += "` tag) and with sane default styles -(e.g. table width is 100% by default). - -Example: - -```jsx -
    " + for(var/o in poll.options) + var/datum/poll_option/option = o + output += "
    - - - Hello world! - - - Label - - -
    -``` - -Props: - -- See inherited props: [Box](#box) -- `collapsing: boolean` - Collapses table to the smallest possible size. - -### `Table.Row` - -A straight forward mapping to `` element. - -Props: - -- See inherited props: [Box](#box) - -### `Table.Cell` - -A straight forward mapping to `` element. - -Props: - -- See inherited props: [Box](#box) -- `collapsing: boolean` - Collapses table cell to the smallest possible size, -and stops any text inside from wrapping. - -### `Tabs` - -Tabs make it easy to explore and switch between different views. - -Here is an example of how you would construct a simple tabbed view: - -```jsx - - - Content for Item one. - - - Content for Item two. - - -``` - -This is a rather simple example. In the real world, you might be -constructing very complex tabbed views which can tax UI performance. -This is because your tabs are being rendered regardless of their -visibility status! - -There is a simple fix however. Tabs accept functions as children, which -will be called to retrieve content only when the tab is visible: - -```jsx - - - {() => ( - - Content for Item one. - - )} - - - {() => ( - - Content for Item two. - - )} - - -``` - -You might not always need this, but it is highly recommended to always -use this method. Notice the `key` prop on tabs - it uniquely identifies -the tab and is used for determining which tab is currently active. It can -be either explicitly provided as a `key` prop, or if omitted, it will be -implicitly derived from the tab's `label` prop. - -Props: - -- `vertical: boolean` - Use a vertical configuration, where tabs will appear -stacked on the left side of the container. -- `altSelection` - Whether the tab buttons select via standard select (color -change) or by adding a white indicator to the selected tab. -Intended for usage on interfaces where tab color has relevance. -- `children: Tab[]` - This component only accepts tabs as its children. - -### `Tabs.Tab` - -An individual tab element. Tabs function like buttons, so they inherit -a lot of `Button` props. - -Props: - -- See inherited props: [Button](#button) -- `key: string` - A unique identifier for the tab. -- `label: string` - Tab label. -- `icon: string` - Tab icon. -- `content/children: any` - Content to render inside the tab. -- `onClick: function` - Called when element is clicked. - -### `Tooltip` - -A boxy tooltip from tgui 1. It is very hacky in its current state, and -requires setting `position: relative` on the container. - -Please note, that [Button](#button) component has a `tooltip` prop, and -it is recommended to use that prop instead. - -Usage: - -```jsx - - Sample text. - - -``` +All code is licensed with the parent license of *tgstation*, **AGPL-3.0**. -Props: +See the main [README](../README.md) for more details. -- `position: string` - Tooltip position. -- `content/children: string` - Content of the tooltip. Must be a plain string. -Fragments or other elements are **not** supported. +The Authors retain all copyright to their respective work here submitted. diff --git a/tgui/bin/tgui b/tgui/bin/tgui index 401d67b62270..eb1f200b31ea 100755 --- a/tgui/bin/tgui +++ b/tgui/bin/tgui @@ -62,6 +62,19 @@ task-clean() { rm -f **/package-lock.json } +## Validates current build against the build stored in git +task-validate-build() { + cd "${base_dir}" + local diff + diff="$(git diff packages/tgui/public/tgui.bundle.*)" + if [[ -n ${diff} ]]; then + echo "Error: our build differs from the build committed into git." + echo "Please rebuild tgui." + exit 1 + fi + echo "tgui: build is ok" +} + ## Installs merge drivers and git hooks task-install-git-hooks() { cd "${base_dir}" @@ -110,6 +123,7 @@ if [[ ${1} == "--ci" ]]; then task-install task-eslint task-webpack --mode=production + task-validate-build exit 0 fi diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md new file mode 100644 index 000000000000..b853fee4de37 --- /dev/null +++ b/tgui/docs/component-reference.md @@ -0,0 +1,1000 @@ +# Component Reference + +> Notice: This documentation might be out of date, so always check the source +> code to see the most up-to-date information. + + + +- [General Concepts](#general-concepts) +- [`tgui/components`](#tguicomponents) + - [`AnimatedNumber`](#animatednumber) + - [`BlockQuote`](#blockquote) + - [`Box`](#box) + - [`Button`](#button) + - [`Button.Checkbox`](#buttoncheckbox) + - [`Button.Confirm`](#buttonconfirm) + - [`Button.Input`](#buttoninput) + - [`ByondUi`](#byondui) + - [`Collapsible`](#collapsible) + - [`ColorBox`](#colorbox) + - [`Dimmer`](#dimmer) + - [`Divider`](#divider) + - [`Dropdown`](#dropdown) + - [`Flex`](#flex) + - [`Flex.Item`](#flexitem) + - [`Grid`](#grid) + - [`Grid.Column`](#gridcolumn) + - [`Icon`](#icon) + - [`Input`](#input) + - [`Knob`](#knob) + - [`LabeledControls`](#labeledcontrols) + - [`LabeledControls.Item`](#labeledcontrolsitem) + - [`LabeledList`](#labeledlist) + - [`LabeledList.Item`](#labeledlistitem) + - [`LabeledList.Divider`](#labeledlistdivider) + - [`Modal`](#modal) + - [`NoticeBox`](#noticebox) + - [`NumberInput`](#numberinput) + - [`ProgressBar`](#progressbar) + - [`Section`](#section) + - [`Slider`](#slider) + - [`Table`](#table) + - [`Table.Row`](#tablerow) + - [`Table.Cell`](#tablecell) + - [`Tabs`](#tabs) + - [`Tabs.Tab`](#tabstab) + - [`Tooltip`](#tooltip) +- [`tgui/layouts`](#tguilayouts) + - [`Window`](#window) + - [`Window.Content`](#windowcontent) + +## General Concepts + +These are the components which you can use for interface construction. +If you have trouble finding the exact prop you need on a component, +please note, that most of these components inherit from other basic +components, such as [Box](#box). This component in particular provides a lot +of styling options for all components, e.g. `color` and `opacity`, thus +it is used a lot in this framework. + +**Event handlers.** +Event handlers are callbacks that you can attack to various element to +listen for browser events. Inferno supports camelcase (`onClick`) and +lowercase (`onclick`) event names. + +- Camel case names are what's called *synthetic* events, and are the +**preferred way** of handling events in React, for efficiency and +performance reasons. Please read +[Inferno Event Handling](https://infernojs.org/docs/guides/event-handling) +to understand what this is about. +- Lower case names are native browser events and should be used sparingly, +for example when you need an explicit IE8 support. **DO NOT** use +lowercase event handlers unless you really know what you are doing. +- [Button](#button) component does not support the lowercase `onclick` event. +Use the camel case `onClick` instead. + +## `tgui/components` + +### `AnimatedNumber` + +This component provides animations for numeric values. + +**Props:** + +- `value: number` - Value to animate. +- `initial: number` - Initial value to use in animation when element +first appears. If you set initial to `0` for example, number will always +animate starting from `0`, and if omitted, it will not play an initial +animation. +- `format: value => value` - Output formatter. + - Example: `value => Math.round(value)`. +- `children: (formattedValue, rawValue) => any` - Pull the animated number to +animate more complex things deeper in the DOM tree. + - Example: `(_, value) => ` + +### `BlockQuote` + +Just a block quote, just like this example in markdown: + +> Here's an example of a block quote. + +**Props:** + +- See inherited props: [Box](#box) + +### `Box` + +The Box component serves as a wrapper component for most of the CSS utility +needs. It creates a new DOM element, a `
    ` by default that can be changed +with the `as` property. Let's say you want to use a `` instead: + +```jsx + +
    ); }; diff --git a/tgui/packages/tgui/components/Dimmer.js b/tgui/packages/tgui/components/Dimmer.js index 9d3ead05493c..f38d0a026a63 100644 --- a/tgui/packages/tgui/components/Dimmer.js +++ b/tgui/packages/tgui/components/Dimmer.js @@ -1,19 +1,18 @@ +import { classes } from 'common/react'; import { Box } from './Box'; export const Dimmer = props => { - const { style, ...rest } = props; + const { className, children, ...rest } = props; return ( + className={classes([ + 'Dimmer', + ...className, + ])} + {...rest}> +
    + {children} +
    +
    ); }; diff --git a/tgui/packages/tgui/components/Divider.js b/tgui/packages/tgui/components/Divider.js new file mode 100644 index 000000000000..5c807c290b8e --- /dev/null +++ b/tgui/packages/tgui/components/Divider.js @@ -0,0 +1,18 @@ +import { classes } from 'common/react'; + +export const Divider = props => { + const { + vertical, + hidden, + } = props; + return ( +